From aa94c30dc357a19d01b4f389c801090b089fc59b Mon Sep 17 00:00:00 2001 From: refraction Date: Fri, 6 Feb 2009 20:04:37 +0000 Subject: [PATCH] Should be the last of the trunk, gotta finish the branches/tags, but thats the main bulk transferred. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@426 96395faa-99c1-11dd-bbfe-3dabce05a288 --- 3rdparty/SoundTouch/3dnow_win.cpp | 350 + 3rdparty/SoundTouch/AAFilter.cpp | 184 + 3rdparty/SoundTouch/AAFilter.h | 91 + 3rdparty/SoundTouch/BPMDetect.h | 159 + 3rdparty/SoundTouch/FIFOSampleBuffer.cpp | 252 + 3rdparty/SoundTouch/FIFOSampleBuffer.h | 174 + 3rdparty/SoundTouch/FIFOSamplePipe.h | 217 + 3rdparty/SoundTouch/FIRFilter.cpp | 272 + 3rdparty/SoundTouch/FIRFilter.h | 163 + 3rdparty/SoundTouch/Makefile.am | 46 + 3rdparty/SoundTouch/RateTransposer.cpp | 626 + 3rdparty/SoundTouch/RateTransposer.h | 162 + 3rdparty/SoundTouch/STTypes.h | 202 + 3rdparty/SoundTouch/SoundTouch.cpp | 474 + 3rdparty/SoundTouch/SoundTouch.h | 252 + 3rdparty/SoundTouch/TDStretch.cpp | 940 + 3rdparty/SoundTouch/TDStretch.h | 236 + 3rdparty/SoundTouch/WavFile.cpp | 714 + 3rdparty/SoundTouch/WavFile.h | 253 + 3rdparty/SoundTouch/cpu_detect.h | 62 + 3rdparty/SoundTouch/cpu_detect_x86_gcc.cpp | 138 + 3rdparty/SoundTouch/cpu_detect_x86_win.cpp | 126 + 3rdparty/SoundTouch/mmx_optimized.cpp | 305 + 3rdparty/SoundTouch/sse_optimized.cpp | 484 + 3rdparty/bzip2/LICENSE | 43 + 3rdparty/bzip2/README | 205 + 3rdparty/bzip2/blocksort.c | 1094 + 3rdparty/bzip2/bzlib.c | 1571 + 3rdparty/bzip2/bzlib.h | 282 + 3rdparty/bzip2/bzlib_private.h | 503 + 3rdparty/bzip2/compress.c | 672 + 3rdparty/bzip2/crctable.c | 104 + 3rdparty/bzip2/decompress.c | 626 + 3rdparty/bzip2/huffman.c | 205 + 3rdparty/bzip2/randtable.c | 84 + 3rdparty/zlib/ChangeLog | 855 + 3rdparty/zlib/Makefile.am | 6 + 3rdparty/zlib/README | 125 + 3rdparty/zlib/adler32.c | 149 + 3rdparty/zlib/compress.c | 79 + 3rdparty/zlib/crc32.c | 423 + 3rdparty/zlib/crc32.h | 441 + 3rdparty/zlib/deflate.c | 1736 + 3rdparty/zlib/deflate.h | 331 + 3rdparty/zlib/gzio.c | 1026 + 3rdparty/zlib/infback.c | 623 + 3rdparty/zlib/inffast.c | 318 + 3rdparty/zlib/inffast.h | 11 + 3rdparty/zlib/inffixed.h | 94 + 3rdparty/zlib/inflate.c | 1368 + 3rdparty/zlib/inflate.h | 115 + 3rdparty/zlib/inftrees.c | 329 + 3rdparty/zlib/inftrees.h | 55 + 3rdparty/zlib/trees.c | 1219 + 3rdparty/zlib/trees.h | 128 + 3rdparty/zlib/uncompr.c | 61 + 3rdparty/zlib/zconf.h | 332 + 3rdparty/zlib/zlib.h | 1357 + 3rdparty/zlib/zutil.c | 318 + 3rdparty/zlib/zutil.h | 269 + bin/.pixmaps/Thumbs.db | Bin 0 -> 5632 bytes bin/.pixmaps/pcsxAbout.bmp | Bin 0 -> 251190 bytes bin/Langs/ar_AR/LC_MESSAGES/pcsx2.mo | Bin 0 -> 6197 bytes bin/Langs/bg_BG/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11915 bytes bin/Langs/cz_CZ/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11085 bytes bin/Langs/de_DE/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11840 bytes bin/Langs/du_DU/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11583 bytes bin/Langs/el_EL/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11225 bytes bin/Langs/es_ES/LC_MESSAGES/pcsx2.mo | Bin 0 -> 10632 bytes bin/Langs/fr_FR/LC_MESSAGES/pcsx2.mo | Bin 0 -> 6176 bytes bin/Langs/hb_HB/LC_MESSAGES/pcsx2.mo | Bin 0 -> 6302 bytes bin/Langs/it_IT/LC_MESSAGES/pcsx2.mo | Bin 0 -> 5411 bytes bin/Langs/ja_JA/LC_MESSAGES/pcsx2.mo | Bin 0 -> 15213 bytes bin/Langs/pe_PE/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11165 bytes bin/Langs/pl_PL/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11183 bytes bin/Langs/po_BR/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11024 bytes bin/Langs/po_PO/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11385 bytes bin/Langs/ro_RO/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11443 bytes bin/Langs/ru_RU/LC_MESSAGES/pcsx2.mo | Bin 0 -> 11125 bytes bin/Langs/sh_SH/LC_MESSAGES/pcsx2.mo | Bin 0 -> 9723 bytes bin/Langs/sw_SW/LC_MESSAGES/other.po | 868 + bin/Langs/sw_SW/LC_MESSAGES/pcsx2.mo | Bin 0 -> 10929 bytes bin/Langs/tc_TC/LC_MESSAGES/Pcsx2.mo | Bin 0 -> 9723 bytes bin/Langs/tr_TR/LC_MESSAGES/pcsx2.mo | Bin 0 -> 10871 bytes bin/compat_list/compat_list.html | 27266 ++++++++++++++++ bin/compat_list/h_console.jpg | Bin 0 -> 544 bytes bin/compat_list/h_region.jpg | Bin 0 -> 486 bytes bin/compat_list/h_serial.jpg | Bin 0 -> 477 bytes bin/compat_list/h_status.jpg | Bin 0 -> 518 bytes bin/compat_list/h_title.jpg | Bin 0 -> 455 bytes bin/compat_list/h_version.jpg | Bin 0 -> 511 bytes bin/compat_list/sq_ingame.jpg | Bin 0 -> 304 bytes bin/compat_list/sq_intro.jpg | Bin 0 -> 305 bytes bin/compat_list/sq_menus.jpg | Bin 0 -> 305 bytes bin/compat_list/sq_nothing.jpg | Bin 0 -> 305 bytes bin/compat_list/sq_playable.jpg | Bin 0 -> 304 bytes bin/patches/013E349D.pnach | 4 + bin/patches/015314A2.pnach | 4 + bin/patches/034836F8.pnach | 13 + bin/patches/0442B1BD.pnach | 4 + bin/patches/05177ECE.pnach | 4 + bin/patches/0518D274.pnach | 4 + bin/patches/0518D275.pnach | 4 + bin/patches/06DE61E0.pnach | 4 + bin/patches/07AD79C9.pnach | 4 + bin/patches/0838D766.pnach | 6 + bin/patches/086273D2.pnach | 4 + bin/patches/08901101.pnach | 13 + bin/patches/08FB9DCF.pnach | 9 + bin/patches/0999F9DB.pnach | 13 + bin/patches/09D35D3F.pnach | 4 + bin/patches/0A342A88.pnach | 4 + bin/patches/0B359BBF.pnach | 5 + bin/patches/0C370E94.pnach | 4 + bin/patches/0F6B6315.pnach | 5 + bin/patches/1025D50A.pnach | 7 + bin/patches/116154AD.pnach | 8 + bin/patches/1248FE3A.pnach | 6 + bin/patches/129C8600.pnach | 8 + bin/patches/12AFF4D4.pnach | 23 + bin/patches/1468C474.pnach | 4 + bin/patches/1510E1D1.pnach | 7 + bin/patches/1629D655.pnach | 20 + bin/patches/163F0461.pnach | 18 + bin/patches/1738A5B0.pnach | 13 + bin/patches/19D145D7.pnach | 12 + bin/patches/1CAC8A56.pnach | 4 + bin/patches/1D2818AF.pnach | 13 + bin/patches/200BC0E6.pnach | 4 + bin/patches/2088950A.pnach | 4 + bin/patches/21068223.pnach | 5 + bin/patches/2251E14D.pnach | 4 + bin/patches/22C36E63.pnach | 4 + bin/patches/25433CBD.pnach | 9 + bin/patches/27E54B37.pnach | 16 + bin/patches/280DAC56.pnach | 16 + bin/patches/295D2F96.pnach | 6 + bin/patches/29D80A23.pnach | 9 + bin/patches/2B2E1535.pnach | 5 + bin/patches/2C728173.pnach | 4 + bin/patches/2CFFFA50.pnach | 9 + bin/patches/2D919421.pnach | 15 + bin/patches/2F56CBC9.pnach | 4 + bin/patches/302797DF.pnach | 5 + bin/patches/304C115C.pnach | 4 + bin/patches/32629F36.pnach | 9 + bin/patches/333F1F59.pnach | 3 + bin/patches/337B927C.pnach | 4 + bin/patches/339A0B8C.pnach | 4 + bin/patches/33F7D21A.pnach | 14 + bin/patches/36FEEE3A.pnach | 19 + bin/patches/37BA81B1.pnach | 4 + bin/patches/37C07E96.pnach | 3 + bin/patches/38F29E28.pnach | 4 + bin/patches/3BD85DA4.pnach | 7 + bin/patches/3CFE3B37.pnach | 6 + bin/patches/3DB65A75.pnach | 5 + bin/patches/3E0A256D.pnach | 4 + bin/patches/3E68955A.pnach | 5 + bin/patches/3EAD47FE.pnach | 4 + bin/patches/3F0452DE.pnach | 4 + bin/patches/3FB69323.pnach | 8 + bin/patches/4043F228.pnach | 4 + bin/patches/41A3191C.pnach | 4 + bin/patches/4321A427.pnach | 5 + bin/patches/4334E17D.pnach | 4 + bin/patches/44A61C8F.pnach | 4 + bin/patches/4691F6F7.pnach | 4 + bin/patches/46A7ECA4.pnach | 4 + bin/patches/472C9E70.pnach | 6 + bin/patches/48FE0C71.pnach | 20 + bin/patches/49DA19CE.pnach | 4 + bin/patches/4B6DDB6B.pnach | 3 + bin/patches/4C0C821D.pnach | 4 + bin/patches/4C4D7072.pnach | 11 + bin/patches/4C9EE7DF.pnach | 20 + bin/patches/4D228733.pnach | 5 + bin/patches/4D6DBB75.pnach | 4 + bin/patches/4DAC50C2.pnach | 5 + bin/patches/4F3D3CF0.pnach | 5 + bin/patches/506644B3.pnach | 5 + bin/patches/512B5046.pnach | 3 + bin/patches/5162BCCA.pnach | 5 + bin/patches/51D8A6A9.pnach | 20 + bin/patches/51F91783.pnach | 4 + bin/patches/53A803AF.pnach | 4 + bin/patches/53DF159B.pnach | 16 + bin/patches/54A548B4.pnach | 6 + bin/patches/54AD76D7.pnach | 8 + bin/patches/54D6BEE3.pnach | 4 + bin/patches/561BE340.pnach | 4 + bin/patches/582EED0D.pnach | 8 + bin/patches/586EA828.pnach | 7 + bin/patches/58EE1AFA.pnach | 4 + bin/patches/5A7635C1.pnach | 4 + bin/patches/5BBC2F40.pnach | 20 + bin/patches/5BC8C9E8.pnach | 3 + bin/patches/5BE3F481.pnach | 4 + bin/patches/5CCA0737.pnach | 7 + bin/patches/5D67AE48.pnach | 4 + bin/patches/5DFBE144.pnach | 8 + bin/patches/5E115FB6.pnach | 3 + bin/patches/5EB127E7.pnach | 4 + bin/patches/5F2A0E36.pnach | 7 + bin/patches/60013EBD.pnach | 5 + bin/patches/60FA8C69.pnach | 4 + bin/patches/6175FE7D.pnach | 4 + bin/patches/624F11F1.pnach | 5 + bin/patches/625AF967.pnach | 4 + bin/patches/627B8252.pnach | 9 + bin/patches/62F6F886.pnach | 4 + bin/patches/63F6B523.pnach | 4 + bin/patches/68EAF48F.pnach | 8 + bin/patches/6926B199.pnach | 4 + bin/patches/692CBA8E.pnach | 4 + bin/patches/6A4EFE60.pnach | 4 + bin/patches/6ADBC24B.pnach | 6 + bin/patches/6BA2F6B9.pnach | 4 + bin/patches/6D70F0E0.pnach | 4 + bin/patches/6EA9DDA9.pnach | 4 + bin/patches/6FB69282.pnach | 3 + bin/patches/7098BE76.pnach | 6 + bin/patches/7130C553.pnach | 5 + bin/patches/71584BAC.pnach | 4 + bin/patches/722BBD62.pnach | 4 + bin/patches/7377BC6F.pnach | 4 + bin/patches/763D3BF9.pnach | 8 + bin/patches/77ECAAA0.pnach | 4 + bin/patches/78168525.pnach | 3 + bin/patches/789D6B71.pnach | 4 + bin/patches/78E20421.pnach | 7 + bin/patches/7ABDBB5E.pnach | 4 + bin/patches/7BA0128E.pnach | 3 + bin/patches/7CD1CDCD.pnach | 12 + bin/patches/7D8F539A.pnach | 7 + bin/patches/7EBEEBBD.pnach | 4 + bin/patches/7F6319C7.pnach | 4 + bin/patches/7FD7A1B9.pnach | 8 + bin/patches/83261085.pnach | 15 + bin/patches/83D0CE43.pnach | 7 + bin/patches/844B58EE.pnach | 4 + bin/patches/848C6247.pnach | 3 + bin/patches/85AE91B3.pnach | 5 + bin/patches/85E92C92.pnach | 9 + bin/patches/8BC79F96.pnach | 9 + bin/patches/8BC95883.pnach | 4 + bin/patches/8BE3D7B2.pnach | 4 + bin/patches/8DC64680.pnach | 30 + bin/patches/8FDE8E16.pnach | 6 + bin/patches/901AAC09.pnach | 4 + bin/patches/93223BE4.pnach | 7 + bin/patches/934F9081.pnach | 4 + bin/patches/93F8A60B.pnach | 4 + bin/patches/941BB7D9.pnach | 29 + bin/patches/94C56923.pnach | 4 + bin/patches/950241D3.pnach | 4 + bin/patches/951555A0.pnach | 4 + bin/patches/95BB1901.pnach | 3 + bin/patches/96B2F56D.pnach | 7 + bin/patches/96B76E56.pnach | 9 + bin/patches/96C20D6F.pnach | 4 + bin/patches/9AAC5309.pnach | 4 + bin/patches/9AAC530D.pnach | 6 + bin/patches/9AC65D6A.pnach | 4 + bin/patches/9B1EE9EB.pnach | 4 + bin/patches/9BE3F92D.pnach | 4 + bin/patches/9D6F46F0.pnach | 4 + bin/patches/A029B109.pnach | 12 + bin/patches/A1B752C7.pnach | 4 + bin/patches/A36CFF6C.pnach | 3 + bin/patches/A39517AB.pnach | 16 + bin/patches/A3ACF3C7.pnach | 8 + bin/patches/A3D63039.pnach | 2 + bin/patches/A4E2C043.pnach | 8 + bin/patches/A5BF36A8.pnach | 3 + bin/patches/A63C896C.pnach | 6 + bin/patches/A719D130.pnach | 4 + bin/patches/A74F99CD.pnach | 4 + bin/patches/A7A2F7C5.pnach | 16 + bin/patches/A8083AE6.pnach | 4 + bin/patches/A88ACA28.pnach | 9 + bin/patches/A8A76AAC.pnach | 4 + bin/patches/A929A697.pnach | 3 + bin/patches/A9759015.pnach | 9 + bin/patches/AA31B5BF.pnach | 4 + bin/patches/AC7E88D9.pnach | 18 + bin/patches/ACB1989A.pnach | 10 + bin/patches/AD9D2B54.pnach | 4 + bin/patches/ADAA1256.pnach | 8 + bin/patches/AE0E098F.pnach | 6 + bin/patches/AE3EAA05.pnach | 14 + bin/patches/AE9EB9A0.pnach | 4 + bin/patches/AEDAEE99.pnach | 3 + bin/patches/AFAC88EF.pnach | 14 + bin/patches/B0621C55.pnach | 4 + bin/patches/B0AE1898.pnach | 6 + bin/patches/B130E5BA.pnach | 3 + bin/patches/B1BE3E51.pnach | 6 + bin/patches/B234036E.pnach | 4 + bin/patches/B2BDE9F3.pnach | 6 + bin/patches/B338676A.pnach | 6 + bin/patches/B34DA141.pnach | 8 + bin/patches/B36EE21E.pnach | 5 + bin/patches/B590CE04.pnach | 5 + bin/patches/B82E4C4B.pnach | 4 + bin/patches/B884B6E9.pnach | 4 + bin/patches/B99379B7.pnach | 4 + bin/patches/BB3D833A.pnach | 4 + bin/patches/BD3DBCF9.pnach | 4 + bin/patches/BF5D9AEC.pnach | 4 + bin/patches/C04FB5FD.pnach | 4 + bin/patches/C0D6A139.pnach | 4 + bin/patches/C1625F14.pnach | 9 + bin/patches/C1767D64.pnach | 5 + bin/patches/C1B141D6.pnach | 7 + bin/patches/C220951A.pnach | 5 + bin/patches/C398F477.pnach | 4 + bin/patches/C3D28EB9.pnach | 4 + bin/patches/C4467D30.pnach | 4 + bin/patches/C488EC04.pnach | 8 + bin/patches/C4A60986.pnach | 4 + bin/patches/C502AD6E.pnach | 8 + bin/patches/C5DEFEA0.pnach | 7 + bin/patches/C9246E9C.pnach | 4 + bin/patches/C9C145BF.pnach | 10 + bin/patches/CA295E61.pnach | 4 + bin/patches/CA6243B9.pnach | 9 + bin/patches/CB4EBD11.pnach | 7 + bin/patches/CBBC2E7F.pnach | 15 + bin/patches/CC4B9CDE.pnach | 6 + bin/patches/CC6AA742.pnach | 4 + bin/patches/CDE7C999.pnach | 22 + bin/patches/CF11CD83.pnach | 6 + bin/patches/CFB873AD.pnach | 6 + bin/patches/D03BEF2A.pnach | 13 + bin/patches/D08648B6.pnach | 3 + bin/patches/D0E17D26.pnach | 4 + bin/patches/D1ACD489.pnach | 5 + bin/patches/D4781770.pnach | 6 + bin/patches/D48A92E1.pnach | 10 + bin/patches/D4FB6049.pnach | 3 + bin/patches/D79F697A.pnach | 4 + bin/patches/DA0535FD.pnach | 4 + bin/patches/DAC14B26.pnach | 7 + bin/patches/DB719F5C.pnach | 7 + bin/patches/DC85FC8F.pnach | 4 + bin/patches/DCC4EEEA.pnach | 10 + bin/patches/DDA2FA6A.pnach | 4 + bin/patches/DEFA4763.pnach | 5 + bin/patches/E0127F2D.pnach | 5 + bin/patches/E0426FC6.pnach | 8 + bin/patches/E0DADD1A.pnach | 5 + bin/patches/E138094A.pnach | 7 + bin/patches/E1C5F607.pnach | 6 + bin/patches/E1FD9A2D.pnach | 6 + bin/patches/E2F1DB6B.pnach | 6 + bin/patches/E4A275B2.pnach | 4 + bin/patches/E677B8F1.pnach | 7 + bin/patches/E7A35274.pnach | 4 + bin/patches/E906EA37.pnach | 7 + bin/patches/EAD76247.pnach | 19 + bin/patches/EADE437E.pnach | 4 + bin/patches/EDD7E0FF.pnach | 4 + bin/patches/EE838B5C.pnach | 7 + bin/patches/EEC3B310.pnach | 9 + bin/patches/EEE2F6A3.pnach | 4 + bin/patches/EF5B6AAD.pnach | 4 + bin/patches/EF9E43EF.pnach | 5 + bin/patches/F1370E83.pnach | 4 + bin/patches/F22CDDAF.pnach | 4 + bin/patches/F266B00B.pnach | 4 + bin/patches/F4992CC1.pnach | 4 + bin/patches/F52FB2BE.pnach | 5 + bin/patches/F56C7948.pnach | 4 + bin/patches/F5C7B45F.pnach | 4 + bin/patches/F6DC728D.pnach | 5 + bin/patches/F6F9A91D.pnach | 4 + bin/patches/F758234F.pnach | 7 + bin/patches/F9E575D0.pnach | 4 + bin/patches/FA7E3081.pnach | 7 + bin/patches/FAC64195.pnach | 3 + bin/patches/FB0E6D72.pnach | 6 + bin/patches/FC618D82.pnach | 8 + bin/patches/FEA030CB.pnach | 23 + bin/patches/FF920E90.pnach | 8 + bin/patches/default.xml | 0 bin/snaps/1.jpg | Bin 0 -> 71955 bytes bin/snaps/2.jpg | Bin 0 -> 68304 bytes bin/snaps/3.jpg | Bin 0 -> 51374 bytes bin/snaps/4.jpg | Bin 0 -> 92740 bytes build.sh | 41 + common/includes/PS2Edefs.h | 885 + common/includes/PS2Etypes.h | 219 + common/tsvnrev/svnrev_template.h | 18 + common/tsvnrev/svnrev_unknown.h | 23 + common/tsvnrev/updateRevision.cmd | 8 + fetch.sh | 16 + pcsx2/3rdparty/zlib/ChangeLog | 855 + pcsx2/3rdparty/zlib/Makefile.am | 6 + pcsx2/3rdparty/zlib/README | 125 + pcsx2/3rdparty/zlib/adler32.c | 149 + pcsx2/3rdparty/zlib/compress.c | 79 + pcsx2/3rdparty/zlib/crc32.c | 423 + pcsx2/3rdparty/zlib/crc32.h | 441 + pcsx2/3rdparty/zlib/deflate.c | 1736 + pcsx2/3rdparty/zlib/deflate.h | 331 + pcsx2/3rdparty/zlib/gzio.c | 1026 + pcsx2/3rdparty/zlib/infback.c | 623 + pcsx2/3rdparty/zlib/inffast.c | 318 + pcsx2/3rdparty/zlib/inffast.h | 11 + pcsx2/3rdparty/zlib/inffixed.h | 94 + pcsx2/3rdparty/zlib/inflate.c | 1368 + pcsx2/3rdparty/zlib/inflate.h | 115 + pcsx2/3rdparty/zlib/inftrees.c | 329 + pcsx2/3rdparty/zlib/inftrees.h | 55 + pcsx2/3rdparty/zlib/trees.c | 1219 + pcsx2/3rdparty/zlib/trees.h | 128 + pcsx2/3rdparty/zlib/uncompr.c | 61 + pcsx2/3rdparty/zlib/zconf.h | 332 + pcsx2/3rdparty/zlib/zlib.h | 1357 + pcsx2/3rdparty/zlib/zutil.c | 318 + pcsx2/3rdparty/zlib/zutil.h | 269 + pcsx2/AlignedMalloc.cpp | 72 + pcsx2/CDVD.cpp | 2171 ++ pcsx2/CDVD.h | 144 + pcsx2/CDVDiso.cpp | 818 + pcsx2/CDVDiso.h | 149 + pcsx2/CDVDisodrv.cpp | 254 + pcsx2/CDVDisodrv.h | 38 + pcsx2/CDVDlib.h | 171 + pcsx2/COP0.cpp | 357 + pcsx2/COP0.h | 28 + pcsx2/COP2.cpp | 89 + pcsx2/Cache.cpp | 400 + pcsx2/Cache.h | 48 + pcsx2/CdRom.cpp | 959 + pcsx2/CdRom.h | 93 + pcsx2/Common.h | 79 + pcsx2/Console.cpp | 179 + pcsx2/Counters.cpp | 863 + pcsx2/Counters.h | 148 + pcsx2/DebugTools/Debug.h | 197 + pcsx2/DebugTools/DisASM.h | 52 + pcsx2/DebugTools/DisR3000A.cpp | 322 + pcsx2/DebugTools/DisR3000asm.cpp | 363 + pcsx2/DebugTools/DisR5900.cpp | 1089 + pcsx2/DebugTools/DisR5900asm.cpp | 1192 + pcsx2/DebugTools/DisVU0Micro.cpp | 83 + pcsx2/DebugTools/DisVU1Micro.cpp | 122 + pcsx2/DebugTools/DisVUmicro.h | 209 + pcsx2/DebugTools/DisVUops.h | 197 + pcsx2/DebugTools/Makefile.am | 7 + pcsx2/Decode_XA.cpp | 322 + pcsx2/Decode_XA.h | 43 + pcsx2/Docs/ChangeLog.txt | 2851 ++ pcsx2/Docs/License.txt | 342 + pcsx2/Docs/PCSX2 FAQ 0.9.4.rtf | 172 + pcsx2/Docs/PS2Edefs.txt | 443 + pcsx2/Docs/RemoteDebugging.txt | 105 + pcsx2/Docs/Translating.txt | 41 + pcsx2/Docs/devblog.txt | 273 + pcsx2/Docs/readme 0.9.4.txt | 146 + pcsx2/Docs/readme Playground.txt | 70 + pcsx2/Docs/specs.tex | 159 + pcsx2/EEregs.h | 40 + pcsx2/Elfheader.cpp | 662 + pcsx2/Elfheader.h | 34 + pcsx2/Exceptions.h | 303 + pcsx2/FPU.cpp | 388 + pcsx2/FPU2.cpp | 42 + pcsx2/FiFo.cpp | 152 + pcsx2/GS.cpp | 793 + pcsx2/GS.h | 334 + pcsx2/Gif.cpp | 533 + pcsx2/Hw.cpp | 1818 ++ pcsx2/Hw.h | 421 + pcsx2/IPU/IPU.cpp | 1743 + pcsx2/IPU/IPU.h | 265 + pcsx2/IPU/Makefile.am | 10 + pcsx2/IPU/acoroutine.S | 181 + pcsx2/IPU/acoroutine.asm | 140 + pcsx2/IPU/coroutine.cpp | 158 + pcsx2/IPU/coroutine.h | 37 + pcsx2/IPU/idct_mmx.obj | Bin 0 -> 11510 bytes pcsx2/IPU/mpeg2lib/Idct.cpp | 307 + pcsx2/IPU/mpeg2lib/Makefile.am | 4 + pcsx2/IPU/mpeg2lib/Mpeg.cpp | 1368 + pcsx2/IPU/mpeg2lib/Mpeg.h | 203 + pcsx2/IPU/mpeg2lib/Vlc.h | 446 + pcsx2/IPU/yuv2rgb.asm | 242 + pcsx2/IPU/yuv2rgb.cpp | 514 + pcsx2/IPU/yuv2rgb.h | 57 + pcsx2/Interpreter.cpp | 284 + pcsx2/IopBios.cpp | 292 + pcsx2/IopBios.h | 41 + pcsx2/IopBios2.h | 94 + pcsx2/IopCounters.cpp | 726 + pcsx2/IopCounters.h | 62 + pcsx2/IopDma.cpp | 382 + pcsx2/IopDma.h | 48 + pcsx2/IopHw.cpp | 1413 + pcsx2/IopHw.h | 130 + pcsx2/IopMem.cpp | 783 + pcsx2/IopMem.h | 103 + pcsx2/IopSio2.cpp | 270 + pcsx2/IopSio2.h | 99 + pcsx2/Linux/.pixmaps/pcsxAbout.xpm | 334 + pcsx2/Linux/ConfigDlg.cpp | 598 + pcsx2/Linux/ConfigDlg.h | 72 + pcsx2/Linux/DebugDlg.cpp | 397 + pcsx2/Linux/DebugDlg.h | 57 + pcsx2/Linux/GtkGui.cpp | 987 + pcsx2/Linux/GtkGui.h | 83 + pcsx2/Linux/Linux.h | 218 + pcsx2/Linux/LnxConsole.cpp | 145 + pcsx2/Linux/LnxMain.cpp | 501 + pcsx2/Linux/LnxMain.h | 53 + pcsx2/Linux/LnxThreads.cpp | 200 + pcsx2/Linux/Makefile.am | 17 + pcsx2/Linux/Pref.cpp | 174 + pcsx2/Linux/buildgui.sh | 27 + pcsx2/Linux/callbacks.h | 387 + pcsx2/Linux/interface.c | 4016 +++ pcsx2/Linux/interface.h | 22 + pcsx2/Linux/memzero.h | 182 + pcsx2/Linux/pcsx2.glade | 8021 +++++ pcsx2/Linux/support.c | 144 + pcsx2/Linux/support.h | 69 + pcsx2/MMI.cpp | 1566 + pcsx2/MTGS.cpp | 1049 + pcsx2/Makefile.am | 23 + pcsx2/Mdec.cpp | 420 + pcsx2/Mdec.h | 110 + pcsx2/MemcpyFast.h | 59 + pcsx2/Memory.cpp | 904 + pcsx2/Memory.h | 303 + pcsx2/MemoryVM.cpp | 2140 ++ pcsx2/Misc.cpp | 823 + pcsx2/Misc.h | 220 + pcsx2/Patch.cpp | 642 + pcsx2/Patch.h | 134 + pcsx2/PathUtils.cpp | 145 + pcsx2/Paths.h | 33 + pcsx2/Plugins.cpp | 884 + pcsx2/Plugins.h | 45 + pcsx2/PrecompiledHeader.cpp | 1 + pcsx2/PrecompiledHeader.h | 118 + pcsx2/PsxCommon.h | 53 + pcsx2/R3000A.cpp | 283 + pcsx2/R3000A.h | 217 + pcsx2/R3000AInterpreter.cpp | 445 + pcsx2/R3000AOpcodeTables.cpp | 383 + pcsx2/R5900.cpp | 693 + pcsx2/R5900.h | 276 + pcsx2/R5900OpcodeImpl.cpp | 816 + pcsx2/R5900OpcodeTables.cpp | 696 + pcsx2/R5900OpcodeTables.h | 879 + pcsx2/RDebug/Makefile.am | 7 + pcsx2/RDebug/deci2.cpp | 28 + pcsx2/RDebug/deci2.h | 71 + pcsx2/RDebug/deci2.txt | 27 + pcsx2/RDebug/deci2_dbgp.cpp | 434 + pcsx2/RDebug/deci2_dbgp.h | 28 + pcsx2/RDebug/deci2_dcmp.cpp | 85 + pcsx2/RDebug/deci2_dcmp.h | 28 + pcsx2/RDebug/deci2_drfp.cpp | 48 + pcsx2/RDebug/deci2_drfp.h | 27 + pcsx2/RDebug/deci2_iloadp.cpp | 108 + pcsx2/RDebug/deci2_iloadp.h | 27 + pcsx2/RDebug/deci2_netmp.cpp | 136 + pcsx2/RDebug/deci2_netmp.h | 27 + pcsx2/RDebug/deci2_ttyp.cpp | 43 + pcsx2/RDebug/deci2_ttyp.h | 28 + pcsx2/RDebug/iloadp.txt | 32 + pcsx2/SPR.cpp | 388 + pcsx2/SPR.h | 29 + pcsx2/SafeArray.h | 254 + pcsx2/SamplProf.h | 32 + pcsx2/SaveState.cpp | 370 + pcsx2/SaveState.h | 205 + pcsx2/Sif.cpp | 608 + pcsx2/Sif.h | 51 + pcsx2/Sifcmd.h | 193 + pcsx2/Sio.cpp | 661 + pcsx2/Sio.h | 134 + pcsx2/SourceLog.cpp | 177 + pcsx2/Stats.cpp | 72 + pcsx2/Stats.h | 44 + pcsx2/StringUtils.h | 75 + pcsx2/System.cpp | 333 + pcsx2/System.h | 213 + pcsx2/ThreadTools.cpp | 194 + pcsx2/Threading.h | 136 + pcsx2/VU.h | 201 + pcsx2/VU0.cpp | 364 + pcsx2/VU0micro.cpp | 454 + pcsx2/VU0microInterp.cpp | 253 + pcsx2/VU1micro.cpp | 454 + pcsx2/VU1microInterp.cpp | 237 + pcsx2/VUflags.cpp | 124 + pcsx2/VUflags.h | 39 + pcsx2/VUmicro.h | 1314 + pcsx2/VUmicroMem.cpp | 197 + pcsx2/VUops.cpp | 2929 ++ pcsx2/VUops.h | 399 + pcsx2/Vif.cpp | 608 + pcsx2/Vif.h | 113 + pcsx2/VifDma.cpp | 2425 ++ pcsx2/VifDma.h | 97 + pcsx2/bin | 1 + pcsx2/build.sh | 45 + pcsx2/cheatscpp.h | 48 + pcsx2/common/PS2Edefs.h | 885 + pcsx2/common/PS2Etypes.h | 219 + pcsx2/configure.ac | 138 + pcsx2/depcomp | 529 + pcsx2/install-sh | 323 + pcsx2/missing | 360 + pcsx2/mkinstalldirs | 158 + pcsx2/pcsxAbout.bmp | Bin 0 -> 251190 bytes pcsx2/tinyxml/Makefile.am | 5 + pcsx2/tinyxml/tinystr.cpp | 116 + pcsx2/tinyxml/tinystr.h | 319 + pcsx2/tinyxml/tinyxml.cpp | 1866 ++ pcsx2/tinyxml/tinyxml.h | 1776 + pcsx2/tinyxml/tinyxmlerror.cpp | 53 + pcsx2/tinyxml/tinyxmlparser.cpp | 1606 + pcsx2/vssprintf.cpp | 812 + pcsx2/vtlb.cpp | 591 + pcsx2/vtlb.h | 79 + pcsx2/windows/AboutDlg.cpp | 57 + pcsx2/windows/AboutDlg.h | 24 + pcsx2/windows/AdvancedDlg.cpp | 183 + pcsx2/windows/Cdrom02.ico | Bin 0 -> 12862 bytes pcsx2/windows/ConfigDlg.cpp | 578 + pcsx2/windows/CpuDlg.cpp | 178 + pcsx2/windows/DebugMemory.cpp | 240 + pcsx2/windows/Debugger.cpp | 679 + pcsx2/windows/Debugger.h | 47 + pcsx2/windows/Debugreg.cpp | 1486 + pcsx2/windows/HacksDlg.cpp | 74 + pcsx2/windows/McdsDlg.c | 126 + pcsx2/windows/McdsDlg.cpp | 1743 + pcsx2/windows/McdsDlg.h | 24 + pcsx2/windows/PatchBrowser.cpp | 352 + pcsx2/windows/RDebugger.cpp | 374 + pcsx2/windows/RDebugger.h | 26 + pcsx2/windows/SamplProf.cpp | 325 + pcsx2/windows/VCprojects/pcsx2_2008.vcproj | 2805 ++ pcsx2/windows/VCprojects/pthreads_2008.vcproj | 320 + .../windows/VCprojects/vsprops/common.vsprops | 36 + .../windows/VCprojects/vsprops/debug.vsprops | 18 + .../VCprojects/vsprops/devbuild.vsprops | 11 + .../windows/VCprojects/vsprops/postBuild.tmpl | 21 + .../VCprojects/vsprops/postBuild.unknown | 21 + pcsx2/windows/VCprojects/vsprops/preBuild.cmd | 20 + .../VCprojects/vsprops/release.vsprops | 27 + .../VCprojects/vsprops/svnrev_template.h | 18 + .../VCprojects/vsprops/svnrev_unknown.h | 23 + pcsx2/windows/Win32.h | 106 + pcsx2/windows/WinConsole.cpp | 144 + pcsx2/windows/WinMain.cpp | 1161 + pcsx2/windows/WinSysExec.cpp | 830 + pcsx2/windows/WinThreads.cpp | 152 + pcsx2/windows/WinVM.cpp | 470 + pcsx2/windows/afxresmw.h | 23 + pcsx2/windows/cheats/browser.cpp | 1129 + pcsx2/windows/cheats/cheats.cpp | 604 + pcsx2/windows/cheats/cheats.h | 39 + pcsx2/windows/ini.cpp | 346 + pcsx2/windows/libs/gnu_gettext.lib | Bin 0 -> 2916 bytes pcsx2/windows/libs/libintlmsc.h | 116 + pcsx2/windows/memzero.h | 598 + pcsx2/windows/pcsx2.rc | 1718 + pcsx2/windows/ps2_silver.bmp | Bin 0 -> 37974 bytes pcsx2/windows/pthreads/ANNOUNCE | 476 + pcsx2/windows/pthreads/BUGS | 133 + pcsx2/windows/pthreads/CONTRIBUTORS | 129 + pcsx2/windows/pthreads/COPYING | 150 + pcsx2/windows/pthreads/FAQ | 403 + pcsx2/windows/pthreads/MAINTAINERS | 4 + pcsx2/windows/pthreads/NEWS | 1110 + pcsx2/windows/pthreads/README | 593 + pcsx2/windows/pthreads/README.NONPORTABLE | 285 + pcsx2/windows/pthreads/TODO | 7 + pcsx2/windows/pthreads/attr.c | 53 + pcsx2/windows/pthreads/barrier.c | 47 + pcsx2/windows/pthreads/cancel.c | 44 + pcsx2/windows/pthreads/cleanup.c | 148 + pcsx2/windows/pthreads/condvar.c | 50 + pcsx2/windows/pthreads/config.h | 134 + pcsx2/windows/pthreads/create.c | 305 + pcsx2/windows/pthreads/dll.c | 92 + pcsx2/windows/pthreads/errno.c | 94 + pcsx2/windows/pthreads/exit.c | 44 + pcsx2/windows/pthreads/fork.c | 39 + pcsx2/windows/pthreads/global.c | 117 + pcsx2/windows/pthreads/implement.h | 717 + pcsx2/windows/pthreads/misc.c | 50 + pcsx2/windows/pthreads/mutex.c | 59 + pcsx2/windows/pthreads/need_errno.h | 132 + pcsx2/windows/pthreads/nonportable.c | 46 + pcsx2/windows/pthreads/private.c | 57 + pcsx2/windows/pthreads/pthread.h | 1388 + pcsx2/windows/pthreads/pthread_attr_destroy.c | 79 + .../pthreads/pthread_attr_getdetachstate.c | 87 + .../pthreads/pthread_attr_getinheritsched.c | 51 + .../pthreads/pthread_attr_getschedparam.c | 52 + .../pthreads/pthread_attr_getschedpolicy.c | 61 + .../windows/pthreads/pthread_attr_getscope.c | 54 + .../pthreads/pthread_attr_getstackaddr.c | 97 + .../pthreads/pthread_attr_getstacksize.c | 100 + pcsx2/windows/pthreads/pthread_attr_init.c | 117 + .../pthreads/pthread_attr_setdetachstate.c | 91 + .../pthreads/pthread_attr_setinheritsched.c | 57 + .../pthreads/pthread_attr_setschedparam.c | 63 + .../pthreads/pthread_attr_setschedpolicy.c | 55 + .../windows/pthreads/pthread_attr_setscope.c | 62 + .../pthreads/pthread_attr_setstackaddr.c | 97 + .../pthreads/pthread_attr_setstacksize.c | 110 + .../pthreads/pthread_barrier_destroy.c | 67 + pcsx2/windows/pthreads/pthread_barrier_init.c | 81 + pcsx2/windows/pthreads/pthread_barrier_wait.c | 99 + .../pthreads/pthread_barrierattr_destroy.c | 83 + .../pthreads/pthread_barrierattr_getpshared.c | 95 + .../pthreads/pthread_barrierattr_init.c | 85 + .../pthreads/pthread_barrierattr_setpshared.c | 119 + pcsx2/windows/pthreads/pthread_cancel.c | 223 + pcsx2/windows/pthreads/pthread_cond_destroy.c | 244 + pcsx2/windows/pthreads/pthread_cond_init.c | 170 + pcsx2/windows/pthreads/pthread_cond_signal.c | 231 + pcsx2/windows/pthreads/pthread_cond_wait.c | 567 + .../pthreads/pthread_condattr_destroy.c | 86 + .../pthreads/pthread_condattr_getpshared.c | 97 + .../windows/pthreads/pthread_condattr_init.c | 87 + .../pthreads/pthread_condattr_setpshared.c | 117 + pcsx2/windows/pthreads/pthread_delay_np.c | 171 + pcsx2/windows/pthreads/pthread_detach.c | 139 + pcsx2/windows/pthreads/pthread_equal.c | 76 + pcsx2/windows/pthreads/pthread_exit.c | 106 + .../windows/pthreads/pthread_getconcurrency.c | 45 + .../windows/pthreads/pthread_getschedparam.c | 75 + pcsx2/windows/pthreads/pthread_getspecific.c | 84 + .../pthreads/pthread_getw32threadhandle_np.c | 53 + pcsx2/windows/pthreads/pthread_join.c | 154 + pcsx2/windows/pthreads/pthread_key_create.c | 108 + pcsx2/windows/pthreads/pthread_key_delete.c | 133 + pcsx2/windows/pthreads/pthread_kill.c | 102 + .../windows/pthreads/pthread_mutex_destroy.c | 146 + pcsx2/windows/pthreads/pthread_mutex_init.c | 109 + pcsx2/windows/pthreads/pthread_mutex_lock.c | 139 + .../pthreads/pthread_mutex_timedlock.c | 196 + .../windows/pthreads/pthread_mutex_trylock.c | 92 + pcsx2/windows/pthreads/pthread_mutex_unlock.c | 119 + .../pthreads/pthread_mutexattr_destroy.c | 83 + .../pthreads/pthread_mutexattr_getkind_np.c | 44 + .../pthreads/pthread_mutexattr_getpshared.c | 95 + .../pthreads/pthread_mutexattr_gettype.c | 56 + .../windows/pthreads/pthread_mutexattr_init.c | 86 + .../pthreads/pthread_mutexattr_setkind_np.c | 44 + .../pthreads/pthread_mutexattr_setpshared.c | 119 + .../pthreads/pthread_mutexattr_settype.c | 143 + .../pthreads/pthread_num_processors_np.c | 56 + pcsx2/windows/pthreads/pthread_once.c | 86 + .../windows/pthreads/pthread_rwlock_destroy.c | 143 + pcsx2/windows/pthreads/pthread_rwlock_init.c | 115 + .../windows/pthreads/pthread_rwlock_rdlock.c | 103 + .../pthreads/pthread_rwlock_timedrdlock.c | 110 + .../pthreads/pthread_rwlock_timedwrlock.c | 140 + .../pthreads/pthread_rwlock_tryrdlock.c | 103 + .../pthreads/pthread_rwlock_trywrlock.c | 123 + .../windows/pthreads/pthread_rwlock_unlock.c | 94 + .../windows/pthreads/pthread_rwlock_wrlock.c | 134 + .../pthreads/pthread_rwlockattr_destroy.c | 85 + .../pthreads/pthread_rwlockattr_getpshared.c | 98 + .../pthreads/pthread_rwlockattr_init.c | 84 + .../pthreads/pthread_rwlockattr_setpshared.c | 121 + pcsx2/windows/pthreads/pthread_self.c | 138 + .../windows/pthreads/pthread_setcancelstate.c | 124 + .../windows/pthreads/pthread_setcanceltype.c | 125 + .../windows/pthreads/pthread_setconcurrency.c | 53 + .../windows/pthreads/pthread_setschedparam.c | 125 + pcsx2/windows/pthreads/pthread_setspecific.c | 168 + pcsx2/windows/pthreads/pthread_spin_destroy.c | 112 + pcsx2/windows/pthreads/pthread_spin_init.c | 131 + pcsx2/windows/pthreads/pthread_spin_lock.c | 83 + pcsx2/windows/pthreads/pthread_spin_trylock.c | 80 + pcsx2/windows/pthreads/pthread_spin_unlock.c | 75 + pcsx2/windows/pthreads/pthread_testcancel.c | 102 + .../pthreads/pthread_timechange_handler_np.c | 107 + .../pthreads/pthread_win32_attach_detach_np.c | 310 + .../ptw32_InterlockedCompareExchange.c | 303 + pcsx2/windows/pthreads/ptw32_MCS_lock.c | 210 + .../pthreads/ptw32_callUserDestroyRoutines.c | 220 + pcsx2/windows/pthreads/ptw32_calloc.c | 56 + .../pthreads/ptw32_cond_check_need_init.c | 94 + pcsx2/windows/pthreads/ptw32_getprocessors.c | 91 + pcsx2/windows/pthreads/ptw32_is_attr.c | 47 + .../pthreads/ptw32_mutex_check_need_init.c | 112 + pcsx2/windows/pthreads/ptw32_new.c | 91 + .../pthreads/ptw32_processInitialize.c | 102 + .../windows/pthreads/ptw32_processTerminate.c | 114 + pcsx2/windows/pthreads/ptw32_relmillisecs.c | 120 + pcsx2/windows/pthreads/ptw32_reuse.c | 147 + .../pthreads/ptw32_rwlock_cancelwrwait.c | 50 + .../pthreads/ptw32_rwlock_check_need_init.c | 93 + pcsx2/windows/pthreads/ptw32_semwait.c | 118 + .../pthreads/ptw32_spinlock_check_need_init.c | 81 + pcsx2/windows/pthreads/ptw32_threadDestroy.c | 82 + pcsx2/windows/pthreads/ptw32_threadStart.c | 360 + pcsx2/windows/pthreads/ptw32_throw.c | 167 + pcsx2/windows/pthreads/ptw32_timespec.c | 83 + pcsx2/windows/pthreads/ptw32_tkAssocCreate.c | 118 + pcsx2/windows/pthreads/ptw32_tkAssocDestroy.c | 114 + pcsx2/windows/pthreads/rwlock.c | 51 + pcsx2/windows/pthreads/sched.c | 53 + pcsx2/windows/pthreads/sched.h | 178 + .../windows/pthreads/sched_get_priority_max.c | 134 + .../windows/pthreads/sched_get_priority_min.c | 135 + pcsx2/windows/pthreads/sched_getscheduler.c | 69 + pcsx2/windows/pthreads/sched_setscheduler.c | 81 + pcsx2/windows/pthreads/sched_yield.c | 71 + pcsx2/windows/pthreads/sem_close.c | 58 + pcsx2/windows/pthreads/sem_destroy.c | 144 + pcsx2/windows/pthreads/sem_getvalue.c | 110 + pcsx2/windows/pthreads/sem_init.c | 169 + pcsx2/windows/pthreads/sem_open.c | 58 + pcsx2/windows/pthreads/sem_post.c | 128 + pcsx2/windows/pthreads/sem_post_multiple.c | 142 + pcsx2/windows/pthreads/sem_timedwait.c | 238 + pcsx2/windows/pthreads/sem_trywait.c | 117 + pcsx2/windows/pthreads/sem_unlink.c | 58 + pcsx2/windows/pthreads/sem_wait.c | 187 + pcsx2/windows/pthreads/semaphore.c | 69 + pcsx2/windows/pthreads/semaphore.h | 166 + pcsx2/windows/pthreads/signal.c | 179 + pcsx2/windows/pthreads/spin.c | 46 + pcsx2/windows/pthreads/sync.c | 43 + pcsx2/windows/pthreads/tsd.c | 44 + pcsx2/windows/pthreads/w32_CancelableWait.c | 160 + pcsx2/windows/resource.h | 736 + pcsx2/x86/BaseblockEx.cpp | 149 + pcsx2/x86/BaseblockEx.h | 77 + pcsx2/x86/Makefile.am | 22 + pcsx2/x86/README | 12 + pcsx2/x86/aR3000A.S | 394 + pcsx2/x86/aVUzerorec.S | 155 + pcsx2/x86/aVif.S | 1632 + pcsx2/x86/aVif.asm | 1941 ++ pcsx2/x86/fast_routines.S | 349 + pcsx2/x86/fast_routines.cpp | 884 + pcsx2/x86/iCOP0.cpp | 406 + pcsx2/x86/iCOP0.h | 49 + pcsx2/x86/iCOP2.cpp | 721 + pcsx2/x86/iCore.cpp | 1206 + pcsx2/x86/iCore.h | 429 + pcsx2/x86/iFPU.cpp | 1733 + pcsx2/x86/iFPU.h | 71 + pcsx2/x86/iGS.cpp | 299 + pcsx2/x86/iHw.cpp | 1153 + pcsx2/x86/iIPU.cpp | 203 + pcsx2/x86/iMMI.cpp | 3413 ++ pcsx2/x86/iMMI.h | 147 + pcsx2/x86/iPsxHw.cpp | 1179 + pcsx2/x86/iPsxMem.cpp | 871 + pcsx2/x86/iR3000A.cpp | 1541 + pcsx2/x86/iR3000A.h | 141 + pcsx2/x86/iR3000Atables.cpp | 2030 ++ pcsx2/x86/iR5900.h | 316 + pcsx2/x86/iR5900Arit.h | 46 + pcsx2/x86/iR5900AritImm.h | 42 + pcsx2/x86/iR5900Branch.h | 49 + pcsx2/x86/iR5900CoissuedLoadStore.cpp | 1739 + pcsx2/x86/iR5900Jump.h | 37 + pcsx2/x86/iR5900LoadStore.h | 95 + pcsx2/x86/iR5900Misc.cpp | 252 + pcsx2/x86/iR5900Move.h | 35 + pcsx2/x86/iR5900MultDiv.h | 37 + pcsx2/x86/iR5900Shift.h | 49 + pcsx2/x86/iVU0micro.cpp | 76 + pcsx2/x86/iVU1micro.cpp | 135 + pcsx2/x86/iVUmicro.cpp | 1713 + pcsx2/x86/iVUmicro.h | 289 + pcsx2/x86/iVUmicroLower.cpp | 1983 ++ pcsx2/x86/iVUmicroUpper.cpp | 3078 ++ pcsx2/x86/iVUops.h | 57 + pcsx2/x86/iVUzerorec.cpp | 4139 +++ pcsx2/x86/iVUzerorec.h | 50 + pcsx2/x86/iVif.cpp | 139 + pcsx2/x86/ir5900tables.cpp | 1049 + pcsx2/x86/ix86-32/aR5900-32.S | 210 + pcsx2/x86/ix86-32/aVif_proc-32.asm | 1839 ++ pcsx2/x86/ix86-32/iCore-32.cpp | 1112 + pcsx2/x86/ix86-32/iR5900-32.cpp | 2062 ++ pcsx2/x86/ix86-32/iR5900Arit.cpp | 1993 ++ pcsx2/x86/ix86-32/iR5900AritImm.cpp | 640 + pcsx2/x86/ix86-32/iR5900Branch.cpp | 1198 + pcsx2/x86/ix86-32/iR5900Jump.cpp | 133 + pcsx2/x86/ix86-32/iR5900LoadStore.cpp | 2765 ++ pcsx2/x86/ix86-32/iR5900Move.cpp | 825 + pcsx2/x86/ix86-32/iR5900MultDiv.cpp | 955 + pcsx2/x86/ix86-32/iR5900Shift.cpp | 1351 + pcsx2/x86/ix86-32/iR5900Templates.cpp | 1005 + pcsx2/x86/ix86-32/recVTLB.cpp | 319 + pcsx2/x86/ix86/Makefile.am | 5 + pcsx2/x86/ix86/ix86.cpp | 3360 ++ pcsx2/x86/ix86/ix86.h | 1750 + pcsx2/x86/ix86/ix86_3dnow.cpp | 202 + pcsx2/x86/ix86/ix86_cpudetect.cpp | 403 + pcsx2/x86/ix86/ix86_fpu.cpp | 286 + pcsx2/x86/ix86/ix86_mmx.cpp | 648 + pcsx2/x86/ix86/ix86_sse.cpp | 1381 + pcsx2/x86/ix86/ix86_tools.cpp | 275 + pcsx2/x86/megaVU.cpp | 282 + pcsx2/x86/megaVU.h | 132 + pcsx2/x86/megaVU_Lower.cpp | 19 + pcsx2/x86/megaVU_Tables.cpp | 227 + pcsx2/x86/megaVU_Tables.h | 195 + pcsx2/x86/megaVU_Upper.cpp | 19 + pcsx2/xmlpatchloader.cpp | 373 + pcsx2_2008.sln | 34 + pcsx2_suite_2008.sln | 74 + 922 files changed, 258571 insertions(+) create mode 100644 3rdparty/SoundTouch/3dnow_win.cpp create mode 100644 3rdparty/SoundTouch/AAFilter.cpp create mode 100644 3rdparty/SoundTouch/AAFilter.h create mode 100644 3rdparty/SoundTouch/BPMDetect.h create mode 100644 3rdparty/SoundTouch/FIFOSampleBuffer.cpp create mode 100644 3rdparty/SoundTouch/FIFOSampleBuffer.h create mode 100644 3rdparty/SoundTouch/FIFOSamplePipe.h create mode 100644 3rdparty/SoundTouch/FIRFilter.cpp create mode 100644 3rdparty/SoundTouch/FIRFilter.h create mode 100644 3rdparty/SoundTouch/Makefile.am create mode 100644 3rdparty/SoundTouch/RateTransposer.cpp create mode 100644 3rdparty/SoundTouch/RateTransposer.h create mode 100644 3rdparty/SoundTouch/STTypes.h create mode 100644 3rdparty/SoundTouch/SoundTouch.cpp create mode 100644 3rdparty/SoundTouch/SoundTouch.h create mode 100644 3rdparty/SoundTouch/TDStretch.cpp create mode 100644 3rdparty/SoundTouch/TDStretch.h create mode 100644 3rdparty/SoundTouch/WavFile.cpp create mode 100644 3rdparty/SoundTouch/WavFile.h create mode 100644 3rdparty/SoundTouch/cpu_detect.h create mode 100644 3rdparty/SoundTouch/cpu_detect_x86_gcc.cpp create mode 100644 3rdparty/SoundTouch/cpu_detect_x86_win.cpp create mode 100644 3rdparty/SoundTouch/mmx_optimized.cpp create mode 100644 3rdparty/SoundTouch/sse_optimized.cpp create mode 100644 3rdparty/bzip2/LICENSE create mode 100644 3rdparty/bzip2/README create mode 100644 3rdparty/bzip2/blocksort.c create mode 100644 3rdparty/bzip2/bzlib.c create mode 100644 3rdparty/bzip2/bzlib.h create mode 100644 3rdparty/bzip2/bzlib_private.h create mode 100644 3rdparty/bzip2/compress.c create mode 100644 3rdparty/bzip2/crctable.c create mode 100644 3rdparty/bzip2/decompress.c create mode 100644 3rdparty/bzip2/huffman.c create mode 100644 3rdparty/bzip2/randtable.c create mode 100644 3rdparty/zlib/ChangeLog create mode 100644 3rdparty/zlib/Makefile.am create mode 100644 3rdparty/zlib/README create mode 100644 3rdparty/zlib/adler32.c create mode 100644 3rdparty/zlib/compress.c create mode 100644 3rdparty/zlib/crc32.c create mode 100644 3rdparty/zlib/crc32.h create mode 100644 3rdparty/zlib/deflate.c create mode 100644 3rdparty/zlib/deflate.h create mode 100644 3rdparty/zlib/gzio.c create mode 100644 3rdparty/zlib/infback.c create mode 100644 3rdparty/zlib/inffast.c create mode 100644 3rdparty/zlib/inffast.h create mode 100644 3rdparty/zlib/inffixed.h create mode 100644 3rdparty/zlib/inflate.c create mode 100644 3rdparty/zlib/inflate.h create mode 100644 3rdparty/zlib/inftrees.c create mode 100644 3rdparty/zlib/inftrees.h create mode 100644 3rdparty/zlib/trees.c create mode 100644 3rdparty/zlib/trees.h create mode 100644 3rdparty/zlib/uncompr.c create mode 100644 3rdparty/zlib/zconf.h create mode 100644 3rdparty/zlib/zlib.h create mode 100644 3rdparty/zlib/zutil.c create mode 100644 3rdparty/zlib/zutil.h create mode 100644 bin/.pixmaps/Thumbs.db create mode 100644 bin/.pixmaps/pcsxAbout.bmp create mode 100644 bin/Langs/ar_AR/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/bg_BG/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/cz_CZ/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/de_DE/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/du_DU/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/el_EL/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/es_ES/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/fr_FR/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/hb_HB/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/it_IT/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/ja_JA/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/pe_PE/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/pl_PL/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/po_BR/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/po_PO/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/ro_RO/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/ru_RU/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/sh_SH/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/sw_SW/LC_MESSAGES/other.po create mode 100644 bin/Langs/sw_SW/LC_MESSAGES/pcsx2.mo create mode 100644 bin/Langs/tc_TC/LC_MESSAGES/Pcsx2.mo create mode 100644 bin/Langs/tr_TR/LC_MESSAGES/pcsx2.mo create mode 100644 bin/compat_list/compat_list.html create mode 100644 bin/compat_list/h_console.jpg create mode 100644 bin/compat_list/h_region.jpg create mode 100644 bin/compat_list/h_serial.jpg create mode 100644 bin/compat_list/h_status.jpg create mode 100644 bin/compat_list/h_title.jpg create mode 100644 bin/compat_list/h_version.jpg create mode 100644 bin/compat_list/sq_ingame.jpg create mode 100644 bin/compat_list/sq_intro.jpg create mode 100644 bin/compat_list/sq_menus.jpg create mode 100644 bin/compat_list/sq_nothing.jpg create mode 100644 bin/compat_list/sq_playable.jpg create mode 100644 bin/patches/013E349D.pnach create mode 100644 bin/patches/015314A2.pnach create mode 100644 bin/patches/034836F8.pnach create mode 100644 bin/patches/0442B1BD.pnach create mode 100644 bin/patches/05177ECE.pnach create mode 100644 bin/patches/0518D274.pnach create mode 100644 bin/patches/0518D275.pnach create mode 100644 bin/patches/06DE61E0.pnach create mode 100644 bin/patches/07AD79C9.pnach create mode 100644 bin/patches/0838D766.pnach create mode 100644 bin/patches/086273D2.pnach create mode 100644 bin/patches/08901101.pnach create mode 100644 bin/patches/08FB9DCF.pnach create mode 100644 bin/patches/0999F9DB.pnach create mode 100644 bin/patches/09D35D3F.pnach create mode 100644 bin/patches/0A342A88.pnach create mode 100644 bin/patches/0B359BBF.pnach create mode 100644 bin/patches/0C370E94.pnach create mode 100644 bin/patches/0F6B6315.pnach create mode 100644 bin/patches/1025D50A.pnach create mode 100644 bin/patches/116154AD.pnach create mode 100644 bin/patches/1248FE3A.pnach create mode 100644 bin/patches/129C8600.pnach create mode 100644 bin/patches/12AFF4D4.pnach create mode 100644 bin/patches/1468C474.pnach create mode 100644 bin/patches/1510E1D1.pnach create mode 100644 bin/patches/1629D655.pnach create mode 100644 bin/patches/163F0461.pnach create mode 100644 bin/patches/1738A5B0.pnach create mode 100644 bin/patches/19D145D7.pnach create mode 100644 bin/patches/1CAC8A56.pnach create mode 100644 bin/patches/1D2818AF.pnach create mode 100644 bin/patches/200BC0E6.pnach create mode 100644 bin/patches/2088950A.pnach create mode 100644 bin/patches/21068223.pnach create mode 100644 bin/patches/2251E14D.pnach create mode 100644 bin/patches/22C36E63.pnach create mode 100644 bin/patches/25433CBD.pnach create mode 100644 bin/patches/27E54B37.pnach create mode 100644 bin/patches/280DAC56.pnach create mode 100644 bin/patches/295D2F96.pnach create mode 100644 bin/patches/29D80A23.pnach create mode 100644 bin/patches/2B2E1535.pnach create mode 100644 bin/patches/2C728173.pnach create mode 100644 bin/patches/2CFFFA50.pnach create mode 100644 bin/patches/2D919421.pnach create mode 100644 bin/patches/2F56CBC9.pnach create mode 100644 bin/patches/302797DF.pnach create mode 100644 bin/patches/304C115C.pnach create mode 100644 bin/patches/32629F36.pnach create mode 100644 bin/patches/333F1F59.pnach create mode 100644 bin/patches/337B927C.pnach create mode 100644 bin/patches/339A0B8C.pnach create mode 100644 bin/patches/33F7D21A.pnach create mode 100644 bin/patches/36FEEE3A.pnach create mode 100644 bin/patches/37BA81B1.pnach create mode 100644 bin/patches/37C07E96.pnach create mode 100644 bin/patches/38F29E28.pnach create mode 100644 bin/patches/3BD85DA4.pnach create mode 100644 bin/patches/3CFE3B37.pnach create mode 100644 bin/patches/3DB65A75.pnach create mode 100644 bin/patches/3E0A256D.pnach create mode 100644 bin/patches/3E68955A.pnach create mode 100644 bin/patches/3EAD47FE.pnach create mode 100644 bin/patches/3F0452DE.pnach create mode 100644 bin/patches/3FB69323.pnach create mode 100644 bin/patches/4043F228.pnach create mode 100644 bin/patches/41A3191C.pnach create mode 100644 bin/patches/4321A427.pnach create mode 100644 bin/patches/4334E17D.pnach create mode 100644 bin/patches/44A61C8F.pnach create mode 100644 bin/patches/4691F6F7.pnach create mode 100644 bin/patches/46A7ECA4.pnach create mode 100644 bin/patches/472C9E70.pnach create mode 100644 bin/patches/48FE0C71.pnach create mode 100644 bin/patches/49DA19CE.pnach create mode 100644 bin/patches/4B6DDB6B.pnach create mode 100644 bin/patches/4C0C821D.pnach create mode 100644 bin/patches/4C4D7072.pnach create mode 100644 bin/patches/4C9EE7DF.pnach create mode 100644 bin/patches/4D228733.pnach create mode 100644 bin/patches/4D6DBB75.pnach create mode 100644 bin/patches/4DAC50C2.pnach create mode 100644 bin/patches/4F3D3CF0.pnach create mode 100644 bin/patches/506644B3.pnach create mode 100644 bin/patches/512B5046.pnach create mode 100644 bin/patches/5162BCCA.pnach create mode 100644 bin/patches/51D8A6A9.pnach create mode 100644 bin/patches/51F91783.pnach create mode 100644 bin/patches/53A803AF.pnach create mode 100644 bin/patches/53DF159B.pnach create mode 100644 bin/patches/54A548B4.pnach create mode 100644 bin/patches/54AD76D7.pnach create mode 100644 bin/patches/54D6BEE3.pnach create mode 100644 bin/patches/561BE340.pnach create mode 100644 bin/patches/582EED0D.pnach create mode 100644 bin/patches/586EA828.pnach create mode 100644 bin/patches/58EE1AFA.pnach create mode 100644 bin/patches/5A7635C1.pnach create mode 100644 bin/patches/5BBC2F40.pnach create mode 100644 bin/patches/5BC8C9E8.pnach create mode 100644 bin/patches/5BE3F481.pnach create mode 100644 bin/patches/5CCA0737.pnach create mode 100644 bin/patches/5D67AE48.pnach create mode 100644 bin/patches/5DFBE144.pnach create mode 100644 bin/patches/5E115FB6.pnach create mode 100644 bin/patches/5EB127E7.pnach create mode 100644 bin/patches/5F2A0E36.pnach create mode 100644 bin/patches/60013EBD.pnach create mode 100644 bin/patches/60FA8C69.pnach create mode 100644 bin/patches/6175FE7D.pnach create mode 100644 bin/patches/624F11F1.pnach create mode 100644 bin/patches/625AF967.pnach create mode 100644 bin/patches/627B8252.pnach create mode 100644 bin/patches/62F6F886.pnach create mode 100644 bin/patches/63F6B523.pnach create mode 100644 bin/patches/68EAF48F.pnach create mode 100644 bin/patches/6926B199.pnach create mode 100644 bin/patches/692CBA8E.pnach create mode 100644 bin/patches/6A4EFE60.pnach create mode 100644 bin/patches/6ADBC24B.pnach create mode 100644 bin/patches/6BA2F6B9.pnach create mode 100644 bin/patches/6D70F0E0.pnach create mode 100644 bin/patches/6EA9DDA9.pnach create mode 100644 bin/patches/6FB69282.pnach create mode 100644 bin/patches/7098BE76.pnach create mode 100644 bin/patches/7130C553.pnach create mode 100644 bin/patches/71584BAC.pnach create mode 100644 bin/patches/722BBD62.pnach create mode 100644 bin/patches/7377BC6F.pnach create mode 100644 bin/patches/763D3BF9.pnach create mode 100644 bin/patches/77ECAAA0.pnach create mode 100644 bin/patches/78168525.pnach create mode 100644 bin/patches/789D6B71.pnach create mode 100644 bin/patches/78E20421.pnach create mode 100644 bin/patches/7ABDBB5E.pnach create mode 100644 bin/patches/7BA0128E.pnach create mode 100644 bin/patches/7CD1CDCD.pnach create mode 100644 bin/patches/7D8F539A.pnach create mode 100644 bin/patches/7EBEEBBD.pnach create mode 100644 bin/patches/7F6319C7.pnach create mode 100644 bin/patches/7FD7A1B9.pnach create mode 100644 bin/patches/83261085.pnach create mode 100644 bin/patches/83D0CE43.pnach create mode 100644 bin/patches/844B58EE.pnach create mode 100644 bin/patches/848C6247.pnach create mode 100644 bin/patches/85AE91B3.pnach create mode 100644 bin/patches/85E92C92.pnach create mode 100644 bin/patches/8BC79F96.pnach create mode 100644 bin/patches/8BC95883.pnach create mode 100644 bin/patches/8BE3D7B2.pnach create mode 100644 bin/patches/8DC64680.pnach create mode 100644 bin/patches/8FDE8E16.pnach create mode 100644 bin/patches/901AAC09.pnach create mode 100644 bin/patches/93223BE4.pnach create mode 100644 bin/patches/934F9081.pnach create mode 100644 bin/patches/93F8A60B.pnach create mode 100644 bin/patches/941BB7D9.pnach create mode 100644 bin/patches/94C56923.pnach create mode 100644 bin/patches/950241D3.pnach create mode 100644 bin/patches/951555A0.pnach create mode 100644 bin/patches/95BB1901.pnach create mode 100644 bin/patches/96B2F56D.pnach create mode 100644 bin/patches/96B76E56.pnach create mode 100644 bin/patches/96C20D6F.pnach create mode 100644 bin/patches/9AAC5309.pnach create mode 100644 bin/patches/9AAC530D.pnach create mode 100644 bin/patches/9AC65D6A.pnach create mode 100644 bin/patches/9B1EE9EB.pnach create mode 100644 bin/patches/9BE3F92D.pnach create mode 100644 bin/patches/9D6F46F0.pnach create mode 100644 bin/patches/A029B109.pnach create mode 100644 bin/patches/A1B752C7.pnach create mode 100644 bin/patches/A36CFF6C.pnach create mode 100644 bin/patches/A39517AB.pnach create mode 100644 bin/patches/A3ACF3C7.pnach create mode 100644 bin/patches/A3D63039.pnach create mode 100644 bin/patches/A4E2C043.pnach create mode 100644 bin/patches/A5BF36A8.pnach create mode 100644 bin/patches/A63C896C.pnach create mode 100644 bin/patches/A719D130.pnach create mode 100644 bin/patches/A74F99CD.pnach create mode 100644 bin/patches/A7A2F7C5.pnach create mode 100644 bin/patches/A8083AE6.pnach create mode 100644 bin/patches/A88ACA28.pnach create mode 100644 bin/patches/A8A76AAC.pnach create mode 100644 bin/patches/A929A697.pnach create mode 100644 bin/patches/A9759015.pnach create mode 100644 bin/patches/AA31B5BF.pnach create mode 100644 bin/patches/AC7E88D9.pnach create mode 100644 bin/patches/ACB1989A.pnach create mode 100644 bin/patches/AD9D2B54.pnach create mode 100644 bin/patches/ADAA1256.pnach create mode 100644 bin/patches/AE0E098F.pnach create mode 100644 bin/patches/AE3EAA05.pnach create mode 100644 bin/patches/AE9EB9A0.pnach create mode 100644 bin/patches/AEDAEE99.pnach create mode 100644 bin/patches/AFAC88EF.pnach create mode 100644 bin/patches/B0621C55.pnach create mode 100644 bin/patches/B0AE1898.pnach create mode 100644 bin/patches/B130E5BA.pnach create mode 100644 bin/patches/B1BE3E51.pnach create mode 100644 bin/patches/B234036E.pnach create mode 100644 bin/patches/B2BDE9F3.pnach create mode 100644 bin/patches/B338676A.pnach create mode 100644 bin/patches/B34DA141.pnach create mode 100644 bin/patches/B36EE21E.pnach create mode 100644 bin/patches/B590CE04.pnach create mode 100644 bin/patches/B82E4C4B.pnach create mode 100644 bin/patches/B884B6E9.pnach create mode 100644 bin/patches/B99379B7.pnach create mode 100644 bin/patches/BB3D833A.pnach create mode 100644 bin/patches/BD3DBCF9.pnach create mode 100644 bin/patches/BF5D9AEC.pnach create mode 100644 bin/patches/C04FB5FD.pnach create mode 100644 bin/patches/C0D6A139.pnach create mode 100644 bin/patches/C1625F14.pnach create mode 100644 bin/patches/C1767D64.pnach create mode 100644 bin/patches/C1B141D6.pnach create mode 100644 bin/patches/C220951A.pnach create mode 100644 bin/patches/C398F477.pnach create mode 100644 bin/patches/C3D28EB9.pnach create mode 100644 bin/patches/C4467D30.pnach create mode 100644 bin/patches/C488EC04.pnach create mode 100644 bin/patches/C4A60986.pnach create mode 100644 bin/patches/C502AD6E.pnach create mode 100644 bin/patches/C5DEFEA0.pnach create mode 100644 bin/patches/C9246E9C.pnach create mode 100644 bin/patches/C9C145BF.pnach create mode 100644 bin/patches/CA295E61.pnach create mode 100644 bin/patches/CA6243B9.pnach create mode 100644 bin/patches/CB4EBD11.pnach create mode 100644 bin/patches/CBBC2E7F.pnach create mode 100644 bin/patches/CC4B9CDE.pnach create mode 100644 bin/patches/CC6AA742.pnach create mode 100644 bin/patches/CDE7C999.pnach create mode 100644 bin/patches/CF11CD83.pnach create mode 100644 bin/patches/CFB873AD.pnach create mode 100644 bin/patches/D03BEF2A.pnach create mode 100644 bin/patches/D08648B6.pnach create mode 100644 bin/patches/D0E17D26.pnach create mode 100644 bin/patches/D1ACD489.pnach create mode 100644 bin/patches/D4781770.pnach create mode 100644 bin/patches/D48A92E1.pnach create mode 100644 bin/patches/D4FB6049.pnach create mode 100644 bin/patches/D79F697A.pnach create mode 100644 bin/patches/DA0535FD.pnach create mode 100644 bin/patches/DAC14B26.pnach create mode 100644 bin/patches/DB719F5C.pnach create mode 100644 bin/patches/DC85FC8F.pnach create mode 100644 bin/patches/DCC4EEEA.pnach create mode 100644 bin/patches/DDA2FA6A.pnach create mode 100644 bin/patches/DEFA4763.pnach create mode 100644 bin/patches/E0127F2D.pnach create mode 100644 bin/patches/E0426FC6.pnach create mode 100644 bin/patches/E0DADD1A.pnach create mode 100644 bin/patches/E138094A.pnach create mode 100644 bin/patches/E1C5F607.pnach create mode 100644 bin/patches/E1FD9A2D.pnach create mode 100644 bin/patches/E2F1DB6B.pnach create mode 100644 bin/patches/E4A275B2.pnach create mode 100644 bin/patches/E677B8F1.pnach create mode 100644 bin/patches/E7A35274.pnach create mode 100644 bin/patches/E906EA37.pnach create mode 100644 bin/patches/EAD76247.pnach create mode 100644 bin/patches/EADE437E.pnach create mode 100644 bin/patches/EDD7E0FF.pnach create mode 100644 bin/patches/EE838B5C.pnach create mode 100644 bin/patches/EEC3B310.pnach create mode 100644 bin/patches/EEE2F6A3.pnach create mode 100644 bin/patches/EF5B6AAD.pnach create mode 100644 bin/patches/EF9E43EF.pnach create mode 100644 bin/patches/F1370E83.pnach create mode 100644 bin/patches/F22CDDAF.pnach create mode 100644 bin/patches/F266B00B.pnach create mode 100644 bin/patches/F4992CC1.pnach create mode 100644 bin/patches/F52FB2BE.pnach create mode 100644 bin/patches/F56C7948.pnach create mode 100644 bin/patches/F5C7B45F.pnach create mode 100644 bin/patches/F6DC728D.pnach create mode 100644 bin/patches/F6F9A91D.pnach create mode 100644 bin/patches/F758234F.pnach create mode 100644 bin/patches/F9E575D0.pnach create mode 100644 bin/patches/FA7E3081.pnach create mode 100644 bin/patches/FAC64195.pnach create mode 100644 bin/patches/FB0E6D72.pnach create mode 100644 bin/patches/FC618D82.pnach create mode 100644 bin/patches/FEA030CB.pnach create mode 100644 bin/patches/FF920E90.pnach create mode 100644 bin/patches/default.xml create mode 100644 bin/snaps/1.jpg create mode 100644 bin/snaps/2.jpg create mode 100644 bin/snaps/3.jpg create mode 100644 bin/snaps/4.jpg create mode 100644 build.sh create mode 100644 common/includes/PS2Edefs.h create mode 100644 common/includes/PS2Etypes.h create mode 100644 common/tsvnrev/svnrev_template.h create mode 100644 common/tsvnrev/svnrev_unknown.h create mode 100644 common/tsvnrev/updateRevision.cmd create mode 100644 fetch.sh create mode 100644 pcsx2/3rdparty/zlib/ChangeLog create mode 100644 pcsx2/3rdparty/zlib/Makefile.am create mode 100644 pcsx2/3rdparty/zlib/README create mode 100644 pcsx2/3rdparty/zlib/adler32.c create mode 100644 pcsx2/3rdparty/zlib/compress.c create mode 100644 pcsx2/3rdparty/zlib/crc32.c create mode 100644 pcsx2/3rdparty/zlib/crc32.h create mode 100644 pcsx2/3rdparty/zlib/deflate.c create mode 100644 pcsx2/3rdparty/zlib/deflate.h create mode 100644 pcsx2/3rdparty/zlib/gzio.c create mode 100644 pcsx2/3rdparty/zlib/infback.c create mode 100644 pcsx2/3rdparty/zlib/inffast.c create mode 100644 pcsx2/3rdparty/zlib/inffast.h create mode 100644 pcsx2/3rdparty/zlib/inffixed.h create mode 100644 pcsx2/3rdparty/zlib/inflate.c create mode 100644 pcsx2/3rdparty/zlib/inflate.h create mode 100644 pcsx2/3rdparty/zlib/inftrees.c create mode 100644 pcsx2/3rdparty/zlib/inftrees.h create mode 100644 pcsx2/3rdparty/zlib/trees.c create mode 100644 pcsx2/3rdparty/zlib/trees.h create mode 100644 pcsx2/3rdparty/zlib/uncompr.c create mode 100644 pcsx2/3rdparty/zlib/zconf.h create mode 100644 pcsx2/3rdparty/zlib/zlib.h create mode 100644 pcsx2/3rdparty/zlib/zutil.c create mode 100644 pcsx2/3rdparty/zlib/zutil.h create mode 100644 pcsx2/AlignedMalloc.cpp create mode 100644 pcsx2/CDVD.cpp create mode 100644 pcsx2/CDVD.h create mode 100644 pcsx2/CDVDiso.cpp create mode 100644 pcsx2/CDVDiso.h create mode 100644 pcsx2/CDVDisodrv.cpp create mode 100644 pcsx2/CDVDisodrv.h create mode 100644 pcsx2/CDVDlib.h create mode 100644 pcsx2/COP0.cpp create mode 100644 pcsx2/COP0.h create mode 100644 pcsx2/COP2.cpp create mode 100644 pcsx2/Cache.cpp create mode 100644 pcsx2/Cache.h create mode 100644 pcsx2/CdRom.cpp create mode 100644 pcsx2/CdRom.h create mode 100644 pcsx2/Common.h create mode 100644 pcsx2/Console.cpp create mode 100644 pcsx2/Counters.cpp create mode 100644 pcsx2/Counters.h create mode 100644 pcsx2/DebugTools/Debug.h create mode 100644 pcsx2/DebugTools/DisASM.h create mode 100644 pcsx2/DebugTools/DisR3000A.cpp create mode 100644 pcsx2/DebugTools/DisR3000asm.cpp create mode 100644 pcsx2/DebugTools/DisR5900.cpp create mode 100644 pcsx2/DebugTools/DisR5900asm.cpp create mode 100644 pcsx2/DebugTools/DisVU0Micro.cpp create mode 100644 pcsx2/DebugTools/DisVU1Micro.cpp create mode 100644 pcsx2/DebugTools/DisVUmicro.h create mode 100644 pcsx2/DebugTools/DisVUops.h create mode 100644 pcsx2/DebugTools/Makefile.am create mode 100644 pcsx2/Decode_XA.cpp create mode 100644 pcsx2/Decode_XA.h create mode 100644 pcsx2/Docs/ChangeLog.txt create mode 100644 pcsx2/Docs/License.txt create mode 100644 pcsx2/Docs/PCSX2 FAQ 0.9.4.rtf create mode 100644 pcsx2/Docs/PS2Edefs.txt create mode 100644 pcsx2/Docs/RemoteDebugging.txt create mode 100644 pcsx2/Docs/Translating.txt create mode 100644 pcsx2/Docs/devblog.txt create mode 100644 pcsx2/Docs/readme 0.9.4.txt create mode 100644 pcsx2/Docs/readme Playground.txt create mode 100644 pcsx2/Docs/specs.tex create mode 100644 pcsx2/EEregs.h create mode 100644 pcsx2/Elfheader.cpp create mode 100644 pcsx2/Elfheader.h create mode 100644 pcsx2/Exceptions.h create mode 100644 pcsx2/FPU.cpp create mode 100644 pcsx2/FPU2.cpp create mode 100644 pcsx2/FiFo.cpp create mode 100644 pcsx2/GS.cpp create mode 100644 pcsx2/GS.h create mode 100644 pcsx2/Gif.cpp create mode 100644 pcsx2/Hw.cpp create mode 100644 pcsx2/Hw.h create mode 100644 pcsx2/IPU/IPU.cpp create mode 100644 pcsx2/IPU/IPU.h create mode 100644 pcsx2/IPU/Makefile.am create mode 100644 pcsx2/IPU/acoroutine.S create mode 100644 pcsx2/IPU/acoroutine.asm create mode 100644 pcsx2/IPU/coroutine.cpp create mode 100644 pcsx2/IPU/coroutine.h create mode 100644 pcsx2/IPU/idct_mmx.obj create mode 100644 pcsx2/IPU/mpeg2lib/Idct.cpp create mode 100644 pcsx2/IPU/mpeg2lib/Makefile.am create mode 100644 pcsx2/IPU/mpeg2lib/Mpeg.cpp create mode 100644 pcsx2/IPU/mpeg2lib/Mpeg.h create mode 100644 pcsx2/IPU/mpeg2lib/Vlc.h create mode 100644 pcsx2/IPU/yuv2rgb.asm create mode 100644 pcsx2/IPU/yuv2rgb.cpp create mode 100644 pcsx2/IPU/yuv2rgb.h create mode 100644 pcsx2/Interpreter.cpp create mode 100644 pcsx2/IopBios.cpp create mode 100644 pcsx2/IopBios.h create mode 100644 pcsx2/IopBios2.h create mode 100644 pcsx2/IopCounters.cpp create mode 100644 pcsx2/IopCounters.h create mode 100644 pcsx2/IopDma.cpp create mode 100644 pcsx2/IopDma.h create mode 100644 pcsx2/IopHw.cpp create mode 100644 pcsx2/IopHw.h create mode 100644 pcsx2/IopMem.cpp create mode 100644 pcsx2/IopMem.h create mode 100644 pcsx2/IopSio2.cpp create mode 100644 pcsx2/IopSio2.h create mode 100644 pcsx2/Linux/.pixmaps/pcsxAbout.xpm create mode 100644 pcsx2/Linux/ConfigDlg.cpp create mode 100644 pcsx2/Linux/ConfigDlg.h create mode 100644 pcsx2/Linux/DebugDlg.cpp create mode 100644 pcsx2/Linux/DebugDlg.h create mode 100644 pcsx2/Linux/GtkGui.cpp create mode 100644 pcsx2/Linux/GtkGui.h create mode 100644 pcsx2/Linux/Linux.h create mode 100644 pcsx2/Linux/LnxConsole.cpp create mode 100644 pcsx2/Linux/LnxMain.cpp create mode 100644 pcsx2/Linux/LnxMain.h create mode 100644 pcsx2/Linux/LnxThreads.cpp create mode 100644 pcsx2/Linux/Makefile.am create mode 100644 pcsx2/Linux/Pref.cpp create mode 100644 pcsx2/Linux/buildgui.sh create mode 100644 pcsx2/Linux/callbacks.h create mode 100644 pcsx2/Linux/interface.c create mode 100644 pcsx2/Linux/interface.h create mode 100644 pcsx2/Linux/memzero.h create mode 100644 pcsx2/Linux/pcsx2.glade create mode 100644 pcsx2/Linux/support.c create mode 100644 pcsx2/Linux/support.h create mode 100644 pcsx2/MMI.cpp create mode 100644 pcsx2/MTGS.cpp create mode 100644 pcsx2/Makefile.am create mode 100644 pcsx2/Mdec.cpp create mode 100644 pcsx2/Mdec.h create mode 100644 pcsx2/MemcpyFast.h create mode 100644 pcsx2/Memory.cpp create mode 100644 pcsx2/Memory.h create mode 100644 pcsx2/MemoryVM.cpp create mode 100644 pcsx2/Misc.cpp create mode 100644 pcsx2/Misc.h create mode 100644 pcsx2/Patch.cpp create mode 100644 pcsx2/Patch.h create mode 100644 pcsx2/PathUtils.cpp create mode 100644 pcsx2/Paths.h create mode 100644 pcsx2/Plugins.cpp create mode 100644 pcsx2/Plugins.h create mode 100644 pcsx2/PrecompiledHeader.cpp create mode 100644 pcsx2/PrecompiledHeader.h create mode 100644 pcsx2/PsxCommon.h create mode 100644 pcsx2/R3000A.cpp create mode 100644 pcsx2/R3000A.h create mode 100644 pcsx2/R3000AInterpreter.cpp create mode 100644 pcsx2/R3000AOpcodeTables.cpp create mode 100644 pcsx2/R5900.cpp create mode 100644 pcsx2/R5900.h create mode 100644 pcsx2/R5900OpcodeImpl.cpp create mode 100644 pcsx2/R5900OpcodeTables.cpp create mode 100644 pcsx2/R5900OpcodeTables.h create mode 100644 pcsx2/RDebug/Makefile.am create mode 100644 pcsx2/RDebug/deci2.cpp create mode 100644 pcsx2/RDebug/deci2.h create mode 100644 pcsx2/RDebug/deci2.txt create mode 100644 pcsx2/RDebug/deci2_dbgp.cpp create mode 100644 pcsx2/RDebug/deci2_dbgp.h create mode 100644 pcsx2/RDebug/deci2_dcmp.cpp create mode 100644 pcsx2/RDebug/deci2_dcmp.h create mode 100644 pcsx2/RDebug/deci2_drfp.cpp create mode 100644 pcsx2/RDebug/deci2_drfp.h create mode 100644 pcsx2/RDebug/deci2_iloadp.cpp create mode 100644 pcsx2/RDebug/deci2_iloadp.h create mode 100644 pcsx2/RDebug/deci2_netmp.cpp create mode 100644 pcsx2/RDebug/deci2_netmp.h create mode 100644 pcsx2/RDebug/deci2_ttyp.cpp create mode 100644 pcsx2/RDebug/deci2_ttyp.h create mode 100644 pcsx2/RDebug/iloadp.txt create mode 100644 pcsx2/SPR.cpp create mode 100644 pcsx2/SPR.h create mode 100644 pcsx2/SafeArray.h create mode 100644 pcsx2/SamplProf.h create mode 100644 pcsx2/SaveState.cpp create mode 100644 pcsx2/SaveState.h create mode 100644 pcsx2/Sif.cpp create mode 100644 pcsx2/Sif.h create mode 100644 pcsx2/Sifcmd.h create mode 100644 pcsx2/Sio.cpp create mode 100644 pcsx2/Sio.h create mode 100644 pcsx2/SourceLog.cpp create mode 100644 pcsx2/Stats.cpp create mode 100644 pcsx2/Stats.h create mode 100644 pcsx2/StringUtils.h create mode 100644 pcsx2/System.cpp create mode 100644 pcsx2/System.h create mode 100644 pcsx2/ThreadTools.cpp create mode 100644 pcsx2/Threading.h create mode 100644 pcsx2/VU.h create mode 100644 pcsx2/VU0.cpp create mode 100644 pcsx2/VU0micro.cpp create mode 100644 pcsx2/VU0microInterp.cpp create mode 100644 pcsx2/VU1micro.cpp create mode 100644 pcsx2/VU1microInterp.cpp create mode 100644 pcsx2/VUflags.cpp create mode 100644 pcsx2/VUflags.h create mode 100644 pcsx2/VUmicro.h create mode 100644 pcsx2/VUmicroMem.cpp create mode 100644 pcsx2/VUops.cpp create mode 100644 pcsx2/VUops.h create mode 100644 pcsx2/Vif.cpp create mode 100644 pcsx2/Vif.h create mode 100644 pcsx2/VifDma.cpp create mode 100644 pcsx2/VifDma.h create mode 100644 pcsx2/bin create mode 100644 pcsx2/build.sh create mode 100644 pcsx2/cheatscpp.h create mode 100644 pcsx2/common/PS2Edefs.h create mode 100644 pcsx2/common/PS2Etypes.h create mode 100644 pcsx2/configure.ac create mode 100644 pcsx2/depcomp create mode 100644 pcsx2/install-sh create mode 100644 pcsx2/missing create mode 100644 pcsx2/mkinstalldirs create mode 100644 pcsx2/pcsxAbout.bmp create mode 100644 pcsx2/tinyxml/Makefile.am create mode 100644 pcsx2/tinyxml/tinystr.cpp create mode 100644 pcsx2/tinyxml/tinystr.h create mode 100644 pcsx2/tinyxml/tinyxml.cpp create mode 100644 pcsx2/tinyxml/tinyxml.h create mode 100644 pcsx2/tinyxml/tinyxmlerror.cpp create mode 100644 pcsx2/tinyxml/tinyxmlparser.cpp create mode 100644 pcsx2/vssprintf.cpp create mode 100644 pcsx2/vtlb.cpp create mode 100644 pcsx2/vtlb.h create mode 100644 pcsx2/windows/AboutDlg.cpp create mode 100644 pcsx2/windows/AboutDlg.h create mode 100644 pcsx2/windows/AdvancedDlg.cpp create mode 100644 pcsx2/windows/Cdrom02.ico create mode 100644 pcsx2/windows/ConfigDlg.cpp create mode 100644 pcsx2/windows/CpuDlg.cpp create mode 100644 pcsx2/windows/DebugMemory.cpp create mode 100644 pcsx2/windows/Debugger.cpp create mode 100644 pcsx2/windows/Debugger.h create mode 100644 pcsx2/windows/Debugreg.cpp create mode 100644 pcsx2/windows/HacksDlg.cpp create mode 100644 pcsx2/windows/McdsDlg.c create mode 100644 pcsx2/windows/McdsDlg.cpp create mode 100644 pcsx2/windows/McdsDlg.h create mode 100644 pcsx2/windows/PatchBrowser.cpp create mode 100644 pcsx2/windows/RDebugger.cpp create mode 100644 pcsx2/windows/RDebugger.h create mode 100644 pcsx2/windows/SamplProf.cpp create mode 100644 pcsx2/windows/VCprojects/pcsx2_2008.vcproj create mode 100644 pcsx2/windows/VCprojects/pthreads_2008.vcproj create mode 100644 pcsx2/windows/VCprojects/vsprops/common.vsprops create mode 100644 pcsx2/windows/VCprojects/vsprops/debug.vsprops create mode 100644 pcsx2/windows/VCprojects/vsprops/devbuild.vsprops create mode 100644 pcsx2/windows/VCprojects/vsprops/postBuild.tmpl create mode 100644 pcsx2/windows/VCprojects/vsprops/postBuild.unknown create mode 100644 pcsx2/windows/VCprojects/vsprops/preBuild.cmd create mode 100644 pcsx2/windows/VCprojects/vsprops/release.vsprops create mode 100644 pcsx2/windows/VCprojects/vsprops/svnrev_template.h create mode 100644 pcsx2/windows/VCprojects/vsprops/svnrev_unknown.h create mode 100644 pcsx2/windows/Win32.h create mode 100644 pcsx2/windows/WinConsole.cpp create mode 100644 pcsx2/windows/WinMain.cpp create mode 100644 pcsx2/windows/WinSysExec.cpp create mode 100644 pcsx2/windows/WinThreads.cpp create mode 100644 pcsx2/windows/WinVM.cpp create mode 100644 pcsx2/windows/afxresmw.h create mode 100644 pcsx2/windows/cheats/browser.cpp create mode 100644 pcsx2/windows/cheats/cheats.cpp create mode 100644 pcsx2/windows/cheats/cheats.h create mode 100644 pcsx2/windows/ini.cpp create mode 100644 pcsx2/windows/libs/gnu_gettext.lib create mode 100644 pcsx2/windows/libs/libintlmsc.h create mode 100644 pcsx2/windows/memzero.h create mode 100644 pcsx2/windows/pcsx2.rc create mode 100644 pcsx2/windows/ps2_silver.bmp create mode 100644 pcsx2/windows/pthreads/ANNOUNCE create mode 100644 pcsx2/windows/pthreads/BUGS create mode 100644 pcsx2/windows/pthreads/CONTRIBUTORS create mode 100644 pcsx2/windows/pthreads/COPYING create mode 100644 pcsx2/windows/pthreads/FAQ create mode 100644 pcsx2/windows/pthreads/MAINTAINERS create mode 100644 pcsx2/windows/pthreads/NEWS create mode 100644 pcsx2/windows/pthreads/README create mode 100644 pcsx2/windows/pthreads/README.NONPORTABLE create mode 100644 pcsx2/windows/pthreads/TODO create mode 100644 pcsx2/windows/pthreads/attr.c create mode 100644 pcsx2/windows/pthreads/barrier.c create mode 100644 pcsx2/windows/pthreads/cancel.c create mode 100644 pcsx2/windows/pthreads/cleanup.c create mode 100644 pcsx2/windows/pthreads/condvar.c create mode 100644 pcsx2/windows/pthreads/config.h create mode 100644 pcsx2/windows/pthreads/create.c create mode 100644 pcsx2/windows/pthreads/dll.c create mode 100644 pcsx2/windows/pthreads/errno.c create mode 100644 pcsx2/windows/pthreads/exit.c create mode 100644 pcsx2/windows/pthreads/fork.c create mode 100644 pcsx2/windows/pthreads/global.c create mode 100644 pcsx2/windows/pthreads/implement.h create mode 100644 pcsx2/windows/pthreads/misc.c create mode 100644 pcsx2/windows/pthreads/mutex.c create mode 100644 pcsx2/windows/pthreads/need_errno.h create mode 100644 pcsx2/windows/pthreads/nonportable.c create mode 100644 pcsx2/windows/pthreads/private.c create mode 100644 pcsx2/windows/pthreads/pthread.h create mode 100644 pcsx2/windows/pthreads/pthread_attr_destroy.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_getdetachstate.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_getinheritsched.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_getschedparam.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_getschedpolicy.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_getscope.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_getstackaddr.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_getstacksize.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_init.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_setdetachstate.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_setinheritsched.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_setschedparam.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_setschedpolicy.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_setscope.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_setstackaddr.c create mode 100644 pcsx2/windows/pthreads/pthread_attr_setstacksize.c create mode 100644 pcsx2/windows/pthreads/pthread_barrier_destroy.c create mode 100644 pcsx2/windows/pthreads/pthread_barrier_init.c create mode 100644 pcsx2/windows/pthreads/pthread_barrier_wait.c create mode 100644 pcsx2/windows/pthreads/pthread_barrierattr_destroy.c create mode 100644 pcsx2/windows/pthreads/pthread_barrierattr_getpshared.c create mode 100644 pcsx2/windows/pthreads/pthread_barrierattr_init.c create mode 100644 pcsx2/windows/pthreads/pthread_barrierattr_setpshared.c create mode 100644 pcsx2/windows/pthreads/pthread_cancel.c create mode 100644 pcsx2/windows/pthreads/pthread_cond_destroy.c create mode 100644 pcsx2/windows/pthreads/pthread_cond_init.c create mode 100644 pcsx2/windows/pthreads/pthread_cond_signal.c create mode 100644 pcsx2/windows/pthreads/pthread_cond_wait.c create mode 100644 pcsx2/windows/pthreads/pthread_condattr_destroy.c create mode 100644 pcsx2/windows/pthreads/pthread_condattr_getpshared.c create mode 100644 pcsx2/windows/pthreads/pthread_condattr_init.c create mode 100644 pcsx2/windows/pthreads/pthread_condattr_setpshared.c create mode 100644 pcsx2/windows/pthreads/pthread_delay_np.c create mode 100644 pcsx2/windows/pthreads/pthread_detach.c create mode 100644 pcsx2/windows/pthreads/pthread_equal.c create mode 100644 pcsx2/windows/pthreads/pthread_exit.c create mode 100644 pcsx2/windows/pthreads/pthread_getconcurrency.c create mode 100644 pcsx2/windows/pthreads/pthread_getschedparam.c create mode 100644 pcsx2/windows/pthreads/pthread_getspecific.c create mode 100644 pcsx2/windows/pthreads/pthread_getw32threadhandle_np.c create mode 100644 pcsx2/windows/pthreads/pthread_join.c create mode 100644 pcsx2/windows/pthreads/pthread_key_create.c create mode 100644 pcsx2/windows/pthreads/pthread_key_delete.c create mode 100644 pcsx2/windows/pthreads/pthread_kill.c create mode 100644 pcsx2/windows/pthreads/pthread_mutex_destroy.c create mode 100644 pcsx2/windows/pthreads/pthread_mutex_init.c create mode 100644 pcsx2/windows/pthreads/pthread_mutex_lock.c create mode 100644 pcsx2/windows/pthreads/pthread_mutex_timedlock.c create mode 100644 pcsx2/windows/pthreads/pthread_mutex_trylock.c create mode 100644 pcsx2/windows/pthreads/pthread_mutex_unlock.c create mode 100644 pcsx2/windows/pthreads/pthread_mutexattr_destroy.c create mode 100644 pcsx2/windows/pthreads/pthread_mutexattr_getkind_np.c create mode 100644 pcsx2/windows/pthreads/pthread_mutexattr_getpshared.c create mode 100644 pcsx2/windows/pthreads/pthread_mutexattr_gettype.c create mode 100644 pcsx2/windows/pthreads/pthread_mutexattr_init.c create mode 100644 pcsx2/windows/pthreads/pthread_mutexattr_setkind_np.c create mode 100644 pcsx2/windows/pthreads/pthread_mutexattr_setpshared.c create mode 100644 pcsx2/windows/pthreads/pthread_mutexattr_settype.c create mode 100644 pcsx2/windows/pthreads/pthread_num_processors_np.c create mode 100644 pcsx2/windows/pthreads/pthread_once.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlock_destroy.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlock_init.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlock_rdlock.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlock_timedrdlock.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlock_timedwrlock.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlock_tryrdlock.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlock_trywrlock.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlock_unlock.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlock_wrlock.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlockattr_destroy.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlockattr_getpshared.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlockattr_init.c create mode 100644 pcsx2/windows/pthreads/pthread_rwlockattr_setpshared.c create mode 100644 pcsx2/windows/pthreads/pthread_self.c create mode 100644 pcsx2/windows/pthreads/pthread_setcancelstate.c create mode 100644 pcsx2/windows/pthreads/pthread_setcanceltype.c create mode 100644 pcsx2/windows/pthreads/pthread_setconcurrency.c create mode 100644 pcsx2/windows/pthreads/pthread_setschedparam.c create mode 100644 pcsx2/windows/pthreads/pthread_setspecific.c create mode 100644 pcsx2/windows/pthreads/pthread_spin_destroy.c create mode 100644 pcsx2/windows/pthreads/pthread_spin_init.c create mode 100644 pcsx2/windows/pthreads/pthread_spin_lock.c create mode 100644 pcsx2/windows/pthreads/pthread_spin_trylock.c create mode 100644 pcsx2/windows/pthreads/pthread_spin_unlock.c create mode 100644 pcsx2/windows/pthreads/pthread_testcancel.c create mode 100644 pcsx2/windows/pthreads/pthread_timechange_handler_np.c create mode 100644 pcsx2/windows/pthreads/pthread_win32_attach_detach_np.c create mode 100644 pcsx2/windows/pthreads/ptw32_InterlockedCompareExchange.c create mode 100644 pcsx2/windows/pthreads/ptw32_MCS_lock.c create mode 100644 pcsx2/windows/pthreads/ptw32_callUserDestroyRoutines.c create mode 100644 pcsx2/windows/pthreads/ptw32_calloc.c create mode 100644 pcsx2/windows/pthreads/ptw32_cond_check_need_init.c create mode 100644 pcsx2/windows/pthreads/ptw32_getprocessors.c create mode 100644 pcsx2/windows/pthreads/ptw32_is_attr.c create mode 100644 pcsx2/windows/pthreads/ptw32_mutex_check_need_init.c create mode 100644 pcsx2/windows/pthreads/ptw32_new.c create mode 100644 pcsx2/windows/pthreads/ptw32_processInitialize.c create mode 100644 pcsx2/windows/pthreads/ptw32_processTerminate.c create mode 100644 pcsx2/windows/pthreads/ptw32_relmillisecs.c create mode 100644 pcsx2/windows/pthreads/ptw32_reuse.c create mode 100644 pcsx2/windows/pthreads/ptw32_rwlock_cancelwrwait.c create mode 100644 pcsx2/windows/pthreads/ptw32_rwlock_check_need_init.c create mode 100644 pcsx2/windows/pthreads/ptw32_semwait.c create mode 100644 pcsx2/windows/pthreads/ptw32_spinlock_check_need_init.c create mode 100644 pcsx2/windows/pthreads/ptw32_threadDestroy.c create mode 100644 pcsx2/windows/pthreads/ptw32_threadStart.c create mode 100644 pcsx2/windows/pthreads/ptw32_throw.c create mode 100644 pcsx2/windows/pthreads/ptw32_timespec.c create mode 100644 pcsx2/windows/pthreads/ptw32_tkAssocCreate.c create mode 100644 pcsx2/windows/pthreads/ptw32_tkAssocDestroy.c create mode 100644 pcsx2/windows/pthreads/rwlock.c create mode 100644 pcsx2/windows/pthreads/sched.c create mode 100644 pcsx2/windows/pthreads/sched.h create mode 100644 pcsx2/windows/pthreads/sched_get_priority_max.c create mode 100644 pcsx2/windows/pthreads/sched_get_priority_min.c create mode 100644 pcsx2/windows/pthreads/sched_getscheduler.c create mode 100644 pcsx2/windows/pthreads/sched_setscheduler.c create mode 100644 pcsx2/windows/pthreads/sched_yield.c create mode 100644 pcsx2/windows/pthreads/sem_close.c create mode 100644 pcsx2/windows/pthreads/sem_destroy.c create mode 100644 pcsx2/windows/pthreads/sem_getvalue.c create mode 100644 pcsx2/windows/pthreads/sem_init.c create mode 100644 pcsx2/windows/pthreads/sem_open.c create mode 100644 pcsx2/windows/pthreads/sem_post.c create mode 100644 pcsx2/windows/pthreads/sem_post_multiple.c create mode 100644 pcsx2/windows/pthreads/sem_timedwait.c create mode 100644 pcsx2/windows/pthreads/sem_trywait.c create mode 100644 pcsx2/windows/pthreads/sem_unlink.c create mode 100644 pcsx2/windows/pthreads/sem_wait.c create mode 100644 pcsx2/windows/pthreads/semaphore.c create mode 100644 pcsx2/windows/pthreads/semaphore.h create mode 100644 pcsx2/windows/pthreads/signal.c create mode 100644 pcsx2/windows/pthreads/spin.c create mode 100644 pcsx2/windows/pthreads/sync.c create mode 100644 pcsx2/windows/pthreads/tsd.c create mode 100644 pcsx2/windows/pthreads/w32_CancelableWait.c create mode 100644 pcsx2/windows/resource.h create mode 100644 pcsx2/x86/BaseblockEx.cpp create mode 100644 pcsx2/x86/BaseblockEx.h create mode 100644 pcsx2/x86/Makefile.am create mode 100644 pcsx2/x86/README create mode 100644 pcsx2/x86/aR3000A.S create mode 100644 pcsx2/x86/aVUzerorec.S create mode 100644 pcsx2/x86/aVif.S create mode 100644 pcsx2/x86/aVif.asm create mode 100644 pcsx2/x86/fast_routines.S create mode 100644 pcsx2/x86/fast_routines.cpp create mode 100644 pcsx2/x86/iCOP0.cpp create mode 100644 pcsx2/x86/iCOP0.h create mode 100644 pcsx2/x86/iCOP2.cpp create mode 100644 pcsx2/x86/iCore.cpp create mode 100644 pcsx2/x86/iCore.h create mode 100644 pcsx2/x86/iFPU.cpp create mode 100644 pcsx2/x86/iFPU.h create mode 100644 pcsx2/x86/iGS.cpp create mode 100644 pcsx2/x86/iHw.cpp create mode 100644 pcsx2/x86/iIPU.cpp create mode 100644 pcsx2/x86/iMMI.cpp create mode 100644 pcsx2/x86/iMMI.h create mode 100644 pcsx2/x86/iPsxHw.cpp create mode 100644 pcsx2/x86/iPsxMem.cpp create mode 100644 pcsx2/x86/iR3000A.cpp create mode 100644 pcsx2/x86/iR3000A.h create mode 100644 pcsx2/x86/iR3000Atables.cpp create mode 100644 pcsx2/x86/iR5900.h create mode 100644 pcsx2/x86/iR5900Arit.h create mode 100644 pcsx2/x86/iR5900AritImm.h create mode 100644 pcsx2/x86/iR5900Branch.h create mode 100644 pcsx2/x86/iR5900CoissuedLoadStore.cpp create mode 100644 pcsx2/x86/iR5900Jump.h create mode 100644 pcsx2/x86/iR5900LoadStore.h create mode 100644 pcsx2/x86/iR5900Misc.cpp create mode 100644 pcsx2/x86/iR5900Move.h create mode 100644 pcsx2/x86/iR5900MultDiv.h create mode 100644 pcsx2/x86/iR5900Shift.h create mode 100644 pcsx2/x86/iVU0micro.cpp create mode 100644 pcsx2/x86/iVU1micro.cpp create mode 100644 pcsx2/x86/iVUmicro.cpp create mode 100644 pcsx2/x86/iVUmicro.h create mode 100644 pcsx2/x86/iVUmicroLower.cpp create mode 100644 pcsx2/x86/iVUmicroUpper.cpp create mode 100644 pcsx2/x86/iVUops.h create mode 100644 pcsx2/x86/iVUzerorec.cpp create mode 100644 pcsx2/x86/iVUzerorec.h create mode 100644 pcsx2/x86/iVif.cpp create mode 100644 pcsx2/x86/ir5900tables.cpp create mode 100644 pcsx2/x86/ix86-32/aR5900-32.S create mode 100644 pcsx2/x86/ix86-32/aVif_proc-32.asm create mode 100644 pcsx2/x86/ix86-32/iCore-32.cpp create mode 100644 pcsx2/x86/ix86-32/iR5900-32.cpp create mode 100644 pcsx2/x86/ix86-32/iR5900Arit.cpp create mode 100644 pcsx2/x86/ix86-32/iR5900AritImm.cpp create mode 100644 pcsx2/x86/ix86-32/iR5900Branch.cpp create mode 100644 pcsx2/x86/ix86-32/iR5900Jump.cpp create mode 100644 pcsx2/x86/ix86-32/iR5900LoadStore.cpp create mode 100644 pcsx2/x86/ix86-32/iR5900Move.cpp create mode 100644 pcsx2/x86/ix86-32/iR5900MultDiv.cpp create mode 100644 pcsx2/x86/ix86-32/iR5900Shift.cpp create mode 100644 pcsx2/x86/ix86-32/iR5900Templates.cpp create mode 100644 pcsx2/x86/ix86-32/recVTLB.cpp create mode 100644 pcsx2/x86/ix86/Makefile.am create mode 100644 pcsx2/x86/ix86/ix86.cpp create mode 100644 pcsx2/x86/ix86/ix86.h create mode 100644 pcsx2/x86/ix86/ix86_3dnow.cpp create mode 100644 pcsx2/x86/ix86/ix86_cpudetect.cpp create mode 100644 pcsx2/x86/ix86/ix86_fpu.cpp create mode 100644 pcsx2/x86/ix86/ix86_mmx.cpp create mode 100644 pcsx2/x86/ix86/ix86_sse.cpp create mode 100644 pcsx2/x86/ix86/ix86_tools.cpp create mode 100644 pcsx2/x86/megaVU.cpp create mode 100644 pcsx2/x86/megaVU.h create mode 100644 pcsx2/x86/megaVU_Lower.cpp create mode 100644 pcsx2/x86/megaVU_Tables.cpp create mode 100644 pcsx2/x86/megaVU_Tables.h create mode 100644 pcsx2/x86/megaVU_Upper.cpp create mode 100644 pcsx2/xmlpatchloader.cpp create mode 100644 pcsx2_2008.sln create mode 100644 pcsx2_suite_2008.sln diff --git a/3rdparty/SoundTouch/3dnow_win.cpp b/3rdparty/SoundTouch/3dnow_win.cpp new file mode 100644 index 0000000000..2e369904ec --- /dev/null +++ b/3rdparty/SoundTouch/3dnow_win.cpp @@ -0,0 +1,350 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Win32 version of the AMD 3DNow! optimized routines for AMD K6-2/Athlon +/// processors. All 3DNow! optimized functions have been gathered into this +/// single source code file, regardless to their class or original source code +/// file, in order to ease porting the library to other compiler and processor +/// platforms. +/// +/// By the way; the performance gain depends heavily on the CPU generation: On +/// K6-2 these routines provided speed-up of even 2.4 times, while on Athlon the +/// difference to the original routines stayed at unremarkable 8%! Such a small +/// improvement on Athlon is due to 3DNow can perform only two operations in +/// parallel, and obviously also the Athlon FPU is doing a very good job with +/// the standard C floating point routines! Here these routines are anyway, +/// although it might not be worth the effort to convert these to GCC platform, +/// for Athlon CPU at least. The situation is different regarding the SSE +/// optimizations though, thanks to the four parallel operations of SSE that +/// already make a difference. +/// +/// This file is to be compiled in Windows platform with Microsoft Visual C++ +/// Compiler. Please see '3dnow_gcc.cpp' for the gcc compiler version for all +/// GNU platforms (if file supplied). +/// +/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++ +/// 6.0 processor pack" update to support 3DNow! instruction set. The update is +/// available for download at Microsoft Developers Network, see here: +/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx +/// +/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and +/// perform a search with keywords "processor pack". +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.10 $ +// +// $Id: 3dnow_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include "cpu_detect.h" +#include "STTypes.h" + +#ifndef _WIN32 +#error "wrong platform - this source code file is exclusively for Win32 platform" +#endif + +using namespace soundtouch; + +#ifdef ALLOW_3DNOW +// 3DNow! routines available only with float sample type + +////////////////////////////////////////////////////////////////////////////// +// +// implementation of 3DNow! optimized functions of class 'TDStretch3DNow' +// +////////////////////////////////////////////////////////////////////////////// + +#include "TDStretch.h" +#include + +// these are declared in 'TDStretch.cpp' +extern int scanOffsets[4][24]; + + +// Calculates cross correlation of two buffers +double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const +{ + uint overlapLengthLocal = overlapLength; + float corr; + + // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors + /* + c-pseudocode: + + corr = 0; + for (i = 0; i < overlapLength / 4; i ++) + { + corr += pV1[0] * pV2[0]; + pV1[1] * pV2[1]; + pV1[2] * pV2[2]; + pV1[3] * pV2[3]; + pV1[4] * pV2[4]; + pV1[5] * pV2[5]; + pV1[6] * pV2[6]; + pV1[7] * pV2[7]; + + pV1 += 8; + pV2 += 8; + } + */ + + _asm + { + // give prefetch hints to CPU of what data are to be needed soonish. + // give more aggressive hints on pV1 as that changes more between different calls + // while pV2 stays the same. + prefetch [pV1] + prefetch [pV2] + prefetch [pV1 + 32] + + mov eax, dword ptr pV2 + mov ebx, dword ptr pV1 + + pxor mm0, mm0 + + mov ecx, overlapLengthLocal + shr ecx, 2 // div by four + + loop1: + movq mm1, [eax] + prefetch [eax + 32] // give a prefetch hint to CPU what data are to be needed soonish + pfmul mm1, [ebx] + prefetch [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish + + movq mm2, [eax + 8] + pfadd mm0, mm1 + pfmul mm2, [ebx + 8] + + movq mm3, [eax + 16] + pfadd mm0, mm2 + pfmul mm3, [ebx + 16] + + movq mm4, [eax + 24] + pfadd mm0, mm3 + pfmul mm4, [ebx + 24] + + add eax, 32 + pfadd mm0, mm4 + add ebx, 32 + + dec ecx + jnz loop1 + + // add halfs of mm0 together and return the result. + // note: mm1 is used as a dummy parameter only, we actually don't care about it's value + pfacc mm0, mm1 + movd corr, mm0 + femms + } + + return corr; +} + + + + +////////////////////////////////////////////////////////////////////////////// +// +// implementation of 3DNow! optimized functions of class 'FIRFilter' +// +////////////////////////////////////////////////////////////////////////////// + +#include "FIRFilter.h" + +FIRFilter3DNow::FIRFilter3DNow() : FIRFilter() +{ + filterCoeffsUnalign = NULL; +} + + +FIRFilter3DNow::~FIRFilter3DNow() +{ + delete[] filterCoeffsUnalign; +} + + +// (overloaded) Calculates filter coefficients for 3DNow! routine +void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor) +{ + uint i; + float fDivider; + + FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor); + + // Scale the filter coefficients so that it won't be necessary to scale the filtering result + // also rearrange coefficients suitably for 3DNow! + // Ensure that filter coeffs array is aligned to 16-byte boundary + delete[] filterCoeffsUnalign; + filterCoeffsUnalign = new float[2 * newLength + 4]; + filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & -16); + + fDivider = (float)resultDivider; + + // rearrange the filter coefficients for mmx routines + for (i = 0; i < newLength; i ++) + { + filterCoeffsAlign[2 * i + 0] = + filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider; + } +} + + +// 3DNow!-optimized version of the filter routine for stereo sound +uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, const uint numSamples) const +{ + float *filterCoeffsLocal = filterCoeffsAlign; + uint count = (numSamples - length) & -2; + uint lengthLocal = length / 4; + + assert(length != 0); + assert(count % 2 == 0); + + /* original code: + + double suml1, suml2; + double sumr1, sumr2; + uint i, j; + + for (j = 0; j < count; j += 2) + { + const float *ptr; + + suml1 = sumr1 = 0.0; + suml2 = sumr2 = 0.0; + ptr = src; + filterCoeffsLocal = filterCoeffs; + for (i = 0; i < lengthLocal; i ++) + { + // unroll loop for efficiency. + + suml1 += ptr[0] * filterCoeffsLocal[0] + + ptr[2] * filterCoeffsLocal[2] + + ptr[4] * filterCoeffsLocal[4] + + ptr[6] * filterCoeffsLocal[6]; + + sumr1 += ptr[1] * filterCoeffsLocal[1] + + ptr[3] * filterCoeffsLocal[3] + + ptr[5] * filterCoeffsLocal[5] + + ptr[7] * filterCoeffsLocal[7]; + + suml2 += ptr[8] * filterCoeffsLocal[0] + + ptr[10] * filterCoeffsLocal[2] + + ptr[12] * filterCoeffsLocal[4] + + ptr[14] * filterCoeffsLocal[6]; + + sumr2 += ptr[9] * filterCoeffsLocal[1] + + ptr[11] * filterCoeffsLocal[3] + + ptr[13] * filterCoeffsLocal[5] + + ptr[15] * filterCoeffsLocal[7]; + + ptr += 16; + filterCoeffsLocal += 8; + } + dest[0] = (float)suml1; + dest[1] = (float)sumr1; + dest[2] = (float)suml2; + dest[3] = (float)sumr2; + + src += 4; + dest += 4; + } + + */ + _asm + { + mov eax, dword ptr dest + mov ebx, dword ptr src + mov edx, count + shr edx, 1 + + loop1: + // "outer loop" : during each round 2*2 output samples are calculated + prefetch [ebx] // give a prefetch hint to CPU what data are to be needed soonish + prefetch [filterCoeffsLocal] // give a prefetch hint to CPU what data are to be needed soonish + + mov esi, ebx + mov edi, filterCoeffsLocal + pxor mm0, mm0 + pxor mm1, mm1 + mov ecx, lengthLocal + + loop2: + // "inner loop" : during each round four FIR filter taps are evaluated for 2*2 output samples + movq mm2, [edi] + movq mm3, mm2 + prefetch [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish + pfmul mm2, [esi] + prefetch [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish + pfmul mm3, [esi + 8] + + movq mm4, [edi + 8] + movq mm5, mm4 + pfadd mm0, mm2 + pfmul mm4, [esi + 8] + pfadd mm1, mm3 + pfmul mm5, [esi + 16] + + movq mm2, [edi + 16] + movq mm6, mm2 + pfadd mm0, mm4 + pfmul mm2, [esi + 16] + pfadd mm1, mm5 + pfmul mm6, [esi + 24] + + movq mm3, [edi + 24] + movq mm7, mm3 + pfadd mm0, mm2 + pfmul mm3, [esi + 24] + pfadd mm1, mm6 + pfmul mm7, [esi + 32] + add esi, 32 + pfadd mm0, mm3 + add edi, 32 + pfadd mm1, mm7 + + dec ecx + jnz loop2 + + movq [eax], mm0 + add ebx, 16 + movq [eax + 8], mm1 + add eax, 16 + + dec edx + jnz loop1 + + femms + } + + return count; +} + + +#endif // ALLOW_3DNOW diff --git a/3rdparty/SoundTouch/AAFilter.cpp b/3rdparty/SoundTouch/AAFilter.cpp new file mode 100644 index 0000000000..9a5cf539c0 --- /dev/null +++ b/3rdparty/SoundTouch/AAFilter.cpp @@ -0,0 +1,184 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// FIR low-pass (anti-alias) filter with filter coefficient design routine and +/// MMX optimization. +/// +/// Anti-alias filter is used to prevent folding of high frequencies when +/// transposing the sample rate with interpolation. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.9 $ +// +// $Id: AAFilter.cpp,v 1.9 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "AAFilter.h" +#include "FIRFilter.h" + +using namespace soundtouch; + +#define PI 3.141592655357989 +#define TWOPI (2 * PI) + +/***************************************************************************** + * + * Implementation of the class 'AAFilter' + * + *****************************************************************************/ + +AAFilter::AAFilter(const uint length) +{ + pFIR = FIRFilter::newInstance(); + cutoffFreq = 0.5; + setLength(length); +} + + + +AAFilter::~AAFilter() +{ + delete pFIR; +} + + + +// Sets new anti-alias filter cut-off edge frequency, scaled to +// sampling frequency (nyquist frequency = 0.5). +// The filter will cut frequencies higher than the given frequency. +void AAFilter::setCutoffFreq(const double newCutoffFreq) +{ + cutoffFreq = newCutoffFreq; + calculateCoeffs(); +} + + + +// Sets number of FIR filter taps +void AAFilter::setLength(const uint newLength) +{ + length = newLength; + calculateCoeffs(); +} + + + +// Calculates coefficients for a low-pass FIR filter using Hamming window +void AAFilter::calculateCoeffs() +{ + uint i; + double cntTemp, temp, tempCoeff,h, w; + double fc2, wc; + double scaleCoeff, sum; + double *work; + SAMPLETYPE *coeffs; + + assert(length > 0); + assert(length % 4 == 0); + assert(cutoffFreq >= 0); + assert(cutoffFreq <= 0.5); + + work = new double[length]; + coeffs = new SAMPLETYPE[length]; + + fc2 = 2.0 * cutoffFreq; + wc = PI * fc2; + tempCoeff = TWOPI / (double)length; + + sum = 0; + for (i = 0; i < length; i ++) + { + cntTemp = (double)i - (double)(length / 2); + + temp = cntTemp * wc; + if (temp != 0) + { + h = fc2 * sin(temp) / temp; // sinc function + } + else + { + h = 1.0; + } + w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window + + temp = w * h; + work[i] = temp; + + // calc net sum of coefficients + sum += temp; + } + + // ensure the sum of coefficients is larger than zero + assert(sum > 0); + + // ensure we've really designed a lowpass filter... + assert(work[length/2] > 0); + assert(work[length/2 + 1] > -1e-6); + assert(work[length/2 - 1] > -1e-6); + + // Calculate a scaling coefficient in such a way that the result can be + // divided by 16384 + scaleCoeff = 16384.0f / sum; + + for (i = 0; i < length; i ++) + { + // scale & round to nearest integer + temp = work[i] * scaleCoeff; + temp += (temp >= 0) ? 0.5 : -0.5; + // ensure no overfloods + assert(temp >= -32768 && temp <= 32767); + coeffs[i] = (SAMPLETYPE)temp; + } + + // Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384 + pFIR->setCoefficients(coeffs, length, 14); + + delete[] work; + delete[] coeffs; +} + + +// Applies the filter to the given sequence of samples. +// Note : The amount of outputted samples is by value of 'filter length' +// smaller than the amount of input samples. +uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const +{ + return pFIR->evaluate(dest, src, numSamples, numChannels); +} + + +uint AAFilter::getLength() const +{ + return pFIR->getLength(); +} diff --git a/3rdparty/SoundTouch/AAFilter.h b/3rdparty/SoundTouch/AAFilter.h new file mode 100644 index 0000000000..4c85dcdbe7 --- /dev/null +++ b/3rdparty/SoundTouch/AAFilter.h @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo +/// while maintaining the original pitch by using a time domain WSOLA-like method +/// with several performance-increasing tweaks. +/// +/// Anti-alias filter is used to prevent folding of high frequencies when +/// transposing the sample rate with interpolation. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.10 $ +// +// $Id: AAFilter.h,v 1.10 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef AAFilter_H +#define AAFilter_H + +#include "STTypes.h" + +namespace soundtouch +{ + +class AAFilter +{ +protected: + class FIRFilter *pFIR; + + /// Low-pass filter cut-off frequency, negative = invalid + double cutoffFreq; + + /// num of filter taps + uint length; + + /// Calculate the FIR coefficients realizing the given cutoff-frequency + void calculateCoeffs(); +public: + AAFilter(uint length); + + ~AAFilter(); + + /// Sets new anti-alias filter cut-off edge frequency, scaled to sampling + /// frequency (nyquist frequency = 0.5). The filter will cut off the + /// frequencies than that. + void setCutoffFreq(double newCutoffFreq); + + /// Sets number of FIR filter taps, i.e. ~filter complexity + void setLength(uint newLength); + + uint getLength() const; + + /// Applies the filter to the given sequence of samples. + /// Note : The amount of outputted samples is by value of 'filter length' + /// smaller than the amount of input samples. + uint evaluate(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples, + uint numChannels) const; +}; + +} + +#endif diff --git a/3rdparty/SoundTouch/BPMDetect.h b/3rdparty/SoundTouch/BPMDetect.h new file mode 100644 index 0000000000..ac616e7e2b --- /dev/null +++ b/3rdparty/SoundTouch/BPMDetect.h @@ -0,0 +1,159 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Beats-per-minute (BPM) detection routine. +/// +/// The beat detection algorithm works as follows: +/// - Use function 'inputSamples' to input a chunks of samples to the class for +/// analysis. It's a good idea to enter a large sound file or stream in smallish +/// chunks of around few kilosamples in order not to extinguish too much RAM memory. +/// - Input sound data is decimated to approx 500 Hz to reduce calculation burden, +/// which is basically ok as low (bass) frequencies mostly determine the beat rate. +/// Simple averaging is used for anti-alias filtering because the resulting signal +/// quality isn't of that high importance. +/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by +/// taking absolute value that's smoothed by sliding average. Signal levels that +/// are below a couple of times the general RMS amplitude level are cut away to +/// leave only notable peaks there. +/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term +/// autocorrelation function of the enveloped signal. +/// - After whole sound data file has been analyzed as above, the bpm level is +/// detected by function 'getBpm' that finds the highest peak of the autocorrelation +/// function, calculates it's precise location and converts this reading to bpm's. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.5 $ +// +// $Id: BPMDetect.h,v 1.5 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _BPMDetect_H_ +#define _BPMDetect_H_ + +#include "STTypes.h" +#include "FIFOSampleBuffer.h" + +/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit. +#define MIN_BPM 45 + +/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit. +#define MAX_BPM 230 + + +/// Class for calculating BPM rate for audio data. +class BPMDetect +{ +protected: + /// Auto-correlation accumulator bins. + float *xcorr; + + /// Amplitude envelope sliding average approximation level accumulator + float envelopeAccu; + + /// RMS volume sliding average approximation level accumulator + float RMSVolumeAccu; + + /// Sample average counter. + int decimateCount; + + /// Sample average accumulator for FIFO-like decimation. + soundtouch::LONG_SAMPLETYPE decimateSum; + + /// Decimate sound by this coefficient to reach approx. 500 Hz. + int decimateBy; + + /// Auto-correlation window length + int windowLen; + + /// Number of channels (1 = mono, 2 = stereo) + int channels; + + /// sample rate + int sampleRate; + + /// Beginning of auto-correlation window: Autocorrelation isn't being updated for + /// the first these many correlation bins. + int windowStart; + + /// FIFO-buffer for decimated processing samples. + soundtouch::FIFOSampleBuffer *buffer; + + /// Initialize the class for processing. + void init(int numChannels, int sampleRate); + + /// Updates auto-correlation function for given number of decimated samples that + /// are read from the internal 'buffer' pipe (samples aren't removed from the pipe + /// though). + void updateXCorr(int process_samples /// How many samples are processed. + ); + + /// Decimates samples to approx. 500 Hz. + /// + /// \return Number of output samples. + int decimate(soundtouch::SAMPLETYPE *dest, ///< Destination buffer + const soundtouch::SAMPLETYPE *src, ///< Source sample buffer + int numsamples ///< Number of source samples. + ); + + /// Calculates amplitude envelope for the buffer of samples. + /// Result is output to 'samples'. + void calcEnvelope(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/output data buffer + int numsamples ///< Number of samples in buffer + ); + +public: + /// Constructor. + BPMDetect(int numChannels, ///< Number of channels in sample data. + int sampleRate ///< Sample rate in Hz. + ); + + /// Destructor. + virtual ~BPMDetect(); + + /// Inputs a block of samples for analyzing: Envelopes the samples and then + /// updates the autocorrelation estimation. When whole song data has been input + /// in smaller blocks using this function, read the resulting bpm with 'getBpm' + /// function. + /// + /// Notice that data in 'samples' array can be disrupted in processing. + void inputSamples(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer + int numSamples ///< Number of samples in buffer + ); + + + /// Analyzes the results and returns the BPM rate. Use this function to read result + /// after whole song data has been input to the class by consecutive calls of + /// 'inputSamples' function. + /// + /// \return Beats-per-minute rate, or zero if detection failed. + float getBpm(); +}; + +#endif // _BPMDetect_H_ diff --git a/3rdparty/SoundTouch/FIFOSampleBuffer.cpp b/3rdparty/SoundTouch/FIFOSampleBuffer.cpp new file mode 100644 index 0000000000..2bf965b763 --- /dev/null +++ b/3rdparty/SoundTouch/FIFOSampleBuffer.cpp @@ -0,0 +1,252 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// A buffer class for temporarily storaging sound samples, operates as a +/// first-in-first-out pipe. +/// +/// Samples are added to the end of the sample buffer with the 'putSamples' +/// function, and are received from the beginning of the buffer by calling +/// the 'receiveSamples' function. The class automatically removes the +/// outputted samples from the buffer, as well as grows the buffer size +/// whenever necessary. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.11 $ +// +// $Id: FIFOSampleBuffer.cpp,v 1.11 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "FIFOSampleBuffer.h" + +using namespace soundtouch; + +// Constructor +FIFOSampleBuffer::FIFOSampleBuffer(uint numChannels) +{ + sizeInBytes = 0; // reasonable initial value + buffer = NULL; //new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE)]; + bufferUnaligned = NULL; + samplesInBuffer = 0; + bufferPos = 0; + channels = numChannels; +} + + +// destructor +FIFOSampleBuffer::~FIFOSampleBuffer() +{ + delete[] bufferUnaligned; +} + + +// Sets number of channels, 1 = mono, 2 = stereo +void FIFOSampleBuffer::setChannels(const uint numChannels) +{ + uint usedBytes; + + usedBytes = channels * samplesInBuffer; + channels = numChannels; + samplesInBuffer = usedBytes / channels; +} + + +// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and +// zeroes this pointer by copying samples from the 'bufferPos' pointer +// location on to the beginning of the buffer. +void FIFOSampleBuffer::rewind() +{ + if (bufferPos) + { + memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer); + bufferPos = 0; + } +} + + +// Adds 'numSamples' pcs of samples from the 'samples' memory position to +// the sample buffer. +void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint numSamples) +{ + memcpy(ptrEnd(numSamples), samples, sizeof(SAMPLETYPE) * numSamples * channels); + samplesInBuffer += numSamples; +} + + +// Increases the number of samples in the buffer without copying any actual +// samples. +// +// This function is used to update the number of samples in the sample buffer +// when accessing the buffer directly with 'ptrEnd' function. Please be +// careful though! +void FIFOSampleBuffer::putSamples(uint numSamples) +{ + uint req; + + req = samplesInBuffer + numSamples; + ensureCapacity(req); + samplesInBuffer += numSamples; +} + + +// Returns a pointer to the end of the used part of the sample buffer (i.e. +// where the new samples are to be inserted). This function may be used for +// inserting new samples into the sample buffer directly. Please be careful! +// +// Parameter 'slackCapacity' tells the function how much free capacity (in +// terms of samples) there _at least_ should be, in order to the caller to +// succesfully insert all the required samples to the buffer. When necessary, +// the function grows the buffer size to comply with this requirement. +// +// When using this function as means for inserting new samples, also remember +// to increase the sample count afterwards, by calling the +// 'putSamples(numSamples)' function. +SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity) +{ + ensureCapacity(samplesInBuffer + slackCapacity); + return buffer + samplesInBuffer * channels; +} + + +// Returns a pointer to the beginning of the currently non-outputted samples. +// This function is provided for accessing the output samples directly. +// Please be careful! +// +// When using this function to output samples, also remember to 'remove' the +// outputted samples from the buffer by calling the +// 'receiveSamples(numSamples)' function +SAMPLETYPE *FIFOSampleBuffer::ptrBegin() const +{ + return buffer + bufferPos * channels; +} + + +// Ensures that the buffer has enought capacity, i.e. space for _at least_ +// 'capacityRequirement' number of samples. The buffer is grown in steps of +// 4 kilobytes to eliminate the need for frequently growing up the buffer, +// as well as to round the buffer size up to the virtual memory page size. +void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement) +{ + SAMPLETYPE *tempUnaligned, *temp; + + if (capacityRequirement > getCapacity()) + { + // enlarge the buffer in 4kbyte steps (round up to next 4k boundary) + sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & -4096; + assert(sizeInBytes % 2 == 0); + tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)]; + if (tempUnaligned == NULL) + { + throw std::runtime_error("Couldn't allocate memory!\n"); + } + temp = (SAMPLETYPE *)(((ulongptr)tempUnaligned + 15) & -16); + memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE)); + delete[] bufferUnaligned; + buffer = temp; + bufferUnaligned = tempUnaligned; + bufferPos = 0; + } + else + { + // simply rewind the buffer (if necessary) + rewind(); + } +} + + +// Returns the current buffer capacity in terms of samples +uint FIFOSampleBuffer::getCapacity() const +{ + return sizeInBytes / (channels * sizeof(SAMPLETYPE)); +} + + +// Returns the number of samples currently in the buffer +uint FIFOSampleBuffer::numSamples() const +{ + return samplesInBuffer; +} + + +// Output samples from beginning of the sample buffer. Copies demanded number +// of samples to output and removes them from the sample buffer. If there +// are less than 'numsample' samples in the buffer, returns all available. +// +// Returns number of samples copied. +uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples) +{ + uint num; + + num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples; + + memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num); + return receiveSamples(num); +} + + +// Removes samples from the beginning of the sample buffer without copying them +// anywhere. Used to reduce the number of samples in the buffer, when accessing +// the sample buffer with the 'ptrBegin' function. +uint FIFOSampleBuffer::receiveSamples(uint maxSamples) +{ + if (maxSamples >= samplesInBuffer) + { + uint temp; + + temp = samplesInBuffer; + samplesInBuffer = 0; + return temp; + } + + samplesInBuffer -= maxSamples; + bufferPos += maxSamples; + + return maxSamples; +} + + +// Returns nonzero if the sample buffer is empty +int FIFOSampleBuffer::isEmpty() const +{ + return (samplesInBuffer == 0) ? 1 : 0; +} + + +// Clears the sample buffer +void FIFOSampleBuffer::clear() +{ + samplesInBuffer = 0; + bufferPos = 0; +} diff --git a/3rdparty/SoundTouch/FIFOSampleBuffer.h b/3rdparty/SoundTouch/FIFOSampleBuffer.h new file mode 100644 index 0000000000..09f74c9a65 --- /dev/null +++ b/3rdparty/SoundTouch/FIFOSampleBuffer.h @@ -0,0 +1,174 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// A buffer class for temporarily storaging sound samples, operates as a +/// first-in-first-out pipe. +/// +/// Samples are added to the end of the sample buffer with the 'putSamples' +/// function, and are received from the beginning of the buffer by calling +/// the 'receiveSamples' function. The class automatically removes the +/// output samples from the buffer as well as grows the storage size +/// whenever necessary. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.9 $ +// +// $Id: FIFOSampleBuffer.h,v 1.9 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FIFOSampleBuffer_H +#define FIFOSampleBuffer_H + +#include "FIFOSamplePipe.h" + +namespace soundtouch +{ + +/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes +/// care of storage size adjustment and data moving during input/output operations. +/// +/// Notice that in case of stereo audio, one sample is considered to consist of +/// both channel data. +class FIFOSampleBuffer : public FIFOSamplePipe +{ +private: + /// Sample buffer. + SAMPLETYPE *buffer; + + // Raw unaligned buffer memory. 'buffer' is made aligned by pointing it to first + // 16-byte aligned location of this buffer + SAMPLETYPE *bufferUnaligned; + + /// Sample buffer size in bytes + uint sizeInBytes; + + /// How many samples are currently in buffer. + uint samplesInBuffer; + + /// Channels, 1=mono, 2=stereo. + uint channels; + + /// Current position pointer to the buffer. This pointer is increased when samples are + /// removed from the pipe so that it's necessary to actually rewind buffer (move data) + /// only new data when is put to the pipe. + uint bufferPos; + + /// Rewind the buffer by moving data from position pointed by 'bufferPos' to real + /// beginning of the buffer. + void rewind(); + + /// Ensures that the buffer has capacity for at least this many samples. + void ensureCapacity(const uint capacityRequirement); + + /// Returns current capacity. + uint getCapacity() const; + +public: + + /// Constructor + FIFOSampleBuffer(uint numChannels = 2 ///< Number of channels, 1=mono, 2=stereo. + ///< Default is stereo. + ); + + /// destructor + ~FIFOSampleBuffer(); + + /// Returns a pointer to the beginning of the output samples. + /// This function is provided for accessing the output samples directly. + /// Please be careful for not to corrupt the book-keeping! + /// + /// When using this function to output samples, also remember to 'remove' the + /// output samples from the buffer by calling the + /// 'receiveSamples(numSamples)' function + virtual SAMPLETYPE *ptrBegin() const; + + /// Returns a pointer to the end of the used part of the sample buffer (i.e. + /// where the new samples are to be inserted). This function may be used for + /// inserting new samples into the sample buffer directly. Please be careful + /// not corrupt the book-keeping! + /// + /// When using this function as means for inserting new samples, also remember + /// to increase the sample count afterwards, by calling the + /// 'putSamples(numSamples)' function. + SAMPLETYPE *ptrEnd( + uint slackCapacity ///< How much free capacity (in samples) there _at least_ + ///< should be so that the caller can succesfully insert the + ///< desired samples to the buffer. If necessary, the function + ///< grows the buffer size to comply with this requirement. + ); + + /// Adds 'numSamples' pcs of samples from the 'samples' memory position to + /// the sample buffer. + virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples. + uint numSamples ///< Number of samples to insert. + ); + + /// Adjusts the book-keeping to increase number of samples in the buffer without + /// copying any actual samples. + /// + /// This function is used to update the number of samples in the sample buffer + /// when accessing the buffer directly with 'ptrEnd' function. Please be + /// careful though! + virtual void putSamples(uint numSamples ///< Number of samples been inserted. + ); + + /// Output samples from beginning of the sample buffer. Copies requested samples to + /// output buffer and removes them from the sample buffer. If there are less than + /// 'numsample' samples in the buffer, returns all that available. + /// + /// \return Number of samples returned. + virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples. + uint maxSamples ///< How many samples to receive at max. + ); + + /// Adjusts book-keeping so that given number of samples are removed from beginning of the + /// sample buffer without copying them anywhere. + /// + /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly + /// with 'ptrBegin' function. + virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. + ); + + /// Returns number of samples currently available. + virtual uint numSamples() const; + + /// Sets number of channels, 1 = mono, 2 = stereo. + void setChannels(uint numChannels); + + /// Returns nonzero if there aren't any samples available for outputting. + virtual int isEmpty() const; + + /// Clears all the samples. + virtual void clear(); +}; + +} + +#endif diff --git a/3rdparty/SoundTouch/FIFOSamplePipe.h b/3rdparty/SoundTouch/FIFOSamplePipe.h new file mode 100644 index 0000000000..33e33c7e36 --- /dev/null +++ b/3rdparty/SoundTouch/FIFOSamplePipe.h @@ -0,0 +1,217 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// 'FIFOSamplePipe' : An abstract base class for classes that manipulate sound +/// samples by operating like a first-in-first-out pipe: New samples are fed +/// into one end of the pipe with the 'putSamples' function, and the processed +/// samples are received from the other end with the 'receiveSamples' function. +/// +/// 'FIFOProcessor' : A base class for classes the do signal processing with +/// the samples while operating like a first-in-first-out pipe. When samples +/// are input with the 'putSamples' function, the class processes them +/// and moves the processed samples to the given 'output' pipe object, which +/// may be either another processing stage, or a fifo sample buffer object. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.8 $ +// +// $Id: FIFOSamplePipe.h,v 1.8 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FIFOSamplePipe_H +#define FIFOSamplePipe_H + +#include +#include +#include "STTypes.h" + +namespace soundtouch +{ + +/// Abstract base class for FIFO (first-in-first-out) sample processing classes. +class FIFOSamplePipe +{ +public: + /// Returns a pointer to the beginning of the output samples. + /// This function is provided for accessing the output samples directly. + /// Please be careful for not to corrupt the book-keeping! + /// + /// When using this function to output samples, also remember to 'remove' the + /// output samples from the buffer by calling the + /// 'receiveSamples(numSamples)' function + virtual SAMPLETYPE *ptrBegin() const = 0; + + /// Adds 'numSamples' pcs of samples from the 'samples' memory position to + /// the sample buffer. + virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples. + uint numSamples ///< Number of samples to insert. + ) = 0; + + + // Moves samples from the 'other' pipe instance to this instance. + void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data. + ) + { + int oNumSamples = other.numSamples(); + + putSamples(other.ptrBegin(), oNumSamples); + other.receiveSamples(oNumSamples); + }; + + /// Output samples from beginning of the sample buffer. Copies requested samples to + /// output buffer and removes them from the sample buffer. If there are less than + /// 'numsample' samples in the buffer, returns all that available. + /// + /// \return Number of samples returned. + virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples. + uint maxSamples ///< How many samples to receive at max. + ) = 0; + + /// Adjusts book-keeping so that given number of samples are removed from beginning of the + /// sample buffer without copying them anywhere. + /// + /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly + /// with 'ptrBegin' function. + virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. + ) = 0; + + /// Returns number of samples currently available. + virtual uint numSamples() const = 0; + + // Returns nonzero if there aren't any samples available for outputting. + virtual int isEmpty() const = 0; + + /// Clears all the samples. + virtual void clear() = 0; +}; + + + +/// Base-class for sound processing routines working in FIFO principle. With this base +/// class it's easy to implement sound processing stages that can be chained together, +/// so that samples that are fed into beginning of the pipe automatically go through +/// all the processing stages. +/// +/// When samples are input to this class, they're first processed and then put to +/// the FIFO pipe that's defined as output of this class. This output pipe can be +/// either other processing stage or a FIFO sample buffer. +class FIFOProcessor :public FIFOSamplePipe +{ +protected: + /// Internal pipe where processed samples are put. + FIFOSamplePipe *output; + + /// Sets output pipe. + void setOutPipe(FIFOSamplePipe *pOutput) + { + assert(output == NULL); + assert(pOutput != NULL); + output = pOutput; + } + + + /// Constructor. Doesn't define output pipe; it has to be set be + /// 'setOutPipe' function. + FIFOProcessor() + { + output = NULL; + } + + + /// Constructor. Configures output pipe. + FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe. + ) + { + output = pOutput; + } + + + /// Destructor. + virtual ~FIFOProcessor() + { + } + + + /// Returns a pointer to the beginning of the output samples. + /// This function is provided for accessing the output samples directly. + /// Please be careful for not to corrupt the book-keeping! + /// + /// When using this function to output samples, also remember to 'remove' the + /// output samples from the buffer by calling the + /// 'receiveSamples(numSamples)' function + virtual SAMPLETYPE *ptrBegin() const + { + return output->ptrBegin(); + } + +public: + + /// Output samples from beginning of the sample buffer. Copies requested samples to + /// output buffer and removes them from the sample buffer. If there are less than + /// 'numsample' samples in the buffer, returns all that available. + /// + /// \return Number of samples returned. + virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples. + uint maxSamples ///< How many samples to receive at max. + ) + { + return output->receiveSamples(outBuffer, maxSamples); + } + + + /// Adjusts book-keeping so that given number of samples are removed from beginning of the + /// sample buffer without copying them anywhere. + /// + /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly + /// with 'ptrBegin' function. + virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. + ) + { + return output->receiveSamples(maxSamples); + } + + + /// Returns number of samples currently available. + virtual uint numSamples() const + { + return output->numSamples(); + } + + + /// Returns nonzero if there aren't any samples available for outputting. + virtual int isEmpty() const + { + return output->isEmpty(); + } +}; + +} + +#endif diff --git a/3rdparty/SoundTouch/FIRFilter.cpp b/3rdparty/SoundTouch/FIRFilter.cpp new file mode 100644 index 0000000000..ac57335a18 --- /dev/null +++ b/3rdparty/SoundTouch/FIRFilter.cpp @@ -0,0 +1,272 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// General FIR digital filter routines with MMX optimization. +/// +/// Note : MMX optimized functions reside in a separate, platform-specific file, +/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp' +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.16 $ +// +// $Id: FIRFilter.cpp,v 1.16 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include "FIRFilter.h" +#include "cpu_detect.h" + +using namespace soundtouch; + +/***************************************************************************** + * + * Implementation of the class 'FIRFilter' + * + *****************************************************************************/ + +FIRFilter::FIRFilter() +{ + resultDivFactor = 0; + length = 0; + lengthDiv8 = 0; + filterCoeffs = NULL; +} + + +FIRFilter::~FIRFilter() +{ + delete[] filterCoeffs; +} + +// Usual C-version of the filter routine for stereo sound +uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const +{ + uint i, j, end; + LONG_SAMPLETYPE suml, sumr; +#ifdef FLOAT_SAMPLES + // when using floating point samples, use a scaler instead of a divider + // because division is much slower operation than multiplying. + double dScaler = 1.0 / (double)resultDivider; +#endif + + assert(length != 0); + + end = 2 * (numSamples - length); + + for (j = 0; j < end; j += 2) + { + const SAMPLETYPE *ptr; + + suml = sumr = 0; + ptr = src + j; + + for (i = 0; i < length; i += 4) + { + // loop is unrolled by factor of 4 here for efficiency + suml += ptr[2 * i + 0] * filterCoeffs[i + 0] + + ptr[2 * i + 2] * filterCoeffs[i + 1] + + ptr[2 * i + 4] * filterCoeffs[i + 2] + + ptr[2 * i + 6] * filterCoeffs[i + 3]; + sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] + + ptr[2 * i + 3] * filterCoeffs[i + 1] + + ptr[2 * i + 5] * filterCoeffs[i + 2] + + ptr[2 * i + 7] * filterCoeffs[i + 3]; + } + +#ifdef INTEGER_SAMPLES + suml >>= resultDivFactor; + sumr >>= resultDivFactor; + // saturate to 16 bit integer limits + suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml; + // saturate to 16 bit integer limits + sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr; +#else + suml *= dScaler; + sumr *= dScaler; +#endif // INTEGER_SAMPLES + dest[j] = (SAMPLETYPE)suml; + dest[j + 1] = (SAMPLETYPE)sumr; + } + return numSamples - length; +} + + + + +// Usual C-version of the filter routine for mono sound +uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const +{ + uint i, j, end; + LONG_SAMPLETYPE sum; +#ifdef FLOAT_SAMPLES + // when using floating point samples, use a scaler instead of a divider + // because division is much slower operation than multiplying. + double dScaler = 1.0 / (double)resultDivider; +#endif + + + assert(length != 0); + + end = numSamples - length; + for (j = 0; j < end; j ++) + { + sum = 0; + for (i = 0; i < length; i += 4) + { + // loop is unrolled by factor of 4 here for efficiency + sum += src[i + 0] * filterCoeffs[i + 0] + + src[i + 1] * filterCoeffs[i + 1] + + src[i + 2] * filterCoeffs[i + 2] + + src[i + 3] * filterCoeffs[i + 3]; + } +#ifdef INTEGER_SAMPLES + sum >>= resultDivFactor; + // saturate to 16 bit integer limits + sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum; +#else + sum *= dScaler; +#endif // INTEGER_SAMPLES + dest[j] = (SAMPLETYPE)sum; + src ++; + } + return end; +} + + +// Set filter coeffiecients and length. +// +// Throws an exception if filter length isn't divisible by 8 +void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor) +{ + assert(newLength > 0); + if (newLength % 8) throw std::runtime_error("FIR filter length not divisible by 8"); + + lengthDiv8 = newLength / 8; + length = lengthDiv8 * 8; + assert(length == newLength); + + resultDivFactor = uResultDivFactor; +#ifdef INTEGER_SAMPLES + resultDivider = (SAMPLETYPE)(1< 0); + assert(lengthDiv8 * 8 == length); + if (numSamples < length) return 0; + assert(resultDivFactor >= 0); + if (numChannels == 2) + { + return evaluateFilterStereo(dest, src, numSamples); + } else { + return evaluateFilterMono(dest, src, numSamples); + } +} + + + +// Operator 'new' is overloaded so that it automatically creates a suitable instance +// depending on if we've a MMX-capable CPU available or not. +void * FIRFilter::operator new(size_t s) +{ + // Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead! + throw std::runtime_error("Don't use 'new FIRFilter', use 'newInstance' member instead!"); + return NULL; +} + + +FIRFilter * FIRFilter::newInstance() +{ + uint uExtensions = 0; + +#if !defined(_MSC_VER) || !defined(__x86_64__) + uExtensions = detectCPUextensions(); +#endif + + // Check if MMX/SSE/3DNow! instruction set extensions supported by CPU + +#ifdef ALLOW_MMX + // MMX routines available only with integer sample types + if (uExtensions & SUPPORT_MMX) + { + return ::new FIRFilterMMX; + } + else +#endif // ALLOW_MMX + +#ifdef ALLOW_SSE + if (uExtensions & SUPPORT_SSE) + { + // SSE support + return ::new FIRFilterSSE; + } + else +#endif // ALLOW_SSE + +#ifdef ALLOW_3DNOW + if (uExtensions & SUPPORT_3DNOW) + { + // 3DNow! support + return ::new FIRFilter3DNow; + } + else +#endif // ALLOW_3DNOW + + { + // ISA optimizations not supported, use plain C version + return ::new FIRFilter; + } +} diff --git a/3rdparty/SoundTouch/FIRFilter.h b/3rdparty/SoundTouch/FIRFilter.h new file mode 100644 index 0000000000..be5cdd2943 --- /dev/null +++ b/3rdparty/SoundTouch/FIRFilter.h @@ -0,0 +1,163 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// General FIR digital filter routines with MMX optimization. +/// +/// Note : MMX optimized functions reside in a separate, platform-specific file, +/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp' +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.17 $ +// +// $Id: FIRFilter.h,v 1.17 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FIRFilter_H +#define FIRFilter_H + +#include "STTypes.h" + +namespace soundtouch +{ + +class FIRFilter +{ +protected: + // Number of FIR filter taps + uint length; + // Number of FIR filter taps divided by 8 + uint lengthDiv8; + + // Result divider factor in 2^k format + uint resultDivFactor; + + // Result divider value. + SAMPLETYPE resultDivider; + + // Memory for filter coefficients + SAMPLETYPE *filterCoeffs; + + virtual uint evaluateFilterStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples) const; + virtual uint evaluateFilterMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples) const; + +public: + FIRFilter(); + virtual ~FIRFilter(); + + /// Operator 'new' is overloaded so that it automatically creates a suitable instance + /// depending on if we've a MMX-capable CPU available or not. + void * operator new(size_t s); + + static FIRFilter *newInstance(); + + /// Applies the filter to the given sequence of samples. + /// Note : The amount of outputted samples is by value of 'filter_length' + /// smaller than the amount of input samples. + /// + /// \return Number of samples copied to 'dest'. + uint evaluate(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples, + uint numChannels) const; + + uint getLength() const; + + virtual void setCoefficients(const SAMPLETYPE *coeffs, + uint newLength, + uint uResultDivFactor); +}; + + +// Optional subclasses that implement CPU-specific optimizations: + +#ifdef ALLOW_MMX + + /// Class that implements MMX optimized functions exclusive for 16bit integer samples type. + class FIRFilterMMX : public FIRFilter + { + protected: + short *filterCoeffsUnalign; + short *filterCoeffsAlign; + + virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const; + public: + FIRFilterMMX(); + ~FIRFilterMMX(); + + virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor); + }; + +#endif // ALLOW_MMX + + +#ifdef ALLOW_3DNOW + + /// Class that implements 3DNow! optimized functions exclusive for floating point samples type. + class FIRFilter3DNow : public FIRFilter + { + protected: + float *filterCoeffsUnalign; + float *filterCoeffsAlign; + + virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const; + public: + FIRFilter3DNow(); + ~FIRFilter3DNow(); + virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor); + }; + +#endif // ALLOW_3DNOW + + +#ifdef ALLOW_SSE + /// Class that implements SSE optimized functions exclusive for floating point samples type. + class FIRFilterSSE : public FIRFilter + { + protected: + float *filterCoeffsUnalign; + float *filterCoeffsAlign; + + virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const; + public: + FIRFilterSSE(); + ~FIRFilterSSE(); + + virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor); + }; + +#endif // ALLOW_SSE + +} + +#endif // FIRFilter_H diff --git a/3rdparty/SoundTouch/Makefile.am b/3rdparty/SoundTouch/Makefile.am new file mode 100644 index 0000000000..a09ae5f081 --- /dev/null +++ b/3rdparty/SoundTouch/Makefile.am @@ -0,0 +1,46 @@ +## Process this file with automake to create Makefile.in +## +## $Id: Makefile.am,v 1.3 2006/02/05 18:33:34 Olli Exp $ +## +## Copyright (C) 2003 - David W. Durham +## +## This file is part of SoundTouch, an audio processing library for pitch/time adjustments +## +## SoundTouch 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. +## +## SoundTouch 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 + +AUTOMAKE_OPTIONS = foreign + +noinst_HEADERS=AAFilter.h cpu_detect.h FIRFilter.h RateTransposer.h TDStretch.h cpu_detect_x86_gcc.cpp +noinst_LIBRARIES = libSoundTouch.a + +if X86_64 +libSoundTouch_a_CXXFLAGS = -fPIC +libSoundTouch_a_CFLAGS = -fPIC +else +libSoundTouch_a_CXXFLAGS = -msse -mmmx +libSoundTouch_a_CFLAGS = -msse -mmmx +endif + +#lib_LTLIBRARIES=libSoundTouch.la +# the mmx_gcc.cpp and cpu_detect_x86_gcc.cpp may need to be conditionally included here from things discovered in configure.ac +libSoundTouch_a_SOURCES=AAFilter.cpp FIRFilter.cpp FIFOSampleBuffer.cpp mmx_optimized.cpp sse_optimized.cpp \ +RateTransposer.cpp SoundTouch.cpp TDStretch.cpp WavFile.cpp cpu_detect_x86_gcc.cpp + +# ??? test for -fcheck-new in configure.ac +# other compiler flags to add +AM_CXXFLAGS=-O3 -msse -fcheck-new -I../../include + +# other linking flags to add +#libSoundTouch_la_LIBADD= + diff --git a/3rdparty/SoundTouch/RateTransposer.cpp b/3rdparty/SoundTouch/RateTransposer.cpp new file mode 100644 index 0000000000..b7414b90a6 --- /dev/null +++ b/3rdparty/SoundTouch/RateTransposer.cpp @@ -0,0 +1,626 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Sample rate transposer. Changes sample rate by using linear interpolation +/// together with anti-alias filtering (first order interpolation with anti- +/// alias filtering should be quite adequate for this application) +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/03/19 10:05:49 $ +// File revision : $Revision: 1.13 $ +// +// $Id: RateTransposer.cpp,v 1.13 2006/03/19 10:05:49 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include "RateTransposer.h" +#include "AAFilter.h" + +using namespace soundtouch; + + +/// A linear samplerate transposer class that uses integer arithmetics. +/// for the transposing. +class RateTransposerInteger : public RateTransposer +{ +protected: + int iSlopeCount; + uint uRate; + SAMPLETYPE sPrevSampleL, sPrevSampleR; + + virtual void resetRegisters(); + + virtual uint transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples); + virtual uint transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples); + +public: + RateTransposerInteger(); + virtual ~RateTransposerInteger(); + + /// Sets new target rate. Normal rate = 1.0, smaller values represent slower + /// rate, larger faster rates. + virtual void setRate(float newRate); + +}; + + +/// A linear samplerate transposer class that uses floating point arithmetics +/// for the transposing. +class RateTransposerFloat : public RateTransposer +{ +protected: + float fSlopeCount; + float fRateStep; + SAMPLETYPE sPrevSampleL, sPrevSampleR; + + virtual void resetRegisters(); + + virtual uint transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples); + virtual uint transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples); + +public: + RateTransposerFloat(); + virtual ~RateTransposerFloat(); +}; + + + +#ifndef min +#define min(a,b) ((a > b) ? b : a) +#define max(a,b) ((a < b) ? b : a) +#endif + + +// Operator 'new' is overloaded so that it automatically creates a suitable instance +// depending on if we've a MMX/SSE/etc-capable CPU available or not. +void * RateTransposer::operator new(size_t s) +{ + // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead! + assert(FALSE); + return NULL; +} + + +RateTransposer *RateTransposer::newInstance() +{ +#ifdef INTEGER_SAMPLES + return ::new RateTransposerInteger; +#else + return ::new RateTransposerFloat; +#endif +} + + +// Constructor +RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer) +{ + uChannels = 2; + bUseAAFilter = TRUE; + + // Instantiates the anti-alias filter with default tap length + // of 32 + pAAFilter = new AAFilter(32); +} + + + +RateTransposer::~RateTransposer() +{ + delete pAAFilter; +} + + + +/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable +void RateTransposer::enableAAFilter(const BOOL newMode) +{ + bUseAAFilter = newMode; +} + + +/// Returns nonzero if anti-alias filter is enabled. +BOOL RateTransposer::isAAFilterEnabled() const +{ + return bUseAAFilter; +} + + +AAFilter *RateTransposer::getAAFilter() const +{ + return pAAFilter; +} + + + +// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower +// uRate, larger faster uRates. +void RateTransposer::setRate(float newRate) +{ + float fCutoff; + + fRate = newRate; + + // design a new anti-alias filter + if (newRate > 1.0f) + { + fCutoff = 0.5f / newRate; + } + else + { + fCutoff = 0.5f * newRate; + } + pAAFilter->setCutoffFreq(fCutoff); +} + + +// Outputs as many samples of the 'outputBuffer' as possible, and if there's +// any room left, outputs also as many of the incoming samples as possible. +// The goal is to drive the outputBuffer empty. +// +// It's allowed for 'output' and 'input' parameters to point to the same +// memory position. +void RateTransposer::flushStoreBuffer() +{ + if (storeBuffer.isEmpty()) return; + + outputBuffer.moveSamples(storeBuffer); +} + + +// Adds 'numSamples' pcs of samples from the 'samples' memory position into +// the input of the object. +void RateTransposer::putSamples(const SAMPLETYPE *samples, uint numSamples) +{ + processSamples(samples, numSamples); +} + + + +// Transposes up the sample rate, causing the observed playback 'rate' of the +// sound to decrease +void RateTransposer::upsample(const SAMPLETYPE *src, uint numSamples) +{ + int count, sizeTemp, num; + + // If the parameter 'uRate' value is smaller than 'SCALE', first transpose + // the samples and then apply the anti-alias filter to remove aliasing. + + // First check that there's enough room in 'storeBuffer' + // (+16 is to reserve some slack in the destination buffer) + sizeTemp = (int)((float)numSamples / fRate + 16.0f); + + // Transpose the samples, store the result into the end of "storeBuffer" + count = transpose(storeBuffer.ptrEnd(sizeTemp), src, numSamples); + storeBuffer.putSamples(count); + + // Apply the anti-alias filter to samples in "store output", output the + // result to "dest" + num = storeBuffer.numSamples(); + count = pAAFilter->evaluate(outputBuffer.ptrEnd(num), + storeBuffer.ptrBegin(), num, uChannels); + outputBuffer.putSamples(count); + + // Remove the processed samples from "storeBuffer" + storeBuffer.receiveSamples(count); +} + + +// Transposes down the sample rate, causing the observed playback 'rate' of the +// sound to increase +void RateTransposer::downsample(const SAMPLETYPE *src, uint numSamples) +{ + int count, sizeTemp; + + // If the parameter 'uRate' value is larger than 'SCALE', first apply the + // anti-alias filter to remove high frequencies (prevent them from folding + // over the lover frequencies), then transpose. */ + + // Add the new samples to the end of the storeBuffer */ + storeBuffer.putSamples(src, numSamples); + + // Anti-alias filter the samples to prevent folding and output the filtered + // data to tempBuffer. Note : because of the FIR filter length, the + // filtering routine takes in 'filter_length' more samples than it outputs. + assert(tempBuffer.isEmpty()); + sizeTemp = storeBuffer.numSamples(); + + count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp), + storeBuffer.ptrBegin(), sizeTemp, uChannels); + + // Remove the filtered samples from 'storeBuffer' + storeBuffer.receiveSamples(count); + + // Transpose the samples (+16 is to reserve some slack in the destination buffer) + sizeTemp = (int)((float)numSamples / fRate + 16.0f); + count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count); + outputBuffer.putSamples(count); +} + + +// Transposes sample rate by applying anti-alias filter to prevent folding. +// Returns amount of samples returned in the "dest" buffer. +// The maximum amount of samples that can be returned at a time is set by +// the 'set_returnBuffer_size' function. +void RateTransposer::processSamples(const SAMPLETYPE *src, uint numSamples) +{ + uint count; + uint sizeReq; + + if (numSamples == 0) return; + assert(pAAFilter); + + // If anti-alias filter is turned off, simply transpose without applying + // the filter + if (bUseAAFilter == FALSE) + { + sizeReq = (int)((float)numSamples / fRate + 1.0f); + count = transpose(outputBuffer.ptrEnd(sizeReq), src, numSamples); + outputBuffer.putSamples(count); + return; + } + + // Transpose with anti-alias filter + if (fRate < 1.0f) + { + upsample(src, numSamples); + } + else + { + downsample(src, numSamples); + } +} + + +// Transposes the sample rate of the given samples using linear interpolation. +// Returns the number of samples returned in the "dest" buffer +inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) +{ + if (uChannels == 2) + { + return transposeStereo(dest, src, numSamples); + } + else + { + return transposeMono(dest, src, numSamples); + } +} + + +// Sets the number of channels, 1 = mono, 2 = stereo +void RateTransposer::setChannels(const uint numchannels) +{ + if (uChannels == numchannels) return; + + assert(numchannels == 1 || numchannels == 2); + uChannels = numchannels; + + storeBuffer.setChannels(uChannels); + tempBuffer.setChannels(uChannels); + outputBuffer.setChannels(uChannels); + + // Inits the linear interpolation registers + resetRegisters(); +} + + +// Clears all the samples in the object +void RateTransposer::clear() +{ + outputBuffer.clear(); + storeBuffer.clear(); +} + + +// Returns nonzero if there aren't any samples available for outputting. +uint RateTransposer::isEmpty() +{ + int res; + + res = FIFOProcessor::isEmpty(); + if (res == 0) return 0; + return storeBuffer.isEmpty(); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// RateTransposerInteger - integer arithmetic implementation +// + +/// fixed-point interpolation routine precision +#define SCALE 65536 + +// Constructor +RateTransposerInteger::RateTransposerInteger() : RateTransposer() +{ + // call these here as these are virtual functions; calling these + // from the base class constructor wouldn't execute the overloaded + // versions (peculiar C++ can be). + resetRegisters(); + setRate(1.0f); +} + + +RateTransposerInteger::~RateTransposerInteger() +{ +} + + +void RateTransposerInteger::resetRegisters() +{ + iSlopeCount = 0; + sPrevSampleL = + sPrevSampleR = 0; +} + + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Mono' version of the routine. Returns the number of samples returned in +// the "dest" buffer +uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) +{ + unsigned int i, used; + LONG_SAMPLETYPE temp, vol1; + + used = 0; + i = 0; + + // Process the last sample saved from the previous call first... + while (iSlopeCount <= SCALE) + { + vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); + temp = vol1 * sPrevSampleL + iSlopeCount * src[0]; + dest[i] = (SAMPLETYPE)(temp / SCALE); + i++; + iSlopeCount += uRate; + } + // now always (iSlopeCount > SCALE) + iSlopeCount -= SCALE; + + while (1) + { + while (iSlopeCount > SCALE) + { + iSlopeCount -= SCALE; + used ++; + if (used >= numSamples - 1) goto end; + } + vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); + temp = src[used] * vol1 + iSlopeCount * src[used + 1]; + dest[i] = (SAMPLETYPE)(temp / SCALE); + + i++; + iSlopeCount += uRate; + } +end: + // Store the last sample for the next round + sPrevSampleL = src[numSamples - 1]; + + return i; +} + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Stereo' version of the routine. Returns the number of samples returned in +// the "dest" buffer +uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) +{ + unsigned int srcPos, i, used; + LONG_SAMPLETYPE temp, vol1; + + if (numSamples == 0) return 0; // no samples, no work + + used = 0; + i = 0; + + // Process the last sample saved from the sPrevSampleLious call first... + while (iSlopeCount <= SCALE) + { + vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); + temp = vol1 * sPrevSampleL + iSlopeCount * src[0]; + dest[2 * i] = (SAMPLETYPE)(temp / SCALE); + temp = vol1 * sPrevSampleR + iSlopeCount * src[1]; + dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE); + i++; + iSlopeCount += uRate; + } + // now always (iSlopeCount > SCALE) + iSlopeCount -= SCALE; + + while (1) + { + while (iSlopeCount > SCALE) + { + iSlopeCount -= SCALE; + used ++; + if (used >= numSamples - 1) goto end; + } + srcPos = 2 * used; + vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); + temp = src[srcPos] * vol1 + iSlopeCount * src[srcPos + 2]; + dest[2 * i] = (SAMPLETYPE)(temp / SCALE); + temp = src[srcPos + 1] * vol1 + iSlopeCount * src[srcPos + 3]; + dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE); + + i++; + iSlopeCount += uRate; + } +end: + // Store the last sample for the next round + sPrevSampleL = src[2 * numSamples - 2]; + sPrevSampleR = src[2 * numSamples - 1]; + + return i; +} + + +// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower +// uRate, larger faster uRates. +void RateTransposerInteger::setRate(float newRate) +{ + uRate = (int)(newRate * SCALE + 0.5f); + RateTransposer::setRate(newRate); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// RateTransposerFloat - floating point arithmetic implementation +// +////////////////////////////////////////////////////////////////////////////// + +// Constructor +RateTransposerFloat::RateTransposerFloat() : RateTransposer() +{ + // call these here as these are virtual functions; calling these + // from the base class constructor wouldn't execute the overloaded + // versions (peculiar C++ can be). + resetRegisters(); + setRate(1.0f); +} + + +RateTransposerFloat::~RateTransposerFloat() +{ +} + + +void RateTransposerFloat::resetRegisters() +{ + fSlopeCount = 0; + sPrevSampleL = + sPrevSampleR = 0; +} + + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Mono' version of the routine. Returns the number of samples returned in +// the "dest" buffer +uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) +{ + unsigned int i, used; + + used = 0; + i = 0; + + // Process the last sample saved from the previous call first... + while (fSlopeCount <= 1.0f) + { + dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]); + i++; + fSlopeCount += fRate; + } + fSlopeCount -= 1.0f; + + if (numSamples == 1) goto end; + + while (1) + { + while (fSlopeCount > 1.0f) + { + fSlopeCount -= 1.0f; + used ++; + if (used >= numSamples - 1) goto end; + } + dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]); + i++; + fSlopeCount += fRate; + } +end: + // Store the last sample for the next round + sPrevSampleL = src[numSamples - 1]; + + return i; +} + + +// Transposes the sample rate of the given samples using linear interpolation. +// 'Mono' version of the routine. Returns the number of samples returned in +// the "dest" buffer +uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) +{ + unsigned int srcPos, i, used; + + if (numSamples == 0) return 0; // no samples, no work + + used = 0; + i = 0; + + // Process the last sample saved from the sPrevSampleLious call first... + while (fSlopeCount <= 1.0f) + { + dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]); + dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleR + fSlopeCount * src[1]); + i++; + fSlopeCount += fRate; + } + // now always (iSlopeCount > 1.0f) + fSlopeCount -= 1.0f; + + if (numSamples == 1) goto end; + + while (1) + { + while (fSlopeCount > 1.0f) + { + fSlopeCount -= 1.0f; + used ++; + if (used >= numSamples - 1) goto end; + } + srcPos = 2 * used; + + dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos] + + fSlopeCount * src[srcPos + 2]); + dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1] + + fSlopeCount * src[srcPos + 3]); + + i++; + fSlopeCount += fRate; + } +end: + // Store the last sample for the next round + sPrevSampleL = src[2 * numSamples - 2]; + sPrevSampleR = src[2 * numSamples - 1]; + + return i; +} diff --git a/3rdparty/SoundTouch/RateTransposer.h b/3rdparty/SoundTouch/RateTransposer.h new file mode 100644 index 0000000000..f73978e639 --- /dev/null +++ b/3rdparty/SoundTouch/RateTransposer.h @@ -0,0 +1,162 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Sample rate transposer. Changes sample rate by using linear interpolation +/// together with anti-alias filtering (first order interpolation with anti- +/// alias filtering should be quite adequate for this application). +/// +/// Use either of the derived classes of 'RateTransposerInteger' or +/// 'RateTransposerFloat' for corresponding integer/floating point tranposing +/// algorithm implementation. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.10 $ +// +// $Id: RateTransposer.h,v 1.10 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef RateTransposer_H +#define RateTransposer_H + +#include "AAFilter.h" +#include "FIFOSamplePipe.h" +#include "FIFOSampleBuffer.h" + +#include "STTypes.h" + +namespace soundtouch +{ + +/// A common linear samplerate transposer class. +/// +/// Note: Use function "RateTransposer::newInstance()" to create a new class +/// instance instead of the "new" operator; that function automatically +/// chooses a correct implementation depending on if integer or floating +/// arithmetics are to be used. +class RateTransposer : public FIFOProcessor +{ +protected: + /// Anti-alias filter object + AAFilter *pAAFilter; + + float fRate; + + uint uChannels; + + /// Buffer for collecting samples to feed the anti-alias filter between + /// two batches + FIFOSampleBuffer storeBuffer; + + /// Buffer for keeping samples between transposing & anti-alias filter + FIFOSampleBuffer tempBuffer; + + /// Output sample buffer + FIFOSampleBuffer outputBuffer; + + BOOL bUseAAFilter; + + void init(); + + virtual void resetRegisters() = 0; + + virtual uint transposeStereo(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples) = 0; + virtual uint transposeMono(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples) = 0; + uint transpose(SAMPLETYPE *dest, + const SAMPLETYPE *src, + uint numSamples); + + void flushStoreBuffer(); + + void downsample(const SAMPLETYPE *src, + uint numSamples); + void upsample(const SAMPLETYPE *src, + uint numSamples); + + /// Transposes sample rate by applying anti-alias filter to prevent folding. + /// Returns amount of samples returned in the "dest" buffer. + /// The maximum amount of samples that can be returned at a time is set by + /// the 'set_returnBuffer_size' function. + void processSamples(const SAMPLETYPE *src, + uint numSamples); + + +public: + RateTransposer(); + virtual ~RateTransposer(); + + /// Operator 'new' is overloaded so that it automatically creates a suitable instance + /// depending on if we're to use integer or floating point arithmetics. + void *operator new(size_t s); + + /// Use this function instead of "new" operator to create a new instance of this class. + /// This function automatically chooses a correct implementation, depending on if + /// integer ot floating point arithmetics are to be used. + static RateTransposer *newInstance(); + + /// Returns the output buffer object + FIFOSamplePipe *getOutput() { return &outputBuffer; }; + + /// Returns the store buffer object + FIFOSamplePipe *getStore() { return &storeBuffer; }; + + /// Return anti-alias filter object + AAFilter *getAAFilter() const; + + /// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable + void enableAAFilter(BOOL newMode); + + /// Returns nonzero if anti-alias filter is enabled. + BOOL isAAFilterEnabled() const; + + /// Sets new target rate. Normal rate = 1.0, smaller values represent slower + /// rate, larger faster rates. + virtual void setRate(float newRate); + + /// Sets the number of channels, 1 = mono, 2 = stereo + void setChannels(uint channels); + + /// Adds 'numSamples' pcs of samples from the 'samples' memory position into + /// the input of the object. + void putSamples(const SAMPLETYPE *samples, uint numSamples); + + /// Clears all the samples in the object + void clear(); + + /// Returns nonzero if there aren't any samples available for outputting. + uint isEmpty(); +}; + +} + +#endif diff --git a/3rdparty/SoundTouch/STTypes.h b/3rdparty/SoundTouch/STTypes.h new file mode 100644 index 0000000000..f3a6a5d5c0 --- /dev/null +++ b/3rdparty/SoundTouch/STTypes.h @@ -0,0 +1,202 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Common type definitions for SoundTouch audio processing library. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.16 $ +// +// $Id: STTypes.h,v 1.16 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef STTypes_H +#define STTypes_H + +//#define INTEGER_SAMPLES 1 + +typedef unsigned int uint; +typedef unsigned long ulong; + +#ifdef __x86_64__ +typedef unsigned long long ulongptr; +#else +typedef unsigned long ulongptr; +#endif + + +#ifdef __GNUC__ + // In GCC, include soundtouch_config.h made by config scritps +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Use Integer as Sample type */ +//#define INTEGER_SAMPLES 1 + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +#endif + +#ifndef _WINDEF_ + // if these aren't defined already by Windows headers, define now + + typedef int BOOL; + +#ifndef FALSE + #define FALSE 0 +#endif + +#ifndef TRUE + #define TRUE 1 +#endif + +#endif // _WINDEF_ + + +namespace soundtouch +{ +/// Activate these undef's to overrule the possible sampletype +/// setting inherited from some other header file: +//#undef INTEGER_SAMPLES +//#undef FLOAT_SAMPLES + +#if !(INTEGER_SAMPLES || FLOAT_SAMPLES) + + /// Choose either 32bit floating point or 16bit integer sampletype + /// by choosing one of the following defines, unless this selection + /// has already been done in some other file. + //// + /// Notes: + /// - In Windows environment, choose the sample format with the + /// following defines. + /// - In GNU environment, the floating point samples are used by + /// default, but integer samples can be chosen by giving the + /// following switch to the configure script: + /// ./configure --enable-integer-samples + /// However, if you still prefer to select the sample format here + /// also in GNU environment, then please #undef the INTEGER_SAMPLE + /// and FLOAT_SAMPLE defines first as in comments above. + //#define INTEGER_SAMPLES 1 //< 16bit integer samples + #define FLOAT_SAMPLES 1 //< 32bit float samples + + #endif + + /// Define this to allow CPU-specific assembler optimizations. Notice that + /// having this enabled on non-x86 platforms doesn't matter; the compiler can + /// drop unsupported extensions on different platforms automatically. + /// However, if you're having difficulties getting the optimized routines + /// compiled with your compler (e.g. some gcc compiler versions may be picky), + /// you may wish to disable the optimizations to make the library compile. + #if !defined(_MSC_VER) || !defined(__x86_64__) + #define ALLOW_OPTIMIZATIONS 1 + #define ALLOW_NONEXACT_SIMD_OPTIMIZATION 1 + #endif + + + // If defined, allows the SIMD-optimized routines to take minor shortcuts + // for improved performance. Undefine to require faithfully similar SIMD + // calculations as in normal C implementation. + + + + #ifdef INTEGER_SAMPLES + // 16bit integer sample type + typedef short SAMPLETYPE; + // data type for sample accumulation: Use 32bit integer to prevent overflows + typedef long LONG_SAMPLETYPE; + + #ifdef FLOAT_SAMPLES + // check that only one sample type is defined + #error "conflicting sample types defined" + #endif // FLOAT_SAMPLES + + #ifdef ALLOW_OPTIMIZATIONS + #if (_WIN32 || __i386__ || __x86_64__) + // Allow MMX optimizations + #define ALLOW_MMX 1 + #endif + #endif + + #else + + // floating point samples + typedef float SAMPLETYPE; + // data type for sample accumulation: Use double to utilize full precision. + typedef double LONG_SAMPLETYPE; + + #ifdef ALLOW_OPTIMIZATIONS + // Allow 3DNow! and SSE optimizations + #if _WIN32 + // #define ALLOW_3DNOW 1 + #endif + + #if (_WIN32 || __i386__ || __x86_64__) + #define ALLOW_SSE 1 + #endif + #endif + + #endif // INTEGER_SAMPLES +}; + +#endif \ No newline at end of file diff --git a/3rdparty/SoundTouch/SoundTouch.cpp b/3rdparty/SoundTouch/SoundTouch.cpp new file mode 100644 index 0000000000..d20fd326ba --- /dev/null +++ b/3rdparty/SoundTouch/SoundTouch.cpp @@ -0,0 +1,474 @@ +////////////////////////////////////////////////////////////////////////////// +/// +/// SoundTouch - main class for tempo/pitch/rate adjusting routines. +/// +/// Notes: +/// - Initialize the SoundTouch object instance by setting up the sound stream +/// parameters with functions 'setSampleRate' and 'setChannels', then set +/// desired tempo/pitch/rate settings with the corresponding functions. +/// +/// - The SoundTouch class behaves like a first-in-first-out pipeline: The +/// samples that are to be processed are fed into one of the pipe by calling +/// function 'putSamples', while the ready processed samples can be read +/// from the other end of the pipeline with function 'receiveSamples'. +/// +/// - The SoundTouch processing classes require certain sized 'batches' of +/// samples in order to process the sound. For this reason the classes buffer +/// incoming samples until there are enough of samples available for +/// processing, then they carry out the processing step and consequently +/// make the processed samples available for outputting. +/// +/// - For the above reason, the processing routines introduce a certain +/// 'latency' between the input and output, so that the samples input to +/// SoundTouch may not be immediately available in the output, and neither +/// the amount of outputtable samples may not immediately be in direct +/// relationship with the amount of previously input samples. +/// +/// - The tempo/pitch/rate control parameters can be altered during processing. +/// Please notice though that they aren't currently protected by semaphores, +/// so in multi-thread application external semaphore protection may be +/// required. +/// +/// - This class utilizes classes 'TDStretch' for tempo change (without modifying +/// pitch) and 'RateTransposer' for changing the playback rate (that is, both +/// tempo and pitch in the same ratio) of the sound. The third available control +/// 'pitch' (change pitch but maintain tempo) is produced by a combination of +/// combining the two other controls. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.13 $ +// +// $Id: SoundTouch.cpp,v 1.13 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +#include "SoundTouch.h" +#include "TDStretch.h" +#include "RateTransposer.h" +#include "cpu_detect.h" + +using namespace soundtouch; + +/// Print library version string +extern "C" void soundtouch_ac_test() +{ + printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION); +} + + +SoundTouch::SoundTouch() +{ + // Initialize rate transposer and tempo changer instances + + pRateTransposer = RateTransposer::newInstance(); + pTDStretch = TDStretch::newInstance(); + + setOutPipe(pTDStretch); + + rate = tempo = 0; + + virtualPitch = + virtualRate = + virtualTempo = 1.0; + + calcEffectiveRateAndTempo(); + + channels = 0; + bSrateSet = FALSE; +} + + + +SoundTouch::~SoundTouch() +{ + delete pRateTransposer; + delete pTDStretch; +} + + + +/// Get SoundTouch library version string +const char *SoundTouch::getVersionString() +{ + static const char *_version = SOUNDTOUCH_VERSION; + + return _version; +} + + +/// Get SoundTouch library version Id +uint SoundTouch::getVersionId() +{ + return SOUNDTOUCH_VERSION_ID; +} + + +// Sets the number of channels, 1 = mono, 2 = stereo +void SoundTouch::setChannels(uint numChannels) +{ + if (numChannels != 1 && numChannels != 2) + { + throw std::runtime_error("Illegal number of channels"); + } + channels = numChannels; + pRateTransposer->setChannels(numChannels); + pTDStretch->setChannels(numChannels); +} + + + +// Sets new rate control value. Normal rate = 1.0, smaller values +// represent slower rate, larger faster rates. +void SoundTouch::setRate(float newRate) +{ + virtualRate = newRate; + calcEffectiveRateAndTempo(); +} + + + +// Sets new rate control value as a difference in percents compared +// to the original rate (-50 .. +100 %) +void SoundTouch::setRateChange(float newRate) +{ + virtualRate = 1.0f + 0.01f * newRate; + calcEffectiveRateAndTempo(); +} + + + +// Sets new tempo control value. Normal tempo = 1.0, smaller values +// represent slower tempo, larger faster tempo. +void SoundTouch::setTempo(float newTempo) +{ + virtualTempo = newTempo; + calcEffectiveRateAndTempo(); +} + + + +// Sets new tempo control value as a difference in percents compared +// to the original tempo (-50 .. +100 %) +void SoundTouch::setTempoChange(float newTempo) +{ + virtualTempo = 1.0f + 0.01f * newTempo; + calcEffectiveRateAndTempo(); +} + + + +// Sets new pitch control value. Original pitch = 1.0, smaller values +// represent lower pitches, larger values higher pitch. +void SoundTouch::setPitch(float newPitch) +{ + virtualPitch = newPitch; + calcEffectiveRateAndTempo(); +} + + + +// Sets pitch change in octaves compared to the original pitch +// (-1.00 .. +1.00) +void SoundTouch::setPitchOctaves(float newPitch) +{ + virtualPitch = (float)exp(0.69314718056f * newPitch); + calcEffectiveRateAndTempo(); +} + + + +// Sets pitch change in semi-tones compared to the original pitch +// (-12 .. +12) +void SoundTouch::setPitchSemiTones(int newPitch) +{ + setPitchOctaves((float)newPitch / 12.0f); +} + + + +void SoundTouch::setPitchSemiTones(float newPitch) +{ + setPitchOctaves(newPitch / 12.0f); +} + + +// Calculates 'effective' rate and tempo values from the +// nominal control values. +void SoundTouch::calcEffectiveRateAndTempo() +{ + float oldTempo = tempo; + float oldRate = rate; + + tempo = virtualTempo / virtualPitch; + rate = virtualPitch * virtualRate; + + if (rate != oldRate) pRateTransposer->setRate(rate); + if (tempo != oldTempo) pTDStretch->setTempo(tempo); + + if (rate > 1.0f) + { + if (output != pRateTransposer) + { + FIFOSamplePipe *transOut; + + assert(output == pTDStretch); + // move samples in the current output buffer to the output of pRateTransposer + transOut = pRateTransposer->getOutput(); + transOut->moveSamples(*output); + // move samples in tempo changer's input to pitch transposer's input + pRateTransposer->moveSamples(*pTDStretch->getInput()); + + output = pRateTransposer; + } + } + else + { + if (output != pTDStretch) + { + FIFOSamplePipe *tempoOut; + + assert(output == pRateTransposer); + // move samples in the current output buffer to the output of pTDStretch + tempoOut = pTDStretch->getOutput(); + tempoOut->moveSamples(*output); + // move samples in pitch transposer's store buffer to tempo changer's input + pTDStretch->moveSamples(*pRateTransposer->getStore()); + + output = pTDStretch; + + } + } +} + + +// Sets sample rate. +void SoundTouch::setSampleRate(uint srate) +{ + bSrateSet = TRUE; + // set sample rate, leave other tempo changer parameters as they are. + pTDStretch->setParameters(srate); +} + + +// Adds 'numSamples' pcs of samples from the 'samples' memory position into +// the input of the object. +void SoundTouch::putSamples(const SAMPLETYPE *samples, uint numSamples) +{ + if (bSrateSet == FALSE) + { + throw std::runtime_error("SoundTouch : Sample rate not defined"); + } + else if (channels == 0) + { + throw std::runtime_error("SoundTouch : Number of channels not defined"); + } + + // Transpose the rate of the new samples if necessary + /* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value... + if (rate == 1.0f) + { + // The rate value is same as the original, simply evaluate the tempo changer. + assert(output == pTDStretch); + if (pRateTransposer->isEmpty() == 0) + { + // yet flush the last samples in the pitch transposer buffer + // (may happen if 'rate' changes from a non-zero value to zero) + pTDStretch->moveSamples(*pRateTransposer); + } + pTDStretch->putSamples(samples, numSamples); + } + */ + else if (rate <= 1.0f) + { + // transpose the rate down, output the transposed sound to tempo changer buffer + assert(output == pTDStretch); + pRateTransposer->putSamples(samples, numSamples); + pTDStretch->moveSamples(*pRateTransposer); + } + else + { + assert(rate > 1.0f); + // evaluate the tempo changer, then transpose the rate up, + assert(output == pRateTransposer); + pTDStretch->putSamples(samples, numSamples); + pRateTransposer->moveSamples(*pTDStretch); + } +} + + +// Flushes the last samples from the processing pipeline to the output. +// Clears also the internal processing buffers. +// +// Note: This function is meant for extracting the last samples of a sound +// stream. This function may introduce additional blank samples in the end +// of the sound stream, and thus it's not recommended to call this function +// in the middle of a sound stream. +void SoundTouch::flush() +{ + int i; + uint nOut; + SAMPLETYPE buff[128]; + + nOut = numSamples(); + + memset(buff, 0, 128 * sizeof(SAMPLETYPE)); + // "Push" the last active samples out from the processing pipeline by + // feeding blank samples into the processing pipeline until new, + // processed samples appear in the output (not however, more than + // 8ksamples in any case) + for (i = 0; i < 128; i ++) + { + putSamples(buff, 64); + if (numSamples() != nOut) break; // new samples have appeared in the output! + } + + // Clear working buffers + pRateTransposer->clear(); + pTDStretch->clearInput(); + // yet leave the 'tempoChanger' output intouched as that's where the + // flushed samples are! +} + + +// Changes a setting controlling the processing system behaviour. See the +// 'SETTING_...' defines for available setting ID's. +BOOL SoundTouch::setSetting(uint settingId, uint value) +{ + uint sampleRate, sequenceMs, seekWindowMs, overlapMs; + + // read current tdstretch routine parameters + pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs); + + switch (settingId) + { + case SETTING_USE_AA_FILTER : + // enables / disabless anti-alias filter + pRateTransposer->enableAAFilter((value != 0) ? TRUE : FALSE); + return TRUE; + + case SETTING_AA_FILTER_LENGTH : + // sets anti-alias filter length + pRateTransposer->getAAFilter()->setLength(value); + return TRUE; + + case SETTING_USE_QUICKSEEK : + // enables / disables tempo routine quick seeking algorithm + pTDStretch->enableQuickSeek((value != 0) ? TRUE : FALSE); + return TRUE; + + case SETTING_SEQUENCE_MS: + // change time-stretch sequence duration parameter + pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs); + return TRUE; + + case SETTING_SEEKWINDOW_MS: + // change time-stretch seek window length parameter + pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs); + return TRUE; + + case SETTING_OVERLAP_MS: + // change time-stretch overlap length parameter + pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value); + return TRUE; + + default : + return FALSE; + } +} + + +// Reads a setting controlling the processing system behaviour. See the +// 'SETTING_...' defines for available setting ID's. +// +// Returns the setting value. +uint SoundTouch::getSetting(uint settingId) const +{ + uint temp; + + switch (settingId) + { + case SETTING_USE_AA_FILTER : + return pRateTransposer->isAAFilterEnabled(); + + case SETTING_AA_FILTER_LENGTH : + return pRateTransposer->getAAFilter()->getLength(); + + case SETTING_USE_QUICKSEEK : + return pTDStretch->isQuickSeekEnabled(); + + case SETTING_SEQUENCE_MS: + pTDStretch->getParameters(NULL, &temp, NULL, NULL); + return temp; + + case SETTING_SEEKWINDOW_MS: + pTDStretch->getParameters(NULL, NULL, &temp, NULL); + return temp; + + case SETTING_OVERLAP_MS: + pTDStretch->getParameters(NULL, NULL, NULL, &temp); + return temp; + + default : + return 0; + } +} + + +// Clears all the samples in the object's output and internal processing +// buffers. +void SoundTouch::clear() +{ + pRateTransposer->clear(); + pTDStretch->clear(); +} + + + +/// Returns number of samples currently unprocessed. +uint SoundTouch::numUnprocessedSamples() const +{ + FIFOSamplePipe * psp; + if (pTDStretch) + { + psp = pTDStretch->getInput(); + if (psp) + { + return psp->numSamples(); + } + } + return 0; +} diff --git a/3rdparty/SoundTouch/SoundTouch.h b/3rdparty/SoundTouch/SoundTouch.h new file mode 100644 index 0000000000..fab3bb9845 --- /dev/null +++ b/3rdparty/SoundTouch/SoundTouch.h @@ -0,0 +1,252 @@ +////////////////////////////////////////////////////////////////////////////// +/// +/// SoundTouch - main class for tempo/pitch/rate adjusting routines. +/// +/// Notes: +/// - Initialize the SoundTouch object instance by setting up the sound stream +/// parameters with functions 'setSampleRate' and 'setChannels', then set +/// desired tempo/pitch/rate settings with the corresponding functions. +/// +/// - The SoundTouch class behaves like a first-in-first-out pipeline: The +/// samples that are to be processed are fed into one of the pipe by calling +/// function 'putSamples', while the ready processed samples can be read +/// from the other end of the pipeline with function 'receiveSamples'. +/// +/// - The SoundTouch processing classes require certain sized 'batches' of +/// samples in order to process the sound. For this reason the classes buffer +/// incoming samples until there are enough of samples available for +/// processing, then they carry out the processing step and consequently +/// make the processed samples available for outputting. +/// +/// - For the above reason, the processing routines introduce a certain +/// 'latency' between the input and output, so that the samples input to +/// SoundTouch may not be immediately available in the output, and neither +/// the amount of outputtable samples may not immediately be in direct +/// relationship with the amount of previously input samples. +/// +/// - The tempo/pitch/rate control parameters can be altered during processing. +/// Please notice though that they aren't currently protected by semaphores, +/// so in multi-thread application external semaphore protection may be +/// required. +/// +/// - This class utilizes classes 'TDStretch' for tempo change (without modifying +/// pitch) and 'RateTransposer' for changing the playback rate (that is, both +/// tempo and pitch in the same ratio) of the sound. The third available control +/// 'pitch' (change pitch but maintain tempo) is produced by a combination of +/// combining the two other controls. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.14 $ +// +// $Id: SoundTouch.h,v 1.14 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef SoundTouch_H +#define SoundTouch_H + +#include "FIFOSamplePipe.h" +#include "STTypes.h" + +namespace soundtouch +{ + +/// Soundtouch library version string +#define SOUNDTOUCH_VERSION "1.3.1" + +/// SoundTouch library version id +#define SOUNDTOUCH_VERSION_ID 010301 + +// +// Available setting IDs for the 'setSetting' & 'get_setting' functions: + +/// Enable/disable anti-alias filter in pitch transposer (0 = disable) +#define SETTING_USE_AA_FILTER 0 + +/// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32) +#define SETTING_AA_FILTER_LENGTH 1 + +/// Enable/disable quick seeking algorithm in tempo changer routine +/// (enabling quick seeking lowers CPU utilization but causes a minor sound +/// quality compromising) +#define SETTING_USE_QUICKSEEK 2 + +/// Time-stretch algorithm single processing sequence length in milliseconds. This determines +/// to how long sequences the original sound is chopped in the time-stretch algorithm. +/// See "STTypes.h" or README for more information. +#define SETTING_SEQUENCE_MS 3 + +/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the +/// best possible overlapping location. This determines from how wide window the algorithm +/// may look for an optimal joining location when mixing the sound sequences back together. +/// See "STTypes.h" or README for more information. +#define SETTING_SEEKWINDOW_MS 4 + +/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences +/// are mixed back together, to form a continuous sound stream, this parameter defines over +/// how long period the two consecutive sequences are let to overlap each other. +/// See "STTypes.h" or README for more information. +#define SETTING_OVERLAP_MS 5 + + +class SoundTouch : public FIFOProcessor +{ +private: + /// Rate transposer class instance + class RateTransposer *pRateTransposer; + + /// Time-stretch class instance + class TDStretch *pTDStretch; + + /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. + float virtualRate; + + /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. + float virtualTempo; + + /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. + float virtualPitch; + + /// Flag: Has sample rate been set? + BOOL bSrateSet; + + /// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and + /// 'virtualPitch' parameters. + void calcEffectiveRateAndTempo(); + +protected : + /// Number of channels + uint channels; + + /// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch' + float rate; + + /// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch' + float tempo; + +public: + SoundTouch(); + virtual ~SoundTouch(); + + /// Get SoundTouch library version string + static const char *getVersionString(); + + /// Get SoundTouch library version Id + static uint getVersionId(); + + /// Sets new rate control value. Normal rate = 1.0, smaller values + /// represent slower rate, larger faster rates. + void setRate(float newRate); + + /// Sets new tempo control value. Normal tempo = 1.0, smaller values + /// represent slower tempo, larger faster tempo. + void setTempo(float newTempo); + + /// Sets new rate control value as a difference in percents compared + /// to the original rate (-50 .. +100 %) + void setRateChange(float newRate); + + /// Sets new tempo control value as a difference in percents compared + /// to the original tempo (-50 .. +100 %) + void setTempoChange(float newTempo); + + /// Sets new pitch control value. Original pitch = 1.0, smaller values + /// represent lower pitches, larger values higher pitch. + void setPitch(float newPitch); + + /// Sets pitch change in octaves compared to the original pitch + /// (-1.00 .. +1.00) + void setPitchOctaves(float newPitch); + + /// Sets pitch change in semi-tones compared to the original pitch + /// (-12 .. +12) + void setPitchSemiTones(int newPitch); + void setPitchSemiTones(float newPitch); + + /// Sets the number of channels, 1 = mono, 2 = stereo + void setChannels(uint numChannels); + + /// Sets sample rate. + void setSampleRate(uint srate); + + /// Flushes the last samples from the processing pipeline to the output. + /// Clears also the internal processing buffers. + // + /// Note: This function is meant for extracting the last samples of a sound + /// stream. This function may introduce additional blank samples in the end + /// of the sound stream, and thus it's not recommended to call this function + /// in the middle of a sound stream. + void flush(); + + /// Adds 'numSamples' pcs of samples from the 'samples' memory position into + /// the input of the object. Notice that sample rate _has_to_ be set before + /// calling this function, otherwise throws a runtime_error exception. + virtual void putSamples( + const SAMPLETYPE *samples, ///< Pointer to sample buffer. + uint numSamples ///< Number of samples in buffer. Notice + ///< that in case of stereo-sound a single sample + ///< contains data for both channels. + ); + + /// Clears all the samples in the object's output and internal processing + /// buffers. + virtual void clear(); + + /// Changes a setting controlling the processing system behaviour. See the + /// 'SETTING_...' defines for available setting ID's. + /// + /// \return 'TRUE' if the setting was succesfully changed + BOOL setSetting(uint settingId, ///< Setting ID number. see SETTING_... defines. + uint value ///< New setting value. + ); + + /// Reads a setting controlling the processing system behaviour. See the + /// 'SETTING_...' defines for available setting ID's. + /// + /// \return the setting value. + uint getSetting(uint settingId ///< Setting ID number, see SETTING_... defines. + ) const; + + /// Returns number of samples currently unprocessed. + virtual uint numUnprocessedSamples() const; + + + /// Other handy functions that are implemented in the ancestor classes (see + /// classes 'FIFOProcessor' and 'FIFOSamplePipe') + /// + /// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch. + /// - numSamples() : Get number of 'ready' samples that can be received with + /// function 'receiveSamples()' + /// - isEmpty() : Returns nonzero if there aren't any 'ready' samples. + /// - clear() : Clears all samples from ready/processing buffers. +}; + +} +#endif diff --git a/3rdparty/SoundTouch/TDStretch.cpp b/3rdparty/SoundTouch/TDStretch.cpp new file mode 100644 index 0000000000..d809623689 --- /dev/null +++ b/3rdparty/SoundTouch/TDStretch.cpp @@ -0,0 +1,940 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo +/// while maintaining the original pitch by using a time domain WSOLA-like +/// method with several performance-increasing tweaks. +/// +/// Note : MMX optimized functions reside in a separate, platform-specific +/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp' +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.24 $ +// +// $Id: TDStretch.cpp,v 1.24 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +#include "STTypes.h" +#include "cpu_detect.h" +#include "TDStretch.h" + +using namespace soundtouch; + +#ifndef min +#define min(a,b) ((a > b) ? b : a) +#define max(a,b) ((a < b) ? b : a) +#endif + + + +/***************************************************************************** + * + * Constant definitions + * + *****************************************************************************/ + + +// Table for the hierarchical mixing position seeking algorithm +int scanOffsets[4][24]={ + { 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806, + 868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0}, + {-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + +/***************************************************************************** + * + * Implementation of the class 'TDStretch' + * + *****************************************************************************/ + + +TDStretch::TDStretch() : FIFOProcessor(&outputBuffer) +{ + bQuickseek = FALSE; + channels = 2; + bMidBufferDirty = FALSE; + + pMidBuffer = NULL; + pRefMidBufferUnaligned = NULL; + overlapLength = 0; + + setParameters(48000, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS); + + setTempo(1.0f); +} + + + + +TDStretch::~TDStretch() +{ + delete[] pMidBuffer; + delete[] pRefMidBufferUnaligned; +} + + + +// Calculates the x having the closest 2^x value for the given value +static int _getClosest2Power(double value) +{ + return (int)(log(value) / log(2.0) + 0.5); +} + + + +// Sets routine control parameters. These control are certain time constants +// defining how the sound is stretched to the desired duration. +// +// 'sampleRate' = sample rate of the sound +// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms) +// 'seekwindowMS' = seeking window length for scanning the best overlapping +// position (default = 28 ms) +// 'overlapMS' = overlapping length (default = 12 ms) + +void TDStretch::setParameters(uint aSampleRate, uint aSequenceMS, + uint aSeekWindowMS, uint aOverlapMS) +{ + this->sampleRate = aSampleRate; + this->sequenceMs = aSequenceMS; + this->seekWindowMs = aSeekWindowMS; + this->overlapMs = aOverlapMS; + + seekLength = (sampleRate * seekWindowMs) / 1000; + seekWindowLength = (sampleRate * sequenceMs) / 1000; + + maxOffset = seekLength; + + calculateOverlapLength(overlapMs); + + // set tempo to recalculate 'sampleReq' + setTempo(tempo); + +} + + + +/// Get routine control parameters, see setParameters() function. +/// Any of the parameters to this function can be NULL, in such case corresponding parameter +/// value isn't returned. +void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs) +{ + if (pSampleRate) + { + *pSampleRate = sampleRate; + } + + if (pSequenceMs) + { + *pSequenceMs = sequenceMs; + } + + if (pSeekWindowMs) + { + *pSeekWindowMs = seekWindowMs; + } + + if (pOverlapMs) + { + *pOverlapMs = overlapMs; + } +} + + +// Overlaps samples in 'midBuffer' with the samples in 'input' +void TDStretch::overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const +{ + int i, itemp; + + for (i = 0; i < (int)overlapLength ; i ++) + { + itemp = overlapLength - i; + output[i] = (input[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits; + } +} + + + +void TDStretch::clearMidBuffer() +{ + if (bMidBufferDirty) + { + memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength); + bMidBufferDirty = FALSE; + } +} + + +void TDStretch::clearInput() +{ + inputBuffer.clear(); + clearMidBuffer(); +} + + +// Clears the sample buffers +void TDStretch::clear() +{ + outputBuffer.clear(); + inputBuffer.clear(); + clearMidBuffer(); +} + + + +// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero +// to enable +void TDStretch::enableQuickSeek(BOOL enable) +{ + bQuickseek = enable; +} + + +// Returns nonzero if the quick seeking algorithm is enabled. +BOOL TDStretch::isQuickSeekEnabled() const +{ + return bQuickseek; +} + + +// Seeks for the optimal overlap-mixing position. +uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos) +{ + if (channels == 2) + { + // stereo sound + if (bQuickseek) + { + return seekBestOverlapPositionStereoQuick(refPos); + } + else + { + return seekBestOverlapPositionStereo(refPos); + } + } + else + { + // mono sound + if (bQuickseek) + { + return seekBestOverlapPositionMonoQuick(refPos); + } + else + { + return seekBestOverlapPositionMono(refPos); + } + } +} + + + + +// Overlaps samples in 'midBuffer' with the samples in 'inputBuffer' at position +// of 'ovlPos'. +inline void TDStretch::overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const +{ + if (channels == 2) + { + // stereo sound + overlapStereo(output, input + 2 * ovlPos); + } else { + // mono sound. + overlapMono(output, input + ovlPos); + } +} + + + + +// Seeks for the optimal overlap-mixing position. The 'stereo' version of the +// routine +// +// The best position is determined as the position where the two overlapped +// sample sequences are 'most alike', in terms of the highest cross-correlation +// value over the overlapping period +uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos) +{ + uint bestOffs; + LONG_SAMPLETYPE bestCorr, corr; + uint i; + + // Slopes the amplitudes of the 'midBuffer' samples + precalcCorrReferenceStereo(); + + bestCorr = INT_MIN; + bestOffs = 0; + + // Scans for the best correlation value by testing each possible position + // over the permitted range. + for (i = 0; i < seekLength; i ++) + { + // Calculates correlation value for the mixing position corresponding + // to 'i' + corr = calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer); + + // Checks for the highest correlation value + if (corr > bestCorr) + { + bestCorr = corr; + bestOffs = i; + } + } + // clear cross correlation routine state if necessary (is so e.g. in MMX routines). + clearCrossCorrState(); + + return bestOffs; +} + + +// Seeks for the optimal overlap-mixing position. The 'stereo' version of the +// routine +// +// The best position is determined as the position where the two overlapped +// sample sequences are 'most alike', in terms of the highest cross-correlation +// value over the overlapping period +uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos) +{ + uint j; + uint bestOffs; + LONG_SAMPLETYPE bestCorr, corr; + uint scanCount, corrOffset, tempOffset; + + // Slopes the amplitude of the 'midBuffer' samples + precalcCorrReferenceStereo(); + + bestCorr = INT_MIN; + bestOffs = 0; + corrOffset = 0; + tempOffset = 0; + + // Scans for the best correlation value using four-pass hierarchical search. + // + // The look-up table 'scans' has hierarchical position adjusting steps. + // In first pass the routine searhes for the highest correlation with + // relatively coarse steps, then rescans the neighbourhood of the highest + // correlation with better resolution and so on. + for (scanCount = 0;scanCount < 4; scanCount ++) + { + j = 0; + while (scanOffsets[scanCount][j]) + { + tempOffset = corrOffset + scanOffsets[scanCount][j]; + if (tempOffset >= seekLength) break; + + // Calculates correlation value for the mixing position corresponding + // to 'tempOffset' + corr = calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer); + + // Checks for the highest correlation value + if (corr > bestCorr) + { + bestCorr = corr; + bestOffs = tempOffset; + } + j ++; + } + corrOffset = bestOffs; + } + // clear cross correlation routine state if necessary (is so e.g. in MMX routines). + clearCrossCorrState(); + + return bestOffs; +} + + + +// Seeks for the optimal overlap-mixing position. The 'mono' version of the +// routine +// +// The best position is determined as the position where the two overlapped +// sample sequences are 'most alike', in terms of the highest cross-correlation +// value over the overlapping period +uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos) +{ + uint bestOffs; + LONG_SAMPLETYPE bestCorr, corr; + uint tempOffset; + const SAMPLETYPE *compare; + + // Slopes the amplitude of the 'midBuffer' samples + precalcCorrReferenceMono(); + + bestCorr = INT_MIN; + bestOffs = 0; + + // Scans for the best correlation value by testing each possible position + // over the permitted range. + for (tempOffset = 0; tempOffset < seekLength; tempOffset ++) + { + compare = refPos + tempOffset; + + // Calculates correlation value for the mixing position corresponding + // to 'tempOffset' + corr = calcCrossCorrMono(pRefMidBuffer, compare); + + // Checks for the highest correlation value + if (corr > bestCorr) + { + bestCorr = corr; + bestOffs = tempOffset; + } + } + // clear cross correlation routine state if necessary (is so e.g. in MMX routines). + clearCrossCorrState(); + + return bestOffs; +} + + +// Seeks for the optimal overlap-mixing position. The 'mono' version of the +// routine +// +// The best position is determined as the position where the two overlapped +// sample sequences are 'most alike', in terms of the highest cross-correlation +// value over the overlapping period +uint TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos) +{ + uint j; + uint bestOffs; + LONG_SAMPLETYPE bestCorr, corr; + uint scanCount, corrOffset, tempOffset; + + // Slopes the amplitude of the 'midBuffer' samples + precalcCorrReferenceMono(); + + bestCorr = INT_MIN; + bestOffs = 0; + corrOffset = 0; + tempOffset = 0; + + // Scans for the best correlation value using four-pass hierarchical search. + // + // The look-up table 'scans' has hierarchical position adjusting steps. + // In first pass the routine searhes for the highest correlation with + // relatively coarse steps, then rescans the neighbourhood of the highest + // correlation with better resolution and so on. + for (scanCount = 0;scanCount < 4; scanCount ++) + { + j = 0; + while (scanOffsets[scanCount][j]) + { + tempOffset = corrOffset + scanOffsets[scanCount][j]; + if (tempOffset >= seekLength) break; + + // Calculates correlation value for the mixing position corresponding + // to 'tempOffset' + corr = calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer); + + // Checks for the highest correlation value + if (corr > bestCorr) + { + bestCorr = corr; + bestOffs = tempOffset; + } + j ++; + } + corrOffset = bestOffs; + } + // clear cross correlation routine state if necessary (is so e.g. in MMX routines). + clearCrossCorrState(); + + return bestOffs; +} + + +/// clear cross correlation routine state if necessary +void TDStretch::clearCrossCorrState() +{ + // default implementation is empty. +} + + +// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower +// tempo, larger faster tempo. +void TDStretch::setTempo(float newTempo) +{ + uint intskip; + + tempo = newTempo; + + // Calculate ideal skip length (according to tempo value) + nominalSkip = tempo * (seekWindowLength - overlapLength); + skipFract = 0; + intskip = (int)(nominalSkip + 0.5f); + + // Calculate how many samples are needed in the 'inputBuffer' to + // process another batch of samples + sampleReq = max(intskip + overlapLength, seekWindowLength) + maxOffset; +} + + + +// Sets the number of channels, 1 = mono, 2 = stereo +void TDStretch::setChannels(uint numChannels) +{ + if (channels == numChannels) return; + assert(numChannels == 1 || numChannels == 2); + + channels = numChannels; + inputBuffer.setChannels(channels); + outputBuffer.setChannels(channels); +} + + +// nominal tempo, no need for processing, just pass the samples through +// to outputBuffer +void TDStretch::processNominalTempo() +{ + assert(tempo == 1.0f); + + if (bMidBufferDirty) + { + // If there are samples in pMidBuffer waiting for overlapping, + // do a single sliding overlapping with them in order to prevent a + // clicking distortion in the output sound + if (inputBuffer.numSamples() < overlapLength) + { + // wait until we've got overlapLength input samples + return; + } + // Mix the samples in the beginning of 'inputBuffer' with the + // samples in 'midBuffer' using sliding overlapping + overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0); + outputBuffer.putSamples(overlapLength); + inputBuffer.receiveSamples(overlapLength); + clearMidBuffer(); + // now we've caught the nominal sample flow and may switch to + // bypass mode + } + + // Simply bypass samples from input to output + outputBuffer.moveSamples(inputBuffer); +} + + +// Processes as many processing frames of the samples 'inputBuffer', store +// the result into 'outputBuffer' +void TDStretch::processSamples() +{ + uint ovlSkip, offset; + int temp; + + /* Removed this small optimization - can introduce a click to sound when tempo setting + crosses the nominal value + if (tempo == 1.0f) + { + // tempo not changed from the original, so bypass the processing + processNominalTempo(); + return; + } + */ + + if (bMidBufferDirty == FALSE) + { + // if midBuffer is empty, move the first samples of the input stream + // into it + if (inputBuffer.numSamples() < overlapLength) + { + // wait until we've got overlapLength samples + return; + } + memcpy(pMidBuffer, inputBuffer.ptrBegin(), channels * overlapLength * sizeof(SAMPLETYPE)); + inputBuffer.receiveSamples(overlapLength); + bMidBufferDirty = TRUE; + } + + // Process samples as long as there are enough samples in 'inputBuffer' + // to form a processing frame. + while (inputBuffer.numSamples() >= sampleReq) + { + // If tempo differs from the normal ('SCALE'), scan for the best overlapping + // position + offset = seekBestOverlapPosition(inputBuffer.ptrBegin()); + + // Mix the samples in the 'inputBuffer' at position of 'offset' with the + // samples in 'midBuffer' using sliding overlapping + // ... first partially overlap with the end of the previous sequence + // (that's in 'midBuffer') + overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), offset); + outputBuffer.putSamples(overlapLength); + + // ... then copy sequence samples from 'inputBuffer' to output + temp = (seekWindowLength - 2 * overlapLength);// & 0xfffffffe; + if (temp > 0) + { + outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), temp); + } + + // Copies the end of the current sequence from 'inputBuffer' to + // 'midBuffer' for being mixed with the beginning of the next + // processing sequence and so on + assert(offset + seekWindowLength <= inputBuffer.numSamples()); + memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + seekWindowLength - overlapLength), + channels * sizeof(SAMPLETYPE) * overlapLength); + bMidBufferDirty = TRUE; + + // Remove the processed samples from the input buffer. Update + // the difference between integer & nominal skip step to 'skipFract' + // in order to prevent the error from accumulating over time. + skipFract += nominalSkip; // real skip size + ovlSkip = (int)skipFract; // rounded to integer skip + skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip + inputBuffer.receiveSamples(ovlSkip); + } +} + + +// Adds 'numsamples' pcs of samples from the 'samples' memory position into +// the input of the object. +void TDStretch::putSamples(const SAMPLETYPE *samples, uint numSamples) +{ + // Add the samples into the input buffer + inputBuffer.putSamples(samples, numSamples); + // Process the samples in input buffer + processSamples(); +} + + + +/// Set new overlap length parameter & reallocate RefMidBuffer if necessary. +void TDStretch::acceptNewOverlapLength(uint newOverlapLength) +{ + uint prevOvl; + + prevOvl = overlapLength; + overlapLength = newOverlapLength; + + if (overlapLength > prevOvl) + { + delete[] pMidBuffer; + delete[] pRefMidBufferUnaligned; + + pMidBuffer = new SAMPLETYPE[overlapLength * 2]; + bMidBufferDirty = TRUE; + clearMidBuffer(); + + pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)]; + // ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency + pRefMidBuffer = (SAMPLETYPE *)((((ulongptr)pRefMidBufferUnaligned) + 15) & -16); + } +} + + +// Operator 'new' is overloaded so that it automatically creates a suitable instance +// depending on if we've a MMX/SSE/etc-capable CPU available or not. +void * TDStretch::operator new(size_t s) +{ + // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead! + assert(FALSE); + return NULL; +} + + +TDStretch * TDStretch::newInstance() +{ + uint uExtensions = 0; + +#if !defined(_MSC_VER) || !defined(__x86_64__) + uExtensions = detectCPUextensions(); +#endif + + // Check if MMX/SSE/3DNow! instruction set extensions supported by CPU + +#ifdef ALLOW_MMX + // MMX routines available only with integer sample types + if (uExtensions & SUPPORT_MMX) + { + return ::new TDStretchMMX; + } + else +#endif // ALLOW_MMX + + +#ifdef ALLOW_SSE + if (uExtensions & SUPPORT_SSE) + { + // SSE support + return ::new TDStretchSSE; + } + else +#endif // ALLOW_SSE + + +#ifdef ALLOW_3DNOW + if (uExtensions & SUPPORT_3DNOW) + { + // 3DNow! support + return ::new TDStretch3DNow; + } + else +#endif // ALLOW_3DNOW + + { + // ISA optimizations not supported, use plain C version + return ::new TDStretch; + } +} + + +////////////////////////////////////////////////////////////////////////////// +// +// Integer arithmetics specific algorithm implementations. +// +////////////////////////////////////////////////////////////////////////////// + +#ifdef INTEGER_SAMPLES + +// Slopes the amplitude of the 'midBuffer' samples so that cross correlation +// is faster to calculate +void TDStretch::precalcCorrReferenceStereo() +{ + int i, cnt2; + int temp, temp2; + + for (i=0 ; i < (int)overlapLength ;i ++) + { + temp = i * (overlapLength - i); + cnt2 = i * 2; + + temp2 = (pMidBuffer[cnt2] * temp) / slopingDivider; + pRefMidBuffer[cnt2] = (short)(temp2); + temp2 = (pMidBuffer[cnt2 + 1] * temp) / slopingDivider; + pRefMidBuffer[cnt2 + 1] = (short)(temp2); + } +} + + +// Slopes the amplitude of the 'midBuffer' samples so that cross correlation +// is faster to calculate +void TDStretch::precalcCorrReferenceMono() +{ + int i; + long temp; + long temp2; + + for (i=0 ; i < (int)overlapLength ;i ++) + { + temp = i * (overlapLength - i); + temp2 = (pMidBuffer[i] * temp) / slopingDivider; + pRefMidBuffer[i] = (short)temp2; + } +} + + +// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo' +// version of the routine. +void TDStretch::overlapStereo(short *output, const short *input) const +{ + int i; + short temp; + uint cnt2; + + for (i = 0; i < (int)overlapLength ; i ++) + { + temp = (short)(overlapLength - i); + cnt2 = 2 * i; + output[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength; + output[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength; + } +} + + +/// Calculates overlap period length in samples. +/// Integer version rounds overlap length to closest power of 2 +/// for a divide scaling operation. +void TDStretch::calculateOverlapLength(uint overlapMs) +{ + uint newOvl; + + overlapDividerBits = _getClosest2Power((sampleRate * overlapMs) / 1000.0); + if (overlapDividerBits > 9) overlapDividerBits = 9; + if (overlapDividerBits < 4) overlapDividerBits = 4; + newOvl = 1<> overlapDividerBits; + } + + return corr; +} + + +long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const +{ + long corr; + uint i; + + corr = 0; + for (i = 2; i < 2 * overlapLength; i += 2) + { + corr += (mixingPos[i] * compare[i] + + mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; + } + + return corr; +} + +#endif // INTEGER_SAMPLES + +////////////////////////////////////////////////////////////////////////////// +// +// Floating point arithmetics specific algorithm implementations. +// + +#ifdef FLOAT_SAMPLES + + +// Slopes the amplitude of the 'midBuffer' samples so that cross correlation +// is faster to calculate +void TDStretch::precalcCorrReferenceStereo() +{ + int i, cnt2; + float temp; + + for (i=0 ; i < (int)overlapLength ;i ++) + { + temp = (float)i * (float)(overlapLength - i); + cnt2 = i * 2; + pRefMidBuffer[cnt2] = (float)(pMidBuffer[cnt2] * temp); + pRefMidBuffer[cnt2 + 1] = (float)(pMidBuffer[cnt2 + 1] * temp); + } +} + + +// Slopes the amplitude of the 'midBuffer' samples so that cross correlation +// is faster to calculate +void TDStretch::precalcCorrReferenceMono() +{ + int i; + float temp; + + for (i=0 ; i < (int)overlapLength ;i ++) + { + temp = (float)i * (float)(overlapLength - i); + pRefMidBuffer[i] = (float)(pMidBuffer[i] * temp); + } +} + + +// SSE-optimized version of the function overlapStereo +void TDStretch::overlapStereo(float *output, const float *input) const +{ + int i; + uint cnt2; + float fTemp; + float fScale; + float fi; + + fScale = 1.0f / (float)overlapLength; + + for (i = 0; i < (int)overlapLength ; i ++) + { + fTemp = (float)(overlapLength - i) * fScale; + fi = (float)i * fScale; + cnt2 = 2 * i; + output[cnt2 + 0] = input[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp; + output[cnt2 + 1] = input[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp; + } +} + + +/// Calculates overlap period length in samples. +void TDStretch::calculateOverlapLength(uint overlapMs) +{ + uint newOvl; + + newOvl = (sampleRate * overlapMs) / 1000; + if (newOvl < 16) newOvl = 16; + + // must be divisible by 8 + newOvl -= newOvl % 8; + + acceptNewOverlapLength(newOvl); +} + + + +double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const +{ + double corr; + uint i; + + corr = 0; + for (i = 1; i < overlapLength; i ++) + { + corr += mixingPos[i] * compare[i]; + } + + return corr; +} + + +double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const +{ + double corr; + uint i; + + corr = 0; + for (i = 2; i < 2 * overlapLength; i += 2) + { + corr += mixingPos[i] * compare[i] + + mixingPos[i + 1] * compare[i + 1]; + } + + return corr; +} + +#endif // FLOAT_SAMPLES diff --git a/3rdparty/SoundTouch/TDStretch.h b/3rdparty/SoundTouch/TDStretch.h new file mode 100644 index 0000000000..a05a7072fc --- /dev/null +++ b/3rdparty/SoundTouch/TDStretch.h @@ -0,0 +1,236 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo +/// while maintaining the original pitch by using a time domain WSOLA-like method +/// with several performance-increasing tweaks. +/// +/// Note : MMX optimized functions reside in a separate, platform-specific file, +/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp' +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.16 $ +// +// $Id: TDStretch.h,v 1.16 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TDStretch_H +#define TDStretch_H + +#include "STTypes.h" +#include "RateTransposer.h" +#include "FIFOSamplePipe.h" + +namespace soundtouch +{ + +// Default values for sound processing parameters: + +/// Default length of a single processing sequence, in milliseconds. This determines to how +/// long sequences the original sound is chopped in the time-stretch algorithm. +/// +/// The larger this value is, the lesser sequences are used in processing. In principle +/// a bigger value sounds better when slowing down tempo, but worse when increasing tempo +/// and vice versa. +/// +/// Increasing this value reduces computational burden & vice versa. +#define DEFAULT_SEQUENCE_MS 61 + +#define DEFAULT_SEEKWINDOW_MS 18 + +#define DEFAULT_OVERLAP_MS 7 + + +/// Class that does the time-stretch (tempo change) effect for the processed +/// sound. +class TDStretch : public FIFOProcessor +{ +protected: + uint channels; + uint sampleReq; + float tempo; + + SAMPLETYPE *pMidBuffer; + SAMPLETYPE *pRefMidBuffer; + SAMPLETYPE *pRefMidBufferUnaligned; + uint overlapLength; + uint overlapDividerBits; + uint slopingDivider; + uint seekLength; + uint seekWindowLength; + uint maxOffset; + float nominalSkip; + float skipFract; + FIFOSampleBuffer outputBuffer; + FIFOSampleBuffer inputBuffer; + BOOL bQuickseek; + BOOL bMidBufferDirty; + + uint sampleRate; + uint sequenceMs; + uint seekWindowMs; + uint overlapMs; + + void acceptNewOverlapLength(uint newOverlapLength); + + virtual void clearCrossCorrState(); + void calculateOverlapLength(uint overlapMs); + + virtual LONG_SAMPLETYPE calcCrossCorrStereo(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const; + virtual LONG_SAMPLETYPE calcCrossCorrMono(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const; + + virtual uint seekBestOverlapPositionStereo(const SAMPLETYPE *refPos); + virtual uint seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos); + virtual uint seekBestOverlapPositionMono(const SAMPLETYPE *refPos); + virtual uint seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos); + uint seekBestOverlapPosition(const SAMPLETYPE *refPos); + + virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const; + virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const; + + void clearMidBuffer(); + void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const; + + void precalcCorrReferenceMono(); + void precalcCorrReferenceStereo(); + + void processNominalTempo(); + + /// Changes the tempo of the given sound samples. + /// Returns amount of samples returned in the "output" buffer. + /// The maximum amount of samples that can be returned at a time is set by + /// the 'set_returnBuffer_size' function. + void processSamples(); + +public: + TDStretch(); + virtual ~TDStretch(); + + /// Operator 'new' is overloaded so that it automatically creates a suitable instance + /// depending on if we've a MMX/SSE/etc-capable CPU available or not. + void *operator new(size_t s); + + /// Use this function instead of "new" operator to create a new instance of this class. + /// This function automatically chooses a correct feature set depending on if the CPU + /// supports MMX/SSE/etc extensions. + static TDStretch *newInstance(); + + /// Returns the output buffer object + FIFOSamplePipe *getOutput() { return &outputBuffer; }; + + /// Returns the input buffer object + FIFOSamplePipe *getInput() { return &inputBuffer; }; + + /// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower + /// tempo, larger faster tempo. + void setTempo(float newTempo); + + /// Returns nonzero if there aren't any samples available for outputting. + virtual void clear(); + + /// Clears the input buffer + void clearInput(); + + /// Sets the number of channels, 1 = mono, 2 = stereo + void setChannels(uint numChannels); + + /// Enables/disables the quick position seeking algorithm. Zero to disable, + /// nonzero to enable + void enableQuickSeek(BOOL enable); + + /// Returns nonzero if the quick seeking algorithm is enabled. + BOOL isQuickSeekEnabled() const; + + /// Sets routine control parameters. These control are certain time constants + /// defining how the sound is stretched to the desired duration. + // + /// 'sampleRate' = sample rate of the sound + /// 'sequenceMS' = one processing sequence length in milliseconds + /// 'seekwindowMS' = seeking window length for scanning the best overlapping + /// position + /// 'overlapMS' = overlapping length + void setParameters(uint sampleRate, ///< Samplerate of sound being processed (Hz) + uint sequenceMS = DEFAULT_SEQUENCE_MS, ///< Single processing sequence length (ms) + uint seekwindowMS = DEFAULT_SEEKWINDOW_MS, ///< Offset seeking window length (ms) + uint overlapMS = DEFAULT_OVERLAP_MS ///< Sequence overlapping length (ms) + ); + + /// Get routine control parameters, see setParameters() function. + /// Any of the parameters to this function can be NULL, in such case corresponding parameter + /// value isn't returned. + void getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs); + + /// Adds 'numsamples' pcs of samples from the 'samples' memory position into + /// the input of the object. + virtual void putSamples( + const SAMPLETYPE *samples, ///< Input sample data + uint numSamples ///< Number of samples in 'samples' so that one sample + ///< contains both channels if stereo + ); +}; + + + +// Implementation-specific class declarations: + +//#ifdef ALLOW_MMX +// /// Class that implements MMX optimized routines for 16bit integer samples type. +// class TDStretchMMX : public TDStretch +// { +// protected: +// long calcCrossCorrStereo(const short *mixingPos, const short *compare) const; +// virtual void overlapStereo(short *output, const short *input) const; +// virtual void clearCrossCorrState(); +// }; +//#endif /// ALLOW_MMX +// +// +//#ifdef ALLOW_3DNOW +// /// Class that implements 3DNow! optimized routines for floating point samples type. +// class TDStretch3DNow : public TDStretch +// { +// protected: +// double calcCrossCorrStereo(const float *mixingPos, const float *compare) const; +// }; +//#endif /// ALLOW_3DNOW + + +#ifdef ALLOW_SSE + /// Class that implements SSE optimized routines for floating point samples type. + class TDStretchSSE : public TDStretch + { + protected: + double calcCrossCorrStereo(const float *mixingPos, const float *compare) const; + }; + +#endif /// ALLOW_SSE + +} +#endif /// TDStretch_H diff --git a/3rdparty/SoundTouch/WavFile.cpp b/3rdparty/SoundTouch/WavFile.cpp new file mode 100644 index 0000000000..77db8cc431 --- /dev/null +++ b/3rdparty/SoundTouch/WavFile.cpp @@ -0,0 +1,714 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Classes for easy reading & writing of WAV sound files. +/// +/// For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly +/// parse the WAV files with such processors. +/// +/// Admittingly, more complete WAV reader routines may exist in public domain, +/// but the reason for 'yet another' one is that those generic WAV reader +/// libraries are exhaustingly large and cumbersome! Wanted to have something +/// simpler here, i.e. something that's not already larger than rest of the +/// SoundTouch/SoundStretch program... +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.15 $ +// +// $Id: WavFile.cpp,v 1.15 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include +#include + +#include "WavFile.h" + +using namespace std; + +const static char riffStr[] = "RIFF"; +const static char waveStr[] = "WAVE"; +const static char fmtStr[] = "fmt "; +const static char dataStr[] = "data"; + + +////////////////////////////////////////////////////////////////////////////// +// +// Helper functions for swapping byte order to correctly read/write WAV files +// with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to +// turn-on the conversion if it appears necessary. +// +// For example, Intel x86 is little-endian and doesn't require conversion, +// while PowerPC of Mac's and many other RISC cpu's are big-endian. + +#ifdef BYTE_ORDER + // In gcc compiler detect the byte order automatically + #if BYTE_ORDER == BIG_ENDIAN + // big-endian platform. + #define _BIG_ENDIAN_ + #endif +#endif + +#ifdef _BIG_ENDIAN_ + // big-endian CPU, swap bytes in 16 & 32 bit words + + // helper-function to swap byte-order of 32bit integer + static inline void _swap32(unsigned int &dwData) + { + dwData = ((dwData >> 24) & 0x000000FF) | + ((dwData >> 8) & 0x0000FF00) | + ((dwData << 8) & 0x00FF0000) | + ((dwData << 24) & 0xFF000000); + } + + // helper-function to swap byte-order of 16bit integer + static inline void _swap16(unsigned short &wData) + { + wData = ((wData >> 8) & 0x00FF) | + ((wData << 8) & 0xFF00); + } + + // helper-function to swap byte-order of buffer of 16bit integers + static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumWords) + { + unsigned long i; + + for (i = 0; i < dwNumWords; i ++) + { + _swap16(pData[i]); + } + } + +#else // BIG_ENDIAN + // little-endian CPU, WAV file is ok as such + + // dummy helper-function + static inline void _swap32(unsigned int &dwData) + { + // do nothing + } + + // dummy helper-function + static inline void _swap16(unsigned short &wData) + { + // do nothing + } + + // dummy helper-function + static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumBytes) + { + // do nothing + } + +#endif // BIG_ENDIAN + + +////////////////////////////////////////////////////////////////////////////// +// +// Class WavInFile +// + +WavInFile::WavInFile(const char *fileName) +{ + int hdrsOk; + + // Try to open the file for reading + fptr = fopen(fileName, "rb"); + if (fptr == NULL) + { + // didn't succeed + string msg = "Error : Unable to open file \""; + msg += fileName; + msg += "\" for reading."; + throw runtime_error(msg); + } + + // Read the file headers + hdrsOk = readWavHeaders(); + if (hdrsOk != 0) + { + // Something didn't match in the wav file headers + string msg = "File \""; + msg += fileName; + msg += "\" is corrupt or not a WAV file"; + throw runtime_error(msg); + } + + if (header.format.fixed != 1) + { + string msg = "File \""; + msg += fileName; + msg += "\" uses unsupported encoding."; + throw runtime_error(msg); + } + + dataRead = 0; +} + + + +WavInFile::~WavInFile() +{ + close(); +} + + + +void WavInFile::rewind() +{ + int hdrsOk; + + fseek(fptr, 0, SEEK_SET); + hdrsOk = readWavHeaders(); + assert(hdrsOk == 0); + dataRead = 0; +} + + +int WavInFile::checkCharTags() +{ + // header.format.fmt should equal to 'fmt ' + if (memcmp(fmtStr, header.format.fmt, 4) != 0) return -1; + // header.data.data_field should equal to 'data' + if (memcmp(dataStr, header.data.data_field, 4) != 0) return -1; + + return 0; +} + + +int WavInFile::read(char *buffer, int maxElems) +{ + int numBytes; + uint afterDataRead; + + // ensure it's 8 bit format + if (header.format.bits_per_sample != 8) + { + throw runtime_error("Error: WavInFile::read(char*, int) works only with 8bit samples."); + } + assert(sizeof(char) == 1); + + numBytes = maxElems; + afterDataRead = dataRead + numBytes; + if (afterDataRead > header.data.data_len) + { + // Don't read more samples than are marked available in header + numBytes = header.data.data_len - dataRead; + assert(numBytes >= 0); + } + + numBytes = fread(buffer, 1, numBytes, fptr); + dataRead += numBytes; + + return numBytes; +} + + +int WavInFile::read(short *buffer, int maxElems) +{ + unsigned int afterDataRead; + int numBytes; + int numElems; + + if (header.format.bits_per_sample == 8) + { + // 8 bit format + char *temp = new char[maxElems]; + int i; + + numElems = read(temp, maxElems); + // convert from 8 to 16 bit + for (i = 0; i < numElems; i ++) + { + buffer[i] = temp[i] << 8; + } + delete[] temp; + } + else + { + // 16 bit format + assert(header.format.bits_per_sample == 16); + assert(sizeof(short) == 2); + + numBytes = maxElems * 2; + afterDataRead = dataRead + numBytes; + if (afterDataRead > header.data.data_len) + { + // Don't read more samples than are marked available in header + numBytes = header.data.data_len - dataRead; + assert(numBytes >= 0); + } + + numBytes = fread(buffer, 1, numBytes, fptr); + dataRead += numBytes; + numElems = numBytes / 2; + + // 16bit samples, swap byte order if necessary + _swap16Buffer((unsigned short *)buffer, numElems); + } + + return numElems; +} + + + +int WavInFile::read(float *buffer, int maxElems) +{ + short *temp = new short[maxElems]; + int num; + int i; + double fscale; + + num = read(temp, maxElems); + + fscale = 1.0 / 32768.0; + // convert to floats, scale to range [-1..+1[ + for (i = 0; i < num; i ++) + { + buffer[i] = (float)(fscale * (double)temp[i]); + } + + delete[] temp; + + return num; +} + + +int WavInFile::eof() const +{ + // return true if all data has been read or file eof has reached + return (dataRead == header.data.data_len || feof(fptr)); +} + + +void WavInFile::close() +{ + fclose(fptr); + fptr = NULL; +} + + + +// test if character code is between a white space ' ' and little 'z' +static int isAlpha(char c) +{ + return (c >= ' ' && c <= 'z') ? 1 : 0; +} + + +// test if all characters are between a white space ' ' and little 'z' +static int isAlphaStr(char *str) +{ + int c; + + c = str[0]; + while (c) + { + if (isAlpha(c) == 0) return 0; + str ++; + c = str[0]; + } + + return 1; +} + + +int WavInFile::readRIFFBlock() +{ + fread(&(header.riff), sizeof(WavRiff), 1, fptr); + + // swap 32bit data byte order if necessary + _swap32((unsigned int &)header.riff.package_len); + + // header.riff.riff_char should equal to 'RIFF'); + if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1; + // header.riff.wave should equal to 'WAVE' + if (memcmp(waveStr, header.riff.wave, 4) != 0) return -1; + + return 0; +} + + + + +int WavInFile::readHeaderBlock() +{ + char label[5]; + string sLabel; + + // lead label string + fread(label, 1, 4, fptr); + label[4] = 0; + + if (isAlphaStr(label) == 0) return -1; // not a valid label + + // Decode blocks according to their label + if (strcmp(label, fmtStr) == 0) + { + int nLen, nDump; + + // 'fmt ' block + memcpy(header.format.fmt, fmtStr, 4); + + // read length of the format field + fread(&nLen, sizeof(int), 1, fptr); + // swap byte order if necessary + _swap32((unsigned int &)nLen); // int format_len; + header.format.format_len = nLen; + + // calculate how much length differs from expected + nDump = nLen - (sizeof(header.format) - 8); + + // if format_len is larger than expected, read only as much data as we've space for + if (nDump > 0) + { + nLen = sizeof(header.format) - 8; + } + + // read data + fread(&(header.format.fixed), nLen, 1, fptr); + + // swap byte order if necessary + _swap16((unsigned short &)header.format.fixed); // short int fixed; + _swap16((unsigned short &)header.format.channel_number); // short int channel_number; + _swap32((unsigned int &)header.format.sample_rate); // int sample_rate; + _swap32((unsigned int &)header.format.byte_rate); // int byte_rate; + _swap16((unsigned short &)header.format.byte_per_sample); // short int byte_per_sample; + _swap16((unsigned short &)header.format.bits_per_sample); // short int bits_per_sample; + + // if format_len is larger than expected, skip the extra data + if (nDump > 0) + { + fseek(fptr, nDump, SEEK_CUR); + } + + return 0; + } + else if (strcmp(label, dataStr) == 0) + { + // 'data' block + memcpy(header.data.data_field, dataStr, 4); + fread(&(header.data.data_len), sizeof(uint), 1, fptr); + + // swap byte order if necessary + _swap32((unsigned int &)header.data.data_len); + + return 1; + } + else + { + uint len, i; + uint temp; + // unknown block + + // read length + fread(&len, sizeof(len), 1, fptr); + // scan through the block + for (i = 0; i < len; i ++) + { + fread(&temp, 1, 1, fptr); + if (feof(fptr)) return -1; // unexpected eof + } + } + return 0; +} + + +int WavInFile::readWavHeaders() +{ + int res; + + memset(&header, 0, sizeof(header)); + + res = readRIFFBlock(); + if (res) return 1; + // read header blocks until data block is found + do + { + // read header blocks + res = readHeaderBlock(); + if (res < 0) return 1; // error in file structure + } while (res == 0); + // check that all required tags are legal + return checkCharTags(); +} + + +uint WavInFile::getNumChannels() const +{ + return header.format.channel_number; +} + + +uint WavInFile::getNumBits() const +{ + return header.format.bits_per_sample; +} + + +uint WavInFile::getBytesPerSample() const +{ + return getNumChannels() * getNumBits() / 8; +} + + +uint WavInFile::getSampleRate() const +{ + return header.format.sample_rate; +} + + + +uint WavInFile::getDataSizeInBytes() const +{ + return header.data.data_len; +} + + +uint WavInFile::getNumSamples() const +{ + return header.data.data_len / header.format.byte_per_sample; +} + + +uint WavInFile::getLengthMS() const +{ + uint numSamples; + uint sampleRate; + + numSamples = getNumSamples(); + sampleRate = getSampleRate(); + + assert(numSamples < UINT_MAX / 1000); + return (1000 * numSamples / sampleRate); +} + + +////////////////////////////////////////////////////////////////////////////// +// +// Class WavOutFile +// + +WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels) +{ + bytesWritten = 0; + fptr = fopen(fileName, "wb"); + if (fptr == NULL) + { + string msg = "Error : Unable to open file \""; + msg += fileName; + msg += "\" for writing."; + //pmsg = msg.c_str; + throw runtime_error(msg); + } + + fillInHeader(sampleRate, bits, channels); + writeHeader(); +} + + + +WavOutFile::~WavOutFile() +{ + close(); +} + + + +void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels) +{ + // fill in the 'riff' part.. + + // copy string 'RIFF' to riff_char + memcpy(&(header.riff.riff_char), riffStr, 4); + // package_len unknown so far + header.riff.package_len = 0; + // copy string 'WAVE' to wave + memcpy(&(header.riff.wave), waveStr, 4); + + + // fill in the 'format' part.. + + // copy string 'fmt ' to fmt + memcpy(&(header.format.fmt), fmtStr, 4); + + header.format.format_len = 0x10; + header.format.fixed = 1; + header.format.channel_number = (short)channels; + header.format.sample_rate = sampleRate; + header.format.bits_per_sample = (short)bits; + header.format.byte_per_sample = (short)(bits * channels / 8); + header.format.byte_rate = header.format.byte_per_sample * sampleRate; + header.format.sample_rate = sampleRate; + + // fill in the 'data' part.. + + // copy string 'data' to data_field + memcpy(&(header.data.data_field), dataStr, 4); + // data_len unknown so far + header.data.data_len = 0; +} + + +void WavOutFile::finishHeader() +{ + // supplement the file length into the header structure + header.riff.package_len = bytesWritten + 36; + header.data.data_len = bytesWritten; + + writeHeader(); +} + + + +void WavOutFile::writeHeader() +{ + WavHeader hdrTemp; + + // swap byte order if necessary + hdrTemp = header; + _swap32((unsigned int &)hdrTemp.riff.package_len); + _swap32((unsigned int &)hdrTemp.format.format_len); + _swap16((unsigned short &)hdrTemp.format.fixed); + _swap16((unsigned short &)hdrTemp.format.channel_number); + _swap32((unsigned int &)hdrTemp.format.sample_rate); + _swap32((unsigned int &)hdrTemp.format.byte_rate); + _swap16((unsigned short &)hdrTemp.format.byte_per_sample); + _swap16((unsigned short &)hdrTemp.format.bits_per_sample); + _swap32((unsigned int &)hdrTemp.data.data_len); + + // write the supplemented header in the beginning of the file + fseek(fptr, 0, SEEK_SET); + fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr); + // jump back to the end of the file + fseek(fptr, 0, SEEK_END); +} + + + +void WavOutFile::close() +{ + finishHeader(); + fclose(fptr); + fptr = NULL; +} + + +void WavOutFile::write(const char *buffer, int numElems) +{ + int res; + + if (header.format.bits_per_sample != 8) + { + throw runtime_error("Error: WavOutFile::write(const char*, int) accepts only 8bit samples."); + } + assert(sizeof(char) == 1); + + res = fwrite(buffer, 1, numElems, fptr); + if (res != numElems) + { + throw runtime_error("Error while writing to a wav file."); + } + + bytesWritten += numElems; +} + + +void WavOutFile::write(const short *buffer, int numElems) +{ + int res; + + // 16 bit samples + if (numElems < 1) return; // nothing to do + + if (header.format.bits_per_sample == 8) + { + int i; + char *temp = new char[numElems]; + // convert from 16bit format to 8bit format + for (i = 0; i < numElems; i ++) + { + temp[i] = buffer[i] >> 8; + } + // write in 8bit format + write(temp, numElems); + delete[] temp; + } + else + { + // 16bit format + unsigned short *pTemp = new unsigned short[numElems]; + + assert(header.format.bits_per_sample == 16); + + // allocate temp buffer to swap byte order if necessary + memcpy(pTemp, buffer, numElems * 2); + _swap16Buffer(pTemp, numElems); + + res = fwrite(pTemp, 2, numElems, fptr); + + delete[] pTemp; + + if (res != numElems) + { + throw runtime_error("Error while writing to a wav file."); + } + bytesWritten += 2 * numElems; + } +} + + +void WavOutFile::write(const float *buffer, int numElems) +{ + int i; + short *temp = new short[numElems]; + int iTemp; + + // convert to 16 bit integer + for (i = 0; i < numElems; i ++) + { + // convert to integer + iTemp = (int)(32768.0f * buffer[i]); + + // saturate + if (iTemp < -32768) iTemp = -32768; + if (iTemp > 32767) iTemp = 32767; + temp[i] = (short)iTemp; + } + + write(temp, numElems); + + delete[] temp; +} diff --git a/3rdparty/SoundTouch/WavFile.h b/3rdparty/SoundTouch/WavFile.h new file mode 100644 index 0000000000..dd239034d2 --- /dev/null +++ b/3rdparty/SoundTouch/WavFile.h @@ -0,0 +1,253 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Classes for easy reading & writing of WAV sound files. +/// +/// For big-endian CPU, define BIG_ENDIAN during compile-time to correctly +/// parse the WAV files with such processors. +/// +/// Admittingly, more complete WAV reader routines may exist in public domain, but +/// the reason for 'yet another' one is that those generic WAV reader libraries are +/// exhaustingly large and cumbersome! Wanted to have something simpler here, i.e. +/// something that's not already larger than rest of the SoundTouch/SoundStretch program... +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.7 $ +// +// $Id: WavFile.h,v 1.7 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef WAVFILE_H +#define WAVFILE_H + +#include + +#ifndef uint +typedef unsigned int uint; +#endif + + +/// WAV audio file 'riff' section header +typedef struct +{ + char riff_char[4]; + int package_len; + char wave[4]; +} WavRiff; + +/// WAV audio file 'format' section header +typedef struct +{ + char fmt[4]; + int format_len; + short fixed; + short channel_number; + int sample_rate; + int byte_rate; + short byte_per_sample; + short bits_per_sample; +} WavFormat; + +/// WAV audio file 'data' section header +typedef struct +{ + char data_field[4]; + uint data_len; +} WavData; + + +/// WAV audio file header +typedef struct +{ + WavRiff riff; + WavFormat format; + WavData data; +} WavHeader; + + +/// Class for reading WAV audio files. +class WavInFile +{ +private: + /// File pointer. + FILE *fptr; + + /// Counter of how many bytes of sample data have been read from the file. + uint dataRead; + + /// WAV header information + WavHeader header; + + /// Read WAV file headers. + /// \return zero if all ok, nonzero if file format is invalid. + int readWavHeaders(); + + /// Checks WAV file header tags. + /// \return zero if all ok, nonzero if file format is invalid. + int checkCharTags(); + + /// Reads a single WAV file header block. + /// \return zero if all ok, nonzero if file format is invalid. + int readHeaderBlock(); + + /// Reads WAV file 'riff' block + int readRIFFBlock(); + +public: + /// Constructor: Opens the given WAV file. If the file can't be opened, + /// throws 'runtime_error' exception. + WavInFile(const char *filename); + + /// Destructor: Closes the file. + ~WavInFile(); + + /// Close the file. Notice that file is automatically closed also when the + /// class instance is deleted. + void close(); + + /// Rewind to beginning of the file + void rewind(); + + /// Get sample rate. + uint getSampleRate() const; + + /// Get number of bits per sample, i.e. 8 or 16. + uint getNumBits() const; + + /// Get sample data size in bytes. Ahem, this should return same information as + /// 'getBytesPerSample'... + uint getDataSizeInBytes() const; + + /// Get total number of samples in file. + uint getNumSamples() const; + + /// Get number of bytes per audio sample (e.g. 16bit stereo = 4 bytes/sample) + uint getBytesPerSample() const; + + /// Get number of audio channels in the file (1=mono, 2=stereo) + uint getNumChannels() const; + + /// Get the audio file length in milliseconds + uint getLengthMS() const; + + /// Reads audio samples from the WAV file. This routine works only for 8 bit samples. + /// Reads given number of elements from the file or if end-of-file reached, as many + /// elements as are left in the file. + /// + /// \return Number of 8-bit integers read from the file. + int read(char *buffer, int maxElems); + + /// Reads audio samples from the WAV file to 16 bit integer format. Reads given number + /// of elements from the file or if end-of-file reached, as many elements as are + /// left in the file. + /// + /// \return Number of 16-bit integers read from the file. + int read(short *buffer, ///< Pointer to buffer where to read data. + int maxElems ///< Size of 'buffer' array (number of array elements). + ); + + /// Reads audio samples from the WAV file to floating point format, converting + /// sample values to range [-1,1[. Reads given number of elements from the file + /// or if end-of-file reached, as many elements as are left in the file. + /// + /// \return Number of elements read from the file. + int read(float *buffer, ///< Pointer to buffer where to read data. + int maxElems ///< Size of 'buffer' array (number of array elements). + ); + + /// Check end-of-file. + /// + /// \return Nonzero if end-of-file reached. + int eof() const; +}; + + + +/// Class for writing WAV audio files. +class WavOutFile +{ +private: + /// Pointer to the WAV file + FILE *fptr; + + /// WAV file header data. + WavHeader header; + + /// Counter of how many bytes have been written to the file so far. + int bytesWritten; + + /// Fills in WAV file header information. + void fillInHeader(const uint sampleRate, const uint bits, const uint channels); + + /// Finishes the WAV file header by supplementing information of amount of + /// data written to file etc + void finishHeader(); + + /// Writes the WAV file header. + void writeHeader(); + +public: + /// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception + /// if file creation fails. + WavOutFile(const char *fileName, ///< Filename + int sampleRate, ///< Sample rate (e.g. 44100 etc) + int bits, ///< Bits per sample (8 or 16 bits) + int channels ///< Number of channels (1=mono, 2=stereo) + ); + + /// Destructor: Finalizes & closes the WAV file. + ~WavOutFile(); + + /// Write data to WAV file. This function works only with 8bit samples. + /// Throws a 'runtime_error' exception if writing to file fails. + void write(const char *buffer, ///< Pointer to sample data buffer. + int numElems ///< How many array items are to be written to file. + ); + + /// Write data to WAV file. Throws a 'runtime_error' exception if writing to + /// file fails. + void write(const short *buffer, ///< Pointer to sample data buffer. + int numElems ///< How many array items are to be written to file. + ); + + /// Write data to WAV file in floating point format, saturating sample values to range + /// [-1..+1[. Throws a 'runtime_error' exception if writing to file fails. + void write(const float *buffer, ///< Pointer to sample data buffer. + int numElems ///< How many array items are to be written to file. + ); + + /// Finalize & close the WAV file. Automatically supplements the WAV file header + /// information according to written data etc. + /// + /// Notice that file is automatically closed also when the class instance is deleted. + void close(); +}; + +#endif diff --git a/3rdparty/SoundTouch/cpu_detect.h b/3rdparty/SoundTouch/cpu_detect.h new file mode 100644 index 0000000000..568817f17b --- /dev/null +++ b/3rdparty/SoundTouch/cpu_detect.h @@ -0,0 +1,62 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// A header file for detecting the Intel MMX instructions set extension. +/// +/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the +/// routine implementations for x86 Windows, x86 gnu version and non-x86 +/// platforms, respectively. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.4 $ +// +// $Id: cpu_detect.h,v 1.4 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _CPU_DETECT_H_ +#define _CPU_DETECT_H_ + +#include "STTypes.h" + +#define SUPPORT_MMX 0x0001 +#define SUPPORT_3DNOW 0x0002 +#define SUPPORT_ALTIVEC 0x0004 +#define SUPPORT_SSE 0x0008 +#define SUPPORT_SSE2 0x0010 + +/// Checks which instruction set extensions are supported by the CPU. +/// +/// \return A bitmask of supported extensions, see SUPPORT_... defines. +uint detectCPUextensions(void); + +/// Disables given set of instruction extensions. See SUPPORT_... defines. +void disableExtensions(uint wDisableMask); + +#endif // _CPU_DETECT_H_ diff --git a/3rdparty/SoundTouch/cpu_detect_x86_gcc.cpp b/3rdparty/SoundTouch/cpu_detect_x86_gcc.cpp new file mode 100644 index 0000000000..75bd771099 --- /dev/null +++ b/3rdparty/SoundTouch/cpu_detect_x86_gcc.cpp @@ -0,0 +1,138 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// gcc version of the x86 CPU detect routine. +/// +/// This file is to be compiled on any platform with the GNU C compiler. +/// Compiler. Please see 'cpu_detect_x86_win.cpp' for the x86 Windows version +/// of this file. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.6 $ +// +// $Id: cpu_detect_x86_gcc.cpp,v 1.6 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "cpu_detect.h" + +#ifndef __GNUC__ +#error wrong platform - this source code file is for the GNU C compiler. +#endif + +using namespace std; + +#include +////////////////////////////////////////////////////////////////////////////// +// +// processor instructions extension detection routines +// +////////////////////////////////////////////////////////////////////////////// + + +// Flag variable indicating whick ISA extensions are disabled (for debugging) +static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions + +// Disables given set of instruction extensions. See SUPPORT_... defines. +void disableExtensions(uint dwDisableMask) +{ + _dwDisabledISA = dwDisableMask; +} + + + +/// Checks which instruction set extensions are supported by the CPU. +uint detectCPUextensions(void) +{ +#ifndef __i386__ + return 0; // always disable extensions on non-x86 platforms. +#else + uint res = 0; + + if (_dwDisabledISA == 0xffffffff) return 0; + + asm volatile( + "\n\txor %%esi, %%esi" // clear %%esi = result register + // check if 'cpuid' instructions is available by toggling eflags bit 21 + + "\n\tpushf" // save eflags to stack + "\n\tpop %%eax" // load eax from stack (with eflags) + "\n\tmovl %%eax, %%ecx" // save the original eflags values to ecx + "\n\txor $0x00200000, %%eax" // toggle bit 21 + "\n\tpush %%eax" // store toggled eflags to stack + "\n\tpopf" // load eflags from stack + "\n\tpushf" // save updated eflags to stack + "\n\tpop %%eax" // load from stack + "\n\txor %%edx, %%edx" // clear edx for defaulting no mmx + "\n\tcmp %%ecx, %%eax" // compare to original eflags values + "\n\tjz end" // jumps to 'end' if cpuid not present + + // cpuid instruction available, test for presence of mmx instructions + + "\n\tmovl $1, %%eax" + "\n\tcpuid" +// movl $0x00800000, %edx // force enable MMX + "\n\ttest $0x00800000, %%edx" + "\n\tjz end" // branch if MMX not available + + "\n\tor $0x01, %%esi" // otherwise add MMX support bit + + "\n\ttest $0x02000000, %%edx" + "\n\tjz test3DNow" // branch if SSE not available + + "\n\tor $0x08, %%esi" // otherwise add SSE support bit + + "\n\ttest3DNow:" + // test for precense of AMD extensions + "\n\tmov $0x80000000, %%eax" + "\n\tcpuid" + "\n\tcmp $0x80000000, %%eax" + "\n\tjbe end" // branch if no AMD extensions detected + + // test for precense of 3DNow! extension + "\n\tmov $0x80000001, %%eax" + "\n\tcpuid" + "\n\ttest $0x80000000, %%edx" + "\n\tjz end" // branch if 3DNow! not detected + + "\n\tor $0x02, %%esi" // otherwise add 3DNow support bit + + "\n\tend:" + + "\n\tmov %%esi, %0" + + : "=r" (res) + : /* no inputs */ + : "%edx", "%eax", "%ecx", "%esi" ); + + return res & ~_dwDisabledISA; +#endif +} diff --git a/3rdparty/SoundTouch/cpu_detect_x86_win.cpp b/3rdparty/SoundTouch/cpu_detect_x86_win.cpp new file mode 100644 index 0000000000..d8f9e58dd0 --- /dev/null +++ b/3rdparty/SoundTouch/cpu_detect_x86_win.cpp @@ -0,0 +1,126 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Win32 version of the x86 CPU detect routine. +/// +/// This file is to be compiled in Windows platform with Microsoft Visual C++ +/// Compiler. Please see 'cpu_detect_x86_gcc.cpp' for the gcc compiler version +/// for all GNU platforms. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.10 $ +// +// $Id: cpu_detect_x86_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include "cpu_detect.h" + +#ifndef _WIN32 +#error wrong platform - this source code file is exclusively for Win32 platform +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// processor instructions extension detection routines +// +////////////////////////////////////////////////////////////////////////////// + +// Flag variable indicating whick ISA extensions are disabled (for debugging) +static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions + + +// Disables given set of instruction extensions. See SUPPORT_... defines. +void disableExtensions(uint dwDisableMask) +{ + _dwDisabledISA = dwDisableMask; +} + + + +/// Checks which instruction set extensions are supported by the CPU. +uint detectCPUextensions(void) +{ + uint res = 0; + + if (_dwDisabledISA == 0xffffffff) return 0; + + _asm + { + ; check if 'cpuid' instructions is available by toggling eflags bit 21 + ; + xor esi, esi ; clear esi = result register + + pushfd ; save eflags to stack + pop eax ; load eax from stack (with eflags) + mov ecx, eax ; save the original eflags values to ecx + xor eax, 0x00200000 ; toggle bit 21 + push eax ; store toggled eflags to stack + popfd ; load eflags from stack + pushfd ; save updated eflags to stack + pop eax ; load from stack + xor edx, edx ; clear edx for defaulting no mmx + cmp eax, ecx ; compare to original eflags values + jz end ; jumps to 'end' if cpuid not present + + ; cpuid instruction available, test for presence of mmx instructions + mov eax, 1 + cpuid + test edx, 0x00800000 + jz end ; branch if MMX not available + + or esi, SUPPORT_MMX ; otherwise add MMX support bit + + test edx, 0x02000000 + jz test3DNow ; branch if SSE not available + + or esi, SUPPORT_SSE ; otherwise add SSE support bit + + test3DNow: + ; test for precense of AMD extensions + mov eax, 0x80000000 + cpuid + cmp eax, 0x80000000 + jbe end ; branch if no AMD extensions detected + + ; test for precense of 3DNow! extension + mov eax, 0x80000001 + cpuid + test edx, 0x80000000 + jz end ; branch if 3DNow! not detected + + or esi, SUPPORT_3DNOW ; otherwise add 3DNow support bit + + end: + + mov res, esi + } + + return res & ~_dwDisabledISA; +} diff --git a/3rdparty/SoundTouch/mmx_optimized.cpp b/3rdparty/SoundTouch/mmx_optimized.cpp new file mode 100644 index 0000000000..f5afb595a5 --- /dev/null +++ b/3rdparty/SoundTouch/mmx_optimized.cpp @@ -0,0 +1,305 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// MMX optimized routines. All MMX optimized functions have been gathered into +/// this single source code file, regardless to their class or original source +/// code file, in order to ease porting the library to other compiler and +/// processor platforms. +/// +/// The MMX-optimizations are programmed using MMX compiler intrinsics that +/// are supported both by Microsoft Visual C++ and GCC compilers, so this file +/// should compile with both toolsets. +/// +/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++ +/// 6.0 processor pack" update to support compiler intrinsic syntax. The update +/// is available for download at Microsoft Developers Network, see here: +/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/06 18:52:43 $ +// File revision : $Revision: 1.1 $ +// +// $Id: mmx_optimized.cpp,v 1.1 2006/02/06 18:52:43 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include "STTypes.h" + +#ifdef ALLOW_MMX +// MMX routines available only with integer sample type + +#if !(_WIN32 || __i386__ || __x86_64__) +#error "wrong platform - this source code file is exclusively for x86 platforms" +#endif + +using namespace soundtouch; + +////////////////////////////////////////////////////////////////////////////// +// +// implementation of MMX optimized functions of class 'TDStretchMMX' +// +////////////////////////////////////////////////////////////////////////////// + +#include "TDStretch.h" +#include +#include + + +// Calculates cross correlation of two buffers +long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const +{ + const __m64 *pVec1, *pVec2; + __m64 shifter; + __m64 accu; + long corr; + uint i; + + pVec1 = (__m64*)pV1; + pVec2 = (__m64*)pV2; + + shifter = _m_from_int(overlapDividerBits); + accu = _mm_setzero_si64(); + + // Process 4 parallel sets of 2 * stereo samples each during each + // round to improve CPU-level parallellization. + for (i = 0; i < overlapLength / 8; i ++) + { + __m64 temp; + + // dictionary of instructions: + // _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3] + // _mm_add_pi32 : 2*32bit add + // _m_psrad : 32bit right-shift + + temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]), + _mm_madd_pi16(pVec1[1], pVec2[1])); + accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter)); + + temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]), + _mm_madd_pi16(pVec1[3], pVec2[3])); + accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter)); + + pVec1 += 4; + pVec2 += 4; + } + + // copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1 + // and finally store the result into the variable "corr" + + accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32)); + corr = _m_to_int(accu); + + // Clear MMS state + _m_empty(); + + return corr; + // Note: Warning about the missing EMMS instruction is harmless + // as it'll be called elsewhere. +} + + + +void TDStretchMMX::clearCrossCorrState() +{ + // Clear MMS state + _m_empty(); + //_asm EMMS; +} + + + +// MMX-optimized version of the function overlapStereo +void TDStretchMMX::overlapStereo(short *output, const short *input) const +{ + const __m64 *pVinput, *pVMidBuf; + __m64 *pVdest; + __m64 mix1, mix2, adder, shifter; + uint i; + + pVinput = (const __m64*)input; + pVMidBuf = (const __m64*)pMidBuffer; + pVdest = (__m64*)output; + + // mix1 = mixer values for 1st stereo sample + // mix1 = mixer values for 2nd stereo sample + // adder = adder for updating mixer values after each round + + mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength); + adder = _mm_set_pi16(1, -1, 1, -1); + mix2 = _mm_add_pi16(mix1, adder); + adder = _mm_add_pi16(adder, adder); + + shifter = _m_from_int(overlapDividerBits); + + for (i = 0; i < overlapLength / 4; i ++) + { + __m64 temp1, temp2; + + // load & shuffle data so that input & mixbuffer data samples are paired + temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r + temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r + + // temp = (temp .* mix) >> shifter + temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter); + temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter); + pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit + + // update mix += adder + mix1 = _mm_add_pi16(mix1, adder); + mix2 = _mm_add_pi16(mix2, adder); + + // --- second round begins here --- + + // load & shuffle data so that input & mixbuffer data samples are paired + temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]); // = i2l m2l i2r m2r + temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]); // = i3l m3l i3r m3r + + // temp = (temp .* mix) >> shifter + temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter); + temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter); + pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit + + // update mix += adder + mix1 = _mm_add_pi16(mix1, adder); + mix2 = _mm_add_pi16(mix2, adder); + + pVinput += 2; + pVMidBuf += 2; + pVdest += 2; + } + + _m_empty(); // clear MMS state +} + + +////////////////////////////////////////////////////////////////////////////// +// +// implementation of MMX optimized functions of class 'FIRFilter' +// +////////////////////////////////////////////////////////////////////////////// + +#include "FIRFilter.h" + + +FIRFilterMMX::FIRFilterMMX() : FIRFilter() +{ + filterCoeffsUnalign = NULL; +} + + +FIRFilterMMX::~FIRFilterMMX() +{ + delete[] filterCoeffsUnalign; +} + + +// (overloaded) Calculates filter coefficients for MMX routine +void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor) +{ + uint i; + FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor); + + // Ensure that filter coeffs array is aligned to 16-byte boundary + delete[] filterCoeffsUnalign; + filterCoeffsUnalign = new short[2 * newLength + 8]; + filterCoeffsAlign = (short *)(((ulongptr)filterCoeffsUnalign + 15) & -16); + + // rearrange the filter coefficients for mmx routines + for (i = 0;i < length; i += 4) + { + filterCoeffsAlign[2 * i + 0] = coeffs[i + 0]; + filterCoeffsAlign[2 * i + 1] = coeffs[i + 2]; + filterCoeffsAlign[2 * i + 2] = coeffs[i + 0]; + filterCoeffsAlign[2 * i + 3] = coeffs[i + 2]; + + filterCoeffsAlign[2 * i + 4] = coeffs[i + 1]; + filterCoeffsAlign[2 * i + 5] = coeffs[i + 3]; + filterCoeffsAlign[2 * i + 6] = coeffs[i + 1]; + filterCoeffsAlign[2 * i + 7] = coeffs[i + 3]; + } +} + + + +// mmx-optimized version of the filter routine for stereo sound +uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uint numSamples) const +{ + // Create stack copies of the needed member variables for asm routines : + uint i, j; + __m64 *pVdest = (__m64*)dest; + + if (length < 2) return 0; + + for (i = 0; i < numSamples / 2; i ++) + { + __m64 accu1; + __m64 accu2; + const __m64 *pVsrc = (const __m64*)src; + const __m64 *pVfilter = (const __m64*)filterCoeffsAlign; + + accu1 = accu2 = _mm_setzero_si64(); + for (j = 0; j < lengthDiv8 * 2; j ++) + { + __m64 temp1, temp2; + + temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]); // = l2 l0 r2 r0 + temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]); // = l3 l1 r3 r1 + + accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0])); // += l2*f2+l0*f0 r2*f2+r0*f0 + accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1])); // += l3*f3+l1*f1 r3*f3+r1*f1 + + temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]); // = l4 l2 r4 r2 + + accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0])); // += l3*f2+l1*f0 r3*f2+r1*f0 + accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1])); // += l4*f3+l2*f1 r4*f3+r2*f1 + + // accu1 += l2*f2+l0*f0 r2*f2+r0*f0 + // += l3*f3+l1*f1 r3*f3+r1*f1 + + // accu2 += l3*f2+l1*f0 r3*f2+r1*f0 + // l4*f3+l2*f1 r4*f3+r2*f1 + + pVfilter += 2; + pVsrc += 2; + } + // accu >>= resultDivFactor + accu1 = _mm_srai_pi32(accu1, resultDivFactor); + accu2 = _mm_srai_pi32(accu2, resultDivFactor); + + // pack 2*2*32bits => 4*16 bits + pVdest[0] = _mm_packs_pi32(accu1, accu2); + src += 4; + pVdest ++; + } + + _m_empty(); // clear emms state + + return (numSamples & 0xfffffffe) - length; +} + +#endif // ALLOW_MMX diff --git a/3rdparty/SoundTouch/sse_optimized.cpp b/3rdparty/SoundTouch/sse_optimized.cpp new file mode 100644 index 0000000000..57598d78d7 --- /dev/null +++ b/3rdparty/SoundTouch/sse_optimized.cpp @@ -0,0 +1,484 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE +/// optimized functions have been gathered into this single source +/// code file, regardless to their class or original source code file, in order +/// to ease porting the library to other compiler and processor platforms. +/// +/// The SSE-optimizations are programmed using SSE compiler intrinsics that +/// are supported both by Microsoft Visual C++ and GCC compilers, so this file +/// should compile with both toolsets. +/// +/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++ +/// 6.0 processor pack" update to support SSE instruction set. The update is +/// available for download at Microsoft Developers Network, see here: +/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx +/// +/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and +/// perform a search with keywords "processor pack". +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2006/02/05 16:44:06 $ +// File revision : $Revision: 1.2 $ +// +// $Id: sse_optimized.cpp,v 1.2 2006/02/05 16:44:06 Olli Exp $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#include "cpu_detect.h" +#include "STTypes.h" + +using namespace soundtouch; + +#ifdef ALLOW_SSE + +// SSE routines available only with float sample type + +////////////////////////////////////////////////////////////////////////////// +// +// implementation of SSE optimized functions of class 'TDStretchSSE' +// +////////////////////////////////////////////////////////////////////////////// + +#include "TDStretch.h" +#include + +// Calculates cross correlation of two buffers +double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const +{ + uint i; + __m128 vSum, *pVec2; + + // Note. It means a major slow-down if the routine needs to tolerate + // unaligned __m128 memory accesses. It's way faster if we can skip + // unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps. + // This can mean up to ~ 10-fold difference (incl. part of which is + // due to skipping every second round for stereo sound though). + // + // Compile-time define ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided + // for choosing if this little cheating is allowed. + +#ifdef ALLOW_NONEXACT_SIMD_OPTIMIZATION + // Little cheating allowed, return valid correlation only for + // aligned locations, meaning every second round for stereo sound. + + #define _MM_LOAD _mm_load_ps + + if (((ulong)pV1) & 15) return -1e50; // skip unaligned locations + +#else + // No cheating allowed, use unaligned load & take the resulting + // performance hit. + #define _MM_LOAD _mm_loadu_ps +#endif + + // ensure overlapLength is divisible by 8 + assert((overlapLength % 8) == 0); + + // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors + // Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not. + pVec2 = (__m128*)pV2; + vSum = _mm_setzero_ps(); + + // Unroll the loop by factor of 4 * 4 operations + for (i = 0; i < overlapLength / 8; i ++) + { + // vSum += pV1[0..3] * pV2[0..3] + vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1),pVec2[0])); + + // vSum += pV1[4..7] * pV2[4..7] + vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 4), pVec2[1])); + + // vSum += pV1[8..11] * pV2[8..11] + vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 8), pVec2[2])); + + // vSum += pV1[12..15] * pV2[12..15] + vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 12), pVec2[3])); + + pV1 += 16; + pVec2 += 4; + } + + // return value = vSum[0] + vSum[1] + vSum[2] + vSum[3] + float *pvSum = (float*)&vSum; + return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]); + + /* This is approximately corresponding routine in C-language: + double corr; + uint i; + + // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors + corr = 0.0; + for (i = 0; i < overlapLength / 8; i ++) + { + corr += pV1[0] * pV2[0] + + pV1[1] * pV2[1] + + pV1[2] * pV2[2] + + pV1[3] * pV2[3] + + pV1[4] * pV2[4] + + pV1[5] * pV2[5] + + pV1[6] * pV2[6] + + pV1[7] * pV2[7] + + pV1[8] * pV2[8] + + pV1[9] * pV2[9] + + pV1[10] * pV2[10] + + pV1[11] * pV2[11] + + pV1[12] * pV2[12] + + pV1[13] * pV2[13] + + pV1[14] * pV2[14] + + pV1[15] * pV2[15]; + + pV1 += 16; + pV2 += 16; + } + */ + + /* This is corresponding routine in assembler. This may be teeny-weeny bit faster + than intrinsic version, but more difficult to maintain & get compiled on multiple + platforms. + + uint overlapLengthLocal = overlapLength; + float corr; + + _asm + { + // Very important note: data in 'pV2' _must_ be aligned to + // 16-byte boundary! + + // give prefetch hints to CPU of what data are to be needed soonish + // give more aggressive hints on pV1 as that changes while pV2 stays + // same between runs + prefetcht0 [pV1] + prefetcht0 [pV2] + prefetcht0 [pV1 + 32] + + mov eax, dword ptr pV1 + mov ebx, dword ptr pV2 + + xorps xmm0, xmm0 + + mov ecx, overlapLengthLocal + shr ecx, 3 // div by eight + + loop1: + prefetcht0 [eax + 64] // give a prefetch hint to CPU what data are to be needed soonish + prefetcht0 [ebx + 32] // give a prefetch hint to CPU what data are to be needed soonish + movups xmm1, [eax] + mulps xmm1, [ebx] + addps xmm0, xmm1 + + movups xmm2, [eax + 16] + mulps xmm2, [ebx + 16] + addps xmm0, xmm2 + + prefetcht0 [eax + 96] // give a prefetch hint to CPU what data are to be needed soonish + prefetcht0 [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish + + movups xmm3, [eax + 32] + mulps xmm3, [ebx + 32] + addps xmm0, xmm3 + + movups xmm4, [eax + 48] + mulps xmm4, [ebx + 48] + addps xmm0, xmm4 + + add eax, 64 + add ebx, 64 + + dec ecx + jnz loop1 + + // add the four floats of xmm0 together and return the result. + + movhlps xmm1, xmm0 // move 3 & 4 of xmm0 to 1 & 2 of xmm1 + addps xmm1, xmm0 + movaps xmm2, xmm1 + shufps xmm2, xmm2, 0x01 // move 2 of xmm2 as 1 of xmm2 + addss xmm2, xmm1 + movss corr, xmm2 + } + + return (double)corr; + */ +} + + +////////////////////////////////////////////////////////////////////////////// +// +// implementation of SSE optimized functions of class 'FIRFilter' +// +////////////////////////////////////////////////////////////////////////////// + +#include "FIRFilter.h" + +FIRFilterSSE::FIRFilterSSE() : FIRFilter() +{ + filterCoeffsUnalign = NULL; +} + + +FIRFilterSSE::~FIRFilterSSE() +{ + delete[] filterCoeffsUnalign; +} + + +// (overloaded) Calculates filter coefficients for SSE routine +void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor) +{ + uint i; + float fDivider; + + FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor); + + // Scale the filter coefficients so that it won't be necessary to scale the filtering result + // also rearrange coefficients suitably for 3DNow! + // Ensure that filter coeffs array is aligned to 16-byte boundary + delete[] filterCoeffsUnalign; + filterCoeffsUnalign = new float[2 * newLength + 4]; + filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & -16); + + fDivider = (float)resultDivider; + + // rearrange the filter coefficients for mmx routines + for (i = 0; i < newLength; i ++) + { + filterCoeffsAlign[2 * i + 0] = + filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider; + } +} + + + +// SSE-optimized version of the filter routine for stereo sound +uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const +{ + int count = (numSamples - length) & -2; + int j; + + assert(count % 2 == 0); + + if (count < 2) return 0; + + assert((length % 8) == 0); + assert(((unsigned long)filterCoeffsAlign) % 16 == 0); + + // filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2' + for (j = 0; j < count; j += 2) + { + const float *pSrc; + const __m128 *pFil; + __m128 sum1, sum2; + uint i; + + pSrc = source; // source audio data + pFil = (__m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients + // are aligned to 16-byte boundary + sum1 = sum2 = _mm_setzero_ps(); + + for (i = 0; i < length / 8; i ++) + { + // Unroll loop for efficiency & calculate filter for 2*2 stereo samples + // at each pass + + // sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset + // sum2 is accu for 2*2 filtered stereo sound data for the next sound sample offset. + + sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc) , pFil[0])); + sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 2), pFil[0])); + + sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 4), pFil[1])); + sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 6), pFil[1])); + + sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 8) , pFil[2])); + sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 10), pFil[2])); + + sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 12), pFil[3])); + sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 14), pFil[3])); + + pSrc += 16; + pFil += 4; + } + + // Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need + // to sum the two hi- and lo-floats of these registers together. + + // post-shuffle & add the filtered values and store to dest. + _mm_storeu_ps(dest, _mm_add_ps( + _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2 + _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0 + )); + source += 4; + dest += 4; + } + + // Ideas for further improvement: + // 1. If it could be guaranteed that 'source' were always aligned to 16-byte + // boundary, a faster aligned '_mm_load_ps' instruction could be used. + // 2. If it could be guaranteed that 'dest' were always aligned to 16-byte + // boundary, a faster '_mm_store_ps' instruction could be used. + + return (uint)count; + + /* original routine in C-language. please notice the C-version has differently + organized coefficients though. + double suml1, suml2; + double sumr1, sumr2; + uint i, j; + + for (j = 0; j < count; j += 2) + { + const float *ptr; + const float *pFil; + + suml1 = sumr1 = 0.0; + suml2 = sumr2 = 0.0; + ptr = src; + pFil = filterCoeffs; + for (i = 0; i < lengthLocal; i ++) + { + // unroll loop for efficiency. + + suml1 += ptr[0] * pFil[0] + + ptr[2] * pFil[2] + + ptr[4] * pFil[4] + + ptr[6] * pFil[6]; + + sumr1 += ptr[1] * pFil[1] + + ptr[3] * pFil[3] + + ptr[5] * pFil[5] + + ptr[7] * pFil[7]; + + suml2 += ptr[8] * pFil[0] + + ptr[10] * pFil[2] + + ptr[12] * pFil[4] + + ptr[14] * pFil[6]; + + sumr2 += ptr[9] * pFil[1] + + ptr[11] * pFil[3] + + ptr[13] * pFil[5] + + ptr[15] * pFil[7]; + + ptr += 16; + pFil += 8; + } + dest[0] = (float)suml1; + dest[1] = (float)sumr1; + dest[2] = (float)suml2; + dest[3] = (float)sumr2; + + src += 4; + dest += 4; + } + */ + + + /* Similar routine in assembly, again obsoleted due to maintainability + _asm + { + // Very important note: data in 'src' _must_ be aligned to + // 16-byte boundary! + mov edx, count + mov ebx, dword ptr src + mov eax, dword ptr dest + shr edx, 1 + + loop1: + // "outer loop" : during each round 2*2 output samples are calculated + + // give prefetch hints to CPU of what data are to be needed soonish + prefetcht0 [ebx] + prefetcht0 [filterCoeffsLocal] + + mov esi, ebx + mov edi, filterCoeffsLocal + xorps xmm0, xmm0 + xorps xmm1, xmm1 + mov ecx, lengthLocal + + loop2: + // "inner loop" : during each round eight FIR filter taps are evaluated for 2*2 samples + prefetcht0 [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish + prefetcht0 [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish + + movups xmm2, [esi] // possibly unaligned load + movups xmm3, [esi + 8] // possibly unaligned load + mulps xmm2, [edi] + mulps xmm3, [edi] + addps xmm0, xmm2 + addps xmm1, xmm3 + + movups xmm4, [esi + 16] // possibly unaligned load + movups xmm5, [esi + 24] // possibly unaligned load + mulps xmm4, [edi + 16] + mulps xmm5, [edi + 16] + addps xmm0, xmm4 + addps xmm1, xmm5 + + prefetcht0 [esi + 64] // give a prefetch hint to CPU what data are to be needed soonish + prefetcht0 [edi + 64] // give a prefetch hint to CPU what data are to be needed soonish + + movups xmm6, [esi + 32] // possibly unaligned load + movups xmm7, [esi + 40] // possibly unaligned load + mulps xmm6, [edi + 32] + mulps xmm7, [edi + 32] + addps xmm0, xmm6 + addps xmm1, xmm7 + + movups xmm4, [esi + 48] // possibly unaligned load + movups xmm5, [esi + 56] // possibly unaligned load + mulps xmm4, [edi + 48] + mulps xmm5, [edi + 48] + addps xmm0, xmm4 + addps xmm1, xmm5 + + add esi, 64 + add edi, 64 + dec ecx + jnz loop2 + + // Now xmm0 and xmm1 both have a filtered 2-channel sample each, but we still need + // to sum the two hi- and lo-floats of these registers together. + + movhlps xmm2, xmm0 // xmm2 = xmm2_3 xmm2_2 xmm0_3 xmm0_2 + movlhps xmm2, xmm1 // xmm2 = xmm1_1 xmm1_0 xmm0_3 xmm0_2 + shufps xmm0, xmm1, 0xe4 // xmm0 = xmm1_3 xmm1_2 xmm0_1 xmm0_0 + addps xmm0, xmm2 + + movaps [eax], xmm0 + add ebx, 16 + add eax, 16 + + dec edx + jnz loop1 + } + */ +} + +#endif // ALLOW_SSE diff --git a/3rdparty/bzip2/LICENSE b/3rdparty/bzip2/LICENSE new file mode 100644 index 0000000000..383aabe35f --- /dev/null +++ b/3rdparty/bzip2/LICENSE @@ -0,0 +1,43 @@ + +-------------------------------------------------------------------------- + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2006 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + +Julian Seward, Cambridge, UK. +jseward@bzip.org +bzip2/libbzip2 version 1.0.4 of 20 December 2006 + +-------------------------------------------------------------------------- diff --git a/3rdparty/bzip2/README b/3rdparty/bzip2/README new file mode 100644 index 0000000000..3b819daf3f --- /dev/null +++ b/3rdparty/bzip2/README @@ -0,0 +1,205 @@ + +This is the README for bzip2/libzip2. +This version is fully compatible with the previous public releases. + +------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in this file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ + +Complete documentation is available in Postscript form (manual.ps), +PDF (manual.pdf) or html (manual.html). A plain-text version of the +manual page is available as bzip2.txt. + + +HOW TO BUILD -- UNIX + +Type 'make'. This builds the library libbz2.a and then the programs +bzip2 and bzip2recover. Six self-tests are run. If the self-tests +complete ok, carry on to installation: + +To install in /usr/local/bin, /usr/local/lib, /usr/local/man and +/usr/local/include, type + + make install + +To install somewhere else, eg, /xxx/yyy/{bin,lib,man,include}, type + + make install PREFIX=/xxx/yyy + +If you are (justifiably) paranoid and want to see what 'make install' +is going to do, you can first do + + make -n install or + make -n install PREFIX=/xxx/yyy respectively. + +The -n instructs make to show the commands it would execute, but not +actually execute them. + + +HOW TO BUILD -- UNIX, shared library libbz2.so. + +Do 'make -f Makefile-libbz2_so'. This Makefile seems to work for +Linux-ELF (RedHat 7.2 on an x86 box), with gcc. I make no claims +that it works for any other platform, though I suspect it probably +will work for most platforms employing both ELF and gcc. + +bzip2-shared, a client of the shared library, is also built, but not +self-tested. So I suggest you also build using the normal Makefile, +since that conducts a self-test. A second reason to prefer the +version statically linked to the library is that, on x86 platforms, +building shared objects makes a valuable register (%ebx) unavailable +to gcc, resulting in a slowdown of 10%-20%, at least for bzip2. + +Important note for people upgrading .so's from 0.9.0/0.9.5 to version +1.0.X. All the functions in the library have been renamed, from (eg) +bzCompress to BZ2_bzCompress, to avoid namespace pollution. +Unfortunately this means that the libbz2.so created by +Makefile-libbz2_so will not work with any program which used an older +version of the library. I do encourage library clients to make the +effort to upgrade to use version 1.0, since it is both faster and more +robust than previous versions. + + +HOW TO BUILD -- Windows 95, NT, DOS, Mac, etc. + +It's difficult for me to support compilation on all these platforms. +My approach is to collect binaries for these platforms, and put them +on the master web site (http://www.bzip.org). Look there. However +(FWIW), bzip2-1.0.X is very standard ANSI C and should compile +unmodified with MS Visual C. If you have difficulties building, you +might want to read README.COMPILATION.PROBLEMS. + +At least using MS Visual C++ 6, you can build from the unmodified +sources by issuing, in a command shell: + + nmake -f makefile.msc + +(you may need to first run the MSVC-provided script VCVARS32.BAT + so as to set up paths to the MSVC tools correctly). + + +VALIDATION + +Correct operation, in the sense that a compressed file can always be +decompressed to reproduce the original, is obviously of paramount +importance. To validate bzip2, I used a modified version of Mark +Nelson's churn program. Churn is an automated test driver which +recursively traverses a directory structure, using bzip2 to compress +and then decompress each file it encounters, and checking that the +decompressed data is the same as the original. + + + +Please read and be aware of the following: + +WARNING: + + This program and library (attempts to) compress data by + performing several non-trivial transformations on it. + Unless you are 100% familiar with *all* the algorithms + contained herein, and with the consequences of modifying them, + you should NOT meddle with the compression or decompression + machinery. Incorrect changes can and very likely *will* + lead to disastrous loss of data. + + +DISCLAIMER: + + I TAKE NO RESPONSIBILITY FOR ANY LOSS OF DATA ARISING FROM THE + USE OF THIS PROGRAM/LIBRARY, HOWSOEVER CAUSED. + + Every compression of a file implies an assumption that the + compressed file can be decompressed to reproduce the original. + Great efforts in design, coding and testing have been made to + ensure that this program works correctly. However, the complexity + of the algorithms, and, in particular, the presence of various + special cases in the code which occur with very low but non-zero + probability make it impossible to rule out the possibility of bugs + remaining in the program. DO NOT COMPRESS ANY DATA WITH THIS + PROGRAM UNLESS YOU ARE PREPARED TO ACCEPT THE POSSIBILITY, HOWEVER + SMALL, THAT THE DATA WILL NOT BE RECOVERABLE. + + That is not to say this program is inherently unreliable. + Indeed, I very much hope the opposite is true. bzip2/libbzip2 + has been carefully constructed and extensively tested. + + +PATENTS: + + To the best of my knowledge, bzip2/libbzip2 does not use any + patented algorithms. However, I do not have the resources + to carry out a patent search. Therefore I cannot give any + guarantee of the above statement. + + + +WHAT'S NEW IN 0.9.0 (as compared to 0.1pl2) ? + + * Approx 10% faster compression, 30% faster decompression + * -t (test mode) is a lot quicker + * Can decompress concatenated compressed files + * Programming interface, so programs can directly read/write .bz2 files + * Less restrictive (BSD-style) licensing + * Flag handling more compatible with GNU gzip + * Much more documentation, i.e., a proper user manual + * Hopefully, improved portability (at least of the library) + +WHAT'S NEW IN 0.9.5 ? + + * Compression speed is much less sensitive to the input + data than in previous versions. Specifically, the very + slow performance caused by repetitive data is fixed. + * Many small improvements in file and flag handling. + * A Y2K statement. + +WHAT'S NEW IN 1.0.0 ? + + See the CHANGES file. + +WHAT'S NEW IN 1.0.2 ? + + See the CHANGES file. + +WHAT'S NEW IN 1.0.3 ? + + See the CHANGES file. + +WHAT'S NEW IN 1.0.4 ? + + See the CHANGES file. + + +I hope you find bzip2 useful. Feel free to contact me at + jseward@bzip.org +if you have any suggestions or queries. Many people mailed me with +comments, suggestions and patches after the releases of bzip-0.15, +bzip-0.21, and bzip2 versions 0.1pl2, 0.9.0, 0.9.5, 1.0.0, 1.0.1, +1.0.2 and 1.0.3, and the changes in bzip2 are largely a result of this +feedback. I thank you for your comments. + +bzip2's "home" is http://www.bzip.org/ + +Julian Seward +jseward@bzip.org +Cambridge, UK. + +18 July 1996 (version 0.15) +25 August 1996 (version 0.21) + 7 August 1997 (bzip2, version 0.1) +29 August 1997 (bzip2, version 0.1pl2) +23 August 1998 (bzip2, version 0.9.0) + 8 June 1999 (bzip2, version 0.9.5) + 4 Sept 1999 (bzip2, version 0.9.5d) + 5 May 2000 (bzip2, version 1.0pre8) +30 December 2001 (bzip2, version 1.0.2pre1) +15 February 2005 (bzip2, version 1.0.3) +20 December 2006 (bzip2, version 1.0.4) diff --git a/3rdparty/bzip2/blocksort.c b/3rdparty/bzip2/blocksort.c new file mode 100644 index 0000000000..aceb5e9508 --- /dev/null +++ b/3rdparty/bzip2/blocksort.c @@ -0,0 +1,1094 @@ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery ---*/ +/*--- blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.4 of 20 December 2006 + Copyright (C) 1996-2006 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting ---*/ +/*--- algorithm, for repetitive blocks ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +void fallbackSimpleSort ( UInt32* fmap, + UInt32* eclass, + Int32 lo, + Int32 hi ) +{ + Int32 i, j, tmp; + UInt32 ec_tmp; + + if (lo == hi) return; + + if (hi - lo > 3) { + for ( i = hi-4; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 ) + fmap[j-4] = fmap[j]; + fmap[j-4] = tmp; + } + } + + for ( i = hi-1; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ ) + fmap[j-1] = fmap[j]; + fmap[j-1] = tmp; + } +} + + +/*---------------------------------------------*/ +#define fswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define fvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + fswap(fmap[yyp1], fmap[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + + +#define fmin(a,b) ((a) < (b)) ? (a) : (b) + +#define fpush(lz,hz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + sp++; } + +#define fpop(lz,hz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; } + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE 100 + + +static +void fallbackQSort3 ( UInt32* fmap, + UInt32* eclass, + Int32 loSt, + Int32 hiSt ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m; + Int32 sp, lo, hi; + UInt32 med, r, r3; + Int32 stackLo[FALLBACK_QSORT_STACK_SIZE]; + Int32 stackHi[FALLBACK_QSORT_STACK_SIZE]; + + r = 0; + + sp = 0; + fpush ( loSt, hiSt ); + + while (sp > 0) { + + AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 ); + + fpop ( lo, hi ); + if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { + fallbackSimpleSort ( fmap, eclass, lo, hi ); + continue; + } + + /* Random partitioning. Median of 3 sometimes fails to + avoid bad cases. Median of 9 seems to help but + looks rather expensive. This too seems to work but + is cheaper. Guidance for the magic constants + 7621 and 32768 is taken from Sedgewick's algorithms + book, chapter 35. + */ + r = ((r * 7621) + 1) % 32768; + r3 = r % 3; + if (r3 == 0) med = eclass[fmap[lo]]; else + if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else + med = eclass[fmap[hi]]; + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unLo]] - (Int32)med; + if (n == 0) { + fswap(fmap[unLo], fmap[ltLo]); + ltLo++; unLo++; + continue; + }; + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unHi]] - (Int32)med; + if (n == 0) { + fswap(fmap[unHi], fmap[gtHi]); + gtHi--; unHi--; + continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "fallbackQSort3(2)" ); + + if (gtHi < ltLo) continue; + + n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n); + m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + if (n - lo > hi - m) { + fpush ( lo, n ); + fpush ( m, hi ); + } else { + fpush ( m, hi ); + fpush ( lo, n ); + } + } +} + +#undef fmin +#undef fpush +#undef fpop +#undef fswap +#undef fvswap +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + eclass exists for [0 .. nblock-1] + ((UChar*)eclass) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)eclass) [0 .. nblock-1] holds block + All other areas of eclass destroyed + fmap [0 .. nblock-1] holds sorted order + bhtab [ 0 .. 2+(nblock/32) ] destroyed +*/ + +#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define WORD_BH(zz) bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz) ((zz) & 0x01f) + +static +void fallbackSort ( UInt32* fmap, + UInt32* eclass, + UInt32* bhtab, + Int32 nblock, + Int32 verb ) +{ + Int32 ftab[257]; + Int32 ftabCopy[256]; + Int32 H, i, j, k, l, r, cc, cc1; + Int32 nNotDone; + Int32 nBhtab; + UChar* eclass8 = (UChar*)eclass; + + /*-- + Initial 1-char radix sort to generate + initial fmap and initial BH bits. + --*/ + if (verb >= 4) + VPrintf0 ( " bucket sorting ...\n" ); + for (i = 0; i < 257; i++) ftab[i] = 0; + for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; + for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; + for (i = 1; i < 257; i++) ftab[i] += ftab[i-1]; + + for (i = 0; i < nblock; i++) { + j = eclass8[i]; + k = ftab[j] - 1; + ftab[j] = k; + fmap[k] = i; + } + + nBhtab = 2 + (nblock / 32); + for (i = 0; i < nBhtab; i++) bhtab[i] = 0; + for (i = 0; i < 256; i++) SET_BH(ftab[i]); + + /*-- + Inductively refine the buckets. Kind-of an + "exponential radix sort" (!), inspired by the + Manber-Myers suffix array construction algorithm. + --*/ + + /*-- set sentinel bits for block-end detection --*/ + for (i = 0; i < 32; i++) { + SET_BH(nblock + 2*i); + CLEAR_BH(nblock + 2*i + 1); + } + + /*-- the log(N) loop --*/ + H = 1; + while (1) { + + if (verb >= 4) + VPrintf1 ( " depth %6d has ", H ); + + j = 0; + for (i = 0; i < nblock; i++) { + if (ISSET_BH(i)) j = i; + k = fmap[i] - H; if (k < 0) k += nblock; + eclass[k] = j; + } + + nNotDone = 0; + r = -1; + while (1) { + + /*-- find the next non-singleton bucket --*/ + k = r + 1; + while (ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (ISSET_BH(k)) { + while (WORD_BH(k) == 0xffffffff) k += 32; + while (ISSET_BH(k)) k++; + } + l = k - 1; + if (l >= nblock) break; + while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (!ISSET_BH(k)) { + while (WORD_BH(k) == 0x00000000) k += 32; + while (!ISSET_BH(k)) k++; + } + r = k - 1; + if (r >= nblock) break; + + /*-- now [l, r] bracket current bucket --*/ + if (r > l) { + nNotDone += (r - l + 1); + fallbackQSort3 ( fmap, eclass, l, r ); + + /*-- scan bucket and generate header bits-- */ + cc = -1; + for (i = l; i <= r; i++) { + cc1 = eclass[fmap[i]]; + if (cc != cc1) { SET_BH(i); cc = cc1; }; + } + } + } + + if (verb >= 4) + VPrintf1 ( "%6d unresolved strings\n", nNotDone ); + + H *= 2; + if (H > nblock || nNotDone == 0) break; + } + + /*-- + Reconstruct the original block in + eclass8 [0 .. nblock-1], since the + previous phase destroyed it. + --*/ + if (verb >= 4) + VPrintf0 ( " reconstructing block ...\n" ); + j = 0; + for (i = 0; i < nblock; i++) { + while (ftabCopy[j] == 0) j++; + ftabCopy[j]--; + eclass8[fmap[i]] = (UChar)j; + } + AssertH ( j < 256, 1005 ); +} + +#undef SET_BH +#undef CLEAR_BH +#undef ISSET_BH +#undef WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting ---*/ +/*--- algorithm. Faster for "normal" ---*/ +/*--- non-repetitive blocks. ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +Bool mainGtU ( UInt32 i1, + UInt32 i2, + UChar* block, + UInt16* quadrant, + UInt32 nblock, + Int32* budget ) +{ + Int32 k; + UChar c1, c2; + UInt16 s1, s2; + + AssertD ( i1 != i2, "mainGtU" ); + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 9 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 10 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 11 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 12 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + + k = nblock + 8; + + do { + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + + if (i1 >= nblock) i1 -= nblock; + if (i2 >= nblock) i2 -= nblock; + + k -= 8; + (*budget)--; + } + while (k >= 0); + + return False; +} + + +/*---------------------------------------------*/ +/*-- + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. +--*/ +static +Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + +static +void mainSimpleSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 lo, + Int32 hi, + Int32 d, + Int32* budget ) +{ + Int32 i, j, h, bigN, hp; + UInt32 v; + + bigN = hi - lo + 1; + if (bigN < 2) return; + + hp = 0; + while (incs[hp] < bigN) hp++; + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (True) { + + /*-- copy 1 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 2 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 3 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + if (*budget < 0) return; + } + } +} + + +/*---------------------------------------------*/ +/*-- + The following is an implementation of + an elegant 3-way quicksort for strings, + described in a paper "Fast Algorithms for + Sorting and Searching Strings", by Robert + Sedgewick and Jon L. Bentley. +--*/ + +#define mswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define mvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + mswap(ptr[yyp1], ptr[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + +static +__inline__ +UChar mmed3 ( UChar a, UChar b, UChar c ) +{ + UChar t; + if (a > b) { t = a; a = b; b = t; }; + if (b > c) { + b = c; + if (a > b) b = a; + } + return b; +} + +#define mmin(a,b) ((a) < (b)) ? (a) : (b) + +#define mpush(lz,hz,dz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + stackD [sp] = dz; \ + sp++; } + +#define mpop(lz,hz,dz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ + dz = stackD [sp]; } + + +#define mnextsize(az) (nextHi[az]-nextLo[az]) + +#define mnextswap(az,bz) \ + { Int32 tz; \ + tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ + tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ + tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; } + + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static +void mainQSort3 ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 loSt, + Int32 hiSt, + Int32 dSt, + Int32* budget ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m, med; + Int32 sp, lo, hi, d; + + Int32 stackLo[MAIN_QSORT_STACK_SIZE]; + Int32 stackHi[MAIN_QSORT_STACK_SIZE]; + Int32 stackD [MAIN_QSORT_STACK_SIZE]; + + Int32 nextLo[3]; + Int32 nextHi[3]; + Int32 nextD [3]; + + sp = 0; + mpush ( loSt, hiSt, dSt ); + + while (sp > 0) { + + AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 ); + + mpop ( lo, hi, d ); + if (hi - lo < MAIN_QSORT_SMALL_THRESH || + d > MAIN_QSORT_DEPTH_THRESH) { + mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget ); + if (*budget < 0) return; + continue; + } + + med = (Int32) + mmed3 ( block[ptr[ lo ]+d], + block[ptr[ hi ]+d], + block[ptr[ (lo+hi)>>1 ]+d] ); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (True) { + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unLo]+d]) - med; + if (n == 0) { + mswap(ptr[unLo], ptr[ltLo]); + ltLo++; unLo++; continue; + }; + if (n > 0) break; + unLo++; + } + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unHi]+d]) - med; + if (n == 0) { + mswap(ptr[unHi], ptr[gtHi]); + gtHi--; unHi--; continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "mainQSort3(2)" ); + + if (gtHi < ltLo) { + mpush(lo, hi, d+1 ); + continue; + } + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; + nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; + nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + if (mnextsize(1) < mnextsize(2)) mnextswap(1,2); + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + + AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" ); + AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" ); + + mpush (nextLo[0], nextHi[0], nextD[0]); + mpush (nextLo[1], nextHi[1], nextD[1]); + mpush (nextLo[2], nextHi[2], nextD[2]); + } +} + +#undef mswap +#undef mvswap +#undef mpush +#undef mpop +#undef mmin +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > N_OVERSHOOT + block32 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)block32) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)block32) [0 .. nblock-1] holds block + All other areas of block32 destroyed + ftab [0 .. 65536 ] destroyed + ptr [0 .. nblock-1] holds sorted order + if (*budget < 0), sorting was abandoned +*/ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static +void mainSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + UInt32* ftab, + Int32 nblock, + Int32 verb, + Int32* budget ) +{ + Int32 i, j, k, ss, sb; + Int32 runningOrder[256]; + Bool bigDone[256]; + Int32 copyStart[256]; + Int32 copyEnd [256]; + UChar c1; + Int32 numQSorted; + UInt16 s; + if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" ); + + /*-- set up the 2-byte frequency table --*/ + for (i = 65536; i >= 0; i--) ftab[i] = 0; + + j = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + quadrant[i-1] = 0; + j = (j >> 8) | ( ((UInt16)block[i-1]) << 8); + ftab[j]++; + quadrant[i-2] = 0; + j = (j >> 8) | ( ((UInt16)block[i-2]) << 8); + ftab[j]++; + quadrant[i-3] = 0; + j = (j >> 8) | ( ((UInt16)block[i-3]) << 8); + ftab[j]++; + } + for (; i >= 0; i--) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + } + + /*-- (emphasises close relationship of block & quadrant) --*/ + for (i = 0; i < BZ_N_OVERSHOOT; i++) { + block [nblock+i] = block[i]; + quadrant[nblock+i] = 0; + } + + if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" ); + + /*-- Complete the initial radix sort --*/ + for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1]; + + s = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + s = (s >> 8) | (block[i-1] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-1; + s = (s >> 8) | (block[i-2] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-2; + s = (s >> 8) | (block[i-3] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-3; + } + for (; i >= 0; i--) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + } + + /*-- + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + --*/ + for (i = 0; i <= 255; i++) { + bigDone [i] = False; + runningOrder[i] = i; + } + + { + Int32 vv; + Int32 h = 1; + do h = 3 * h + 1; while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) { + runningOrder[j] = runningOrder[j-h]; + j = j - h; + if (j <= (h - 1)) goto zero; + } + zero: + runningOrder[j] = vv; + } + } while (h != 1); + } + + /*-- + The main sorting loop. + --*/ + + numQSorted = 0; + + for (i = 0; i <= 255; i++) { + + /*-- + Process big buckets, starting with the least full. + Basically this is a 3-step process in which we call + mainQSort3 to sort the small buckets [ss, j], but + also make a big effort to avoid the calls if we can. + --*/ + ss = runningOrder[i]; + + /*-- + Step 1: + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j], for j != ss. + Hopefully previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + --*/ + for (j = 0; j <= 255; j++) { + if (j != ss) { + sb = (ss << 8) + j; + if ( ! (ftab[sb] & SETMASK) ) { + Int32 lo = ftab[sb] & CLEARMASK; + Int32 hi = (ftab[sb+1] & CLEARMASK) - 1; + if (hi > lo) { + if (verb >= 4) + VPrintf4 ( " qsort [0x%x, 0x%x] " + "done %d this %d\n", + ss, j, numQSorted, hi - lo + 1 ); + mainQSort3 ( + ptr, block, quadrant, nblock, + lo, hi, BZ_N_RADIX, budget + ); + numQSorted += (hi - lo + 1); + if (*budget < 0) return; + } + } + ftab[sb] |= SETMASK; + } + } + + AssertH ( !bigDone[ss], 1006 ); + + /*-- + Step 2: + Now scan this big bucket [ss] so as to synthesise the + sorted order for small buckets [t, ss] for all t, + including, magically, the bucket [ss,ss] too. + This will avoid doing Real Work in subsequent Step 1's. + --*/ + { + for (j = 0; j <= 255; j++) { + copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; + copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; + } + for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyStart[c1]++ ] = k; + } + for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyEnd[c1]-- ] = k; + } + } + + AssertH ( (copyStart[ss]-1 == copyEnd[ss]) + || + /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. + Necessity for this case is demonstrated by compressing + a sequence of approximately 48.5 million of character + 251; 1.0.0/1.0.1 will then die here. */ + (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), + 1007 ) + + for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK; + + /*-- + Step 3: + The [ss] big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + + The quadrant array provides a way to incrementally + cache sort orderings, as they appear, so as to + make subsequent comparisons in fullGtU() complete + faster. For repetitive blocks this makes a big + difference (but not big enough to be able to avoid + the fallback sorting mechanism, exponential radix sort). + + The precise meaning is: at all times: + + for 0 <= i < nblock and 0 <= j <= nblock + + if block[i] != block[j], + + then the relative values of quadrant[i] and + quadrant[j] are meaningless. + + else { + if quadrant[i] < quadrant[j] + then the string starting at i lexicographically + precedes the string starting at j + + else if quadrant[i] > quadrant[j] + then the string starting at j lexicographically + precedes the string starting at i + + else + the relative ordering of the strings starting + at i and j has not yet been determined. + } + --*/ + bigDone[ss] = True; + + if (i < 255) { + Int32 bbStart = ftab[ss << 8] & CLEARMASK; + Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; + Int32 shifts = 0; + + while ((bbSize >> shifts) > 65534) shifts++; + + for (j = bbSize-1; j >= 0; j--) { + Int32 a2update = ptr[bbStart + j]; + UInt16 qVal = (UInt16)(j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZ_N_OVERSHOOT) + quadrant[a2update + nblock] = qVal; + } + AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 ); + } + + } + + if (verb >= 4) + VPrintf3 ( " %d pointers, %d sorted, %d scanned\n", + nblock, numQSorted, nblock - numQSorted ); +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)arr2) [0 .. nblock-1] holds block + arr1 exists for [0 .. nblock-1] + + Post: + ((UChar*)arr2) [0 .. nblock-1] holds block + All other areas of block destroyed + ftab [ 0 .. 65536 ] destroyed + arr1 [0 .. nblock-1] holds sorted order +*/ +void BZ2_blockSort ( EState* s ) +{ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt32* ftab = s->ftab; + Int32 nblock = s->nblock; + Int32 verb = s->verbosity; + Int32 wfact = s->workFactor; + UInt16* quadrant; + Int32 budget; + Int32 budgetInit; + Int32 i; + + if (nblock < 10000) { + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } else { + /* Calculate the location for quadrant, remembering to get + the alignment right. Assumes that &(block[0]) is at least + 2-byte aligned -- this should be ok since block is really + the first section of arr2. + */ + i = nblock+BZ_N_OVERSHOOT; + if (i & 1) i++; + quadrant = (UInt16*)(&(block[i])); + + /* (wfact-1) / 3 puts the default-factor-30 + transition point at very roughly the same place as + with v0.1 and v0.9.0. + Not that it particularly matters any more, since the + resulting compressed stream is now the same regardless + of whether or not we use the main sort or fallback sort. + */ + if (wfact < 1 ) wfact = 1; + if (wfact > 100) wfact = 100; + budgetInit = nblock * ((wfact-1) / 3); + budget = budgetInit; + + mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget ); + if (verb >= 3) + VPrintf3 ( " %d work, %d block, ratio %5.2f\n", + budgetInit - budget, + nblock, + (float)(budgetInit - budget) / + (float)(nblock==0 ? 1 : nblock) ); + if (budget < 0) { + if (verb >= 2) + VPrintf0 ( " too repetitive; using fallback" + " sorting algorithm\n" ); + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } + } + + s->origPtr = -1; + for (i = 0; i < s->nblock; i++) + if (ptr[i] == 0) + { s->origPtr = i; break; }; + + AssertH( s->origPtr != -1, 1003 ); +} + + +/*-------------------------------------------------------------*/ +/*--- end blocksort.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/3rdparty/bzip2/bzlib.c b/3rdparty/bzip2/bzlib.c new file mode 100644 index 0000000000..5b70a84bbc --- /dev/null +++ b/3rdparty/bzip2/bzlib.c @@ -0,0 +1,1571 @@ + +/*-------------------------------------------------------------*/ +/*--- Library top-level functions. ---*/ +/*--- bzlib.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.4 of 20 December 2006 + Copyright (C) 1996-2006 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- made zero-length BZ_FLUSH work correctly in bzCompress(). + fixed bzWrite/bzRead to ignore zero-length requests. + fixed bzread to correctly handle read requests after EOF. + wrong parameter order in call to bzDecompressInit in + bzBuffToBuffDecompress. Fixed. +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Compression stuff ---*/ +/*---------------------------------------------------*/ + + +/*---------------------------------------------------*/ +#ifndef BZ_NO_STDIO +void BZ2_bz__AssertH__fail ( int errcode ) +{ + fprintf(stderr, + "\n\nbzip2/libbzip2: internal error number %d.\n" + "This is a bug in bzip2/libbzip2, %s.\n" + "Please report it to me at: jseward@bzip.org. If this happened\n" + "when you were using some program which uses libbzip2 as a\n" + "component, you should also report this bug to the author(s)\n" + "of that program. Please make an effort to report this bug;\n" + "timely and accurate bug reports eventually lead to higher\n" + "quality software. Thanks. Julian Seward, 15 February 2005.\n\n", + errcode, + BZ2_bzlibVersion() + ); + + if (errcode == 1007) { + fprintf(stderr, + "\n*** A special note about internal error number 1007 ***\n" + "\n" + "Experience suggests that a common cause of i.e. 1007\n" + "is unreliable memory or other hardware. The 1007 assertion\n" + "just happens to cross-check the results of huge numbers of\n" + "memory reads/writes, and so acts (unintendedly) as a stress\n" + "test of your memory system.\n" + "\n" + "I suggest the following: try compressing the file again,\n" + "possibly monitoring progress in detail with the -vv flag.\n" + "\n" + "* If the error cannot be reproduced, and/or happens at different\n" + " points in compression, you may have a flaky memory system.\n" + " Try a memory-test program. I have used Memtest86\n" + " (www.memtest86.com). At the time of writing it is free (GPLd).\n" + " Memtest86 tests memory much more thorougly than your BIOSs\n" + " power-on test, and may find failures that the BIOS doesn't.\n" + "\n" + "* If the error can be repeatably reproduced, this is a bug in\n" + " bzip2, and I would very much like to hear about it. Please\n" + " let me know, and, ideally, save a copy of the file causing the\n" + " problem -- without which I will be unable to investigate it.\n" + "\n" + ); + } + + exit(3); +} +#endif + + +/*---------------------------------------------------*/ +static +int bz_config_ok ( void ) +{ + if (sizeof(int) != 4) return 0; + if (sizeof(short) != 2) return 0; + if (sizeof(char) != 1) return 0; + return 1; +} + + +/*---------------------------------------------------*/ +static +void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) +{ + void* v = malloc ( items * size ); + return v; +} + +static +void default_bzfree ( void* opaque, void* addr ) +{ + if (addr != NULL) free ( addr ); +} + + +/*---------------------------------------------------*/ +static +void prepare_new_block ( EState* s ) +{ + Int32 i; + s->nblock = 0; + s->numZ = 0; + s->state_out_pos = 0; + BZ_INITIALISE_CRC ( s->blockCRC ); + for (i = 0; i < 256; i++) s->inUse[i] = False; + s->blockNo++; +} + + +/*---------------------------------------------------*/ +static +void init_RL ( EState* s ) +{ + s->state_in_ch = 256; + s->state_in_len = 0; +} + + +static +Bool isempty_RL ( EState* s ) +{ + if (s->state_in_ch < 256 && s->state_in_len > 0) + return False; else + return True; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressInit) + ( bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 n; + EState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL || + blockSize100k < 1 || blockSize100k > 9 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(EState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + + s->arr1 = NULL; + s->arr2 = NULL; + s->ftab = NULL; + + n = 100000 * blockSize100k; + s->arr1 = BZALLOC( n * sizeof(UInt32) ); + s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) ); + s->ftab = BZALLOC( 65537 * sizeof(UInt32) ); + + if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) { + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + if (s != NULL) BZFREE(s); + return BZ_MEM_ERROR; + } + + s->blockNo = 0; + s->state = BZ_S_INPUT; + s->mode = BZ_M_RUNNING; + s->combinedCRC = 0; + s->blockSize100k = blockSize100k; + s->nblockMAX = 100000 * blockSize100k - 19; + s->verbosity = verbosity; + s->workFactor = workFactor; + + s->block = (UChar*)s->arr2; + s->mtfv = (UInt16*)s->arr1; + s->zbits = NULL; + s->ptr = (UInt32*)s->arr1; + + strm->state = s; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + init_RL ( s ); + prepare_new_block ( s ); + return BZ_OK; +} + + +/*---------------------------------------------------*/ +static +void add_pair_to_block ( EState* s ) +{ + Int32 i; + UChar ch = (UChar)(s->state_in_ch); + for (i = 0; i < s->state_in_len; i++) { + BZ_UPDATE_CRC( s->blockCRC, ch ); + } + s->inUse[s->state_in_ch] = True; + switch (s->state_in_len) { + case 1: + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 2: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 3: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + default: + s->inUse[s->state_in_len-4] = True; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = ((UChar)(s->state_in_len-4)); + s->nblock++; + break; + } +} + + +/*---------------------------------------------------*/ +static +void flush_RL ( EState* s ) +{ + if (s->state_in_ch < 256) add_pair_to_block ( s ); + init_RL ( s ); +} + + +/*---------------------------------------------------*/ +#define ADD_CHAR_TO_BLOCK(zs,zchh0) \ +{ \ + UInt32 zchh = (UInt32)(zchh0); \ + /*-- fast track the common case --*/ \ + if (zchh != zs->state_in_ch && \ + zs->state_in_len == 1) { \ + UChar ch = (UChar)(zs->state_in_ch); \ + BZ_UPDATE_CRC( zs->blockCRC, ch ); \ + zs->inUse[zs->state_in_ch] = True; \ + zs->block[zs->nblock] = (UChar)ch; \ + zs->nblock++; \ + zs->state_in_ch = zchh; \ + } \ + else \ + /*-- general, uncommon cases --*/ \ + if (zchh != zs->state_in_ch || \ + zs->state_in_len == 255) { \ + if (zs->state_in_ch < 256) \ + add_pair_to_block ( zs ); \ + zs->state_in_ch = zchh; \ + zs->state_in_len = 1; \ + } else { \ + zs->state_in_len++; \ + } \ +} + + +/*---------------------------------------------------*/ +static +Bool copy_input_until_stop ( EState* s ) +{ + Bool progress_in = False; + + if (s->mode == BZ_M_RUNNING) { + + /*-- fast track the common case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + } + + } else { + + /*-- general, uncommon case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- flush/finish end? --*/ + if (s->avail_in_expect == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + s->avail_in_expect--; + } + } + return progress_in; +} + + +/*---------------------------------------------------*/ +static +Bool copy_output_until_stop ( EState* s ) +{ + Bool progress_out = False; + + while (True) { + + /*-- no output space? --*/ + if (s->strm->avail_out == 0) break; + + /*-- block done? --*/ + if (s->state_out_pos >= s->numZ) break; + + progress_out = True; + *(s->strm->next_out) = s->zbits[s->state_out_pos]; + s->state_out_pos++; + s->strm->avail_out--; + s->strm->next_out++; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + return progress_out; +} + + +/*---------------------------------------------------*/ +static +Bool handle_compress ( bz_stream* strm ) +{ + Bool progress_in = False; + Bool progress_out = False; + EState* s = strm->state; + + while (True) { + + if (s->state == BZ_S_OUTPUT) { + progress_out |= copy_output_until_stop ( s ); + if (s->state_out_pos < s->numZ) break; + if (s->mode == BZ_M_FINISHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + prepare_new_block ( s ); + s->state = BZ_S_INPUT; + if (s->mode == BZ_M_FLUSHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + } + + if (s->state == BZ_S_INPUT) { + progress_in |= copy_input_until_stop ( s ); + if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { + flush_RL ( s ); + BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) ); + s->state = BZ_S_OUTPUT; + } + else + if (s->nblock >= s->nblockMAX) { + BZ2_compressBlock ( s, False ); + s->state = BZ_S_OUTPUT; + } + else + if (s->strm->avail_in == 0) { + break; + } + } + + } + + return progress_in || progress_out; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action ) +{ + Bool progress; + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + preswitch: + switch (s->mode) { + + case BZ_M_IDLE: + return BZ_SEQUENCE_ERROR; + + case BZ_M_RUNNING: + if (action == BZ_RUN) { + progress = handle_compress ( strm ); + return progress ? BZ_RUN_OK : BZ_PARAM_ERROR; + } + else + if (action == BZ_FLUSH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FLUSHING; + goto preswitch; + } + else + if (action == BZ_FINISH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FINISHING; + goto preswitch; + } + else + return BZ_PARAM_ERROR; + + case BZ_M_FLUSHING: + if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FLUSH_OK; + s->mode = BZ_M_RUNNING; + return BZ_RUN_OK; + + case BZ_M_FINISHING: + if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (!progress) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FINISH_OK; + s->mode = BZ_M_IDLE; + return BZ_STREAM_END; + } + return BZ_OK; /*--not reached--*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressEnd) ( bz_stream *strm ) +{ + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + BZFREE(strm->state); + + strm->state = NULL; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/*--- Decompression stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressInit) + ( bz_stream* strm, + int verbosity, + int small ) +{ + DState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL) return BZ_PARAM_ERROR; + if (small != 0 && small != 1) return BZ_PARAM_ERROR; + if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR; + + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(DState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + strm->state = s; + s->state = BZ_X_MAGIC_1; + s->bsLive = 0; + s->bsBuff = 0; + s->calculatedCombinedCRC = 0; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + s->smallDecompress = (Bool)small; + s->ll4 = NULL; + s->ll16 = NULL; + s->tt = NULL; + s->currBlockNo = 0; + s->verbosity = verbosity; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_FAST ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + /* restore */ + UInt32 c_calculatedBlockCRC = s->calculatedBlockCRC; + UChar c_state_out_ch = s->state_out_ch; + Int32 c_state_out_len = s->state_out_len; + Int32 c_nblock_used = s->nblock_used; + Int32 c_k0 = s->k0; + UInt32* c_tt = s->tt; + UInt32 c_tPos = s->tPos; + char* cs_next_out = s->strm->next_out; + unsigned int cs_avail_out = s->strm->avail_out; + /* end restore */ + + UInt32 avail_out_INIT = cs_avail_out; + Int32 s_save_nblockPP = s->save_nblock+1; + unsigned int total_out_lo32_old; + + while (True) { + + /* try to finish existing run */ + if (c_state_out_len > 0) { + while (True) { + if (cs_avail_out == 0) goto return_notr; + if (c_state_out_len == 1) break; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + c_state_out_len--; + cs_next_out++; + cs_avail_out--; + } + s_state_out_len_eq_one: + { + if (cs_avail_out == 0) { + c_state_out_len = 1; goto return_notr; + }; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + cs_next_out++; + cs_avail_out--; + } + } + /* Only caused by corrupt data stream? */ + if (c_nblock_used > s_save_nblockPP) + return True; + + /* can a new run be started? */ + if (c_nblock_used == s_save_nblockPP) { + c_state_out_len = 0; goto return_notr; + }; + c_state_out_ch = c_k0; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (k1 != c_k0) { + c_k0 = k1; goto s_state_out_len_eq_one; + }; + if (c_nblock_used == s_save_nblockPP) + goto s_state_out_len_eq_one; + + c_state_out_len = 2; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + c_state_out_len = 3; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + BZ_GET_FAST_C(k1); c_nblock_used++; + c_state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST_C(c_k0); c_nblock_used++; + } + + return_notr: + total_out_lo32_old = s->strm->total_out_lo32; + s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out); + if (s->strm->total_out_lo32 < total_out_lo32_old) + s->strm->total_out_hi32++; + + /* save */ + s->calculatedBlockCRC = c_calculatedBlockCRC; + s->state_out_ch = c_state_out_ch; + s->state_out_len = c_state_out_len; + s->nblock_used = c_nblock_used; + s->k0 = c_k0; + s->tt = c_tt; + s->tPos = c_tPos; + s->strm->next_out = cs_next_out; + s->strm->avail_out = cs_avail_out; + /* end save */ + } + return False; +} + + + +/*---------------------------------------------------*/ +__inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab ) +{ + Int32 nb, na, mid; + nb = 0; + na = 256; + do { + mid = (nb + na) >> 1; + if (indx >= cftab[mid]) nb = mid; else na = mid; + } + while (na - nb != 1); + return nb; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_SMALL ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompress) ( bz_stream *strm ) +{ + Bool corrupt; + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + while (True) { + if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR; + if (s->state == BZ_X_OUTPUT) { + if (s->smallDecompress) + corrupt = unRLE_obuf_to_output_SMALL ( s ); else + corrupt = unRLE_obuf_to_output_FAST ( s ); + if (corrupt) return BZ_DATA_ERROR; + if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) { + BZ_FINALISE_CRC ( s->calculatedBlockCRC ); + if (s->verbosity >= 3) + VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC, + s->calculatedBlockCRC ); + if (s->verbosity >= 2) VPrintf0 ( "]" ); + if (s->calculatedBlockCRC != s->storedBlockCRC) + return BZ_DATA_ERROR; + s->calculatedCombinedCRC + = (s->calculatedCombinedCRC << 1) | + (s->calculatedCombinedCRC >> 31); + s->calculatedCombinedCRC ^= s->calculatedBlockCRC; + s->state = BZ_X_BLKHDR_1; + } else { + return BZ_OK; + } + } + if (s->state >= BZ_X_MAGIC_1) { + Int32 r = BZ2_decompress ( s ); + if (r == BZ_STREAM_END) { + if (s->verbosity >= 3) + VPrintf2 ( "\n combined CRCs: stored = 0x%08x, computed = 0x%08x", + s->storedCombinedCRC, s->calculatedCombinedCRC ); + if (s->calculatedCombinedCRC != s->storedCombinedCRC) + return BZ_DATA_ERROR; + return r; + } + if (s->state != BZ_X_OUTPUT) return r; + } + } + + AssertH ( 0, 6001 ); + + return 0; /*NOTREACHED*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressEnd) ( bz_stream *strm ) +{ + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->tt != NULL) BZFREE(s->tt); + if (s->ll16 != NULL) BZFREE(s->ll16); + if (s->ll4 != NULL) BZFREE(s->ll4); + + BZFREE(strm->state); + strm->state = NULL; + + return BZ_OK; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ +/*--- File I/O stuff ---*/ +/*---------------------------------------------------*/ + +#define BZ_SETERR(eee) \ +{ \ + if (bzerror != NULL) *bzerror = eee; \ + if (bzf != NULL) bzf->lastErr = eee; \ +} + +typedef + struct { + FILE* handle; + Char buf[BZ_MAX_UNUSED]; + Int32 bufN; + Bool writing; + bz_stream strm; + Int32 lastErr; + Bool initialisedOk; + } + bzFile; + + +/*---------------------------------------------*/ +static Bool myfeof ( FILE* f ) +{ + Int32 c = fgetc ( f ); + if (c == EOF) return True; + ungetc ( c, f ); + return False; +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzWriteOpen) + ( int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 ret; + bzFile* bzf = NULL; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (blockSize100k < 1 || blockSize100k > 9) || + (workFactor < 0 || workFactor > 250) || + (verbosity < 0 || verbosity > 4)) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + bzf->initialisedOk = False; + bzf->bufN = 0; + bzf->handle = f; + bzf->writing = True; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + if (workFactor == 0) workFactor = 30; + ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = 0; + bzf->initialisedOk = True; + return bzf; +} + + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWrite) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return; }; + + bzf->strm.avail_in = len; + bzf->strm.next_in = buf; + + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN ); + if (ret != BZ_RUN_OK) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (bzf->strm.avail_in == 0) + { BZ_SETERR(BZ_OK); return; }; + } +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWriteClose) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out ) +{ + BZ2_bzWriteClose64 ( bzerror, b, abandon, + nbytes_in, NULL, nbytes_out, NULL ); +} + + +void BZ_API(BZ2_bzWriteClose64) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0; + if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0; + if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0; + if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0; + + if ((!abandon) && bzf->lastErr == BZ_OK) { + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH ); + if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (ret == BZ_STREAM_END) break; + } + } + + if ( !abandon && !ferror ( bzf->handle ) ) { + fflush ( bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (nbytes_in_lo32 != NULL) + *nbytes_in_lo32 = bzf->strm.total_in_lo32; + if (nbytes_in_hi32 != NULL) + *nbytes_in_hi32 = bzf->strm.total_in_hi32; + if (nbytes_out_lo32 != NULL) + *nbytes_out_lo32 = bzf->strm.total_out_lo32; + if (nbytes_out_hi32 != NULL) + *nbytes_out_hi32 = bzf->strm.total_out_hi32; + + BZ_SETERR(BZ_OK); + BZ2_bzCompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzReadOpen) + ( int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused ) +{ + bzFile* bzf = NULL; + int ret; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (small != 0 && small != 1) || + (verbosity < 0 || verbosity > 4) || + (unused == NULL && nUnused != 0) || + (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED))) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + + bzf->initialisedOk = False; + bzf->handle = f; + bzf->bufN = 0; + bzf->writing = False; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + while (nUnused > 0) { + bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++; + unused = ((void*)( 1 + ((UChar*)(unused)) )); + nUnused--; + } + + ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + + bzf->initialisedOk = True; + return bzf; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b ) +{ + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + + if (bzf->initialisedOk) + (void)BZ2_bzDecompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzRead) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return 0; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return 0; }; + + bzf->strm.avail_out = len; + bzf->strm.next_out = buf; + + while (True) { + + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + + if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) { + n = fread ( bzf->buf, sizeof(UChar), + BZ_MAX_UNUSED, bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + bzf->bufN = n; + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + } + + ret = BZ2_bzDecompress ( &(bzf->strm) ); + + if (ret != BZ_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return 0; }; + + if (ret == BZ_OK && myfeof(bzf->handle) && + bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0) + { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; }; + + if (ret == BZ_STREAM_END) + { BZ_SETERR(BZ_STREAM_END); + return len - bzf->strm.avail_out; }; + if (bzf->strm.avail_out == 0) + { BZ_SETERR(BZ_OK); return len; }; + + } + + return 0; /*not reached*/ +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadGetUnused) + ( int* bzerror, + BZFILE* b, + void** unused, + int* nUnused ) +{ + bzFile* bzf = (bzFile*)b; + if (bzf == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (bzf->lastErr != BZ_STREAM_END) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (unused == NULL || nUnused == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + + BZ_SETERR(BZ_OK); + *nUnused = bzf->strm.avail_in; + *unused = bzf->strm.next_in; +} +#endif + + +/*---------------------------------------------------*/ +/*--- Misc convenience stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffCompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + blockSize100k < 1 || blockSize100k > 9 || + verbosity < 0 || verbosity > 4 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzCompressInit ( &strm, blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzCompress ( &strm, BZ_FINISH ); + if (ret == BZ_FINISH_OK) goto output_overflow; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzCompressEnd ( &strm ); + return BZ_OK; + + output_overflow: + BZ2_bzCompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + + errhandler: + BZ2_bzCompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffDecompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + (small != 0 && small != 1) || + verbosity < 0 || verbosity > 4) + return BZ_PARAM_ERROR; + + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzDecompressInit ( &strm, verbosity, small ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzDecompress ( &strm ); + if (ret == BZ_OK) goto output_overflow_or_eof; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzDecompressEnd ( &strm ); + return BZ_OK; + + output_overflow_or_eof: + if (strm.avail_out > 0) { + BZ2_bzDecompressEnd ( &strm ); + return BZ_UNEXPECTED_EOF; + } else { + BZ2_bzDecompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + }; + + errhandler: + BZ2_bzDecompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +/*-- + return version like "0.9.5d, 4-Sept-1999". +--*/ +const char * BZ_API(BZ2_bzlibVersion)(void) +{ + return BZ_VERSION; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) +# include +# include +# define SET_BINARY_MODE(file) _setmode(_fileno(file),O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif +static +BZFILE * bzopen_or_bzdopen + ( const char *path, /* no use when bzdopen */ + int fd, /* no use when bzdopen */ + const char *mode, + int open_mode) /* bzopen: 0, bzdopen:1 */ +{ + int bzerr; + char unused[BZ_MAX_UNUSED]; + int blockSize100k = 9; + int writing = 0; + char mode2[10] = ""; + FILE *fp = NULL; + BZFILE *bzfp = NULL; + int verbosity = 0; + int workFactor = 30; + int smallMode = 0; + int nUnused = 0; + + if (mode == NULL) return NULL; + while (*mode) { + switch (*mode) { + case 'r': + writing = 0; break; + case 'w': + writing = 1; break; + case 's': + smallMode = 1; break; + default: + if (isdigit((int)(*mode))) { + blockSize100k = *mode-BZ_HDR_0; + } + } + mode++; + } + strcat(mode2, writing ? "w" : "r" ); + strcat(mode2,"b"); /* binary mode */ + + if (open_mode==0) { + if (path==NULL || strcmp(path,"")==0) { + fp = (writing ? stdout : stdin); + SET_BINARY_MODE(fp); + } else { + fp = fopen(path,mode2); + } + } else { +#ifdef BZ_STRICT_ANSI + fp = NULL; +#else + fp = _fdopen(fd,mode2); +#endif + } + if (fp == NULL) return NULL; + + if (writing) { + /* Guard against total chaos and anarchy -- JRS */ + if (blockSize100k < 1) blockSize100k = 1; + if (blockSize100k > 9) blockSize100k = 9; + bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k, + verbosity,workFactor); + } else { + bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode, + unused,nUnused); + } + if (bzfp == NULL) { + if (fp != stdin && fp != stdout) fclose(fp); + return NULL; + } + return bzfp; +} + + +/*---------------------------------------------------*/ +/*-- + open file for read or write. + ex) bzopen("file","w9") + case path="" or NULL => use stdin or stdout. +--*/ +BZFILE * BZ_API(BZ2_bzopen) + ( const char *path, + const char *mode ) +{ + return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0); +} + + +/*---------------------------------------------------*/ +BZFILE * BZ_API(BZ2_bzdopen) + ( int fd, + const char *mode ) +{ + return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len ) +{ + int bzerr, nread; + if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0; + nread = BZ2_bzRead(&bzerr,b,buf,len); + if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) { + return nread; + } else { + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len ) +{ + int bzerr; + + BZ2_bzWrite(&bzerr,b,buf,len); + if(bzerr == BZ_OK){ + return len; + }else{ + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzflush) (BZFILE *b) +{ + /* do nothing now... */ + return 0; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzclose) (BZFILE* b) +{ + int bzerr; + FILE *fp; + + if (b==NULL) {return;} + fp = ((bzFile *)b)->handle; + if(((bzFile*)b)->writing){ + BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL); + if(bzerr != BZ_OK){ + BZ2_bzWriteClose(NULL,b,1,NULL,NULL); + } + }else{ + BZ2_bzReadClose(&bzerr,b); + } + if(fp!=stdin && fp!=stdout){ + fclose(fp); + } +} + + +/*---------------------------------------------------*/ +/*-- + return last error code +--*/ +static const char *bzerrorstrings[] = { + "OK" + ,"SEQUENCE_ERROR" + ,"PARAM_ERROR" + ,"MEM_ERROR" + ,"DATA_ERROR" + ,"DATA_ERROR_MAGIC" + ,"IO_ERROR" + ,"UNEXPECTED_EOF" + ,"OUTBUFF_FULL" + ,"CONFIG_ERROR" + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ +}; + + +const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum) +{ + int err = ((bzFile *)b)->lastErr; + + if(err>0) err = 0; + *errnum = err; + return bzerrorstrings[err*-1]; +} +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/3rdparty/bzip2/bzlib.h b/3rdparty/bzip2/bzlib.h new file mode 100644 index 0000000000..941c9c5908 --- /dev/null +++ b/3rdparty/bzip2/bzlib.h @@ -0,0 +1,282 @@ + +/*-------------------------------------------------------------*/ +/*--- Public header file for the library. ---*/ +/*--- bzlib.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.4 of 20 December 2006 + Copyright (C) 1996-2006 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_H +#define _BZLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define BZ_RUN 0 +#define BZ_FLUSH 1 +#define BZ_FINISH 2 + +#define BZ_OK 0 +#define BZ_RUN_OK 1 +#define BZ_FLUSH_OK 2 +#define BZ_FINISH_OK 3 +#define BZ_STREAM_END 4 +#define BZ_SEQUENCE_ERROR (-1) +#define BZ_PARAM_ERROR (-2) +#define BZ_MEM_ERROR (-3) +#define BZ_DATA_ERROR (-4) +#define BZ_DATA_ERROR_MAGIC (-5) +#define BZ_IO_ERROR (-6) +#define BZ_UNEXPECTED_EOF (-7) +#define BZ_OUTBUFF_FULL (-8) +#define BZ_CONFIG_ERROR (-9) + +typedef + struct { + char *next_in; + unsigned int avail_in; + unsigned int total_in_lo32; + unsigned int total_in_hi32; + + char *next_out; + unsigned int avail_out; + unsigned int total_out_lo32; + unsigned int total_out_hi32; + + void *state; + + void *(*bzalloc)(void *,int,int); + void (*bzfree)(void *,void *); + void *opaque; + } + bz_stream; + + +#ifndef BZ_IMPORT +#define BZ_EXPORT +#endif + +#ifndef BZ_NO_STDIO +/* Need a definitition for FILE */ +#include +#endif + +#ifdef _WIN32 +# include +# ifdef small + /* windows.h define small to char */ +# undef small +# endif +# ifdef BZ_EXPORT +# define BZ_API(func) WINAPI func +# define BZ_EXTERN extern +# else + /* import windows dll dynamically */ +# define BZ_API(func) (WINAPI * func) +# define BZ_EXTERN +# endif +#else +# define BZ_API(func) func +# define BZ_EXTERN extern +#endif + + +/*-- Core (low-level) library functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzCompressInit) ( + bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompress) ( + bz_stream* strm, + int action + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) ( + bz_stream *strm, + int verbosity, + int small + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompress) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) ( + bz_stream *strm + ); + + + +/*-- High(er) level library functions --*/ + +#ifndef BZ_NO_STDIO +#define BZ_MAX_UNUSED 5000 + +typedef void BZFILE; + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) ( + int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadClose) ( + int* bzerror, + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) ( + int* bzerror, + BZFILE* b, + void** unused, + int* nUnused + ); + +BZ_EXTERN int BZ_API(BZ2_bzRead) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) ( + int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN void BZ_API(BZ2_bzWrite) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 + ); +#endif + + +/*-- Utility functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity + ); + + +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ + +BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) ( + void + ); + +#ifndef BZ_NO_STDIO +BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) ( + const char *path, + const char *mode + ); + +BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) ( + int fd, + const char *mode + ); + +BZ_EXTERN int BZ_API(BZ2_bzread) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzwrite) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzflush) ( + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzclose) ( + BZFILE* b + ); + +BZ_EXTERN const char * BZ_API(BZ2_bzerror) ( + BZFILE *b, + int *errnum + ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/3rdparty/bzip2/bzlib_private.h b/3rdparty/bzip2/bzlib_private.h new file mode 100644 index 0000000000..df7a22dd31 --- /dev/null +++ b/3rdparty/bzip2/bzlib_private.h @@ -0,0 +1,503 @@ + +/*-------------------------------------------------------------*/ +/*--- Private header file for the library. ---*/ +/*--- bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.4 of 20 December 2006 + Copyright (C) 1996-2006 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_PRIVATE_H +#define _BZLIB_PRIVATE_H + +#include + +#ifndef BZ_NO_STDIO +#include +#include +#include +#endif + +#include "bzlib.h" + + + +/*-- General stuff. --*/ + +#define BZ_VERSION "1.0.4, 20-Dec-2006" + +typedef char Char; +typedef unsigned char Bool; +typedef unsigned char UChar; +typedef int Int32; +typedef unsigned int UInt32; +typedef short Int16; +typedef unsigned short UInt16; + +#define True ((Bool)1) +#define False ((Bool)0) + +#ifndef __GNUC__ +#define __inline__ /* */ +#endif + +#ifndef BZ_NO_STDIO + +extern void BZ2_bz__AssertH__fail ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); } + +#if BZ_DEBUG +#define AssertD(cond,msg) \ + { if (!(cond)) { \ + fprintf ( stderr, \ + "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\ + exit(1); \ + }} +#else +#define AssertD(cond,msg) /* */ +#endif + +#define VPrintf0(zf) \ + fprintf(stderr,zf) +#define VPrintf1(zf,za1) \ + fprintf(stderr,zf,za1) +#define VPrintf2(zf,za1,za2) \ + fprintf(stderr,zf,za1,za2) +#define VPrintf3(zf,za1,za2,za3) \ + fprintf(stderr,zf,za1,za2,za3) +#define VPrintf4(zf,za1,za2,za3,za4) \ + fprintf(stderr,zf,za1,za2,za3,za4) +#define VPrintf5(zf,za1,za2,za3,za4,za5) \ + fprintf(stderr,zf,za1,za2,za3,za4,za5) + +#else + +extern void bz_internal_error ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) bz_internal_error ( errcode ); } +#define AssertD(cond,msg) do { } while (0) +#define VPrintf0(zf) do { } while (0) +#define VPrintf1(zf,za1) do { } while (0) +#define VPrintf2(zf,za1,za2) do { } while (0) +#define VPrintf3(zf,za1,za2,za3) do { } while (0) +#define VPrintf4(zf,za1,za2,za3,za4) do { } while (0) +#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0) + +#endif + + +#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1) +#define BZFREE(ppp) (strm->bzfree)(strm->opaque,(ppp)) + + +/*-- Header bytes. --*/ + +#define BZ_HDR_B 0x42 /* 'B' */ +#define BZ_HDR_Z 0x5a /* 'Z' */ +#define BZ_HDR_h 0x68 /* 'h' */ +#define BZ_HDR_0 0x30 /* '0' */ + +/*-- Constants for the back end. --*/ + +#define BZ_MAX_ALPHA_SIZE 258 +#define BZ_MAX_CODE_LEN 23 + +#define BZ_RUNA 0 +#define BZ_RUNB 1 + +#define BZ_N_GROUPS 6 +#define BZ_G_SIZE 50 +#define BZ_N_ITERS 4 + +#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE)) + + + +/*-- Stuff for randomising repetitive blocks. --*/ + +extern Int32 BZ2_rNums[512]; + +#define BZ_RAND_DECLS \ + Int32 rNToGo; \ + Int32 rTPos \ + +#define BZ_RAND_INIT_MASK \ + s->rNToGo = 0; \ + s->rTPos = 0 \ + +#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0) + +#define BZ_RAND_UPD_MASK \ + if (s->rNToGo == 0) { \ + s->rNToGo = BZ2_rNums[s->rTPos]; \ + s->rTPos++; \ + if (s->rTPos == 512) s->rTPos = 0; \ + } \ + s->rNToGo--; + + + +/*-- Stuff for doing CRCs. --*/ + +extern UInt32 BZ2_crc32Table[256]; + +#define BZ_INITIALISE_CRC(crcVar) \ +{ \ + crcVar = 0xffffffffL; \ +} + +#define BZ_FINALISE_CRC(crcVar) \ +{ \ + crcVar = ~(crcVar); \ +} + +#define BZ_UPDATE_CRC(crcVar,cha) \ +{ \ + crcVar = (crcVar << 8) ^ \ + BZ2_crc32Table[(crcVar >> 24) ^ \ + ((UChar)cha)]; \ +} + + + +/*-- States and modes for compression. --*/ + +#define BZ_M_IDLE 1 +#define BZ_M_RUNNING 2 +#define BZ_M_FLUSHING 3 +#define BZ_M_FINISHING 4 + +#define BZ_S_OUTPUT 1 +#define BZ_S_INPUT 2 + +#define BZ_N_RADIX 2 +#define BZ_N_QSORT 12 +#define BZ_N_SHELL 18 +#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) + + + + +/*-- Structure holding all the compression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* mode this stream is in, and whether inputting */ + /* or outputting data */ + Int32 mode; + Int32 state; + + /* remembers avail_in when flush/finish requested */ + UInt32 avail_in_expect; + + /* for doing the block sorting */ + UInt32* arr1; + UInt32* arr2; + UInt32* ftab; + Int32 origPtr; + + /* aliases for arr1 and arr2 */ + UInt32* ptr; + UChar* block; + UInt16* mtfv; + UChar* zbits; + + /* for deciding when to use the fallback sorting algorithm */ + Int32 workFactor; + + /* run-length-encoding of the input */ + UInt32 state_in_ch; + Int32 state_in_len; + BZ_RAND_DECLS; + + /* input and output limits and current posns */ + Int32 nblock; + Int32 nblockMAX; + Int32 numZ; + Int32 state_out_pos; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + UChar unseqToSeq[256]; + + /* the buffer for bit stream creation */ + UInt32 bsBuff; + Int32 bsLive; + + /* block and combined CRCs */ + UInt32 blockCRC; + UInt32 combinedCRC; + + /* misc administratium */ + Int32 verbosity; + Int32 blockNo; + Int32 blockSize100k; + + /* stuff for coding the MTF values */ + Int32 nMTF; + Int32 mtfFreq [BZ_MAX_ALPHA_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + /* second dimension: only 3 needed; 4 makes index calculations faster */ + UInt32 len_pack[BZ_MAX_ALPHA_SIZE][4]; + + } + EState; + + + +/*-- externs for compression. --*/ + +extern void +BZ2_blockSort ( EState* ); + +extern void +BZ2_compressBlock ( EState*, Bool ); + +extern void +BZ2_bsInitWrite ( EState* ); + +extern void +BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 ); + +extern void +BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 ); + + + +/*-- states for decompression. --*/ + +#define BZ_X_IDLE 1 +#define BZ_X_OUTPUT 2 + +#define BZ_X_MAGIC_1 10 +#define BZ_X_MAGIC_2 11 +#define BZ_X_MAGIC_3 12 +#define BZ_X_MAGIC_4 13 +#define BZ_X_BLKHDR_1 14 +#define BZ_X_BLKHDR_2 15 +#define BZ_X_BLKHDR_3 16 +#define BZ_X_BLKHDR_4 17 +#define BZ_X_BLKHDR_5 18 +#define BZ_X_BLKHDR_6 19 +#define BZ_X_BCRC_1 20 +#define BZ_X_BCRC_2 21 +#define BZ_X_BCRC_3 22 +#define BZ_X_BCRC_4 23 +#define BZ_X_RANDBIT 24 +#define BZ_X_ORIGPTR_1 25 +#define BZ_X_ORIGPTR_2 26 +#define BZ_X_ORIGPTR_3 27 +#define BZ_X_MAPPING_1 28 +#define BZ_X_MAPPING_2 29 +#define BZ_X_SELECTOR_1 30 +#define BZ_X_SELECTOR_2 31 +#define BZ_X_SELECTOR_3 32 +#define BZ_X_CODING_1 33 +#define BZ_X_CODING_2 34 +#define BZ_X_CODING_3 35 +#define BZ_X_MTF_1 36 +#define BZ_X_MTF_2 37 +#define BZ_X_MTF_3 38 +#define BZ_X_MTF_4 39 +#define BZ_X_MTF_5 40 +#define BZ_X_MTF_6 41 +#define BZ_X_ENDHDR_2 42 +#define BZ_X_ENDHDR_3 43 +#define BZ_X_ENDHDR_4 44 +#define BZ_X_ENDHDR_5 45 +#define BZ_X_ENDHDR_6 46 +#define BZ_X_CCRC_1 47 +#define BZ_X_CCRC_2 48 +#define BZ_X_CCRC_3 49 +#define BZ_X_CCRC_4 50 + + + +/*-- Constants for the fast MTF decoder. --*/ + +#define MTFA_SIZE 4096 +#define MTFL_SIZE 16 + + + +/*-- Structure holding all the decompression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* state indicator for this stream */ + Int32 state; + + /* for doing the final run-length decoding */ + UChar state_out_ch; + Int32 state_out_len; + Bool blockRandomised; + BZ_RAND_DECLS; + + /* the buffer for bit stream reading */ + UInt32 bsBuff; + Int32 bsLive; + + /* misc administratium */ + Int32 blockSize100k; + Bool smallDecompress; + Int32 currBlockNo; + Int32 verbosity; + + /* for undoing the Burrows-Wheeler transform */ + Int32 origPtr; + UInt32 tPos; + Int32 k0; + Int32 unzftab[256]; + Int32 nblock_used; + Int32 cftab[257]; + Int32 cftabCopy[257]; + + /* for undoing the Burrows-Wheeler transform (FAST) */ + UInt32 *tt; + + /* for undoing the Burrows-Wheeler transform (SMALL) */ + UInt16 *ll16; + UChar *ll4; + + /* stored and calculated CRCs */ + UInt32 storedBlockCRC; + UInt32 storedCombinedCRC; + UInt32 calculatedBlockCRC; + UInt32 calculatedCombinedCRC; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + Bool inUse16[16]; + UChar seqToUnseq[256]; + + /* for decoding the MTF values */ + UChar mtfa [MTFA_SIZE]; + Int32 mtfbase[256 / MTFL_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + + Int32 limit [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 base [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 perm [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 minLens[BZ_N_GROUPS]; + + /* save area for scalars in the main decompress code */ + Int32 save_i; + Int32 save_j; + Int32 save_t; + Int32 save_alphaSize; + Int32 save_nGroups; + Int32 save_nSelectors; + Int32 save_EOB; + Int32 save_groupNo; + Int32 save_groupPos; + Int32 save_nextSym; + Int32 save_nblockMAX; + Int32 save_nblock; + Int32 save_es; + Int32 save_N; + Int32 save_curr; + Int32 save_zt; + Int32 save_zn; + Int32 save_zvec; + Int32 save_zj; + Int32 save_gSel; + Int32 save_gMinlen; + Int32* save_gLimit; + Int32* save_gBase; + Int32* save_gPerm; + + } + DState; + + + +/*-- Macros for decompression. --*/ + +#define BZ_GET_FAST(cccc) \ + s->tPos = s->tt[s->tPos]; \ + cccc = (UChar)(s->tPos & 0xff); \ + s->tPos >>= 8; + +#define BZ_GET_FAST_C(cccc) \ + c_tPos = c_tt[c_tPos]; \ + cccc = (UChar)(c_tPos & 0xff); \ + c_tPos >>= 8; + +#define SET_LL4(i,n) \ + { if (((i) & 0x1) == 0) \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4); \ + } + +#define GET_LL4(i) \ + ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF) + +#define SET_LL(i,n) \ + { s->ll16[i] = (UInt16)(n & 0x0000ffff); \ + SET_LL4(i, n >> 16); \ + } + +#define GET_LL(i) \ + (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16)) + +#define BZ_GET_SMALL(cccc) \ + cccc = BZ2_indexIntoF ( s->tPos, s->cftab ); \ + s->tPos = GET_LL(s->tPos); + + +/*-- externs for decompression. --*/ + +extern Int32 +BZ2_indexIntoF ( Int32, Int32* ); + +extern Int32 +BZ2_decompress ( DState* ); + +extern void +BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*, + Int32, Int32, Int32 ); + + +#endif + + +/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/ + +#ifdef BZ_NO_STDIO +#ifndef NULL +#define NULL 0 +#endif +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/3rdparty/bzip2/compress.c b/3rdparty/bzip2/compress.c new file mode 100644 index 0000000000..7be1976f69 --- /dev/null +++ b/3rdparty/bzip2/compress.c @@ -0,0 +1,672 @@ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting) ---*/ +/*--- compress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.4 of 20 December 2006 + Copyright (C) 1996-2006 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- changed setting of nGroups in sendMTFValues() + so as to do a bit better on small files +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +void BZ2_bsInitWrite ( EState* s ) +{ + s->bsLive = 0; + s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static +void bsFinishWrite ( EState* s ) +{ + while (s->bsLive > 0) { + s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24); + s->numZ++; + s->bsBuff <<= 8; + s->bsLive -= 8; + } +} + + +/*---------------------------------------------------*/ +#define bsNEEDW(nz) \ +{ \ + while (s->bsLive >= 8) { \ + s->zbits[s->numZ] \ + = (UChar)(s->bsBuff >> 24); \ + s->numZ++; \ + s->bsBuff <<= 8; \ + s->bsLive -= 8; \ + } \ +} + + +/*---------------------------------------------------*/ +static +__inline__ +void bsW ( EState* s, Int32 n, UInt32 v ) +{ + bsNEEDW ( n ); + s->bsBuff |= (v << (32 - s->bsLive - n)); + s->bsLive += n; +} + + +/*---------------------------------------------------*/ +static +void bsPutUInt32 ( EState* s, UInt32 u ) +{ + bsW ( s, 8, (u >> 24) & 0xffL ); + bsW ( s, 8, (u >> 16) & 0xffL ); + bsW ( s, 8, (u >> 8) & 0xffL ); + bsW ( s, 8, u & 0xffL ); +} + + +/*---------------------------------------------------*/ +static +void bsPutUChar ( EState* s, UChar c ) +{ + bsW( s, 8, (UInt32)c ); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e ( EState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->unseqToSeq[i] = s->nInUse; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +static +void generateMTFValues ( EState* s ) +{ + UChar yy[256]; + Int32 i, j; + Int32 zPend; + Int32 wr; + Int32 EOB; + + /* + After sorting (eg, here), + s->arr1 [ 0 .. s->nblock-1 ] holds sorted order, + and + ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] + holds the original block data. + + The first thing to do is generate the MTF values, + and put them in + ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ]. + Because there are strictly fewer or equal MTF values + than block values, ptr values in this area are overwritten + with MTF values only when they are no longer needed. + + The final compressed bitstream is generated into the + area starting at + (UChar*) (&((UChar*)s->arr2)[s->nblock]) + + These storage aliases are set up in bzCompressInit(), + except for the last one, which is arranged in + compressBlock(). + */ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt16* mtfv = s->mtfv; + + makeMaps_e ( s ); + EOB = s->nInUse+1; + + for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0; + + wr = 0; + zPend = 0; + for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i; + + for (i = 0; i < s->nblock; i++) { + UChar ll_i; + AssertD ( wr <= i, "generateMTFValues(1)" ); + j = ptr[i]-1; if (j < 0) j += s->nblock; + ll_i = s->unseqToSeq[block[j]]; + AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" ); + + if (yy[0] == ll_i) { + zPend++; + } else { + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + { + register UChar rtmp; + register UChar* ryy_j; + register UChar rll_i; + rtmp = yy[1]; + yy[1] = yy[0]; + ryy_j = &(yy[1]); + rll_i = ll_i; + while ( rll_i != rtmp ) { + register UChar rtmp2; + ryy_j++; + rtmp2 = rtmp; + rtmp = *ryy_j; + *ryy_j = rtmp2; + }; + yy[0] = rtmp; + j = ryy_j - &(yy[0]); + mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++; + } + + } + } + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + + mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++; + + s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST 0 +#define BZ_GREATER_ICOST 15 + +static +void sendMTFValues ( EState* s ) +{ + Int32 v, t, i, j, gs, ge, totc, bt, bc, iter; + Int32 nSelectors, alphaSize, minLen, maxLen, selCtr; + Int32 nGroups, nBytes; + + /*-- + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + is a global since the decoder also needs it. + + Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + are also globals only used in this proc. + Made global to keep stack frame size small. + --*/ + + + UInt16 cost[BZ_N_GROUPS]; + Int32 fave[BZ_N_GROUPS]; + + UInt16* mtfv = s->mtfv; + + if (s->verbosity >= 3) + VPrintf3( " %d in block, %d after MTF & 1-2 coding, " + "%d+2 syms in use\n", + s->nblock, s->nMTF, s->nInUse ); + + alphaSize = s->nInUse+2; + for (t = 0; t < BZ_N_GROUPS; t++) + for (v = 0; v < alphaSize; v++) + s->len[t][v] = BZ_GREATER_ICOST; + + /*--- Decide how many coding tables to use ---*/ + AssertH ( s->nMTF > 0, 3001 ); + if (s->nMTF < 200) nGroups = 2; else + if (s->nMTF < 600) nGroups = 3; else + if (s->nMTF < 1200) nGroups = 4; else + if (s->nMTF < 2400) nGroups = 5; else + nGroups = 6; + + /*--- Generate an initial set of coding tables ---*/ + { + Int32 nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = s->nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs-1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize-1) { + ge++; + aFreq += s->mtfFreq[ge]; + } + + if (ge > gs + && nPart != nGroups && nPart != 1 + && ((nGroups-nPart) % 2 == 1)) { + aFreq -= s->mtfFreq[ge]; + ge--; + } + + if (s->verbosity >= 3) + VPrintf5( " initial group %d, [%d .. %d], " + "has %d syms (%4.1f%%)\n", + nPart, gs, ge, aFreq, + (100.0 * (float)aFreq) / (float)(s->nMTF) ); + + for (v = 0; v < alphaSize; v++) + if (v >= gs && v <= ge) + s->len[nPart-1][v] = BZ_LESSER_ICOST; else + s->len[nPart-1][v] = BZ_GREATER_ICOST; + + nPart--; + gs = ge+1; + remF -= aFreq; + } + } + + /*--- + Iterate up to BZ_N_ITERS times to improve the tables. + ---*/ + for (iter = 0; iter < BZ_N_ITERS; iter++) { + + for (t = 0; t < nGroups; t++) fave[t] = 0; + + for (t = 0; t < nGroups; t++) + for (v = 0; v < alphaSize; v++) + s->rfreq[t][v] = 0; + + /*--- + Set up an auxiliary length table which is used to fast-track + the common case (nGroups == 6). + ---*/ + if (nGroups == 6) { + for (v = 0; v < alphaSize; v++) { + s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; + s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; + s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (True) { + + /*--- Set group start & end marks. --*/ + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + + /*-- + Calculate the cost of this group as coded + by each of the coding tables. + --*/ + for (t = 0; t < nGroups; t++) cost[t] = 0; + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + register UInt32 cost01, cost23, cost45; + register UInt16 icv; + cost01 = cost23 = cost45 = 0; + +# define BZ_ITER(nn) \ + icv = mtfv[gs+(nn)]; \ + cost01 += s->len_pack[icv][0]; \ + cost23 += s->len_pack[icv][1]; \ + cost45 += s->len_pack[icv][2]; \ + + BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4); + BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9); + BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); + BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); + BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); + BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); + BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); + BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); + BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); + BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); + +# undef BZ_ITER + + cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16; + cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16; + cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + UInt16 icv = mtfv[i]; + for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv]; + } + } + + /*-- + Find the coding table which is best for this group, + and record its identity in the selector table. + --*/ + bc = 999999999; bt = -1; + for (t = 0; t < nGroups; t++) + if (cost[t] < bc) { bc = cost[t]; bt = t; }; + totc += bc; + fave[bt]++; + s->selector[nSelectors] = bt; + nSelectors++; + + /*-- + Increment the symbol frequencies for the selected table. + --*/ + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + +# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++ + + BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4); + BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9); + BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); + BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); + BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); + BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); + BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); + BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); + BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); + BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); + +# undef BZ_ITUR + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) + s->rfreq[bt][ mtfv[i] ]++; + } + + gs = ge+1; + } + if (s->verbosity >= 3) { + VPrintf2 ( " pass %d: size is %d, grp uses are ", + iter+1, totc/8 ); + for (t = 0; t < nGroups; t++) + VPrintf1 ( "%d ", fave[t] ); + VPrintf0 ( "\n" ); + } + + /*-- + Recompute the tables based on the accumulated frequencies. + --*/ + /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See + comment in huffman.c for details. */ + for (t = 0; t < nGroups; t++) + BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), + alphaSize, 17 /*20*/ ); + } + + + AssertH( nGroups < 8, 3002 ); + AssertH( nSelectors < 32768 && + nSelectors <= (2 + (900000 / BZ_G_SIZE)), + 3003 ); + + + /*--- Compute MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) pos[i] = i; + for (i = 0; i < nSelectors; i++) { + ll_i = s->selector[i]; + j = 0; + tmp = pos[j]; + while ( ll_i != tmp ) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + }; + pos[0] = tmp; + s->selectorMtf[i] = j; + } + }; + + /*--- Assign actual codes for the tables. --*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + AssertH ( !(maxLen > 17 /*20*/ ), 3004 ); + AssertH ( !(minLen < 1), 3005 ); + BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), + minLen, maxLen, alphaSize ); + } + + /*--- Transmit the mapping table. ---*/ + { + Bool inUse16[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = False; + for (j = 0; j < 16; j++) + if (s->inUse[i * 16 + j]) inUse16[i] = True; + } + + nBytes = s->numZ; + for (i = 0; i < 16; i++) + if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0); + + for (i = 0; i < 16; i++) + if (inUse16[i]) + for (j = 0; j < 16; j++) { + if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0); + } + + if (s->verbosity >= 3) + VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes ); + } + + /*--- Now the selectors. ---*/ + nBytes = s->numZ; + bsW ( s, 3, nGroups ); + bsW ( s, 15, nSelectors ); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1); + bsW(s,1,0); + } + if (s->verbosity >= 3) + VPrintf1( "selectors %d, ", s->numZ-nBytes ); + + /*--- Now the coding tables. ---*/ + nBytes = s->numZ; + + for (t = 0; t < nGroups; t++) { + Int32 curr = s->len[t][0]; + bsW ( s, 5, curr ); + for (i = 0; i < alphaSize; i++) { + while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ }; + while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ }; + bsW ( s, 1, 0 ); + } + } + + if (s->verbosity >= 3) + VPrintf1 ( "code lengths %d, ", s->numZ-nBytes ); + + /*--- And finally, the block data proper ---*/ + nBytes = s->numZ; + selCtr = 0; + gs = 0; + while (True) { + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + AssertH ( s->selector[selCtr] < nGroups, 3006 ); + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + UInt16 mtfv_i; + UChar* s_len_sel_selCtr + = &(s->len[s->selector[selCtr]][0]); + Int32* s_code_sel_selCtr + = &(s->code[s->selector[selCtr]][0]); + +# define BZ_ITAH(nn) \ + mtfv_i = mtfv[gs+(nn)]; \ + bsW ( s, \ + s_len_sel_selCtr[mtfv_i], \ + s_code_sel_selCtr[mtfv_i] ) + + BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4); + BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9); + BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); + BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); + BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); + BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); + BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); + BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); + BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); + BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); + +# undef BZ_ITAH + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + bsW ( s, + s->len [s->selector[selCtr]] [mtfv[i]], + s->code [s->selector[selCtr]] [mtfv[i]] ); + } + } + + + gs = ge+1; + selCtr++; + } + AssertH( selCtr == nSelectors, 3007 ); + + if (s->verbosity >= 3) + VPrintf1( "codes %d\n", s->numZ-nBytes ); +} + + +/*---------------------------------------------------*/ +void BZ2_compressBlock ( EState* s, Bool is_last_block ) +{ + if (s->nblock > 0) { + + BZ_FINALISE_CRC ( s->blockCRC ); + s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); + s->combinedCRC ^= s->blockCRC; + if (s->blockNo > 1) s->numZ = 0; + + if (s->verbosity >= 2) + VPrintf4( " block %d: crc = 0x%08x, " + "combined CRC = 0x%08x, size = %d\n", + s->blockNo, s->blockCRC, s->combinedCRC, s->nblock ); + + BZ2_blockSort ( s ); + } + + s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]); + + /*-- If this is the first block, create the stream header. --*/ + if (s->blockNo == 1) { + BZ2_bsInitWrite ( s ); + bsPutUChar ( s, BZ_HDR_B ); + bsPutUChar ( s, BZ_HDR_Z ); + bsPutUChar ( s, BZ_HDR_h ); + bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) ); + } + + if (s->nblock > 0) { + + bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 ); + bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 ); + bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 ); + + /*-- Now the block's CRC, so it is in a known place. --*/ + bsPutUInt32 ( s, s->blockCRC ); + + /*-- + Now a single bit indicating (non-)randomisation. + As of version 0.9.5, we use a better sorting algorithm + which makes randomisation unnecessary. So always set + the randomised bit to 'no'. Of course, the decoder + still needs to be able to handle randomised blocks + so as to maintain backwards compatibility with + older versions of bzip2. + --*/ + bsW(s,1,0); + + bsW ( s, 24, s->origPtr ); + generateMTFValues ( s ); + sendMTFValues ( s ); + } + + + /*-- If this is the last block, add the stream trailer. --*/ + if (is_last_block) { + + bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 ); + bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 ); + bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 ); + bsPutUInt32 ( s, s->combinedCRC ); + if (s->verbosity >= 2) + VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC ); + bsFinishWrite ( s ); + } +} + + +/*-------------------------------------------------------------*/ +/*--- end compress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/3rdparty/bzip2/crctable.c b/3rdparty/bzip2/crctable.c new file mode 100644 index 0000000000..5fd7cf6506 --- /dev/null +++ b/3rdparty/bzip2/crctable.c @@ -0,0 +1,104 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for doing CRCs ---*/ +/*--- crctable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.4 of 20 December 2006 + Copyright (C) 1996-2006 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*-- + I think this is an implementation of the AUTODIN-II, + Ethernet & FDDI 32-bit CRC standard. Vaguely derived + from code by Rob Warnock, in Section 51 of the + comp.compression FAQ. +--*/ + +UInt32 BZ2_crc32Table[256] = { + + /*-- Ugly, innit? --*/ + + 0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L, + 0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L, + 0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L, + 0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL, + 0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L, + 0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L, + 0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L, + 0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL, + 0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L, + 0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L, + 0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L, + 0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL, + 0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L, + 0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L, + 0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L, + 0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL, + 0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL, + 0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L, + 0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L, + 0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL, + 0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL, + 0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L, + 0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L, + 0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL, + 0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL, + 0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L, + 0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L, + 0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL, + 0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL, + 0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L, + 0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L, + 0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL, + 0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L, + 0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL, + 0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL, + 0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L, + 0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L, + 0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL, + 0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL, + 0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L, + 0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L, + 0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL, + 0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL, + 0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L, + 0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L, + 0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL, + 0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL, + 0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L, + 0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L, + 0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL, + 0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L, + 0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L, + 0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L, + 0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL, + 0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L, + 0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L, + 0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L, + 0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL, + 0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L, + 0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L, + 0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L, + 0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL, + 0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L, + 0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L +}; + + +/*-------------------------------------------------------------*/ +/*--- end crctable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/3rdparty/bzip2/decompress.c b/3rdparty/bzip2/decompress.c new file mode 100644 index 0000000000..4aa7f53c0c --- /dev/null +++ b/3rdparty/bzip2/decompress.c @@ -0,0 +1,626 @@ + +/*-------------------------------------------------------------*/ +/*--- Decompression machinery ---*/ +/*--- decompress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.4 of 20 December 2006 + Copyright (C) 1996-2006 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +static +void makeMaps_d ( DState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->seqToUnseq[s->nInUse] = i; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +#define RETURN(rrr) \ + { retVal = rrr; goto save_state_and_return; }; + +#define GET_BITS(lll,vvv,nnn) \ + case lll: s->state = lll; \ + while (True) { \ + if (s->bsLive >= nnn) { \ + UInt32 v; \ + v = (s->bsBuff >> \ + (s->bsLive-nnn)) & ((1 << nnn)-1); \ + s->bsLive -= nnn; \ + vvv = v; \ + break; \ + } \ + if (s->strm->avail_in == 0) RETURN(BZ_OK); \ + s->bsBuff \ + = (s->bsBuff << 8) | \ + ((UInt32) \ + (*((UChar*)(s->strm->next_in)))); \ + s->bsLive += 8; \ + s->strm->next_in++; \ + s->strm->avail_in--; \ + s->strm->total_in_lo32++; \ + if (s->strm->total_in_lo32 == 0) \ + s->strm->total_in_hi32++; \ + } + +#define GET_UCHAR(lll,uuu) \ + GET_BITS(lll,uuu,8) + +#define GET_BIT(lll,uuu) \ + GET_BITS(lll,uuu,1) + +/*---------------------------------------------------*/ +#define GET_MTF_VAL(label1,label2,lval) \ +{ \ + if (groupPos == 0) { \ + groupNo++; \ + if (groupNo >= nSelectors) \ + RETURN(BZ_DATA_ERROR); \ + groupPos = BZ_G_SIZE; \ + gSel = s->selector[groupNo]; \ + gMinlen = s->minLens[gSel]; \ + gLimit = &(s->limit[gSel][0]); \ + gPerm = &(s->perm[gSel][0]); \ + gBase = &(s->base[gSel][0]); \ + } \ + groupPos--; \ + zn = gMinlen; \ + GET_BITS(label1, zvec, zn); \ + while (1) { \ + if (zn > 20 /* the longest code */) \ + RETURN(BZ_DATA_ERROR); \ + if (zvec <= gLimit[zn]) break; \ + zn++; \ + GET_BIT(label2, zj); \ + zvec = (zvec << 1) | zj; \ + }; \ + if (zvec - gBase[zn] < 0 \ + || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE) \ + RETURN(BZ_DATA_ERROR); \ + lval = gPerm[zvec - gBase[zn]]; \ +} + + +/*---------------------------------------------------*/ +Int32 BZ2_decompress ( DState* s ) +{ + UChar uc; + Int32 retVal; + Int32 minLen, maxLen; + bz_stream* strm = s->strm; + + /* stuff that needs to be saved/restored */ + Int32 i; + Int32 j; + Int32 t; + Int32 alphaSize; + Int32 nGroups; + Int32 nSelectors; + Int32 EOB; + Int32 groupNo; + Int32 groupPos; + Int32 nextSym; + Int32 nblockMAX; + Int32 nblock; + Int32 es; + Int32 N; + Int32 curr; + Int32 zt; + Int32 zn; + Int32 zvec; + Int32 zj; + Int32 gSel; + Int32 gMinlen; + Int32* gLimit; + Int32* gBase; + Int32* gPerm; + + if (s->state == BZ_X_MAGIC_1) { + /*initialise the save area*/ + s->save_i = 0; + s->save_j = 0; + s->save_t = 0; + s->save_alphaSize = 0; + s->save_nGroups = 0; + s->save_nSelectors = 0; + s->save_EOB = 0; + s->save_groupNo = 0; + s->save_groupPos = 0; + s->save_nextSym = 0; + s->save_nblockMAX = 0; + s->save_nblock = 0; + s->save_es = 0; + s->save_N = 0; + s->save_curr = 0; + s->save_zt = 0; + s->save_zn = 0; + s->save_zvec = 0; + s->save_zj = 0; + s->save_gSel = 0; + s->save_gMinlen = 0; + s->save_gLimit = NULL; + s->save_gBase = NULL; + s->save_gPerm = NULL; + } + + /*restore from the save area*/ + i = s->save_i; + j = s->save_j; + t = s->save_t; + alphaSize = s->save_alphaSize; + nGroups = s->save_nGroups; + nSelectors = s->save_nSelectors; + EOB = s->save_EOB; + groupNo = s->save_groupNo; + groupPos = s->save_groupPos; + nextSym = s->save_nextSym; + nblockMAX = s->save_nblockMAX; + nblock = s->save_nblock; + es = s->save_es; + N = s->save_N; + curr = s->save_curr; + zt = s->save_zt; + zn = s->save_zn; + zvec = s->save_zvec; + zj = s->save_zj; + gSel = s->save_gSel; + gMinlen = s->save_gMinlen; + gLimit = s->save_gLimit; + gBase = s->save_gBase; + gPerm = s->save_gPerm; + + retVal = BZ_OK; + + switch (s->state) { + + GET_UCHAR(BZ_X_MAGIC_1, uc); + if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_2, uc); + if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_3, uc) + if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8) + if (s->blockSize100k < (BZ_HDR_0 + 1) || + s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC); + s->blockSize100k -= BZ_HDR_0; + + if (s->smallDecompress) { + s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) ); + s->ll4 = BZALLOC( + ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) + ); + if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR); + } else { + s->tt = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) ); + if (s->tt == NULL) RETURN(BZ_MEM_ERROR); + } + + GET_UCHAR(BZ_X_BLKHDR_1, uc); + + if (uc == 0x17) goto endhdr_2; + if (uc != 0x31) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_2, uc); + if (uc != 0x41) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_3, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_4, uc); + if (uc != 0x26) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_5, uc); + if (uc != 0x53) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_6, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + + s->currBlockNo++; + if (s->verbosity >= 2) + VPrintf1 ( "\n [%d: huff+mtf ", s->currBlockNo ); + + s->storedBlockCRC = 0; + GET_UCHAR(BZ_X_BCRC_1, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_2, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_3, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_4, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + + GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1); + + s->origPtr = 0; + GET_UCHAR(BZ_X_ORIGPTR_1, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_2, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_3, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + + if (s->origPtr < 0) + RETURN(BZ_DATA_ERROR); + if (s->origPtr > 10 + 100000*s->blockSize100k) + RETURN(BZ_DATA_ERROR); + + /*--- Receive the mapping table ---*/ + for (i = 0; i < 16; i++) { + GET_BIT(BZ_X_MAPPING_1, uc); + if (uc == 1) + s->inUse16[i] = True; else + s->inUse16[i] = False; + } + + for (i = 0; i < 256; i++) s->inUse[i] = False; + + for (i = 0; i < 16; i++) + if (s->inUse16[i]) + for (j = 0; j < 16; j++) { + GET_BIT(BZ_X_MAPPING_2, uc); + if (uc == 1) s->inUse[i * 16 + j] = True; + } + makeMaps_d ( s ); + if (s->nInUse == 0) RETURN(BZ_DATA_ERROR); + alphaSize = s->nInUse+2; + + /*--- Now the selectors ---*/ + GET_BITS(BZ_X_SELECTOR_1, nGroups, 3); + if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR); + GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15); + if (nSelectors < 1) RETURN(BZ_DATA_ERROR); + for (i = 0; i < nSelectors; i++) { + j = 0; + while (True) { + GET_BIT(BZ_X_SELECTOR_3, uc); + if (uc == 0) break; + j++; + if (j >= nGroups) RETURN(BZ_DATA_ERROR); + } + s->selectorMtf[i] = j; + } + + /*--- Undo the MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], tmp, v; + for (v = 0; v < nGroups; v++) pos[v] = v; + + for (i = 0; i < nSelectors; i++) { + v = s->selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { pos[v] = pos[v-1]; v--; } + pos[0] = tmp; + s->selector[i] = tmp; + } + } + + /*--- Now the coding tables ---*/ + for (t = 0; t < nGroups; t++) { + GET_BITS(BZ_X_CODING_1, curr, 5); + for (i = 0; i < alphaSize; i++) { + while (True) { + if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR); + GET_BIT(BZ_X_CODING_2, uc); + if (uc == 0) break; + GET_BIT(BZ_X_CODING_3, uc); + if (uc == 0) curr++; else curr--; + } + s->len[t][i] = curr; + } + } + + /*--- Create the Huffman decoding tables ---*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + BZ2_hbCreateDecodeTables ( + &(s->limit[t][0]), + &(s->base[t][0]), + &(s->perm[t][0]), + &(s->len[t][0]), + minLen, maxLen, alphaSize + ); + s->minLens[t] = minLen; + } + + /*--- Now the MTF values ---*/ + + EOB = s->nInUse+1; + nblockMAX = 100000 * s->blockSize100k; + groupNo = -1; + groupPos = 0; + + for (i = 0; i <= 255; i++) s->unzftab[i] = 0; + + /*-- MTF init --*/ + { + Int32 ii, jj, kk; + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj); + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + /*-- end MTF init --*/ + + nblock = 0; + GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym); + + while (True) { + + if (nextSym == EOB) break; + + if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) { + + es = -1; + N = 1; + do { + if (nextSym == BZ_RUNA) es = es + (0+1) * N; else + if (nextSym == BZ_RUNB) es = es + (1+1) * N; + N = N * 2; + GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym); + } + while (nextSym == BZ_RUNA || nextSym == BZ_RUNB); + + es++; + uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ]; + s->unzftab[uc] += es; + + if (s->smallDecompress) + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->ll16[nblock] = (UInt16)uc; + nblock++; + es--; + } + else + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->tt[nblock] = (UInt32)uc; + nblock++; + es--; + }; + + continue; + + } else { + + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + + /*-- uc = MTF ( nextSym-1 ) --*/ + { + Int32 ii, jj, kk, pp, lno, off; + UInt32 nn; + nn = (UInt32)(nextSym - 1); + + if (nn < MTFL_SIZE) { + /* avoid general-case expense */ + pp = s->mtfbase[0]; + uc = s->mtfa[pp+nn]; + while (nn > 3) { + Int32 z = pp+nn; + s->mtfa[(z) ] = s->mtfa[(z)-1]; + s->mtfa[(z)-1] = s->mtfa[(z)-2]; + s->mtfa[(z)-2] = s->mtfa[(z)-3]; + s->mtfa[(z)-3] = s->mtfa[(z)-4]; + nn -= 4; + } + while (nn > 0) { + s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; + }; + s->mtfa[pp] = uc; + } else { + /* general case */ + lno = nn / MTFL_SIZE; + off = nn % MTFL_SIZE; + pp = s->mtfbase[lno] + off; + uc = s->mtfa[pp]; + while (pp > s->mtfbase[lno]) { + s->mtfa[pp] = s->mtfa[pp-1]; pp--; + }; + s->mtfbase[lno]++; + while (lno > 0) { + s->mtfbase[lno]--; + s->mtfa[s->mtfbase[lno]] + = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1]; + lno--; + } + s->mtfbase[0]--; + s->mtfa[s->mtfbase[0]] = uc; + if (s->mtfbase[0] == 0) { + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj]; + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + } + } + /*-- end uc = MTF ( nextSym-1 ) --*/ + + s->unzftab[s->seqToUnseq[uc]]++; + if (s->smallDecompress) + s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else + s->tt[nblock] = (UInt32)(s->seqToUnseq[uc]); + nblock++; + + GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym); + continue; + } + } + + /* Now we know what nblock is, we can do a better sanity + check on s->origPtr. + */ + if (s->origPtr < 0 || s->origPtr >= nblock) + RETURN(BZ_DATA_ERROR); + + /*-- Set up cftab to facilitate generation of T^(-1) --*/ + s->cftab[0] = 0; + for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1]; + for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1]; + for (i = 0; i <= 256; i++) { + if (s->cftab[i] < 0 || s->cftab[i] > nblock) { + /* s->cftab[i] can legitimately be == nblock */ + RETURN(BZ_DATA_ERROR); + } + } + + s->state_out_len = 0; + s->state_out_ch = 0; + BZ_INITIALISE_CRC ( s->calculatedBlockCRC ); + s->state = BZ_X_OUTPUT; + if (s->verbosity >= 2) VPrintf0 ( "rt+rld" ); + + if (s->smallDecompress) { + + /*-- Make a copy of cftab, used in generation of T --*/ + for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i]; + + /*-- compute the T vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->ll16[i]); + SET_LL(i, s->cftabCopy[uc]); + s->cftabCopy[uc]++; + } + + /*-- Compute T^(-1) by pointer reversal on T --*/ + i = s->origPtr; + j = GET_LL(i); + do { + Int32 tmp = GET_LL(j); + SET_LL(j, i); + i = j; + j = tmp; + } + while (i != s->origPtr); + + s->tPos = s->origPtr; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_SMALL(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } else { + + /*-- compute the T^(-1) vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->tt[i] & 0xff); + s->tt[s->cftab[uc]] |= (i << 8); + s->cftab[uc]++; + } + + s->tPos = s->tt[s->origPtr] >> 8; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_FAST(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_FAST(s->k0); s->nblock_used++; + } + + } + + RETURN(BZ_OK); + + + + endhdr_2: + + GET_UCHAR(BZ_X_ENDHDR_2, uc); + if (uc != 0x72) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_3, uc); + if (uc != 0x45) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_4, uc); + if (uc != 0x38) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_5, uc); + if (uc != 0x50) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_6, uc); + if (uc != 0x90) RETURN(BZ_DATA_ERROR); + + s->storedCombinedCRC = 0; + GET_UCHAR(BZ_X_CCRC_1, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_2, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_3, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_4, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + + s->state = BZ_X_IDLE; + RETURN(BZ_STREAM_END); + + default: AssertH ( False, 4001 ); + } + + AssertH ( False, 4002 ); + + save_state_and_return: + + s->save_i = i; + s->save_j = j; + s->save_t = t; + s->save_alphaSize = alphaSize; + s->save_nGroups = nGroups; + s->save_nSelectors = nSelectors; + s->save_EOB = EOB; + s->save_groupNo = groupNo; + s->save_groupPos = groupPos; + s->save_nextSym = nextSym; + s->save_nblockMAX = nblockMAX; + s->save_nblock = nblock; + s->save_es = es; + s->save_N = N; + s->save_curr = curr; + s->save_zt = zt; + s->save_zn = zn; + s->save_zvec = zvec; + s->save_zj = zj; + s->save_gSel = gSel; + s->save_gMinlen = gMinlen; + s->save_gLimit = gLimit; + s->save_gBase = gBase; + s->save_gPerm = gPerm; + + return retVal; +} + + +/*-------------------------------------------------------------*/ +/*--- end decompress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/3rdparty/bzip2/huffman.c b/3rdparty/bzip2/huffman.c new file mode 100644 index 0000000000..00066bd0ab --- /dev/null +++ b/3rdparty/bzip2/huffman.c @@ -0,0 +1,205 @@ + +/*-------------------------------------------------------------*/ +/*--- Huffman coding low-level stuff ---*/ +/*--- huffman.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.4 of 20 December 2006 + Copyright (C) 1996-2006 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------------*/ +#define WEIGHTOF(zz0) ((zz0) & 0xffffff00) +#define DEPTHOF(zz1) ((zz1) & 0x000000ff) +#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3)) + +#define ADDWEIGHTS(zw1,zw2) \ + (WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \ + (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2))) + +#define UPHEAP(z) \ +{ \ + Int32 zz, tmp; \ + zz = z; tmp = heap[zz]; \ + while (weight[tmp] < weight[heap[zz >> 1]]) { \ + heap[zz] = heap[zz >> 1]; \ + zz >>= 1; \ + } \ + heap[zz] = tmp; \ +} + +#define DOWNHEAP(z) \ +{ \ + Int32 zz, yy, tmp; \ + zz = z; tmp = heap[zz]; \ + while (True) { \ + yy = zz << 1; \ + if (yy > nHeap) break; \ + if (yy < nHeap && \ + weight[heap[yy+1]] < weight[heap[yy]]) \ + yy++; \ + if (weight[tmp] < weight[heap[yy]]) break; \ + heap[zz] = heap[yy]; \ + zz = yy; \ + } \ + heap[zz] = tmp; \ +} + + +/*---------------------------------------------------*/ +void BZ2_hbMakeCodeLengths ( UChar *len, + Int32 *freq, + Int32 alphaSize, + Int32 maxLen ) +{ + /*-- + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + --*/ + Int32 nNodes, nHeap, n1, n2, i, j, k; + Bool tooLong; + + Int32 heap [ BZ_MAX_ALPHA_SIZE + 2 ]; + Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ]; + Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ]; + + for (i = 0; i < alphaSize; i++) + weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + + while (True) { + + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + UPHEAP(nHeap); + } + + AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 ); + + while (nHeap > 1) { + n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + nNodes++; + parent[n1] = parent[n2] = nNodes; + weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]); + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + UPHEAP(nHeap); + } + + AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 ); + + tooLong = False; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { k = parent[k]; j++; } + len[i-1] = j; + if (j > maxLen) tooLong = True; + } + + if (! tooLong) break; + + /* 17 Oct 04: keep-going condition for the following loop used + to be 'i < alphaSize', which missed the last element, + theoretically leading to the possibility of the compressor + looping. However, this count-scaling step is only needed if + one of the generated Huffman code words is longer than + maxLen, which up to and including version 1.0.2 was 20 bits, + which is extremely unlikely. In version 1.0.3 maxLen was + changed to 17 bits, which has minimal effect on compression + ratio, but does mean this scaling step is used from time to + time, enough to verify that it works. + + This means that bzip2-1.0.3 and later will only produce + Huffman codes with a maximum length of 17 bits. However, in + order to preserve backwards compatibility with bitstreams + produced by versions pre-1.0.3, the decompressor must still + handle lengths of up to 20. */ + + for (i = 1; i <= alphaSize; i++) { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbAssignCodes ( Int32 *code, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) + if (length[i] == n) { code[i] = vec; vec++; }; + vec <<= 1; + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbCreateDecodeTables ( Int32 *limit, + Int32 *base, + Int32 *perm, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 pp, i, j, vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) + for (j = 0; j < alphaSize; j++) + if (length[j] == i) { perm[pp] = j; pp++; }; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0; + for (i = 0; i < alphaSize; i++) base[length[i]+1]++; + + for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1]; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0; + vec = 0; + + for (i = minLen; i <= maxLen; i++) { + vec += (base[i+1] - base[i]); + limit[i] = vec-1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) + base[i] = ((limit[i-1] + 1) << 1) - base[i]; +} + + +/*-------------------------------------------------------------*/ +/*--- end huffman.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/3rdparty/bzip2/randtable.c b/3rdparty/bzip2/randtable.c new file mode 100644 index 0000000000..87a2f05012 --- /dev/null +++ b/3rdparty/bzip2/randtable.c @@ -0,0 +1,84 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for randomising repetitive blocks ---*/ +/*--- randtable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.4 of 20 December 2006 + Copyright (C) 1996-2006 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------*/ +Int32 BZ2_rNums[512] = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 +}; + + +/*-------------------------------------------------------------*/ +/*--- end randtable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/3rdparty/zlib/ChangeLog b/3rdparty/zlib/ChangeLog new file mode 100644 index 0000000000..7f6869d323 --- /dev/null +++ b/3rdparty/zlib/ChangeLog @@ -0,0 +1,855 @@ + + ChangeLog file for zlib + +Changes in 1.2.3 (18 July 2005) +- Apply security vulnerability fixes to contrib/infback9 as well +- Clean up some text files (carriage returns, trailing space) +- Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant] + +Changes in 1.2.2.4 (11 July 2005) +- Add inflatePrime() function for starting inflation at bit boundary +- Avoid some Visual C warnings in deflate.c +- Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit + compile +- Fix some spelling errors in comments [Betts] +- Correct inflateInit2() error return documentation in zlib.h +- Added zran.c example of compressed data random access to examples + directory, shows use of inflatePrime() +- Fix cast for assignments to strm->state in inflate.c and infback.c +- Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer] +- Move declarations of gf2 functions to right place in crc32.c [Oberhumer] +- Add cast in trees.c t avoid a warning [Oberhumer] +- Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer] +- Update make_vms.com [Zinser] +- Initialize state->write in inflateReset() since copied in inflate_fast() +- Be more strict on incomplete code sets in inflate_table() and increase + ENOUGH and MAXD -- this repairs a possible security vulnerability for + invalid inflate input. Thanks to Tavis Ormandy and Markus Oberhumer for + discovering the vulnerability and providing test cases. +- Add ia64 support to configure for HP-UX [Smith] +- Add error return to gzread() for format or i/o error [Levin] +- Use malloc.h for OS/2 [Necasek] + +Changes in 1.2.2.3 (27 May 2005) +- Replace 1U constants in inflate.c and inftrees.c for 64-bit compile +- Typecast fread() return values in gzio.c [Vollant] +- Remove trailing space in minigzip.c outmode (VC++ can't deal with it) +- Fix crc check bug in gzread() after gzungetc() [Heiner] +- Add the deflateTune() function to adjust internal compression parameters +- Add a fast gzip decompressor, gun.c, to examples (use of inflateBack) +- Remove an incorrect assertion in examples/zpipe.c +- Add C++ wrapper in infback9.h [Donais] +- Fix bug in inflateCopy() when decoding fixed codes +- Note in zlib.h how much deflateSetDictionary() actually uses +- Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used) +- Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer] +- Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer] +- Add gzdirect() function to indicate transparent reads +- Update contrib/minizip [Vollant] +- Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer] +- Add casts in crc32.c to avoid warnings [Oberhumer] +- Add contrib/masmx64 [Vollant] +- Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant] + +Changes in 1.2.2.2 (30 December 2004) +- Replace structure assignments in deflate.c and inflate.c with zmemcpy to + avoid implicit memcpy calls (portability for no-library compilation) +- Increase sprintf() buffer size in gzdopen() to allow for large numbers +- Add INFLATE_STRICT to check distances against zlib header +- Improve WinCE errno handling and comments [Chang] +- Remove comment about no gzip header processing in FAQ +- Add Z_FIXED strategy option to deflateInit2() to force fixed trees +- Add updated make_vms.com [Coghlan], update README +- Create a new "examples" directory, move gzappend.c there, add zpipe.c, + fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html. +- Add FAQ entry and comments in deflate.c on uninitialized memory access +- Add Solaris 9 make options in configure [Gilbert] +- Allow strerror() usage in gzio.c for STDC +- Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer] +- Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant] +- Use z_off_t for adler32_combine() and crc32_combine() lengths +- Make adler32() much faster for small len +- Use OS_CODE in deflate() default gzip header + +Changes in 1.2.2.1 (31 October 2004) +- Allow inflateSetDictionary() call for raw inflate +- Fix inflate header crc check bug for file names and comments +- Add deflateSetHeader() and gz_header structure for custom gzip headers +- Add inflateGetheader() to retrieve gzip headers +- Add crc32_combine() and adler32_combine() functions +- Add alloc_func, free_func, in_func, out_func to Z_PREFIX list +- Use zstreamp consistently in zlib.h (inflate_back functions) +- Remove GUNZIP condition from definition of inflate_mode in inflate.h + and in contrib/inflate86/inffast.S [Truta, Anderson] +- Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson] +- Update projects/README.projects and projects/visualc6 [Truta] +- Update win32/DLL_FAQ.txt [Truta] +- Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta] +- Deprecate Z_ASCII; use Z_TEXT instead [Truta] +- Use a new algorithm for setting strm->data_type in trees.c [Truta] +- Do not define an exit() prototype in zutil.c unless DEBUG defined +- Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta] +- Add comment in zlib.h for Z_NO_FLUSH parameter to deflate() +- Fix Darwin build version identification [Peterson] + +Changes in 1.2.2 (3 October 2004) +- Update zlib.h comments on gzip in-memory processing +- Set adler to 1 in inflateReset() to support Java test suite [Walles] +- Add contrib/dotzlib [Ravn] +- Update win32/DLL_FAQ.txt [Truta] +- Update contrib/minizip [Vollant] +- Move contrib/visual-basic.txt to old/ [Truta] +- Fix assembler builds in projects/visualc6/ [Truta] + +Changes in 1.2.1.2 (9 September 2004) +- Update INDEX file +- Fix trees.c to update strm->data_type (no one ever noticed!) +- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown] +- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE) +- Add limited multitasking protection to DYNAMIC_CRC_TABLE +- Add NO_vsnprintf for VMS in zutil.h [Mozilla] +- Don't declare strerror() under VMS [Mozilla] +- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize +- Update contrib/ada [Anisimkov] +- Update contrib/minizip [Vollant] +- Fix configure to not hardcode directories for Darwin [Peterson] +- Fix gzio.c to not return error on empty files [Brown] +- Fix indentation; update version in contrib/delphi/ZLib.pas and + contrib/pascal/zlibpas.pas [Truta] +- Update mkasm.bat in contrib/masmx86 [Truta] +- Update contrib/untgz [Truta] +- Add projects/README.projects [Truta] +- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta] +- Update win32/DLL_FAQ.txt [Truta] +- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta] +- Remove an unnecessary assignment to curr in inftrees.c [Truta] +- Add OS/2 to exe builds in configure [Poltorak] +- Remove err dummy parameter in zlib.h [Kientzle] + +Changes in 1.2.1.1 (9 January 2004) +- Update email address in README +- Several FAQ updates +- Fix a big fat bug in inftrees.c that prevented decoding valid + dynamic blocks with only literals and no distance codes -- + Thanks to "Hot Emu" for the bug report and sample file +- Add a note to puff.c on no distance codes case. + +Changes in 1.2.1 (17 November 2003) +- Remove a tab in contrib/gzappend/gzappend.c +- Update some interfaces in contrib for new zlib functions +- Update zlib version number in some contrib entries +- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta] +- Support shared libraries on Hurd and KFreeBSD [Brown] +- Fix error in NO_DIVIDE option of adler32.c + +Changes in 1.2.0.8 (4 November 2003) +- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas +- Add experimental NO_DIVIDE #define in adler32.c + - Possibly faster on some processors (let me know if it is) +- Correct Z_BLOCK to not return on first inflate call if no wrap +- Fix strm->data_type on inflate() return to correctly indicate EOB +- Add deflatePrime() function for appending in the middle of a byte +- Add contrib/gzappend for an example of appending to a stream +- Update win32/DLL_FAQ.txt [Truta] +- Delete Turbo C comment in README [Truta] +- Improve some indentation in zconf.h [Truta] +- Fix infinite loop on bad input in configure script [Church] +- Fix gzeof() for concatenated gzip files [Johnson] +- Add example to contrib/visual-basic.txt [Michael B.] +- Add -p to mkdir's in Makefile.in [vda] +- Fix configure to properly detect presence or lack of printf functions +- Add AS400 support [Monnerat] +- Add a little Cygwin support [Wilson] + +Changes in 1.2.0.7 (21 September 2003) +- Correct some debug formats in contrib/infback9 +- Cast a type in a debug statement in trees.c +- Change search and replace delimiter in configure from % to # [Beebe] +- Update contrib/untgz to 0.2 with various fixes [Truta] +- Add build support for Amiga [Nikl] +- Remove some directories in old that have been updated to 1.2 +- Add dylib building for Mac OS X in configure and Makefile.in +- Remove old distribution stuff from Makefile +- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X +- Update links in README + +Changes in 1.2.0.6 (13 September 2003) +- Minor FAQ updates +- Update contrib/minizip to 1.00 [Vollant] +- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta] +- Update POSTINC comment for 68060 [Nikl] +- Add contrib/infback9 with deflate64 decoding (unsupported) +- For MVS define NO_vsnprintf and undefine FAR [van Burik] +- Add pragma for fdopen on MVS [van Burik] + +Changes in 1.2.0.5 (8 September 2003) +- Add OF to inflateBackEnd() declaration in zlib.h +- Remember start when using gzdopen in the middle of a file +- Use internal off_t counters in gz* functions to properly handle seeks +- Perform more rigorous check for distance-too-far in inffast.c +- Add Z_BLOCK flush option to return from inflate at block boundary +- Set strm->data_type on return from inflate + - Indicate bits unused, if at block boundary, and if in last block +- Replace size_t with ptrdiff_t in crc32.c, and check for correct size +- Add condition so old NO_DEFLATE define still works for compatibility +- FAQ update regarding the Windows DLL [Truta] +- INDEX update: add qnx entry, remove aix entry [Truta] +- Install zlib.3 into mandir [Wilson] +- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta] +- Adapt the zlib interface to the new DLL convention guidelines [Truta] +- Introduce ZLIB_WINAPI macro to allow the export of functions using + the WINAPI calling convention, for Visual Basic [Vollant, Truta] +- Update msdos and win32 scripts and makefiles [Truta] +- Export symbols by name, not by ordinal, in win32/zlib.def [Truta] +- Add contrib/ada [Anisimkov] +- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta] +- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant] +- Add contrib/masm686 [Truta] +- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm + [Truta, Vollant] +- Update contrib/delphi; rename to contrib/pascal; add example [Truta] +- Remove contrib/delphi2; add a new contrib/delphi [Truta] +- Avoid inclusion of the nonstandard in contrib/iostream, + and fix some method prototypes [Truta] +- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip + [Truta] +- Avoid the use of backslash (\) in contrib/minizip [Vollant] +- Fix file time handling in contrib/untgz; update makefiles [Truta] +- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines + [Vollant] +- Remove contrib/vstudio/vc15_16 [Vollant] +- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta] +- Update README.contrib [Truta] +- Invert the assignment order of match_head and s->prev[...] in + INSERT_STRING [Truta] +- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings + [Truta] +- Compare function pointers with 0, not with NULL or Z_NULL [Truta] +- Fix prototype of syncsearch in inflate.c [Truta] +- Introduce ASMINF macro to be enabled when using an ASM implementation + of inflate_fast [Truta] +- Change NO_DEFLATE to NO_GZCOMPRESS [Truta] +- Modify test_gzio in example.c to take a single file name as a + parameter [Truta] +- Exit the example.c program if gzopen fails [Truta] +- Add type casts around strlen in example.c [Truta] +- Remove casting to sizeof in minigzip.c; give a proper type + to the variable compared with SUFFIX_LEN [Truta] +- Update definitions of STDC and STDC99 in zconf.h [Truta] +- Synchronize zconf.h with the new Windows DLL interface [Truta] +- Use SYS16BIT instead of __32BIT__ to distinguish between + 16- and 32-bit platforms [Truta] +- Use far memory allocators in small 16-bit memory models for + Turbo C [Truta] +- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in + zlibCompileFlags [Truta] +- Cygwin has vsnprintf [Wilson] +- In Windows16, OS_CODE is 0, as in MSDOS [Truta] +- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson] + +Changes in 1.2.0.4 (10 August 2003) +- Minor FAQ updates +- Be more strict when checking inflateInit2's windowBits parameter +- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well +- Add gzip wrapper option to deflateInit2 using windowBits +- Add updated QNX rule in configure and qnx directory [Bonnefoy] +- Make inflate distance-too-far checks more rigorous +- Clean up FAR usage in inflate +- Add casting to sizeof() in gzio.c and minigzip.c + +Changes in 1.2.0.3 (19 July 2003) +- Fix silly error in gzungetc() implementation [Vollant] +- Update contrib/minizip and contrib/vstudio [Vollant] +- Fix printf format in example.c +- Correct cdecl support in zconf.in.h [Anisimkov] +- Minor FAQ updates + +Changes in 1.2.0.2 (13 July 2003) +- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons +- Attempt to avoid warnings in crc32.c for pointer-int conversion +- Add AIX to configure, remove aix directory [Bakker] +- Add some casts to minigzip.c +- Improve checking after insecure sprintf() or vsprintf() calls +- Remove #elif's from crc32.c +- Change leave label to inf_leave in inflate.c and infback.c to avoid + library conflicts +- Remove inflate gzip decoding by default--only enable gzip decoding by + special request for stricter backward compatibility +- Add zlibCompileFlags() function to return compilation information +- More typecasting in deflate.c to avoid warnings +- Remove leading underscore from _Capital #defines [Truta] +- Fix configure to link shared library when testing +- Add some Windows CE target adjustments [Mai] +- Remove #define ZLIB_DLL in zconf.h [Vollant] +- Add zlib.3 [Rodgers] +- Update RFC URL in deflate.c and algorithm.txt [Mai] +- Add zlib_dll_FAQ.txt to contrib [Truta] +- Add UL to some constants [Truta] +- Update minizip and vstudio [Vollant] +- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h +- Expand use of NO_DUMMY_DECL to avoid all dummy structures +- Added iostream3 to contrib [Schwardt] +- Replace rewind() with fseek() for WinCE [Truta] +- Improve setting of zlib format compression level flags + - Report 0 for huffman and rle strategies and for level == 0 or 1 + - Report 2 only for level == 6 +- Only deal with 64K limit when necessary at compile time [Truta] +- Allow TOO_FAR check to be turned off at compile time [Truta] +- Add gzclearerr() function [Souza] +- Add gzungetc() function + +Changes in 1.2.0.1 (17 March 2003) +- Add Z_RLE strategy for run-length encoding [Truta] + - When Z_RLE requested, restrict matches to distance one + - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE +- Correct FASTEST compilation to allow level == 0 +- Clean up what gets compiled for FASTEST +- Incorporate changes to zconf.in.h [Vollant] + - Refine detection of Turbo C need for dummy returns + - Refine ZLIB_DLL compilation + - Include additional header file on VMS for off_t typedef +- Try to use _vsnprintf where it supplants vsprintf [Vollant] +- Add some casts in inffast.c +- Enchance comments in zlib.h on what happens if gzprintf() tries to + write more than 4095 bytes before compression +- Remove unused state from inflateBackEnd() +- Remove exit(0) from minigzip.c, example.c +- Get rid of all those darn tabs +- Add "check" target to Makefile.in that does the same thing as "test" +- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in +- Update contrib/inflate86 [Anderson] +- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant] +- Add msdos and win32 directories with makefiles [Truta] +- More additions and improvements to the FAQ + +Changes in 1.2.0 (9 March 2003) +- New and improved inflate code + - About 20% faster + - Does not allocate 32K window unless and until needed + - Automatically detects and decompresses gzip streams + - Raw inflate no longer needs an extra dummy byte at end + - Added inflateBack functions using a callback interface--even faster + than inflate, useful for file utilities (gzip, zip) + - Added inflateCopy() function to record state for random access on + externally generated deflate streams (e.g. in gzip files) + - More readable code (I hope) +- New and improved crc32() + - About 50% faster, thanks to suggestions from Rodney Brown +- Add deflateBound() and compressBound() functions +- Fix memory leak in deflateInit2() +- Permit setting dictionary for raw deflate (for parallel deflate) +- Fix const declaration for gzwrite() +- Check for some malloc() failures in gzio.c +- Fix bug in gzopen() on single-byte file 0x1f +- Fix bug in gzread() on concatenated file with 0x1f at end of buffer + and next buffer doesn't start with 0x8b +- Fix uncompress() to return Z_DATA_ERROR on truncated input +- Free memory at end of example.c +- Remove MAX #define in trees.c (conflicted with some libraries) +- Fix static const's in deflate.c, gzio.c, and zutil.[ch] +- Declare malloc() and free() in gzio.c if STDC not defined +- Use malloc() instead of calloc() in zutil.c if int big enough +- Define STDC for AIX +- Add aix/ with approach for compiling shared library on AIX +- Add HP-UX support for shared libraries in configure +- Add OpenUNIX support for shared libraries in configure +- Use $cc instead of gcc to build shared library +- Make prefix directory if needed when installing +- Correct Macintosh avoidance of typedef Byte in zconf.h +- Correct Turbo C memory allocation when under Linux +- Use libz.a instead of -lz in Makefile (assure use of compiled library) +- Update configure to check for snprintf or vsnprintf functions and their + return value, warn during make if using an insecure function +- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that + is lost when library is used--resolution is to build new zconf.h +- Documentation improvements (in zlib.h): + - Document raw deflate and inflate + - Update RFCs URL + - Point out that zlib and gzip formats are different + - Note that Z_BUF_ERROR is not fatal + - Document string limit for gzprintf() and possible buffer overflow + - Note requirement on avail_out when flushing + - Note permitted values of flush parameter of inflate() +- Add some FAQs (and even answers) to the FAQ +- Add contrib/inflate86/ for x86 faster inflate +- Add contrib/blast/ for PKWare Data Compression Library decompression +- Add contrib/puff/ simple inflate for deflate format description + +Changes in 1.1.4 (11 March 2002) +- ZFREE was repeated on same allocation on some error conditions. + This creates a security problem described in + http://www.zlib.org/advisory-2002-03-11.txt +- Returned incorrect error (Z_MEM_ERROR) on some invalid data +- Avoid accesses before window for invalid distances with inflate window + less than 32K. +- force windowBits > 8 to avoid a bug in the encoder for a window size + of 256 bytes. (A complete fix will be available in 1.1.5). + +Changes in 1.1.3 (9 July 1998) +- fix "an inflate input buffer bug that shows up on rare but persistent + occasions" (Mark) +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) +- fix gzseek(..., SEEK_SET) in write mode +- fix crc check after a gzeek (Frank Faubert) +- fix miniunzip when the last entry in a zip file is itself a zip file + (J Lillge) +- add contrib/asm586 and contrib/asm686 (Brian Raiter) + See http://www.muppetlabs.com/~breadbox/software/assembly.html +- add support for Delphi 3 in contrib/delphi (Bob Dellaca) +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) +- added a FAQ file + +- Support gzdopen on Mac with Metrowerks (Jason Linhart) +- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) +- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) +- avoid some warnings with Borland C (Tom Tanner) +- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) +- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) +- allow several arguments to configure (Tim Mooney, Frodo Looijaard) +- use libdir and includedir in Makefile.in (Tim Mooney) +- support shared libraries on OSF1 V4 (Tim Mooney) +- remove so_locations in "make clean" (Tim Mooney) +- fix maketree.c compilation error (Glenn, Mark) +- Python interface to zlib now in Python 1.5 (Jeremy Hylton) +- new Makefile.riscos (Rich Walker) +- initialize static descriptors in trees.c for embedded targets (Nick Smith) +- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) +- add the OS/2 files in Makefile.in too (Andrew Zabolotny) +- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) +- fix maketree.c to allow clean compilation of inffixed.h (Mark) +- fix parameter check in deflateCopy (Gunther Nikl) +- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) +- Many portability patches by Christian Spieler: + . zutil.c, zutil.h: added "const" for zmem* + . Make_vms.com: fixed some typos + . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists + . msdos/Makefile.msc: remove "default rtl link library" info from obj files + . msdos/Makefile.*: use model-dependent name for the built zlib library + . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: + new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) +- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) +- replace __far with _far for better portability (Christian Spieler, Tom Lane) +- fix test for errno.h in configure (Tim Newsham) + +Changes in 1.1.2 (19 March 98) +- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) + See http://www.winimage.com/zLibDll/unzip.html +- preinitialize the inflate tables for fixed codes, to make the code + completely thread safe (Mark) +- some simplifications and slight speed-up to the inflate code (Mark) +- fix gzeof on non-compressed files (Allan Schrum) +- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) +- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) +- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) +- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) +- do not wrap extern "C" around system includes (Tom Lane) +- mention zlib binding for TCL in README (Andreas Kupries) +- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) +- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) +- allow "configure --prefix $HOME" (Tim Mooney) +- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) +- move Makefile.sas to amiga/Makefile.sas + +Changes in 1.1.1 (27 Feb 98) +- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) +- remove block truncation heuristic which had very marginal effect for zlib + (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the + compression ratio on some files. This also allows inlining _tr_tally for + matches in deflate_slow. +- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) + +Changes in 1.1.0 (24 Feb 98) +- do not return STREAM_END prematurely in inflate (John Bowler) +- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) +- compile with -DFASTEST to get compression code optimized for speed only +- in minigzip, try mmap'ing the input file first (Miguel Albrecht) +- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain + on Sun but significant on HP) + +- add a pointer to experimental unzip library in README (Gilles Vollant) +- initialize variable gcc in configure (Chris Herborth) + +Changes in 1.0.9 (17 Feb 1998) +- added gzputs and gzgets functions +- do not clear eof flag in gzseek (Mark Diekhans) +- fix gzseek for files in transparent mode (Mark Diekhans) +- do not assume that vsprintf returns the number of bytes written (Jens Krinke) +- replace EXPORT with ZEXPORT to avoid conflict with other programs +- added compress2 in zconf.h, zlib.def, zlib.dnt +- new asm code from Gilles Vollant in contrib/asm386 +- simplify the inflate code (Mark): + . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() + . ZALLOC the length list in inflate_trees_fixed() instead of using stack + . ZALLOC the value area for huft_build() instead of using stack + . Simplify Z_FINISH check in inflate() + +- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 +- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) +- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with + the declaration of FAR (Gilles VOllant) +- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) +- read_buf buf parameter of type Bytef* instead of charf* +- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) +- do not redeclare unlink in minigzip.c for WIN32 (John Bowler) +- fix check for presence of directories in "make install" (Ian Willis) + +Changes in 1.0.8 (27 Jan 1998) +- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) +- fix gzgetc and gzputc for big endian systems (Markus Oberhumer) +- added compress2() to allow setting the compression level +- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) +- use constant arrays for the static trees in trees.c instead of computing + them at run time (thanks to Ken Raeburn for this suggestion). To create + trees.h, compile with GEN_TREES_H and run "make test". +- check return code of example in "make test" and display result +- pass minigzip command line options to file_compress +- simplifying code of inflateSync to avoid gcc 2.8 bug + +- support CC="gcc -Wall" in configure -s (QingLong) +- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) +- fix test for shared library support to avoid compiler warnings +- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) +- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) +- do not use fdopen for Metrowerks on Mac (Brad Pettit)) +- add checks for gzputc and gzputc in example.c +- avoid warnings in gzio.c and deflate.c (Andreas Kleinert) +- use const for the CRC table (Ken Raeburn) +- fixed "make uninstall" for shared libraries +- use Tracev instead of Trace in infblock.c +- in example.c use correct compressed length for test_sync +- suppress +vnocompatwarnings in configure for HPUX (not always supported) + +Changes in 1.0.7 (20 Jan 1998) +- fix gzseek which was broken in write mode +- return error for gzseek to negative absolute position +- fix configure for Linux (Chun-Chung Chen) +- increase stack space for MSC (Tim Wegner) +- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) +- define EXPORTVA for gzprintf (Gilles Vollant) +- added man page zlib.3 (Rick Rodgers) +- for contrib/untgz, fix makedir() and improve Makefile + +- check gzseek in write mode in example.c +- allocate extra buffer for seeks only if gzseek is actually called +- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) +- add inflateSyncPoint in zconf.h +- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def + +Changes in 1.0.6 (19 Jan 1998) +- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and + gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) +- Fix a deflate bug occurring only with compression level 0 (thanks to + Andy Buckler for finding this one). +- In minigzip, pass transparently also the first byte for .Z files. +- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() +- check Z_FINISH in inflate (thanks to Marc Schluper) +- Implement deflateCopy (thanks to Adam Costello) +- make static libraries by default in configure, add --shared option. +- move MSDOS or Windows specific files to directory msdos +- suppress the notion of partial flush to simplify the interface + (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) +- suppress history buffer provided by application to simplify the interface + (this feature was not implemented anyway in 1.0.4) +- next_in and avail_in must be initialized before calling inflateInit or + inflateInit2 +- add EXPORT in all exported functions (for Windows DLL) +- added Makefile.nt (thanks to Stephen Williams) +- added the unsupported "contrib" directory: + contrib/asm386/ by Gilles Vollant + 386 asm code replacing longest_match(). + contrib/iostream/ by Kevin Ruland + A C++ I/O streams interface to the zlib gz* functions + contrib/iostream2/ by Tyge Løvset + Another C++ I/O streams interface + contrib/untgz/ by "Pedro A. Aranda Guti\irrez" + A very simple tar.gz file extractor using zlib + contrib/visual-basic.txt by Carlos Rios + How to use compress(), uncompress() and the gz* functions from VB. +- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression + level) in minigzip (thanks to Tom Lane) + +- use const for rommable constants in deflate +- added test for gzseek and gztell in example.c +- add undocumented function inflateSyncPoint() (hack for Paul Mackerras) +- add undocumented function zError to convert error code to string + (for Tim Smithers) +- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code. +- Use default memcpy for Symantec MSDOS compiler. +- Add EXPORT keyword for check_func (needed for Windows DLL) +- add current directory to LD_LIBRARY_PATH for "make test" +- create also a link for libz.so.1 +- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) +- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) +- added -soname for Linux in configure (Chun-Chung Chen, +- assign numbers to the exported functions in zlib.def (for Windows DLL) +- add advice in zlib.h for best usage of deflateSetDictionary +- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) +- allow compilation with ANSI keywords only enabled for TurboC in large model +- avoid "versionString"[0] (Borland bug) +- add NEED_DUMMY_RETURN for Borland +- use variable z_verbose for tracing in debug mode (L. Peter Deutsch). +- allow compilation with CC +- defined STDC for OS/2 (David Charlap) +- limit external names to 8 chars for MVS (Thomas Lund) +- in minigzip.c, use static buffers only for 16-bit systems +- fix suffix check for "minigzip -d foo.gz" +- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) +- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) +- added makelcc.bat for lcc-win32 (Tom St Denis) +- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) +- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion. +- check for unistd.h in configure (for off_t) +- remove useless check parameter in inflate_blocks_free +- avoid useless assignment of s->check to itself in inflate_blocks_new +- do not flush twice in gzclose (thanks to Ken Raeburn) +- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h +- use NO_ERRNO_H instead of enumeration of operating systems with errno.h +- work around buggy fclose on pipes for HP/UX +- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) +- fix configure if CC is already equal to gcc + +Changes in 1.0.5 (3 Jan 98) +- Fix inflate to terminate gracefully when fed corrupted or invalid data +- Use const for rommable constants in inflate +- Eliminate memory leaks on error conditions in inflate +- Removed some vestigial code in inflate +- Update web address in README + +Changes in 1.0.4 (24 Jul 96) +- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF + bit, so the decompressor could decompress all the correct data but went + on to attempt decompressing extra garbage data. This affected minigzip too. +- zlibVersion and gzerror return const char* (needed for DLL) +- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) +- use z_error only for DEBUG (avoid problem with DLLs) + +Changes in 1.0.3 (2 Jul 96) +- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS + small and medium models; this makes the library incompatible with previous + versions for these models. (No effect in large model or on other systems.) +- return OK instead of BUF_ERROR if previous deflate call returned with + avail_out as zero but there is nothing to do +- added memcmp for non STDC compilers +- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) +- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) +- better check for 16-bit mode MSC (avoids problem with Symantec) + +Changes in 1.0.2 (23 May 96) +- added Windows DLL support +- added a function zlibVersion (for the DLL support) +- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) +- Bytef is define's instead of typedef'd only for Borland C +- avoid reading uninitialized memory in example.c +- mention in README that the zlib format is now RFC1950 +- updated Makefile.dj2 +- added algorithm.doc + +Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] +- fix array overlay in deflate.c which sometimes caused bad compressed data +- fix inflate bug with empty stored block +- fix MSDOS medium model which was broken in 0.99 +- fix deflateParams() which could generated bad compressed data. +- Bytef is define'd instead of typedef'ed (work around Borland bug) +- added an INDEX file +- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), + Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) +- speed up adler32 for modern machines without auto-increment +- added -ansi for IRIX in configure +- static_init_done in trees.c is an int +- define unlink as delete for VMS +- fix configure for QNX +- add configure branch for SCO and HPUX +- avoid many warnings (unused variables, dead assignments, etc...) +- no fdopen for BeOS +- fix the Watcom fix for 32 bit mode (define FAR as empty) +- removed redefinition of Byte for MKWERKS +- work around an MWKERKS bug (incorrect merge of all .h files) + +Changes in 0.99 (27 Jan 96) +- allow preset dictionary shared between compressor and decompressor +- allow compression level 0 (no compression) +- add deflateParams in zlib.h: allow dynamic change of compression level + and compression strategy. +- test large buffers and deflateParams in example.c +- add optional "configure" to build zlib as a shared library +- suppress Makefile.qnx, use configure instead +- fixed deflate for 64-bit systems (detected on Cray) +- fixed inflate_blocks for 64-bit systems (detected on Alpha) +- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) +- always return Z_BUF_ERROR when deflate() has nothing to do +- deflateInit and inflateInit are now macros to allow version checking +- prefix all global functions and types with z_ with -DZ_PREFIX +- make falloc completely reentrant (inftrees.c) +- fixed very unlikely race condition in ct_static_init +- free in reverse order of allocation to help memory manager +- use zlib-1.0/* instead of zlib/* inside the tar.gz +- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith + -Wconversion -Wstrict-prototypes -Wmissing-prototypes" +- allow gzread on concatenated .gz files +- deflateEnd now returns Z_DATA_ERROR if it was premature +- deflate is finally (?) fully deterministic (no matches beyond end of input) +- Document Z_SYNC_FLUSH +- add uninstall in Makefile +- Check for __cpluplus in zlib.h +- Better test in ct_align for partial flush +- avoid harmless warnings for Borland C++ +- initialize hash_head in deflate.c +- avoid warning on fdopen (gzio.c) for HP cc -Aa +- include stdlib.h for STDC compilers +- include errno.h for Cray +- ignore error if ranlib doesn't exist +- call ranlib twice for NeXTSTEP +- use exec_prefix instead of prefix for libz.a +- renamed ct_* as _tr_* to avoid conflict with applications +- clear z->msg in inflateInit2 before any error return +- initialize opaque in example.c, gzio.c, deflate.c and inflate.c +- fixed typo in zconf.h (_GNUC__ => __GNUC__) +- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) +- fix typo in Make_vms.com (f$trnlnm -> f$getsyi) +- in fcalloc, normalize pointer if size > 65520 bytes +- don't use special fcalloc for 32 bit Borland C++ +- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc... +- use Z_BINARY instead of BINARY +- document that gzclose after gzdopen will close the file +- allow "a" as mode in gzopen. +- fix error checking in gzread +- allow skipping .gz extra-field on pipes +- added reference to Perl interface in README +- put the crc table in FAR data (I dislike more and more the medium model :) +- added get_crc_table +- added a dimension to all arrays (Borland C can't count). +- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast +- guard against multiple inclusion of *.h (for precompiled header on Mac) +- Watcom C pretends to be Microsoft C small model even in 32 bit mode. +- don't use unsized arrays to avoid silly warnings by Visual C++: + warning C4746: 'inflate_mask' : unsized array treated as '__far' + (what's wrong with far data in far model?). +- define enum out of inflate_blocks_state to allow compilation with C++ + +Changes in 0.95 (16 Aug 95) +- fix MSDOS small and medium model (now easier to adapt to any compiler) +- inlined send_bits +- fix the final (:-) bug for deflate with flush (output was correct but + not completely flushed in rare occasions). +- default window size is same for compression and decompression + (it's now sufficient to set MAX_WBITS in zconf.h). +- voidp -> voidpf and voidnp -> voidp (for consistency with other + typedefs and because voidnp was not near in large model). + +Changes in 0.94 (13 Aug 95) +- support MSDOS medium model +- fix deflate with flush (could sometimes generate bad output) +- fix deflateReset (zlib header was incorrectly suppressed) +- added support for VMS +- allow a compression level in gzopen() +- gzflush now calls fflush +- For deflate with flush, flush even if no more input is provided. +- rename libgz.a as libz.a +- avoid complex expression in infcodes.c triggering Turbo C bug +- work around a problem with gcc on Alpha (in INSERT_STRING) +- don't use inline functions (problem with some gcc versions) +- allow renaming of Byte, uInt, etc... with #define. +- avoid warning about (unused) pointer before start of array in deflate.c +- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c +- avoid reserved word 'new' in trees.c + +Changes in 0.93 (25 June 95) +- temporarily disable inline functions +- make deflate deterministic +- give enough lookahead for PARTIAL_FLUSH +- Set binary mode for stdin/stdout in minigzip.c for OS/2 +- don't even use signed char in inflate (not portable enough) +- fix inflate memory leak for segmented architectures + +Changes in 0.92 (3 May 95) +- don't assume that char is signed (problem on SGI) +- Clear bit buffer when starting a stored block +- no memcpy on Pyramid +- suppressed inftest.c +- optimized fill_window, put longest_match inline for gcc +- optimized inflate on stored blocks. +- untabify all sources to simplify patches + +Changes in 0.91 (2 May 95) +- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h +- Document the memory requirements in zconf.h +- added "make install" +- fix sync search logic in inflateSync +- deflate(Z_FULL_FLUSH) now works even if output buffer too short +- after inflateSync, don't scare people with just "lo world" +- added support for DJGPP + +Changes in 0.9 (1 May 95) +- don't assume that zalloc clears the allocated memory (the TurboC bug + was Mark's bug after all :) +- let again gzread copy uncompressed data unchanged (was working in 0.71) +- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented +- added a test of inflateSync in example.c +- moved MAX_WBITS to zconf.h because users might want to change that. +- document explicitly that zalloc(64K) on MSDOS must return a normalized + pointer (zero offset) +- added Makefiles for Microsoft C, Turbo C, Borland C++ +- faster crc32() + +Changes in 0.8 (29 April 95) +- added fast inflate (inffast.c) +- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this + is incompatible with previous versions of zlib which returned Z_OK. +- work around a TurboC compiler bug (bad code for b << 0, see infutil.h) + (actually that was not a compiler bug, see 0.81 above) +- gzread no longer reads one extra byte in certain cases +- In gzio destroy(), don't reference a freed structure +- avoid many warnings for MSDOS +- avoid the ERROR symbol which is used by MS Windows + +Changes in 0.71 (14 April 95) +- Fixed more MSDOS compilation problems :( There is still a bug with + TurboC large model. + +Changes in 0.7 (14 April 95) +- Added full inflate support. +- Simplified the crc32() interface. The pre- and post-conditioning + (one's complement) is now done inside crc32(). WARNING: this is + incompatible with previous versions; see zlib.h for the new usage. + +Changes in 0.61 (12 April 95) +- workaround for a bug in TurboC. example and minigzip now work on MSDOS. + +Changes in 0.6 (11 April 95) +- added minigzip.c +- added gzdopen to reopen a file descriptor as gzFile +- added transparent reading of non-gziped files in gzread. +- fixed bug in gzread (don't read crc as data) +- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose). +- don't allocate big arrays in the stack (for MSDOS) +- fix some MSDOS compilation problems + +Changes in 0.5: +- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but + not yet Z_FULL_FLUSH. +- support decompression but only in a single step (forced Z_FINISH) +- added opaque object for zalloc and zfree. +- added deflateReset and inflateReset +- added a variable zlib_version for consistency checking. +- renamed the 'filter' parameter of deflateInit2 as 'strategy'. + Added Z_FILTERED and Z_HUFFMAN_ONLY constants. + +Changes in 0.4: +- avoid "zip" everywhere, use zlib instead of ziplib. +- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush + if compression method == 8. +- added adler32 and crc32 +- renamed deflateOptions as deflateInit2, call one or the other but not both +- added the method parameter for deflateInit2. +- added inflateInit2 +- simplied considerably deflateInit and inflateInit by not supporting + user-provided history buffer. This is supported only in deflateInit2 + and inflateInit2. + +Changes in 0.3: +- prefix all macro names with Z_ +- use Z_FINISH instead of deflateEnd to finish compression. +- added Z_HUFFMAN_ONLY +- added gzerror() diff --git a/3rdparty/zlib/Makefile.am b/3rdparty/zlib/Makefile.am new file mode 100644 index 0000000000..ff26496e22 --- /dev/null +++ b/3rdparty/zlib/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = libpcsx2zlib.a + +libpcsx2zlib_a_SOURCES = \ +adler32.c crc32.c deflate.h inffast.c inflate.c inftrees.h trees.h zlib.h \ +crc32.h gzio.c inffast.h inflate.h uncompr.c zutil.c \ +compress.c deflate.c infback.c inffixed.h inftrees.c trees.c zconf.h zutil.h diff --git a/3rdparty/zlib/README b/3rdparty/zlib/README new file mode 100644 index 0000000000..758cc50020 --- /dev/null +++ b/3rdparty/zlib/README @@ -0,0 +1,125 @@ +ZLIB DATA COMPRESSION LIBRARY + +zlib 1.2.3 is a general purpose data compression library. All the code is +thread safe. The data format used by the zlib library is described by RFCs +(Request for Comments) 1950 to 1952 in the files +http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) +and rfc1952.txt (gzip format). These documents are also available in other +formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example +of the library is given in the file example.c which also tests that the library +is working correctly. Another example is given in the file minigzip.c. The +compression library itself is composed of all source files except example.c and +minigzip.c. + +To compile all files and run the test program, follow the instructions given at +the top of Makefile. In short "make test; make install" should work for most +machines. For Unix: "./configure; make test; make install". For MSDOS, use one +of the special makefiles such as Makefile.msc. For VMS, use make_vms.com. + +Questions about zlib should be sent to , or to Gilles Vollant + for the Windows DLL version. The zlib home page is +http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem, +please check this site to verify that you have the latest version of zlib; +otherwise get the latest version and check whether the problem still exists or +not. + +PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking +for help. + +Mark Nelson wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://dogma.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.2.3 are documented in the file ChangeLog. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit +http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html +See the zlib home page http://www.zlib.org for details. + +A Perl interface to zlib written by Paul Marquess is in the +CPAN (Comprehensive Perl Archive Network) sites +http://www.cpan.org/modules/by-module/Compress/ + +A Python interface to zlib written by A.M. Kuchling is +available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries is +availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html + +An experimental package to read and write files in .zip format, written on top +of zlib by Gilles Vollant , is available in the +contrib/minizip directory of zlib. + + +Notes for some targets: + +- For Windows DLL versions, please see win32/DLL_FAQ.txt + +- For 64-bit Irix, deflate.c must be compiled without any optimization. With + -O, one libpng test fails. The test works in 32 bit mode (with the -n32 + compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works + when compiled with cc. + +- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is + necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with + other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For PalmOs, see http://palmzlib.sourceforge.net/ + +- When building a shared, i.e. dynamic library on Mac OS X, the library must be + installed before testing (do "make install" before "make test"), since the + library location is specified in the library. + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-2004 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. Please +read the FAQ for more information on the distribution of modified source +versions. diff --git a/3rdparty/zlib/adler32.c b/3rdparty/zlib/adler32.c new file mode 100644 index 0000000000..f201d6701e --- /dev/null +++ b/3rdparty/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/3rdparty/zlib/compress.c b/3rdparty/zlib/compress.c new file mode 100644 index 0000000000..d37e84f5e3 --- /dev/null +++ b/3rdparty/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/3rdparty/zlib/crc32.c b/3rdparty/zlib/crc32.c new file mode 100644 index 0000000000..32814c20c8 --- /dev/null +++ b/3rdparty/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/3rdparty/zlib/crc32.h b/3rdparty/zlib/crc32.h new file mode 100644 index 0000000000..5de49bc978 --- /dev/null +++ b/3rdparty/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/3rdparty/zlib/deflate.c b/3rdparty/zlib/deflate.c new file mode 100644 index 0000000000..529f716b7a --- /dev/null +++ b/3rdparty/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/3rdparty/zlib/deflate.h b/3rdparty/zlib/deflate.h new file mode 100644 index 0000000000..222c53e043 --- /dev/null +++ b/3rdparty/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/3rdparty/zlib/gzio.c b/3rdparty/zlib/gzio.c new file mode 100644 index 0000000000..2de9a36441 --- /dev/null +++ b/3rdparty/zlib/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id$ */ + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_BEST_SPEED; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/3rdparty/zlib/infback.c b/3rdparty/zlib/infback.c new file mode 100644 index 0000000000..1e03e1bab0 --- /dev/null +++ b/3rdparty/zlib/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/3rdparty/zlib/inffast.c b/3rdparty/zlib/inffast.c new file mode 100644 index 0000000000..fa31cad905 --- /dev/null +++ b/3rdparty/zlib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/3rdparty/zlib/inffast.h b/3rdparty/zlib/inffast.h new file mode 100644 index 0000000000..614fa7877d --- /dev/null +++ b/3rdparty/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/3rdparty/zlib/inffixed.h b/3rdparty/zlib/inffixed.h new file mode 100644 index 0000000000..423d5c5b50 --- /dev/null +++ b/3rdparty/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/3rdparty/zlib/inflate.c b/3rdparty/zlib/inflate.c new file mode 100644 index 0000000000..33ea902928 --- /dev/null +++ b/3rdparty/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/3rdparty/zlib/inflate.h b/3rdparty/zlib/inflate.h new file mode 100644 index 0000000000..fbbc871432 --- /dev/null +++ b/3rdparty/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/3rdparty/zlib/inftrees.c b/3rdparty/zlib/inftrees.c new file mode 100644 index 0000000000..38ded81c36 --- /dev/null +++ b/3rdparty/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/3rdparty/zlib/inftrees.h b/3rdparty/zlib/inftrees.h new file mode 100644 index 0000000000..dc0fd567ea --- /dev/null +++ b/3rdparty/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/3rdparty/zlib/trees.c b/3rdparty/zlib/trees.c new file mode 100644 index 0000000000..7a04802862 --- /dev/null +++ b/3rdparty/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/3rdparty/zlib/trees.h b/3rdparty/zlib/trees.h new file mode 100644 index 0000000000..1ca868b848 --- /dev/null +++ b/3rdparty/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/3rdparty/zlib/uncompr.c b/3rdparty/zlib/uncompr.c new file mode 100644 index 0000000000..ad6db0a67c --- /dev/null +++ b/3rdparty/zlib/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/3rdparty/zlib/zconf.h b/3rdparty/zlib/zconf.h new file mode 100644 index 0000000000..e3b0c962e3 --- /dev/null +++ b/3rdparty/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/3rdparty/zlib/zlib.h b/3rdparty/zlib/zlib.h new file mode 100644 index 0000000000..62d0e4675b --- /dev/null +++ b/3rdparty/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/3rdparty/zlib/zutil.c b/3rdparty/zlib/zutil.c new file mode 100644 index 0000000000..0f4bd7871d --- /dev/null +++ b/3rdparty/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/3rdparty/zlib/zutil.h b/3rdparty/zlib/zutil.h new file mode 100644 index 0000000000..0e3a9a0638 --- /dev/null +++ b/3rdparty/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 9 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/bin/.pixmaps/Thumbs.db b/bin/.pixmaps/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..6f6a4c94406be25fbbe4732925c6a8a87ad487f9 GIT binary patch literal 5632 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 MLA$@@zaN2r0)Wxd2mk;8 literal 0 HcmV?d00001 diff --git a/bin/.pixmaps/pcsxAbout.bmp b/bin/.pixmaps/pcsxAbout.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a5ceb9aa37d5d16e08a7097151f46ae5fb168e1d GIT binary patch literal 251190 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 z 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 literal 0 HcmV?d00001 diff --git a/bin/Langs/de_DE/LC_MESSAGES/pcsx2.mo b/bin/Langs/de_DE/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..9852dd0c6092222ad23ce5a03e6604eecacff5fe GIT binary patch literal 11840 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 SBc0hu{TBKXCbd07-v0w{)hjpv literal 0 HcmV?d00001 diff --git a/bin/Langs/du_DU/LC_MESSAGES/pcsx2.mo b/bin/Langs/du_DU/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..47541b89cb8c7c0dd7c3a47e001e1da7a1e12a94 GIT binary patch literal 11583 zcmb7}3veVydB+F)z_#oVFb|uDO&@T&JABfe zGt11ZbSDS|Co!1B0Ror+CWL^Cyb5rol5&X4aT4bNsS2rtkbD&KwPfyR+->bj=^!JzUc~8LgAoNnG^vodmGT0pAhwJM<6a<%p-vBQH z&wF-(mw+1bLhyy)Uho3&df$H(d=~8!AX5fQpx(dDx2wLr?%OGNA{V#zRgZ}{P``-k`&$mJG`90r%-VYb^TnOsSd{4%KTosTfY$1tdMkAdRz)u7gO3pfET_~&l} zCC7L8_WQttv_AyGO7LlqUjW7TH$ctz@8FBTJv4+@f|r5UdtCJW>mKj+_y8z-`dv`t zKMTGZ{2C}bdfD@e^x-`hZz7Q7bJcVh6x;9Eh-^&_D8{|)du@V7zf;|bsYAE4g< z9}txX&wf5O23`T`{rf>Z|DbPw9MpWj14@36`u@j3@$(m;=Km}3DELKCa(T%MiuYat zj?+E_O0Rc$Y=N@J*MrO8TR_e8B~W~P6}%SwCU_qB3WR}3f?S;^(`*e+cDj+zUXK5*z?E&&?iZJf8Hp>hVrc@?P`prpFxA_ul|&zI%NC z1K>+&e-xCw9|Prg-v+M+UyiZpyQ`q|b|)x4YoPeix$-@%>qkp93YIFZkzQ_We(Q^3#6QD6 z0xC}aBZ#X6&osq4UJ8oOy}o@lI8Xa}P~X1?6o2pZ_yKT)_Jg42`5O?E37!BYzdaa( z)_XCi^42D_%Nt(kAbrD&w$eJ_dK4@VA;(jpyYf7_(t$mpzQ9~LH>eI`1a>O$>ocn z*85MO=KmLP5BMEW^Zz%fc(;ee==trS#@E0f15bgn-#_*Ed5>QOPt*S$P~S;dl-98g z-T>b1+mC<;X+Q4qUqQ`xIl^%92b5lJ25$pz18)T%0vQ@ygs`6p9s$MQb)eRN6s&;r zpuYcMQ1s|tIY1*d6m zf?EHl!1KY!Kujxm9F%;%1}=fmzN&a0g4fWt;3vWNgR-X+7_sp6pyKdvf_yUgA~*r= z!C9UGwjo`zQ(2yFtuD8&Z4t3pGfRmYSulH@OU4EzQ=b-mPThN=K zH$bn2J_yZ0AA+=&pM-Qtu0QJT3f}!~Fv|^Y;;vuhH-+8-X}!M$T>^El^SGIUP=D}x zNV>fXx)w6fI7EncGMWJu`{f_HEObBgZs@0=B4-2Ux6-wbZtYj zyPKgOhjw4`g&aBs$?w*npMeIi&4EVC-)k@JUhCXE-#1o4+06zNLYPVLKIjDWI!M<^ z=%dgrYH-PKbjc2X6`FvmknA&t?t|oW+8;gwT?ZY7Fri=pilD2YB}l%bs|>vcnujie zbk(5`xVs?8z@LQ_Gvt2>)V1PRo|`enWDEJaO&yDiVP0Fe zj9W~Tn$0q^m_|toXTk82Z6&!ij*DXIrA|zDCpOci!==?yFkA^YVW5(9VmPSeVa`l$ z`NglkFqwwcs1{5`Nfu0XnvF1x!Z?^7u5Z@Uq{U4V=V{Vx+BBol?gZ1!9K}&?tfY%b z>%sKmim9|Mg4Errjq6DoOo#FQ+zH?8-f2(q2yX9?hSY3R5GT0_o6V$Vj#W%5$yh2^Etk=I9VEd z8e_)KHKww-a?UX;m8mCB)9x5=@TFn9c|)Lu?^1fn^>@N1BU5YUle#qrO4A3;h?(!y z*3DX&&bxbc0K89%s+6hfQ7S~7C|9B zg_-0w!o4IwDjm(71Q13-xqTV!7Pf^%E;q$fB5|>OP%h$Q95AmoKHO~Yy zbQ5M78a0~+rz9pCme2}gW{hHM!fw!ZaY9VEybZDlXnR=A9o8cLHYlPuE>kabnIY$ zj%e_kmWv;X3^$5FE5(K(b$FyZT^c2t9MV69U*#%0JIb}$gbVkFs zl5|5pqqPu7-E?PpW%ux?Jz)qYvd?-VZEnQLmf6xrFc+m`cBpA3a+1I*$<{`&+(|dI z!j<95RKWJMOFt{cUd`LKRkFYazi#<9%SS$Sm;q{YCUwFWIT`lst`yDzK?QPDD!W=+I=E zM{8jXF}H}fZpUM<%s49)@3;Vp4;AUoDBn4P+~LO-i>A@dV3f`B*mT&I88>$Jabv}} zyJ}>z%2noVZ9QDIWZXrT;`U8>5W8t_(8<;L@Auqo*vXp7un6Vd>k7MR5nXcjQ#NH{ zSel&T`N(X2gwH5HpD>G)(3 zuAUgXZsf?-M~*OXWXWzun#GU1y*#-zGQTiVnl#53Cgx|ax?|yFd1#KEa%4FTZaJZna$2zIdOj+98%_8RO@aiFgy?F=`EjbKU}9VfZfsBV+0waq%I0B6nRFl+{M z;w4x`oK4$ka61=oc~ zff{vCHF4Bc?I>s2bi-QGz=3ls$DIDLb>~|y2PJQ8VQ2=f)9BQujY*Fud+#*cZpn zxttaHZRM%P^mNlZ&Gtu1*3N0fSZ`qWFf_p4j)8>%ky5*JdVjW^yy=8=mLYx3*9Kj$ zIMH@bx;@kJ2i z!*XZelNNVl=cE9%66KqTvy92L4Cj!Sc2B|n5S+1L#T=&f9dp2+fK@}Wq)G?7Ghl9< z7TCQMC2%zyd50VKpKvs`&8!FlJKWgCn_hJD#;ov${STPe*@ig_D@1>`VKTBU8QHmq z4%pe*W~_F@z}VB6vp0DBX$;7pL0K1K=){motj3PBst-$ zl`v4p#qH>n3t>I;)Ehl3*~xTQuP)|lm+3F0yJoi)kxXIi8@bt+v4`a<0`wLw2Wn&^ z81Y7Tm@f*jJ)W?!qBTyk+G!T{T?kYwL|pCS_}>bRrt#&1gp|`&0c3Lhn=)G-3+)+$ zKmRu=2XH_svO^{3ZtEhWTX12WFH{|``ap3MCf55+MRC7s*F&~V3Z6;;V#A4?)UkJf z){G6yoNUYHK)KZ+@nQRGCM2(J9=!Y@`>?W*ol4v(YUdYD&LyHoHkcJ0BahvXV5t+> z&K7q`(%^?ufi};{CS?g%8E9w?*KGKbfZ4#9HrTV^?2xI`a5iwtYV`8lR8h9I{UiW8 zuBM#`ZFH*#;@Q;?GFLr7vm>TtsTL_SM+B-Qu$^i`e;#+n`y?@PYK~w{s#O-^wo^o0 zNNIFuT4Tm`wa0sH$_Fj%BADl+n@OiGHBf8FLs%w~9;+;?Tv=`vDnL3t4UDR0AnCYF zsNUHX{j+X4!tDjrBwSKd$cr$HcPR&=BoQHh)*78IiV-Agt~NnY!&N1EV(_-I&Dlfi z>}?=haE_Z(e2Rm~jSbSW`q_+Psuu;FWt_!Rdn3o)$l|<3En{wSd0}ZNm{M-5FjQA2 zIL(296HwrA=dQAySb3wXZfq-asv|^9#+!x;OgVd_1g@P#B6al*c^x5Wlry(Gu@ckb zq)}}9oYb9b>faVW(8Sa{evo;`3xy$K%7P752#SC0$U+N+Q2j{QDvKJ3wF7r5hLi^L zfdQtOi)Ffc$Su}+Rop51x>D&21Wy>;-*MWm^^p248O3*bK@%q;r`LHwNvWa|?WM%F z_*7PuNt7o0ItiSfMfYs~WZ{K{kR@=!aLM@{PL;)Wq6NrmmoY$ClvIl(`-G0jE{0Ph zQ3VC*b%kzYcc3Y1DNp@)(Isz~(;}b6tj4-GI+YCK{n@j>VvL?qJdKTCWkz9ZyUzdSV$Ksu$)D+ zR7RXyi92{*Hw)!FqWU>%Oy#Wo0?d z^tTnaC0k0QiXb>d+TC0<-t8T-?@>b?eT{#&QWak#g+UM-omd&lc0|cgfu5QsN^qGx zAGY1Sd7E&psTG=)gDdMAsPybuYq4M0UZJO-cK0eiPcgnI`1Hs3iaPzu&koHG=1lu` m*MQD8vP5+DNuENo>})+r*6J=mvu;-tG1 literal 0 HcmV?d00001 diff --git a/bin/Langs/el_EL/LC_MESSAGES/pcsx2.mo b/bin/Langs/el_EL/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..1de54a1232dfd4db9b180817500212cd9ebd5c47 GIT binary patch literal 11225 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 literal 0 HcmV?d00001 diff --git a/bin/Langs/es_ES/LC_MESSAGES/pcsx2.mo b/bin/Langs/es_ES/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..d88a28cb79dca85584f1539e07df164d2ad7b303 GIT binary patch literal 10632 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 literal 0 HcmV?d00001 diff --git a/bin/Langs/fr_FR/LC_MESSAGES/pcsx2.mo b/bin/Langs/fr_FR/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..113d30e1a514e7ea937adf9f5b9f6f020a0559f9 GIT binary patch literal 6176 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 ODR#E&oDUj0q5cQ(6W*Z! literal 0 HcmV?d00001 diff --git a/bin/Langs/hb_HB/LC_MESSAGES/pcsx2.mo b/bin/Langs/hb_HB/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..58bcfc6d0f7031218bef1ddbb690801405047be6 GIT binary patch literal 6302 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 literal 0 HcmV?d00001 diff --git a/bin/Langs/it_IT/LC_MESSAGES/pcsx2.mo b/bin/Langs/it_IT/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..4f929ba5646ddb889baaafe00900cca1e0c6ca81 GIT binary patch literal 5411 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 literal 0 HcmV?d00001 diff --git a/bin/Langs/ja_JA/LC_MESSAGES/pcsx2.mo b/bin/Langs/ja_JA/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..a8a4c5e87e8570439585ce4d8b22bdb6e3ceb492 GIT binary patch literal 15213 zcmbW634C2uoyQML9n1(QgMg^x1#Q|wlcp()gw1Si(>Bc`8>JxdlH4SZzP$J5EnQfI z-Ya64bT3uM5$%i$g~BLeML@s-1txJpaCC-MM=gwuK?esI)cO9-Jui8A4TGKc=|A6l z{`c(XfBxrxPoFvF@STR=v_p(}A8b0pn2++De5bU=Joj#6PJqwDx5L+A1w8yXWBvjj z4V69~z73uXr@>iJ`RBOwSuTCPOSeFpGHvj1xXiu(sCz#Ek3v7>^jAaW{{(y&-0b)T zsPcD0_2(Naqfe=}75Uxn)D{ZQ@yGyEWY!tqsj66yE7C*pZf?=N@!xZ}<6 zWb`9Y`F{i758rj7F+7?%Q0W%Pkj(%*3|0P{d^E= zT#rHJ`#w}VKZBU0c>}7xW8P~FOKVPqnwPmy<&d+=EF&qMX+_m1ymGiltXI5t4NU+$Q9+yt3&b2C)_2jQ9Ur_jzP ze>Cnn@KAUf)Yd!`s=kHpeGi;TdH|}PE$|F@Gt@jh3svp~sQG^#YW&mK466S~Xy?!I zM8}h%%AEq0?`)_42vmO-Le1A=r|*QycZp*h%5M9i-Oo^Vcr{de>!8YSb?MJQ+52v& z{{PVN2sZCoq|b-SzXqzEz@=}1vac;rNO7 z)z3e`8d%{)^LRGYc$%T?ehEAkrl9QZGf?yY6{vO}h3fAQq3q{h9bba#zbetZOoMuV zEL6D}Q1&_p$_`J5>hEHBF1#3OoS%l6iunhqdLD#YuOGmA_&k(-e~`_pew_|gt`#c( zGN|?jp!%7H8sBQD_OFHW;Pp`byBmHOjzG=NG@OI#I|eHM`=Rt7gonWzsQONWYUgaI zdK#hH>xP<#%b@D%gX(7*s^2$2jr-G3^Kc7PzWbr-c?@d*JPrQ}{vN7a#fPGGd>>SQ zW4W`{k;pSy?dd`Jr33XU%(NPi#BID|YiB7bz+%*g(h zLY2SFvClC9k3gStTm@x^*TJ{J%~0iTbm_l!>D};9^k0U`zsJ458*05Dfbw&XJN;8o ze);E6`{7lndfz@PYX2yx_KtVyzlN%J29#Z#;n)Fjl_mqVzc)e6+ZSE>K9_z1YMy@W z_!`uFz3r5!+)?ld($k^*O*NFCImhW2Le+l(R6hqDhv3^uXQAr58mgZkhgzrYP~+SM z>)>s$0{#lBz27?ikJBHHv(mVahHB?H$M-sVQ0+{IYNraS{d1t~ZNB3gSVQ_Y$EThC zD2zz?>K*fNF6l4BYWP!#shA_GqJ38baV2K4OQ)d5wGOJ?VW@HJg6i+*p~m|~sBwJL zy?@a0F{pe$g7OPLf!g0MJHCTXsJi(RsKcCH=O>cInj7$I4*)7`Gy=fIsF}uk2}5sapmR&CSCdKp#1Q~ zQ1iC~s{i|-_Q}0ae(Qes{&A>&zXH#Oe}w9H9g8Es(E;`T2KY|61-=7*!Rc>@wtlGj zxYy|)avX(f@5fN<@`BTkL6v(A9tHpC^haVO%6A-8f8P&PuLqBTGoji)8>(Lm9WQWP z4jF>!g_?&Qj-wD0GjC&1YIhz~J0FJf=jS_p7yL`o%c1s5ztjH>RC_BOuZ6OoPeRSh z-#dN{YCI1^_45gMK78K2Kk2ln{aT1CFlR#56NjqjYWOZV4AuTkQ1-qD9t*$b_$bsk zo`Gub=TQFXw^02&`t*n=K&|IVQ2FYh*0}|$f7inXxDBe^C!zZBE7%5Kb?Jp?ME$%7 z9)ms)Ro@Lz?cE4%e_eVH)coEFtKm1H%Dn(}KD+_d{#hI{DmNdho(tfSa5>cY2AzJ$ zaV0z!{k2f@vIDA}&q3AqMaQo~jpKgDAHd^CKMz&!t1kTqsChdUV|h263zc2~We0sw z^=6>TUjvnI2h@D+al99*z3)Md<5{SB_B;JFf>h~`bNm3*xMw=fb3D(n#j(@zGRG?% z3y#-0Zh&?jpw{IksBzxz^mjtd)4fph_D`@9j=~SY|AIB}JscLQXA#uC>wxmR7sJ1T zmpOe79#8rzSP!>D`I{%9^1lkz{u@y1c?d?QdXIKI0WKze5v=&Sn_t zLe?UmLcWKrKxQKW^52N=DSW85&O_EAhoftT<-ey;3_9h<;I|Q$$@~nFZE0QBBhMfg zAZ!8idE|0Lzl#yg-7X}C+=6J2>-T*`aX6nRcK^(US`XRM&k_CpF@pX3IDhX!o+#1j z@t4RikT`M$@-5^AM897lS_|1<@%IW3zeBD?3djKRDDoJx3HdlO9XW(KxeO)|{l0qE1Vvk?6jA)iL}DZ%eM5o|p2 zTb^@BkJH@`Uq&uP>YeUs#|`jmWIM9N={(0b;KRt3$lH-GAjRL`@^C70L{R~|ktdNE zPIokHb(O;w1Q= z$b94`gDcX;suzKER z7x@q5*GLC44S62=S1< zJkCA;#_>+a55TR+QY3|Z4fz}79mt;${l1F4jx0wGLlz<*K^7qT?L=X{{fK^lkIX?{Q^NgP;8&1dLe@8xO@2=S&HO}?#DZ)*GnnWl=R!Z3F_lZ=sewX#fHzC1UVjG$XhaXIOl7M- z)Em$Ck-IgK>ot{a89(JUFPZN}m9^yu{jBXlES~Qj^vM^?rhENd&WmLe>1-mu##FZZ zL+QNl*g=_4<8NR6F@aNr^XYY4VGlsXEVVOlQ`3Rp*&=s-nkps`S{En$zSb{k(7L zmn^Z)G)~&zj!)zP_v`)id?UWK_sZta(EsoiqC5v3RDjvTtQy zHa)~cI+f3+lSx0DBatbX#-{G3L@JT@eAyF5)MpywsTp}|S>BB4xf!#)RoOH%9qXuV z_Eu6bkxqFtr?2)-I@g=NdX}>H`bpE+myOygHkI__S+5~RJIQp;H!?!|CoxdS#`9D% zo{C;d?`ve_oakdZQxjF2E+qTBrRhAWcpp~M-Q>lR1&qgb(prDxn`ZI10qqvNGy zR+)n5+>Rb)giV$H%Cn3XrOe)%;`z82{Y>PSBER5gNvcR97w<{>Uc29$9?H-$^iI{? zmGc;eRYx{8)f?Y54aK8A6oxW(uXyKrC-;%4&acj&Y?`F*P51exxpKANTVN;hkjR_n zX3zG;Yqc+%RY}eiot@pKBxafBWWTMyd8m+#GpVLIWxLE2WjsfttLX@^9#|sw*0;@5}qE^F8U+=iA(NlT6MgebU2$Y3pFaLvc1)GLiDVc=7D^ z`q{(UvvebqO%G(_Lv<9(`jr>*M^CxD_L=jSW_~(56wiCDy?tiB+jjG_J{fWTUd?K= zpk=P zql5}M=1hxgx24}(lP-9DX}OOTsq`vu)gb+5*;WxC_#X3zOW{=NNybwvOiRk^Q@}!< zbrrEf-eX@fZ@GLr!yjB;g{Drepv@9QSmlAmT#mAGQDvpY#x2LI>SS$l$@D6piSD6g zt<_MPgq#k>DaX{yRQIM?kMY1sThrKs3eUj9fW&J{&1vhmNsY9lv#G7CQ!i`;)Y{tO zDI-=^q{1w(HJ&0A%Sz7{z)_%^J1JFLOv#NI1!Z1aMh9%pER7G&wlh6wU3A*(VOCQW zC40kqHg(5NuvzM_@~nF@OJzz)KxmLk7X}8sOdRveS(C(SoWWKohhN9MByyhqvF-TR zXS4Nj{3J2oEN4-QjiJ1WUY~?pPwmy_<|);zl_?HmJf@V#DYIBqwdE=zMPtLI$TBs3 zQ8J1aqg`TR#1WeDSUiWrx1;v7r>$Ghr0~eIA}yMRYGW9ZrF@@rn%QSvdTo?W4Af_$z$WKadNz(b zAbxfVd_!N_&)FlmkjbR8`JC6teTpMDQ%B6+WBjbukx(vwfmJhKX2LzKGyIzI+kg<%4;q3 z4to8X=d9XKS??S<=b20G9MJDsrDcjsKT*x8-31akYSW&ym9Y(J(4G*knvRa<*$Dk^ z_xp*D2fZbUoVt`$)F{8$?nixe1$CVA*&)%VM@OvPj!DNpJ?q( z2Z1ib8;IjIQ~HyUk<_7QNeNXa+;ay}esA6`s@Igrl4DZZ#bZvZ&br5@mQzPxiJbgVBpNaB_5$y|oFwn?&jd{DUHn%qu-@4FWJxc@2dvX-6 z9({#O`O;QA#VhQl`1LZmT&&U7J=NIq4aBr7Vrl$~>wF>#xjI%PGNvPmo2s1aSnD{? z@ifQNBf4xZm&@exki~v=+8N6kp&T#&%FfBoM4lz?Oe>h?wC}3yXfT9TWrjTdxv4MFt|%nM z1r5e~SLpnlo|~SlDAC)U(}QKyxzEAKl}`AWNsgr}#coHw99A0;q*P7_frtRMA?utJ7y4?!f?PNbY?4e>$nrO4J=>1x*YK}#D!FVQ< zT;s)4YoaaaGY%Zx70iECOJ7xY^zB;b#p)Xu*Dq-Hx|`cOTH2OY#M(Nm8ujVKnOVix z2p+q(rsni2{X5H>JFjl;8C5m&YHG+>)$XrM=nL58yQsOoy{fgXs;S;v*jCrta_aK7 zrOg#f@N`w3*?1~vzXs~qEi2M1;ynfLoRoX|$kL8jYjuW5qIxhtl&ntq)mLQBqhxgT zQq}1b>UoRenRp6^Rngkg+B`9Uxz#llax0v)Rh?@xI(~^Y=VX%cMCxp>cQBsCyPccJ zrK@UdYHO>m$V}u`r~3VDRdb5(9gg2R@54Qbe1*{k()RBLPY-`Ccsg7Vu3LLChG0MQ z>~aT>2Hy^L2M@{iKl$pG9cIhZ+xAC)!u~KF_JnOF%!eNd8^aAIYzxjO_gE#yg7>QF6QFUjEzGum2e@?fgxycTDw8y04mOZi?(9 zJ85aE?5J&aE|tReFvoI5*GeuCT`Ud1wE43p*cbdu@K7*PoKknS6xOqB%^~5>GVaxi z>uZW?JJ|I1K=1lL|;Hlv5;N`UqJ9As^3?9*yQ<$cT3&R`2?ZLyrzS4`Qa7h@_ z$Z{;Mv8jEc`6{*F5M#tkv50!J^V`AKgBMCHcHDODAj>-17OR=43Nu@WH8+Oaqf3+V zjy@B7CHPLThc4Y3b=fY4KCKV3$ovZrvcQ6lgDjw!`QVGx9%K;(Ob1znlj$H!gv&6; zo^X&Uzb_bNSys!)g0bOWZ@Fz^DR{aoxNmJkWLr3c21+f(7}rPMq+$w)2urU&7P8;CwuLZ80OXij8=HW#Og4nBDU|VRv|a^ddTY76&8p@OC>N zsOhHrSA`pbk>dFlJi6%{J96p{uBnc&zq@I|5Uq{e5v}idmdP7?$}FWDdCCl>8+XbK zlQ-&{WhvQZQ)ZdG5vR;DY2!`KVb9}WWc|m3{o8Nk)UY#!l|ALQ)Fe9_DYf6V^>UNp zrOf1dR_f6$KMD44{c-RmitambBbBX%^`6$Y>^)p*z4nxt94C~oq%B=EyNTT#?Z@%H zSA;&+vkX6QRTP+DP4Kg*DgN`Pz%~zm=WQ!f3ng*)+Ck)wccA z^iZ%j_*QUdusgb^v)6QUV`ZlyeO-v%Uqe3z%>1HT*zmjHR#(woT%uZRukCD6t5qGkzq6Wc z!*$-e`+~8}TN&>y!Ar=mX3%tq0rEylBq)>Sk&2V>iQw&`Pv zeb&}feK1y`&f8e%|0-y+PLxzVZ(6X#9KEXTw-5h17!95a9%SjB+qRlucF%AS?B4kF z`h8n&ofMo-3O}9Cc*aC?$Rrf6@uXk^a9z{P(Bufl8_{^XD9JkHS< zgVFVicI@5s_{KMaJM5v+++4J%U_{wuL}i+ZT~iX*%lRE(-@4m7P&skCSDXiTyT^7e z3FyZi0<}czvHg-@|IU1HhZ}z}gAOWp-4{(vSYY7IVOKGqOLDR2-n$Q-Ej#3#P!Hd* zaV4J>jp0Qoig$fN|bN#8(4MNiln?BPBQbLb4OX8i_SYwSG24Mi@r(b{L5hf zaIp2ZnQOnq*mf@7a+Uqy8_!*IU~MuwMd z={Ca;72}1HZo2+%`-EbR$)A_HI<(`{34DOn+S|iu`0g{*e#r8jY3IQuHr_P+im5F> zF0|U3o?;klKetL1ZtPoIwvK7h+$T$EjyF;Bxves>j%8k{*Ne1MZzos8-Tm3XmS=m_ z7bVI%TU;0G#`wClUzCxuj<2*SG>h!_Im@8>>e$`m?+bmpl}KkAdukxoV$6!@!fDhE(On3Xx`qwgJ`s$pdwIv$&fL0h@3P!?3~)5$)0jtC>76w@-IBbV810J#~+N^Pm()1_guFs%>M!* C8_nzh literal 0 HcmV?d00001 diff --git a/bin/Langs/pe_PE/LC_MESSAGES/pcsx2.mo b/bin/Langs/pe_PE/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..b0cec5a54a53bcc6769b0b065c27e9d423eb89fd GIT binary patch literal 11165 zcmbuD4RBmnb%1X|LYz$lg@i%~q&Gj_*o~!Fa()#OB1_DbgQommQ-0W#AA_4{e;l3%pET`XFzsK3 z{8LYx`oDtGzY1RkPa6IJ%J)Bo7r}F08O?t&6n!p%qUY;P{Z&xL*<#wepnQJ|6n$?u z?YrPFP#!Vu?|`qSJOQO&35q=)f}QXYcoqC6lzIOSihb))=2yQMaTB~)icrSC0$vTT zGaQCjQg#hLVe0?X@CC#Ff->LL7(?_JfMWNzLecYXDC@c(_CVXTe;La9ziP_ggj*?p z3!o$!#m(>Dc@sQF!c``e$MbOpv2R2Q2PHfycu3_NfbwK zhO*v$Q1m?rS(H$p-}b;5)2b|~LJ zZQB3Rl#fFhZvl$^PMi7$6n*{?%J}~R?}R^rVwc-q6MfeQyD1Ms@#{l|N1(*xr{Ng< zB9w7{3Pq2Xvx#nm7sGR5AH+0j!0=uue$7E#p=P11>w~Z#J^}mS5|lW*8smyycS0F| zHQ{PyI@*EVq{{xi$cR9hb z1HJ{yybnV0-v^-R`5`EJe-_>hzX4wc19(0BA)JC&5^UGP4;nrNuc7=r{AKu`rhElT z?4{fX^*KsC22n+Q4QGhnb5QL092EO3LGi;6O#Qz@>37Z>Bl}+jMeo-cYQrs1?6Vz8 z|C^x1)tylEA2j6=DBr)`lrvD~FPQQnDD%G`iakFDW!yhB?O%ZsH-8SrZqGo84-ZPe zlTgMxZQ8$Q>i-GKcmE1yoj*1_?}|v@S3B=OfCxE1CNABU1>o`KT80cCv`=}6BvKvY+8 zDD(Eio8S(#AL*1Zp+ zih8%&Q&9T-4HWw|;VtkdP{!NA;yd6V6#F~?<-3QV*zZ#ilc=vjiIWph?E4(N z4lY88rwgx+)_aNJ8=&mt&G3`(7AWhfLRsJ6LK!D8{5QiFq1fqMj4%3Zf-?TauphqG zl-~}e-z3}(b5P>p8-~w7*{@YN371X%?#kM}o-e%Zmco%#b{gP1Zd^ePJq@d_C z1!dek+yozjxJW$=yWk(e?eH|b1HO{YBKq%zGF}o&oV^2z-UpzDkHSsxEAV#sXYg_O zBiIi=@+%SNp{9Hp!6yFcfrPHw2XU!72+x59M4tPQaioNN4!IYRN5-5&V#pVfwa>#8 z?lBd=YACwNb4!Hqt}1?z=flXSky+%u$a|1?AiskQBEN^+fINuEBX)di_!i|7^Q!jn zBDOq)d<=1rk0O%KejAavXg%lhvJ=6~>emsm!8?%~5sh>svd`rV8$cvhB3lGa@lB4`HO9L?m|P`88xSBEEe)vhjHh6-7k$!rPEZ-i)v zZ#ISBfFDHeMskS6_)#Q|w4Miec^@Kq{W7Evc^M+lH4(x+@|)&e_S*<@i7CGpK7yo? z$4uRC!Sj$;ArG0l%MHI^*bCo;>_VD_^9!&z>oyirI;5E?YdL2eWR} zx6jT`W~ZcCSaK{C@3$sO)C^>E6m~hO!gMyx0K2VRLB$7C`KeNBik3mVK4E3XrS13DUPxHxS&p^77^VtU%5--u(UE!Wi}PS$qNOvOj7 z8M|odP>X2j(Ndo3QeOAI|c4lo? z?JVV{QcgCNSAFr!fsA9%@M7nSj-AU{j!U6XQhkh^&1Z|+66<2pjOrU1*U5r~A?0nK zmCx9Y>PzLXEr!O|*KT&N-KJ+98!wC`yA%2Vqh;;9-m>|izVddx`QTR5-4;7cV$kp# zJ&F5fSuKXUGA3QpR!)$&i#nCd*=fBisU30dL}_x;a->f&m6HXu^f&Ipqc*iI=gG?Z z>{2eH_cD}t2%GKe*CV+SrVJ+!gJnaX{=~jp&(>$)?z8nk=d;W+(%awlbM)yxTc6~} z_*wgmCwIPdm_kXLfbWl62X0le@C{Dy59>>*96cRN57`+@Z|U#bsyp;hDLt(xQ*P06 za7{WIwt#8?aSwZJ0!UDwlPcATL@(dvz+>R8d-x zEhmw)#_wu<%NlQQXj%2~hWb^{Zm3`5&zH8eJhY)@jqf(Jv^{1T!mU7rvzi%_f^*2! zi^sB<{Q+sgu*Q_zw2K?k(^6Zs^&YWM!Lj&jzLQ-jjj|ELZ4lb!N6KC+%dufb?&y9YxDJ_jNzgT zcHzqcI&6{H-`W`)D7ZS4bql%FA!^g4;i9|R$$VF|?M^1P@@$4($qn|~Wiek~t)4Br z9Lw6Tc3TsUHLG@Kr>52JQr@KOJ(HmsLY*+AOL`TH;j?s0%U$bA-K|o8 zX{MltO1WYd4aDP^hJ7;=<`2mXTVtX9uslo1NP+(xry9OX$$s773aYv{ zR`n(C>DGy%@t6vy)$yHtnLJf!e8J}$Io-Oofl8VG>#ujhx z&)c(lRwf~AnTb701*D|BOdxHeXMt()%T+Z+17k zG1ab@ySkph1wH1{I1=u$b~LREp>cOY?Hkwalg%}I-7d&=&m?6$IB`g4tjQEfNBWxt zpOmm*=U*G00#eH@RqiWACrfrgO-blvCdcZG^*Y7uWGaoBXUMzZ$;X-L@|z+0CQPL4 zrRdtz$voS!JNj7BqA9d)GIFb>S(iAT>DTqM`*lVA-kIh`D|eP5D?OdsXL0k6ZY$yW zDLaT$b+yypeM4tz4-am8QoBTi+`p$K{W?*WDRQoIIL8$0PG!iAa?GWtf9M^D#H9ou(o-%iJlQR_ff zMlt>FP4td-3=Mbm_v+olJwto0yKi`JA~whY+%abKmR~6YP20hrjch|V+_NKwXJ9F7i+nKr}Hne9b(bm4Jb9+n@2)^$a zJ5)f~qIIx%J;!`Df1AcT4vFpdzTLf}$;4Q@r)a}(l#WC`ZA;4Q(YH)wi!mj?OOK!Q z0=?*a$D3Xwz?0#viyTltHvr%P$qrSHoEX9@Ytu*S4a-!@t;;J=sTz$7((WmOm zOMW%3mgoLdqK0N z%F8cc0acANh;B6HwN_aPzm#R8L%8tG8Ewqm^o~`vOsQ)ADWfJ`RK2Z;%NqnIeByv71XSEkWBURm%KB9rRK8!@b0 z@s{zt3e{@4P?l@_DqpV&ki*N~(V!77qWxR-j9pedzbb>Cr5l?1P2?=yB7M)&DKvjq z=zyQ6XZO$3lb|@uSV2>JHC+zc-HhqlK5f||9$C{Eu~pmtuvMd~%@qkHEZ?CQPoEUu z9`lx4k*)ptsIle;r)1OUdZ3pYRkgCr{tI^rGx^QAxSB7sGuW%T#-^>CouQBUOBF`; zgC(zim{9SVsd-o}o?_e5Sq7^$m)Dfn?XkvDGC&|PqSx*8R-A>KbWNbF2H@HTS!%Qw z&h%L;D$e+66bol;S`z{Pb9?0XGq&UHGuF3!yxO>CFSY7OY}#wE>oH%;tb|6@tFKVu z>oQiZ5h-5KsQ8EdickL4J!bW-JQyaoaKs)w5uDHqi^mu?3Na5K2KolQ6f8GMK^5Y6 zz8VWXtu|yCZ&6pv%Ry7-Sie2nc~zzfP-o7oMiCX|&DGpMT2l4-;|pG;o&5twBhA#k z2J7MLvfq&PMe(U6Kdbnv^HyI%w~(7v{aCeGMe#8IaSR0WD*UVKr%yId5t8KD$p^RX~oyyC1qij6FU0(bghnID3altg$CjCz+)3<{Y|&N7_0kiX9^l6B;_wtjX?Z1gyxbRY>FkSvuf2RgZdy znJF|Q)iHwElr4`-q>){9NhbWxub5&BTL=J&h zt*##DbJd`#Lj9w^EO%)cr<=u;J$;^z-G;P>_xTqr)x;|86-Du=m!HL2 z?Ne#Pva#q&RhOGpxkR>dt;A1Uab1gUu-MkeA()oENNsqW$&FR{&2Z29O>{jHV8*VE z{+H|@-TzwtH0~wGwOmZto-h}^YO~H2t8&~cOMY){lko7@^%`j?x+bovhz>;+-UIm& zAFNpFle$J*wJyC==X3 zCC;SAV)I!hjj8Zf+_R=-^?(hXqF2XO^)k2bCBljj_2s@5Wtr$#jpWmloJRZ%;?#St z9rt1}4tp}OS1Xeu&DX7Cj7V#cz~z85r;M=+(N8k)+9y;wn6HpdNc5p6xXc9%$AQ(T z1almQlGh}U)E97OcpSM$H5`IV*j}V}sD%4Rak|I<--=1Dc zCk(o+`{^^?)6>)a`~CXY{TushPrvM~9=}JCUqZ@{_q?ye)(C(2eft+Y?<)8m_(XWg zQwlyAs>!qAv*7`FIedw$zXLvj@+nA{-U`(7XI#1F%A2m7z$>_a4qgWDarf_Y_aB1% z^WNs_ABAfFVfZxoF~`qCJ^xksB=}9J_kSO1Jm;at`M<9IlBX8^Tmg0eX;9B!1vTEQ z-Tg!G$&`<|`?tZTQ=W!uw+5w;yWtqzg4e=#LiPJsQ2PB_sP})v@!Js9djAF0|4-lx z;FEr_;5a-$x$2m^`nNlN-0|yB@4FObXdES|c^`)w=S@)ax)V;qC3pXgPS{f zRl8?Frs5rj>gRUHS;s}k(~hr%()Wfdw;VI5_rC_Jzx!PM!|-{O-w&nlPeHA_^YA)& zEzY9%o`$m9E1||2K#eTT#B&%^4_tZ$jzoEW|Y4J+A&8Q2y`%SN;gp{67Vy|G$H>-xnRf;_Ck$s=sf! z`X4~~^^aWn@~aDbJ_V|uYoPi$3}u&bC_PTQ`)8oW>qF`5RgQhA=k9?_&$}N=k8gDM z-{i_~hqBMZQ1ANy)Vx0I>OT&p_s>AR|DPaB(fclxoqht3z=`Jg~&Q1iPNN-qz5vQq3rktsCj=48hAOKOMi!<=5Y*;!ds!nxdk<^49Y&Q zb$kduK=~1Zn<&_1Ma`p)qfM}ecy&t@CQ)+UU#tI z4Ai=*!cFKy`N8L*p8GPq5q=F`0-uGkWw)!K^zULLtcvLr*}1w5f%3QUQhXqt9zy66^ zX#{n9uSPWYyOA3ZgG?ZVY8&Hbp?qEE0sTVcZHV;x>&Qvu_mC$cZ$xfFWLy0XA@4%T=-GCm+m^E#zg$2J)N8?%&y6g|@p^j2Zqmxp<~4oQ97_nuw2Je(#;g zDdbg%ep+YmNA6UCpVp;*^20wwCXpIKDE2zYgGd$8`g#Pp5xE07ifHXd$cvE`gm7_jx1c}sU%69m{#o9qfW!9=3wfTrfPAXjg5_QRX^)@ zg0Rk2(#YFkC*$@^-0u2WREt_s);IG}TC|;sI~!3$x5YdOy;3=><BIf;WXP0ex=#R((xN-JSI&O&2HQFOhMcc{)gW~Mw|K3(=oRsSplsK$AR z5mYljqbEE4;;-H?mH4$N@TQ|U^``Sy!%rf=0twf+GorL=Ib$A6n9`mO@go`bmGkTtyUbE6IGMQxV3y^BTUpL z^IMw0aQx0(STv?~Xgtk)CeB-Rvq)F65IQ?uHp{IXRoa(ZXW7%HTseL7er;yg_iMqJ z_j%9qRQdQtw3*niO?7!~-!^O2>7VYVn;Ylw0_0SwH z&m1wMW+4wY&4!<5VS-^Y6L(sDbJlM~b<>NoO|!6iqUx1%Jlm_3&V@mqg}R6`uTohF z19~u(%J|&UvPr^#<%mBd=5PlGGYbo6#;euJ5wFtmYq*KlV;ZvnhvngdsdO5xDCGqm zjiOkqjbc$1VL?kyp0msD-i3E|S$of&Z5!{Y-?r?Y`d#+?={rLU?YXndc6;vZSj^q9 zO92mOHf@4J(s%W;ScKXKx`Se!Dvz2JGX$He&0;er4Rw={zwSA$LfzCtw9CNi<$zhL znl2&Dq|qQW?L5s)Ei~7ks7l+Ifr)xEL_I}ecZb&pNl08WjWpZj zsy@Q4cGT*7vs4piX&SX!hDAwCG)$rGcjz(dgvK8RZ5t;vXx`OL;zr`PCwVLhOY8ix zSHla&jK{c$=FQSFB*wRGh;hYgGwR}ka~%^Wb$o|0`Y0v~l=Uu`WG2lEyr} zp*~UugI;*D%NP#J_m7N?%yv^#kJ4_-?^7EPhO>0Lk$WzS<1tI~mrJu;-nO8k^q9yyHUl+=f+y9TSR739r%^HVcY?xyfcAYgrqnM|HQu zdPKQeStH&YkyEk%PQUAS!Zh@(UeLsBoCMqRL>m`q!9*KqD7MRe%)`RCpRraxenDws zSSsi3uD6i4vWOAL;;4po)3)(Lk>SXQ)gPK?3UvjTfB-Xoa#gks7%%pj@8z(#vX8U)tTq*i6Gw$lK`xuF$QZ&zZ~4+gk}sDKlBY@`^FDoHB^P zD^zS?$xT*c%H0^hmou*9m<3ixXNXrCkK*DUL+F^}EME=%gw3bHnlv5#>FNMA=~an- zXnxn)uZFEK$jm}e=VoSf05WCVD##Q-RaI=#7u2?}N9nj%wVZIg&hdK38@%GgRb?G% z3##@)qQIuNx;=;0)t4?$nrVAxF)^p1jTWcERUBEKvbq`JmpTMNd(<&q`3^P3y>4+z zNkaJrMwC0HXqe-Sn)l+QsWqdy5J+uzKj)FX&7#?la?8H6O(@Ss{9mm3v zRw$Ko2&{3~YI>`A($oysO4VtP?P-sF){4EFr#JnesaSq6J(!MoJM}uFn;IsVbce-d zyS8?`=>iv8OKaZgHM6s$rEZPKT^;jWFvfzlzNv>BKEX!qU0|=uEm-k~is-LePNzDj zWJyF+VVDZ!M5H7f8<|S7Xu}UsbDMZ;cRcpWlw6^B#s*Nw_6yF1_{tFy{N7;O5dLr&bqDaG!aT0!il+l>}aFFe+LyJ5#0 zCdDIk?%q_`O^WD}v7fRj6T^~ti~FN<^-*4<^ZBG%o|-u|b)sTUS5~TXON%4ROE@t> zTfK~y*|#Un#Ia*HjOyRb=J<7!$8Q`xcHOaKG#p(C&qnISwR>r0YGrg`X|z0LPA*L@ z%)R)orNzp~JUiv+YT|d&me0bQG_z~-^JZ~sp<-^WEKJSK-!WA#uT-kl+i6;4RHLgQ z`@5N+T0F6)vE4d9e@81Ci<8FfBMWm2m7UQaA3HXp6_2q-SNmNCn}z4HarWb=bBjTp z36bcwnUhm1)ynElOO1wPVzkl;V#TOQ^O9PWjd)5Rr&~B`&+s=Uct<(fFS5#|;mIQk zwT4S~`92L&wIT%@=9i>xif_I-WwXpaS!O3rgUBnL8YU{imcLWVx4flNoyD6rdyyXu z$OBtZ(`K6+QIm$taXTh|yfYm1sa_8T{mkF8*`^&IuU?GsW_o0c>=Pv0QK;9goF8ah zOIh!HG9c1LO{Je+X|B_;L zF}=t^@AA7*%D~HmUc})=X=ta<_l+f~N!h1{w=4ZraXbAy<2=ECeDbI zgMMJTXe%!g)H=@%G{KxR@Kh&nVX*B72EN~loAxc;h|YtEV|CmM8*I8c!NBL-Gvb|6 z#!ArAv|ZAAfiDaT7xp|HelP0>gJx7@G0L;8H|`q3-i9`*xtNBtr+3oepVJ>lw~Oh` zW&7dy))Q=Rg^GyeuS38p^6ho&J9cm@~n&UWY{&IrD36i}p}kBgq!1Caj@F{x7C=X2?rP;)^|Hn8YvDI=HVowmOK0 zNhWd`pNcri<##|zRUAD zRTOzY3LG`-cn69Yno=&2^o!$|^iKem`>-W%*^J+scbN8D0}pHVCYzOZj{6X!ZHh9Y zW623a~D%XKk~I=%vxji>%i{NY3~nw=WaEYU&fjkejZq=>nMv>^l4zJt`*EV zRcGgrZvySyo|jhQv~O418evCDQry&c0*%(%Z}_pmTC_NcS|jlpp)gPpFBHWZNq^ES zCun$Acv|O#t{3Yo!5VhznWjCiH5l3S+)_1c|5SCcow9#;C-BxmJ=O<-Kb+?|e@nPRqHh z#;sSW6cKXxtl+KN6C^vARE#yOd)i)jC!G}r;?x^=6JK%iv7e~@tk(PomZm>x?zF*0 z{qi0>KOmm5pZLMZAhwF_@;#aF8+`Ir6t!OxtO?!cgkjKi=XX9N=qO%%M_8oSJ;!<( zC%^Dxg5GU|>PFsa23ra)th;T;(?^AsU1zX9x%x1Uu&3b5-2gu(j@09JKb5XcPaA(s zBDJ$;*@u?ndR$|NBG4Q)JNJtH>=0iY>jONdl5^K z*9>@9GaZm8u;oUaIXVQOc0}sDy$3Q6Yb~2(rHskn^(n#*^MZ4W-M4dgId&gDN$h-$ z*stmFoo@|0A0(`udZsu=FhXU;JMHW@iN|chv@pH>{GedY{EHJ%$8WUG_nQ8Q_y6;R BIPm}g literal 0 HcmV?d00001 diff --git a/bin/Langs/po_BR/LC_MESSAGES/pcsx2.mo b/bin/Langs/po_BR/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..bad7bf6ca21b1cd4408f8d07d3c0d3da3debd6b9 GIT binary patch literal 11024 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 literal 0 HcmV?d00001 diff --git a/bin/Langs/po_PO/LC_MESSAGES/pcsx2.mo b/bin/Langs/po_PO/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..752871e7c843d6a7072ecc84acf67731a5dc874f GIT binary patch literal 11385 zcmbW64UlA2b;qB42n=5$A}9gw0^7SQJF`2pe2zevo$mR7nVrt`%(562-gLj|dAr^H zn)flY3o!}?C4PlS#2_j}LMp+kScN81l1gG}Du$$jB$bk+tl-89%STa_h)F7xTKWC& zyWP_>?7ACY&FTNUAMf6C&pG$pbME}>TTl3m$MYEUOHlDd&wCzh^zp;<;_04u9{3V? z3V7TZIi3cp%iF=XgB!q;z^h&T4d9z8-vlzGHw)_fJ+8dq%1f>sgD2CzA3OoP%e8;R zwciKw=RM%+9|P6@E8tn+lMbH-_5Jt3Q^A9v=6@9wKd*t}^L1B$+?n|}CxdD~3)J`L zf#UZ9*S-lnjq-NaejWH$$~!^zn+GM2TfqTvFL*KdAgFQw43vDo1#12uID8RAwBCP! z8vkeDCE%%V$#EOFfpW!R=ITG|@F|DSgPQMngdsi(pw>MIiqES+t?Nc`7@TqK9|I-F z`&{`!a0}&!L0Iwr#^FDM;`=2~+0jL3=j*)^6u)~wmgc<=)I2Tlo#4HoiauE+1)2Vt@}Yx>-iEW{vHR#$JaoO^VcAM-aqgo zK3;b9KLOS6B$Ue%ymx{cXS>6z9p2z@!eJSdocFl$`yDQVn!g2Vy#22JQ{efO9{?ri zCqV7De+PGfZ^KA5?*u4)l|k`24~p+Pcoq0T@Obd+;5P6nunxWgZUQS9nea~VQt(mm zBJew|{35uE^64;R-sE}j1;y6~5WeL8ASih~0!r^+1{vD>Iw-q%*3~}`O75?KTJMh? z{tVPOCtr}S=S)!T=Q`W~s{Jw$)Aoj3{g^A4LCK*8YWxPM^=F{u@kvnQJq*eY{s>gR zZ-A29w_N!&&H>f`B2av91SP*c4nt6SxdYU??*gs8I(!0@ z9sIq+7eV>^<1Wsz0IL6uASUK5g6e-4D1IIS5v}(eD7*h}Q2cIW5c%tOfg8bLP~YDM zN{)BB_78(a%J+j>&vT&G_iv!&`U)ty{4aP9c>1OJ_xFPu{~=K8`!cBSo&Y6}r(F4) z;0ctU1~u<9;5_&}Q2qO1PW4xU($Dpv=38<2afc6sRqDSA%AU?GVefG^6<{U^Z_!7_Ln6&qgp!off!=E}lc}re@ zw!;g-?Ri=Yo`-ai1}4L%Nr;0xd&SZ31+?*=g$?`xpe^&DvD0n3y(v+3Rhwjn+5fn*mM z^aykdq(^hsp+4v_sQ38@g*~p~{SGAyJ+dV|(zVVZJs*P}gjS#rL3cv$gFX*UL0^O} zhkgyxBfb8LeamAY_v%gZqP6MyZ77C*3)%*K7Ls4;JhI815Nh|c-U z<@X@j%l~c!iOLC`$(h26(G2`>kqF<1Mad&7hUiJQ_9Iei|-> zjW9i6rotreI~uhX!@8Pq{gJ#u_YxJHDld4ykX$LJ+ni@0tU^D4b5L*_@ep+1$=vR)TYLFzR9EVX1 zXI^18XhvyZEEoCEvssJktYt=v+lsr3UZLVI!$2jW*|ag)wAHM{5n3o$hDv6c(ZZ-@HgDW-He6>m?%(3NN0P%L0=3_m zMbx*#Y7yLqK6R-E4M8hPjo)ZQRWn{Ov6Q=zEiMMJ`lNnC3+V9g)P+VPYCFo)%15KD zQ8T+3N*Y3DyNjmW$PlHS+#1WeKE=}RYmU}u?B=8OfajykQywV}9z&m@qxGqj=a1TF zzOwU;!?ZKw48B+hmap-&@O4Tr+WO3I&@&FqbW{t>=HloU({HA;>XKRXlQf7?OlqQ5 zE~Og%}Lc&qi%)C?O3yfLbAvvds`4a26y zCmNQ}^jnM=wgTgKoVN87Vw$|5srp0OC`(Y!Hw2Ke}4$k!!X`;v&S7d1u#GsuN6+wibiw!dYdZ>*h| zT9~vO{sC&MxZyPEc5>gPQ8d8BL5s~WURfu<@qqbSUiWMnkAq;JHxVqv!HPE#)|b4A ztmRzxATh-cPKO$W1tUhlN&ne%3qu=&hK@xaY zE=Xc5imTmqB8@dtFySH+itKhD@X#>kXRMY7*OZ1jrDE1>d(&AX4dFlOo4ru854 z3|soF{E$3LsL8=paWLB^=A_#yjK{(48G21jyPE7DqL`$9oNn=U1uMpGe{Yx7lLJ;T z!XkV0gtSpC^ZJS9TiA}0B*dc(cr!P7+OO+WLDkUus?o}xAyb;3@AK@mrm!=PR+8Mw zxSFw0Q|Y)V!z>!&C}!P$Y7Ru1SwSra9>`J}appd}(EV`Cg86&R1>&{ZKi0+oye+jJ-mM-&~%}@kb=^w>Ii354U zb)r{b<8)mK{FvjW&Mq`9{j?P_4SN;*9kTA(?UkSrR8uoutK+AyrekOjYH_ z@|xNP8Ym2U6^la-cR0M<;T2vUV^!EWI&>;40sDT*o9nJ&Ztj-yu-R#&6cZ8uEH;P( z=P+H_#@tefB`WLzZE$1SvKDIcwsszu#DOdUwaI`IBuoIK?kiEePrF2G!FAfC%h9z!SM{Nam&i$gn zg#)G*Ec&?nkBC}wRZ_53>YKcb`nWS-uAAwD|ZhTN24D^k}X}IWD5pxp{YtJ}N z$As)4|HisZc1<4O3^31jCtDyR}N}s_0}os4n@t z17hqvF0m)2b`a-hx6`iO(}!!fM|8Ak5-dWI_L3ZJocoiM6O?1P&tu(w4KJtztxBMP zIQ&-9u(7h4nxB*2av!^Sh1_N-KP?h07Yl?DPPb^K#rIP)a~rnEQ|x8B{>fTDTr0pI zHsz7gn?}YFE{fd75U1J7!3=dw}zkSE{?R4y) z4VFWV;`-fE8ky~%p6M@+n2DL;>B-C9GqbDIH^ougKj-*XJ~B2xHDz{07HmR2H8b#J6VN_xnz`6Oz2cvDEp%nI_6KBkR7>!znQRO-QA!6u~sp&v%8 z>8T%k#h_itVqVBl$sxm-=JEL8Vp!FHBYU%&?-eF%A&!^t96L%Y>&&GtJNL>~Cn9myVO9ZJfubN#j{F03rUR@3dPdcj1^IS=W z4y@JYx*dHENxwFKkN@C4g?9qDL^y!#;lTsaX zU&6&>?kkn$0YByv5B{Pb4juc4HgP*<{*=shJ9pKgE#ZtUPr$9McCv75s5bv5>yAvZ zn}m1rZ=WzCKG+UvZ2E~=K1k|~ujfpm#Oai0;1dx?dF|z-lZ0bhYqRjqaVyCgmywf( z>mCY|l!bTG@ez)hWh~C+BlVH?kJN^)yG_*yKZ78ArR>(Jb{Hd+;xN{B?v?*m67Cx6 zigLHmleQmLf?bbnx2lHTU`9GqNptOkTy&ws$W&%16WfuvxMaTKhfXJXMsMwH%x^6D zI=MPZ(!r4XIky7ECEZ?lExWokdHt&-m^x61u7dd0B*m>J9`_G%RLk!04e@v`S7z4#A$TcY`$EQ*f#{cy}HsGN&yh1N9sAjG)t{ln4$vgmy4^9O#=#j8gw*lwj-Pnlqr01x+o` zeY4t|C5$=sNI4>t&SNb23yx_DkCirSH zFMBaUN@%tPXKkg{x&`a#iDF0n_C{bUp{rEc-P-*PzyDzOMj+CaK-7Xodn16rxE*P7 z(Gm14h=M)mVwPD8@H!zYcTR}&wreZXxwWH_H`Waivn18dY3h1<*2m=+26KK)aIfov z&F-eac{wJy1Q5bC}G6=Ek{AE66}0YgNE;5wCTMXoM< zYd~5uNk6G4(?vH-T}9U}xgcTZabFR%=*w*dVHDZW%SpdG zT;RBKWnq=*0#|2WihdQA=l2vEe{WRBgY_7P;hsTv734l_F?(TA3>GokxO-oL8c~k@ zug&3_uvmAOK^nnr8P?4uM)!4S^>xSU>*cGjy3lBPrG)#8rKsUmGIH9^D(AfXGQ-B) zNTqH)bbhsAZ!~-xOv;?P)?}6PlC|5)9>%3<&HaRL4OEKKQ4}9Ei&;y{M@*gQ+3v0* zetMM8ikvjV1}|xigF^$cYD*}()`WevAly%}X%^&-Bi(QCYi~C?8`Rx$Se4@bbYH0G z-{&8Q*r(6wryGvEWo=&Iues>J(b%gFv0`2G>RM%jLBscS-O+pavoE>L=r{Nx0yzGn z_95kDK0LJef0t_I|GD}I!Zhk0Z1$r`{tpS8X4Bf^jvElghRSVycX)9^aJ&teG6zPJ za1n1?Wod3y&OLqblI`R?`|9NfM>+xE`aNZjYdlUXTyS-l21#xDj}?>g3Ah}bOJ zMj0CqcOSB*IWnnF1hHiIa~1s`BASu0>4HSTcJ3okh+pF{@AGoTYeZt!2s2^=o#mPO k2A%5?U6ByTx+@ad)3P5bKC%}qCe7H~)1}Jv43{JS2hv%Qo&W#< literal 0 HcmV?d00001 diff --git a/bin/Langs/ro_RO/LC_MESSAGES/pcsx2.mo b/bin/Langs/ro_RO/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..bf318427d371b7e14b6c7848ee3f26f393d4db71 GIT binary patch literal 11443 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 E0hxca;s5{u literal 0 HcmV?d00001 diff --git a/bin/Langs/ru_RU/LC_MESSAGES/pcsx2.mo b/bin/Langs/ru_RU/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..ee50ae6b15e3bd4824b3fa44bebc77eef0f1007a GIT binary patch literal 11125 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 literal 0 HcmV?d00001 diff --git a/bin/Langs/sh_SH/LC_MESSAGES/pcsx2.mo b/bin/Langs/sh_SH/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..33b36e000c948e93fa85f58b5b8659669cbe4f12 GIT binary patch literal 9723 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\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "%s: Error loading %s: %s" +msgstr "%s: Kan ej ladda %s: %s" + +msgid "&About..." +msgstr "&Om..." + +msgid "&Arguments" +msgstr "&Argument" + +msgid "&Config" +msgstr "&Konfiguration" + +msgid "&Configure" +msgstr "&Konfigurera" + +msgid "&Debug" +msgstr "&Debug" + +msgid "&File" +msgstr "&Arkiv" + +msgid "&Graphics" +msgstr "&Grafik" + +msgid "&Help" +msgstr "&Hjälp" + +msgid "&Language" +msgstr "&Språk" + +msgid "&Load" +msgstr "&Ladda" + +msgid "&Logging" +msgstr "&Loggar" + +msgid "&Low" +msgstr "&Låg" + +msgid "&Memcards" +msgstr "&Minneskort" + +msgid "&Misc" +msgstr "&Övrigt" + +msgid "&Open ELF File" +msgstr "&Öppna ELF fil" + +msgid "&Other..." +msgstr "&Annat..." + +msgid "&Patches" +msgstr "&Patchar" + +msgid "&Process Priority" +msgstr "&Process Prioritet" + +msgid "&Remote Debugging" +msgstr "&Fjärrstyrd Debug" + +msgid "&Run" +msgstr "&Kör" + +msgid "&Run CD" +msgstr "&Kör CD" + +msgid "&Save" +msgstr "&Spara" + +msgid "&Sound" +msgstr "&Ljud" + +msgid "&States" +msgstr "Sparpunkter" + +msgid "*PCSX2*: Error Loading State %d" +msgstr "*PCSX2*: Kan inte ladda sparpunkt %d" + +msgid "*PCSX2*: Error Loading State %s" +msgstr "*PCSX2*: Kan inte ladda sparpunkt %s" + +msgid "*PCSX2*: Error Saving State %d" +msgstr "*PCSX2*: Kan inte spara sparpunkt %d" + +msgid "*PCSX2*: Error Saving State %s" +msgstr "*PCSX2*: Kan inte spara sparpunkt %s" + +msgid "*PCSX2*: Loaded State %d" +msgstr "*PCSX2*: Sparpunkt laddad %d" + +msgid "*PCSX2*: Loaded State %s" +msgstr "*PCSX2*: Sparpunkt laddad %s" + +msgid "*PCSX2*: Saving State %d" +msgstr "*PCSX2*: Sparar punkt %d" + +msgid "*PCSX2*: Saving State %s" +msgstr "*PCSX2*: Sparar punkt %s" + +msgid "0x" +msgstr "0x" + +msgid "About" +msgstr "Om" + +msgid "About PCSX2" +msgstr "Om PCSX2" + +msgid "About..." +msgstr "Om..." + +msgid "Address " +msgstr "Address" + +msgid "All Files" +msgstr "Alla Filer" + +msgid "Arabic" +msgstr "Arabiska" + +msgid "Arguments" +msgstr "Argument" + +msgid "Bios" +msgstr "BIOS" + +msgid "Bios Log" +msgstr "BIOS Log" + +msgid "Bulgarian" +msgstr "Bulgariska" + +msgid "C&dvdrom" +msgstr "C&DVDROM" + +msgid "C&ontrollers" +msgstr "K&ontroller" + +msgid "C&pu" +msgstr "C&PU" + +msgid "CDVDinit error: %d" +msgstr "CDVD Initieringsfel: %d" + +msgid "Cancel" +msgstr "Avbryt" + +msgid "Cannot load '%s', wrong PS2E version (%x != %x)" +msgstr "Kan inte ladda '%s',Fel PS2E version (%x != %x)" + +msgid "Catalan" +msgstr "Kataloniska" + +msgid "Cdr Log" +msgstr "CDR Log" + +msgid "Cdvdrom" +msgstr "CDVDROM" + +msgid "Clear BPs" +msgstr "Rensa BPs" + +msgid "Close" +msgstr "Stäng" + +msgid "Conf" +msgstr "Konfiguration" + +msgid "Configuration" +msgstr "Konfiguration" + +msgid "Configure" +msgstr "Konfigurera" + +msgid "Configure..." +msgstr "Konfigurera" + +msgid "Controllers" +msgstr "Kontroller" + +msgid "Cop0 Log" +msgstr "COP0 Log" + +msgid "Could Not Load CDVD Plugin '%s': %s" +msgstr "Kunde inte ladda CDVD Plugin '%s': %s" + +msgid "Could Not Load DEV9 Plugin '%s': %s" +msgstr "Kunde inte ladda DEV9 Plugin '%s': %s" + +msgid "Could Not Load FW Plugin '%s': %s" +msgstr "Kunde inte ladda Firewire Plugin '%s': %s" + +msgid "Could Not Load GS Plugin '%s': %s" +msgstr "Kunde inte ladda Grafik Plugin '%s': %s" + +msgid "Could Not Load PAD1 Plugin '%s': %s" +msgstr "Kunde inte ladda PAD1 Plugin '%s': %s" + +msgid "Could Not Load PAD2 Plugin '%s': %s" +msgstr "Kunde inte ladda PAD2 Plugin '%s': %s" + +msgid "Could Not Load SPU2 Plugin '%s': %s" +msgstr "Kunde inte ladda SPU2 Plugin '%s': %s" + +msgid "Could Not Load USB Plugin '%s': %s" +msgstr "Kunde inte ladda USB Plugin '%s': %s" + +msgid "Could not create threads or event" +msgstr "Kunde inte skapa trådar eller händelse." + +msgid "Could not open '%s' directory" +msgstr "Kunde inte öppna mappen '%s'" + +msgid "Couldn't find pixmap file: %s" +msgstr "Kunde inte hitta pixmap filen: %s" + +msgid "Cpu" +msgstr "CPU" + +msgid "Cpu Config" +msgstr "CPU Konfiguration" + +msgid "Cpu Log" +msgstr "CPU Log" + +msgid "D&ev9" +msgstr "D&ev9" + +msgid "DEV9init error: %d" +msgstr "DEV9 Initieringsfel: %d" + +msgid "Data " +msgstr "Data" + +msgid "Debug" +msgstr "Debug" + +msgid "Dev9" +msgstr "Dev9" + +msgid "Disable Recompiler" +msgstr "Avaktivera Omkompilerare" + +msgid "Disable VUs recompilation" +msgstr "Inaktivera VUs omkompilation" + +msgid "Dma Log" +msgstr "DMA Log" + +msgid "Dump File = \"dump.txt\"" +msgstr "Dumpa Fil = \"dump.txt\"" + +msgid "Dump code" +msgstr "Dumpa kod" + +msgid "Dutch" +msgstr "Hollendska" + +msgid "E&xecute" +msgstr "K&ör" + +msgid "E&xit" +msgstr "&Avsluta" + +msgid "EE Debug Mode" +msgstr "EE Debug Läge" + +msgid "EE Logs" +msgstr "EE Logs" + +msgid "Elf Log" +msgstr "ELF Log" + +msgid "Emulator" +msgstr "Emulator" + +msgid "Enable Console Output" +msgstr "Aktivera konsol" + +msgid "Enable Patches" +msgstr "Aktivera patcher" + +msgid "Enable Reg Caching" +msgstr "Aktivera Reg Cachning" + +msgid "English" +msgstr "Engelska" + +msgid "Enter &Debugger..." +msgstr "Starta &Debugger..." + +msgid "Enter Debugger ..." +msgstr "Starta &Debugger" + +msgid "Error Loading Symbol" +msgstr "Kunde inte ladda symbol" + +msgid "Error Opening CDVD Plugin" +msgstr "Kunde inte öppna CD-ROM Plugin" + +msgid "Error Opening DEV9 Plugin" +msgstr "Kunde inte öppna DEV9 Plugin" + +msgid "Error Opening FW Plugin" +msgstr "Kunde inte öppna Firewire Plugin" + +msgid "Error Opening GS Plugin" +msgstr "Kunde inte öppna Grafik Plugin" + +msgid "Error Opening PAD1 Plugin" +msgstr "Kunde inte öppna PAD1 Plugin" + +msgid "Error Opening PAD2 Plugin" +msgstr "Kunde inte öppna PAD2 Plugin" + +msgid "Error Opening SPU2 Plugin" +msgstr "Kunde inte öppna SPU2 Plugin" + +msgid "Error Opening USB Plugin" +msgstr "Kunde inte öppna USB Plugin" + +msgid "Error allocating memory" +msgstr "Kunde inte allokollera minne" + +msgid "Error initializing Recompiler, switching to Interpreter" +msgstr "Kunde inte initiera omkompilerare, går till Interpreter" + +msgid "Error loading pixmap file: %s" +msgstr "Kunde inte öppna pixmap Fil: %s" + +msgid "Exit" +msgstr "Avsluta" + +msgid "FW" +msgstr "Firewire" + +msgid "FWinit error: %d" +msgstr "Firewire Initieringsfel: %d" + +msgid "Failed loading MemCard %s" +msgstr "Kunde inte ladda minneskort: %s" + +msgid "File" +msgstr "Arkiv" + +msgid "Fill in the command line arguments for opened program:" +msgstr "Fyll i kommando rads argumenten för det öppnade programmet:" + +msgid "Fire&Wire" +msgstr "Fire&Wire" + +msgid "First Controller" +msgstr "Första kontrollern" + +msgid "Fpu Log" +msgstr "FPU Log" + +msgid "French" +msgstr "Franska" + +msgid "From 0x" +msgstr "Från 0x" + +msgid "GIF Log" +msgstr "GIF Log" + +msgid "GPU Log" +msgstr "GPU Log" + +msgid "GSinit error: %d" +msgstr "Grafiska Syncen kunde inte initieras: %d" + +msgid "German" +msgstr "Tyska" + +msgid "Go" +msgstr "Starta" + +msgid "Graphics" +msgstr "Grafik" + +msgid "Greek" +msgstr "Grekiska" + +msgid "Greets to:" +msgstr "Hälsningar till:" + +msgid "" +"Greets to: Bobbi, Keith, CpUMasteR, Nave, Snake785, Raziel\n" +"Special thanks to: Sjeep, Dreamtime, F|RES, BGnome, MrBrown, \n" +"Seta-San, Skarmeth, blackd_wd, _Demo_\n" +"Credits: Hiryu && Sjeep for their libcdvd (iso parsing and filesystem driver code)\n" +"Some betatester/support dudes: bositman, Seta-san, falcon4ever, jegHegy, Razorblade, CKemu, Belmont, parotaku, crushtest, ChaosCode" +msgstr "" +"Hälsningar till: Bobbi, Keith, CpUMasteR, Nave, Snake785, Raziel\n" +"Danke an: Sjeep, Dreamtime, F|RES, BGnome, MrBrown, \n" +"Seta-San, Skarmeth, blackd_wd, _Demo_\n" +"Meriten: Hiryu && Sjeep fr Ihre libcdvd (ISO Parsing und Filesystem Treiber Code)\n" +"Einige Betatester/Hilfskrfte: bositman, Seta-san, falcon4ever, jegHegy, Razorblade, CKemu" + +msgid "Gte Log" +msgstr "GTE Log" + +msgid "Help" +msgstr "Hjälp" + +msgid "High" +msgstr "Hög" + +msgid "Hw Log" +msgstr "Hårdvaru Log" + +msgid "IOP Debug Mode" +msgstr "IOP Debug läge" + +msgid "IOP Log" +msgstr "IOP Log" + +msgid "IOP Logs" +msgstr "IOP Log" + +msgid "IPU Log" +msgstr "IPU Log" + +msgid "If you don't know what to write leave it blank" +msgstr "Om du inte vet vad du ska skriva, lämna den tom" + +msgid "" +"In debugger:\n" +"Put EE in stop state\n" +"and IOP in run state" +msgstr "" +"I Debugger: \n" +"Sätt EE i stoppad status\n" +"och IOP i pågående status" + +msgid "" +"In debugger:\n" +"Put IOP in stop state\n" +"and EE in run state" +msgstr "" +"I Debugger: \n" +"Sätt IOP i stoppad status\n" +"och EE i pågående status" + +msgid "Interpreter -The slowest but the most compatible cpu core" +msgstr "Interpreter -Den långsammaste men mest kompitabla cpu kärnan" + +msgid "Italian" +msgstr "Italienska" + +msgid "Japanese" +msgstr "Japanska" + +msgid "Language" +msgstr "Språk" + +msgid "Load" +msgstr "Ladda" + +msgid "Load Elf" +msgstr "Ladda Elf" + +msgid "Log" +msgstr "Log" + +msgid "Log On/Off" +msgstr "Loggning På/Av" + +msgid "Log to STDOUT" +msgstr "Logga till STDOUT" + +msgid "Logging" +msgstr "Loggning" + +msgid "MMI Log" +msgstr "MMI Log" + +msgid "Mem Log" +msgstr "Minnes Log" + +msgid "Memcard Manager" +msgstr "Minneskorts Skötare" + +msgid "Memory Card 1" +msgstr "Minneskort 1" + +msgid "Memory Card 2" +msgstr "Minneskort 2" + +msgid "Memory Dump" +msgstr "Minnesdump" + +msgid "" +"Nah, you have to be in\n" +"Interpreter Mode to debug" +msgstr "" +"Nej, du måste vara i\n" +"Interpreter läge för att debugga" + +msgid "New Patch" +msgstr "Ny Patch" + +msgid "Normal" +msgstr "Normal" + +msgid "Not enough params for inicommand\n" +msgstr "Inte tillräckligt med parametrar för inikommando\n" + +msgid "Note: this is intented for developers only." +msgstr "Notera: detta är endast menat för utvecklare." + +msgid "Notice on debugging EE" +msgstr "Notis på EE debug" + +msgid "Notice on debugging IOP" +msgstr "Notis på IOP debug" + +msgid "OK" +msgstr "Ok" + +msgid "Ok" +msgstr "Ok" + +msgid "Options" +msgstr "Alternativ" + +msgid "Other..." +msgstr "Annat..." + +msgid "PAD1init error: %d" +msgstr "Kunde inte initiera Pad1: %d" + +msgid "PAD2init error: %d" +msgstr "Kunde inte initiera pad2: %d" + +msgid "PCSX" +msgstr "PCSX" + +msgid "" +"PCSX2\r\r\r\n" +"Version x.x" +msgstr "" +"PCSX2\r\r\r\n" +"Version x.x" + +msgid "PCSX2 %s Compile Date - %s %s" +msgstr "PCSX2 %s Kompilerat: %s %s" + +msgid "PCSX2 Debugger" +msgstr "PCSX2 Debugger" + +msgid "PCSX2 EMU\n" +msgstr "PCSX2 Emulator\n" + +msgid "" +"PCSX2 For Linux\n" +"Version %s" +msgstr "" +"PCSX2 För Linux\n" +"Version %s" + +msgid "PCSX2 Msg" +msgstr "PCSX2 Meddelande" + +msgid "PCSX2 Quitting\n" +msgstr "PCSX2 Stänger\n" + +msgid "PCSX2 State Format" +msgstr "PCSX2 Sparpunktsformat" + +msgid "" +"PCSX2 a ps2 emulator\n" +"\n" +"written by:\n" +"linuzappz, shadow, florin,\n" +"alexey silinov, asadr, goldfinger,\n" +"nachbrenner, auMatt, loser\n" +"\n" +"Webmaster: Thorgal" +msgstr "" +"PCSX2 - En PS2 Emulator\n" +"\n" +"Skriven av:\n" +"linuzappz, shadow, florin,\n" +"alexey silinov, saqibakhtar, goldfinger,\n" +"Nachbrenner, auMatt, loser\n" +"\n" +"Webmaster: Thorgal" + +msgid "Pad Log" +msgstr "PAD Log" + +msgid "Patches Browser" +msgstr "Patch Utforskare" + +msgid "Pcsx About" +msgstr "PCSX om" + +msgid "Pcsx2 Msg" +msgstr "PCSX2 Meddelande" + +msgid "Pcsx2 needs to be configured" +msgstr "PCSX2 måste konfigureras" + +msgid "Plugins & Bios" +msgstr "Plugins & BIOS" + +msgid "Portuguese" +msgstr "Portugisiska" + +msgid "Processor doesn't supports MMX, can't run recompiler without that" +msgstr "Processorn är inte kompitabel med MMX, kan inte köra omkompilerare utan det." + +msgid "Program arguments" +msgstr "ProgramArgument" + +msgid "Ps2 Memory Card (*.ps2)" +msgstr "PS2 Minneskort (*.ps2)" + +msgid "Ps2 Output" +msgstr "PS2 Konsol" + +msgid "RPC Log" +msgstr "RPC Log" + +msgid "Raw Dump" +msgstr "Rådata dump" + +msgid "Re&set" +msgstr "&Starta om" + +msgid "Ready" +msgstr "Redo" + +msgid "Recompiler - Much faster than Intepreter(Needs MMX)" +msgstr "Omkompilerare - Mycket snabbare än Interpreter(Kräver MMX)" + +msgid "Recompiler Options" +msgstr "Omkompilerarens alternativ" + +msgid "Recompiler+VuRecs - The fastest setting (Needs SSE,SSE2)" +msgstr "Omkompilerare + VuRecs - Snabbaste alternativer (Kräver SSE,SSE2)" + +msgid "Refresh List" +msgstr "Uppdatera Lista" + +msgid "Reset" +msgstr "Starta om" + +msgid "Resetting..." +msgstr "Startar om..." + +msgid "Romanian" +msgstr "Rumenska" + +msgid "Run" +msgstr "Kör" + +msgid "Run CD" +msgstr "Kör CD" + +msgid "Russian" +msgstr "Ryska" + +msgid "SPR Log" +msgstr "SPR Log" + +msgid "SPU2init error: %d" +msgstr "Kunde inte initiera SPU2: %d" + +msgid "SYMs Log" +msgstr "SYMs Log" + +msgid "Save" +msgstr "Spara" + +msgid "Save Patch" +msgstr "Spara Patch" + +msgid "Search game name patch:" +msgstr "Sök efter spelpatch:" + +msgid "Second Controller" +msgstr "Andra kontrollern" + +msgid "Select Bios Dir" +msgstr "Välj BIOS mapp" + +msgid "Select Bios Directory" +msgstr "Välj BIOS mapp" + +msgid "Select Mcd" +msgstr "Välj Mcd" + +msgid "Select Plugins Dir" +msgstr "Välj plugin mapp" + +msgid "Select Plugins Directory" +msgstr "Välj pluginmapp" + +msgid "Select Psx Elf File" +msgstr "Välj PSX Elf Fil" + +msgid "Select State File" +msgstr "Välj sparpunktsfil" + +msgid "Set BP Addr" +msgstr "Set BP Address" + +msgid "Set BP Count" +msgstr "Set BP Räknare" + +msgid "Set Bios Directory" +msgstr "Set BIOS mapp" + +msgid "Set Dump Addr (in Hex):" +msgstr "Set Dump Address (i hexadecimalt tal):" + +msgid "Set New BP Address (in Hex):" +msgstr "Set ny BP Address (i hexadecimalt tal)" + +msgid "Set New BP Count (in Hex):" +msgstr "Set ny BP räkning (i hexadecimalt tal):" + +msgid "Set New PC Address (in Hex):" +msgstr "Set ny PC address (i hexadecimalt tal):" + +msgid "Set PC" +msgstr "Set PC" + +msgid "Set Plugins Directory" +msgstr "Set Plugin Mapp" + +msgid "SetBreakPoint Addr" +msgstr "Set BP Address" + +msgid "SetPCDlg" +msgstr "Set PC Dialog" + +msgid "Sif Log" +msgstr "SIF Log" + +msgid "Skip" +msgstr "Skippa" + +msgid "Slot &1" +msgstr "Slot &1" + +msgid "Slot &2" +msgstr "Slot &2" + +msgid "Slot &3" +msgstr "Slot &3" + +msgid "Slot &4" +msgstr "Slot &4" + +msgid "Slot &5" +msgstr "Slot &5" + +msgid "Slot 1" +msgstr "Slot 1" + +msgid "Slot 2" +msgstr "Slot 2" + +msgid "Slot 3" +msgstr "Slot 3" + +msgid "Slot 4" +msgstr "Slot 4" + +msgid "Slot 5" +msgstr "Slot 5" + +msgid "Sound" +msgstr "Ljud" + +msgid "Spanish" +msgstr "Spanska" + +msgid "States" +msgstr "Sparpunkter" + +msgid "Step" +msgstr "Steg" + +msgid "Test" +msgstr "Test" + +msgid "Test..." +msgstr "Test" + +msgid "This plugin reports that should not work correctly" +msgstr "Denna plugin rapporterar att den funkar bra" + +msgid "This plugin reports that should work correctly" +msgstr "Denna plugin rapporterar att den funkar bra" + +msgid "" +"Tip: If you don't know what to write\n" +"leave it blank" +msgstr "" +"Tips: Om du inte vet vad du ska skriva\n" +"lämna den tom" + +msgid "To 0x" +msgstr "Till 0x" + +msgid "Turkish" +msgstr "Turkiska" + +msgid "U&SB" +msgstr "U&SB" + +msgid "USBinit error: %d" +msgstr "Kunde inte initiera Usb: %d" + +msgid "Unable to hack in %s%s\n" +msgstr "" +"Kunde inte hacka %s%s \n" +"\n" + +msgid "Unable to load bios: '%s', PCSX2 can't run without that" +msgstr "Kunde inte ladda BIOS: '%s', PCSX2 Kan inte funka utan det!" + +msgid "Unrecoverable error while running recompiler" +msgstr "Ireparabelt fel under omkompilerarens körning" + +msgid "Usb" +msgstr "USB" + +msgid "VU Micro Log" +msgstr "VU Micro Log" + +msgid "VU0 Log" +msgstr "VU0 Log" + +msgid "Vif Log" +msgstr "VIF Log" + +msgid "button69" +msgstr "Knapp 69" + +msgid "button70" +msgstr "Knapp70" + +msgid "memWrite32" +msgstr "memWrite32" + +msgid "patch file for this game not found. Can't apply any patches\n" +msgstr "Hittade ingen patch för detta spel. Kan inte applicera patch. \n" + +msgid "written by..." +msgstr "Skrivet av..." + diff --git a/bin/Langs/sw_SW/LC_MESSAGES/pcsx2.mo b/bin/Langs/sw_SW/LC_MESSAGES/pcsx2.mo new file mode 100644 index 0000000000000000000000000000000000000000..d53edb0f45554734ad5bb87a03002253bd47420b GIT binary patch literal 10929 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

Pxox&d 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 + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
The Compatibility List Contains + 1814 Titles
+
99 235 120 645 715
0 Story SLPM 65002 JPS2Playable +
+
0.9.4
10,000 Bullets SLES 53481 EPS2Ingame +
+
0.9.4
18 Wheeler - American Pro Trucker SLES 50214 EPS2Ingame +
+
0.9.4
18 Wheeler - American Pro Trucker SLUS 20210 UPS2Playable +
+
0.9.4
187 Ride or Die SLUS 21116 UPS2Intro +
+
0.9.4
24 - The Game SLUS 21268 UPS2Ingame +
+
0.9.4
25 To Life SLUS 21016 UPS2Playable +
+
0.9.4
4x4 Evolution SLUS 20091 UPS2Intro +
+
0.9.4
7 Sins SLES 53280 EPS2Intro +
+
0.9.3
7 Sins SLES 53297 EPS2Nothing +
+
0.9.4
Ace Combat 4 - Shattered Skies PBPX 95201 JPS2Ingame +
+
0.9.4
Ace Combat 4 - Shattered Skies SLUS 20152 UPS2Menus +
+
0.9.4
Ace Combat 5 - The Unsung War SLUS 20851 UPS2Intro +
+
0.9.4
Ace Combat Zero - The Belkan War SLUS 21346 UPS2Intro +
+
0.9.4
Action Girlz Racing SLES 52956 EPS2Ingame +
+
0.9.2
Activision Anthology PBPX 95201 JPS2Playable +
+
0.9.4
Adventures Of Cookie And Cream, The SLUS 20170 UPS2Playable +
+
0.9.4
Aeon Flux SLUS 21205 UPS2Playable +
+
0.9.4
AFL Live 2003 SLES 51168 EPS2Playable +
+
0.9.2
Agassi - Tennis Generation SLUS 20446 UPS2Nothing +
+
0.9.4
Age of Empires II - The Age of Kings SLES 50282 EPS2Menus +
+
0.9.4
Aggressive Inline SLUS 20327 UPS2Playable +
+
0.9.4
Air Blade SCES 50246 EPS2Playable +
+
0.9.4
Airblade SLUS 20346 UPS2Playable +
+
0.9.4
Airborne Troops Countdown To DDay SLUS 21125 UPS2Menus +
+
0.9.4
Airforce Delta Strike SLKA 25133 JPS2Playable +
+
0.9.4
Akai ito SLPM 65732 JPS2Playable +
+
0.9.4
Akira Psycho Ball SLES 50919 EPS2Ingame +
+
0.9.4
Alex Ferguson's Player Manager 2001 SLES 50429 EPS2Ingame +
+
0.9.4
Alfa Romeo Racing Italiano SLUS 21321 UPS2Nothing +
+
0.9.4
Alias SLUS 20673 UPS2Ingame +
+
0.9.4
Alien Hominid SLES 53139 EPS2Playable +
+
0.9.2
Alien Hominid SLUS 21090 UPS2Playable +
+
0.9.4
Aliens VS Predator - Extinction SLUS 20147 UPS2Playable +
+
0.9.4
All Star Pro Wrestling 3 SLPM 65300 JPS2Intro +
+
0.9.4
Alone in the Dark 4 SLES 50185 EPS2Playable +
+
0.9.4
Alpine Racer 3 SCES 50887 EPS2Menus +
+
0.9.4
Alter Echo SLUS 20465 UPS2Ingame +
+
0.9.4
American Arcade PBPX 95201 JPS2Ingame +
+
0.9.4
American Chopper 2 SLUS 21288 UPS2Ingame +
+
0.9.4
AMF Xtreme Bowling SLUS 21347 UPS2Ingame +
+
0.9.4
Amplitude SCES 51706 EPS2Menus +
+
0.9.4
Amplitude SCUS 97258 UPS2Intro +
+
0.9.4
And1 Streetball SLUS 21237 UPS2Ingame +
+
0.9.2
Animaniacs - The Great Edgar Hunt SLES 52729 EPS2Ingame +
+
0.9.4
Another Century's Episode 2 SLPS 25623 JPS2Menus +
+
0.9.4
Ant Bully, The SLUS 21415 UPS2Menus +
+
0.9.4
Antz Extreme Racing SLUS 20392 UPS2Playable +
+
0.9.4
Anubis 2 SLES 53571 EPS2Playable +
+
0.9.4
Ape Escape - Million Monkeys SCPS 15115 JPS2Ingame +
+
0.9.2
Ape Escape - Pumped and Primed SLUS 21096 UPS2Intro +
+
0.9.4
Ape Escape 2 SLUS 20685 UPS2Playable +
+
0.9.4
Ape Escape 3 SCUS 97501 UPS2Nothing +
+
0.9.4
Aqua Aqua - Wetrix 2 PBPX 95201 JPS2Playable +
+
0.9.4
Aqua Aqua - Wetrix 2 SLES 50053 EPS2Intro +
+
0.9.4
Ar Tonelico - Melody Of Elemia SLUS 21445 UPS2Playable +
+
0.9.4
Arc The Lad - End of Darkness SLUS 21165 UPS2Ingame +
+
0.9.4
Arc the Lad - Twilight of the Spirits SCES 51910 EPS2Ingame +
+
0.9.2
Arc the Lad - Twilight of the Spirits SCUS 97231 UPS2Ingame +
+
0.9.2
Arcade USA SLES 53446 EPS2Intro +
+
0.9.4
Arcade, The SLES 52778 EPS2Playable +
+
0.9.4
Arcana Heart SLPM 66850 JPS2Playable +
+
0.9.4
Arctic Thunder SLUS 20217 UPS2Intro +
+
0.9.4
Area 51 SLES 52570 EPS2Intro +
+
0.9.2
Area 51 SLUS 20595 UPS2Intro +
+
0.9.4
Argos no Senshi SLPS 25178 JPS2Ingame +
+
0.9.4
Armored Core - Last Raven SLUS 21338 UPS2Menus +
+
0.9.4
Armored Core - Nine Breaker SLUS 21200 UPS2Playable +
+
0.9.4
Armored Core 2 - Another Age SLPS 25040 JPS2Playable +
+
0.9.2
Armored Core 2 SLUS 20014 UPS2Playable +
+
0.9.4
Armored Core 3 SLPS 25112 JPS2Playable +
+
0.9.2
Army Men - Green Rogue SLUS 20087 UPS2Ingame +
+
0.9.4
Army Men - Major Malfunction SLES 53996 EPS2Menus +
+
0.9.2
Army Men - Sarge's Heroes 2 SLES 50192 EPS2Menus +
+
0.9.2
Army Men - Sarge's Heroes 2 SLUS 20132 UPS2Intro +
+
0.9.4
Art Of Fighting Anthology SLUS 21487 UPS2Playable +
+
0.9.4
Artic Thunder SLUS 20217 UPS2Ingame +
+
0.9.2
Asterix And Obelix - Kick Buttix SLUS 20866 UPS2Intro +
+
0.9.4
Astro Boy SLUS 20867 UPS2Playable +
+
0.9.4
Atelier Iris - Eternal Mana SLUS 21113 UPS2Playable +
+
0.9.4
Atelier Iris - Grand Phantasm SLPM 66436 JPS2Menus +
+
0.9.2
Atelier Iris 2 - Azoth Of Destiny SLUS 21327 UPS2Playable +
+
0.9.4
Atelier Iris 3 - Grand Phantasm SLUS 21564 UPS2Menus +
+
0.9.4
Athens 2004 SCUS 97379 UPS2Playable +
+
0.9.4
ATV Offroad Fury 2 SCUS 97369 UPS2Menus +
+
0.9.4
ATV Offroad Fury 3 SCUS 97405 UPS2Ingame +
+
0.9.4
ATV Offroad Fury 4 SCUS 97479 UPS2Playable +
+
0.9.4
ATV Offroad Fury SCUS 97104 UPS2Menus +
+
0.9.4
ATV Quad Power Racing 2 SLUS 20570 UPS2Playable +
+
0.9.4
Auto Modellista SLES 51191 EPS2Playable +
+
0.9.4
Auto Modellista SLUS 20642 UPS2Playable +
+
0.9.4
Avatar - The Last Airbender - Burning Earth SLUS 21588 UPS2Ingame +
+
0.9.4
Avatar - The Last Airbender SLUS 21395 UPS2Playable +
+
0.9.4
Ayumi Hamasaki - A Visual Mix SLPM 65086 JPS2Menus +
+
0.9.4
Azur & Asmar SLES 54695 EPS2Ingame +
+
0.9.4
Backyard Football 2006 SLUS 20876 UPS2Playable +
+
0.9.4
Backyard Football 2008 SLUS 21670 UPS2Menus +
+
0.9.4
Backyard Wrestling SLUS 20638 UPS2Intro +
+
0.9.2
Bad Boys - Miami Takedown SLUS 20982 UPS2Ingame +
+
0.9.4
Baldurs Gate - Dark Alliance SLUS 20035 UPS2Menus +
+
0.9.4
Baphomets Fluch - Der Schlafende Drache SLES 51978 GPS2Playable +
+
0.9.2
Barbarian SLES 50972 EPS2Playable +
+
0.9.2
Barbarian SLUS 20136 UPS2Intro +
+
0.9.4
Barbie - The Island Princess SLUS 21684 UPS2Ingame +
+
0.9.4
Bard's Tale, The SLUS 20803 UPS2Intro +
+
0.9.4
Barnyard SLUS 21277 UPS2Intro +
+
0.9.2
Bass Strike SLUS 20325 UPS2Playable +
+
0.9.4
Batman - Rise Of Sin Tzu SLUS 20709 UPS2Intro +
+
0.9.4
Batman Begins SLUS 21198 UPS2Intro +
+
0.9.4
Batman Vengeance SLUS 20226 UPS2Intro +
+
0.9.4
Battle Assault 3 Featuring Gundam Seed SLUS 20929 UPS2Playable +
+
0.9.4
Battle Engine Aquila SLUS 20495 UPS2Intro +
+
0.9.4
Battle Stadium SLPS 25675 JPS2Playable +
+
0.9.4
Battlefield 2 - Modern Combat SLUS 21026 UPS2Ingame +
+
0.9.4
Battlestar Galactica SLUS 20421 UPS2Ingame +
+
0.9.4
Beach King Stunts Racer SLES 51383 EPS2Playable +
+
0.9.2
Beatdown - Fist of Vengeance SLUS 21150 UPS2Playable +
+
0.9.4
Beatmania IIDX 9th Style SLPM 65946 JPS2Playable +
+
0.9.4
Beatmania SLUS 21239 UPS2Playable +
+
0.9.4
Bee Movie Game SLUS 21622 UPS2Ingame +
+
0.9.4
Ben 10 - Protector Of Earth SLUS 21661 UPS2Playable +
+
0.9.4
Berserk SLPM 65688 JPS2Playable +
+
0.9.4
Beyond Good and Evil SLUS 20763 UPS2Intro +
+
0.9.4
Big Mutha Truckers 2 - Truck Me Harder SLUS 21086 UPS2Playable +
+
0.9.4
Big Mutha Truckers SLES 51355 EPS2Menus +
+
0.9.2
Big Mutha Truckers SLUS 20291 UPS2Nothing +
+
0.9.4
Bike Street Vert Dirt SLUS 20310 UPS2Playable +
+
0.9.4
Billiard Xciting SLES 51859 EPS2Nothing +
+
0.9.2
Biohazard 4 SLPM 66213 JPS2Playable +
+
0.9.4
Biohazard Code - Veronica SLPM 65022 JPS2Playable +
+
0.9.4
Bionicle Heroes SLUS 21428 UPS2Ingame +
+
0.9.4
Bionicle SLUS 20818 UPS2Ingame +
+
0.9.4
Black and Bruised SLUS 20506 UPS2Ingame +
+
0.9.4
Black Matrix 2 SLPS 20187 JPS2Ingame +
+
0.9.4
Black SLES 53886 EPS2Nothing +
+
0.9.2
Black SLUS 21376 UPS2Nothing +
+
0.9.4
Blade 2 SLUS 20360 UPS2Playable +
+
0.9.4
Blitz - The League SLUS 21128 UPS2Intro +
+
0.9.4
Blood Omen 2 SLUS 20024 UPS2Nothing +
+
0.9.4
Blood the Last Vampire - First Volume SCPS 15007 JPS2Playable +
+
0.9.4
Blood the Last Vampire - Second Volume SCPS 15008 JPS2Playable +
+
0.9.4
Blood+ Souyoku no Battle Rondo SCPS 15112 JPS2Playable +
+
0.9.4
BloodRayne 2 SLES 53831 EPS2Playable +
+
0.9.4
BloodRayne 2 SLUS 20862 UPS2Ingame +
+
0.9.4
Bloodrayne SLUS 20461 UPS2Playable +
+
0.9.4
Bloody Roar 3 SLES 50203 EPS2Ingame +
+
0.9.4
Bloody Roar 3 SLPM 62055 JPS2Ingame +
+
0.9.4
Bloody Roar 4 SLES 51877 EPS2Ingame +
+
0.9.2
Bloody Roar 4 SLUS 20795 UPS2Playable +
+
0.9.4
Blowout SLUS 20850 UPS2Playable +
+
0.9.4
BMX XXX SLES 50880 EPS2Playable +
+
0.9.2
BMX XXX SLUS 20415 UPS2Playable +
+
0.9.4
Bode Miller Alpine Skiing SLUS 21381 UPS2Nothing +
+
0.9.4
Boku to Mao SCPS 11008 JPS2Playable +
+
0.9.4
Bombastic SLES 51966 EPS2Menus +
+
0.9.4
Bombastic SLUS 20764 UPS2Menus +
+
0.9.4
Bomberman Hardball SLES 53301 EPS2Playable +
+
0.9.4
Bomberman Kart SLPM 62118 JPS2Playable +
+
0.9.2
Bouncer, The SCES 50241 EPS2Ingame +
+
0.9.2
Bouncer, The SLUS 20069 UPS2Ingame +
+
0.9.4
Brave - The Search For Spirit Dancer SLUS 21127 UPS2Menus +
+
0.9.4
Bravo Music SCPS 11013 JPS2Playable +
+
0.9.4
Breath of Fire - Dragon Quarter SLUS 20499 UPS2Ingame +
+
0.9.4
Brian Lara Cricket 2005 SLES 52717 EPS2Ingame +
+
0.9.3
Brian Lara International Cricket 2007 SLES 54230 EPS2Ingame +
+
0.9.4
Britney's Dance Beat SLUS 20402 UPS2Ingame +
+
0.9.4
Brothers In Arms - Earned In Blood SLUS 21310 UPS2Menus +
+
0.9.4
Brothers In Arms - Road To Hill 30 SLUS 21163 UPS2Menus +
+
0.9.4
Brunswick Pro Bowling SLUS 21566 UPS2Playable +
+
0.9.4
Buffy - Chaos Bleeds SLES 51890 EPS2Playable +
+
0.9.2
Buffy The Vampire Slayer - Chaos Bleeds SLUS 20566 UPS2Ingame +
+
0.9.4
Bujingai - The Forsaken City SLUS 20895 UPS2Playable +
+
0.9.4
Bully SLUS 21269 UPS2Ingame +
+
0.9.4
Burnout - Revenge SLES 53506 EPS2Nothing +
+
0.9.2
Burnout - Revenge SLUS 21242 UPS2Intro +
+
0.9.4
Burnout 2 SLUS 20497 UPS2Playable +
+
0.9.2
Burnout 3 - Takedown SLUS 21050 UPS2Intro +
+
0.9.4
Burnout Dominator SLUS 21596 UPS2Intro +
+
0.9.4
Burnout SLES 50445 EPS2Playable +
+
0.9.2
Burnout SLUS 20307 UPS2Playable +
+
0.9.4
Bust A Bloc SLES 51713 EPS2Ingame +
+
0.9.4
Butt Ugly Martians - Zoom Or Doom SLUS 20527 UPS2Ingame +
+
0.9.4
Buzz - The Big Quiz SCES 53925 EPS2Intro +
+
0.9.2
Cabelas - African Safari SLUS 21379 UPS2Ingame +
+
0.9.4
Cabelas - Alaskan Adventures SLUS 21384 UPS2Ingame +
+
0.9.4
Cabelas - Big Game Hunter SLUS 20534 UPS2Playable +
+
0.9.4
Cabelas - Dangerous Hunts 2 SLUS 21350 UPS2Playable +
+
0.9.4
Cabelas - Trophy Bucks SLUS 21624 UPS2Intro +
+
0.9.4
Call Of Duty - Finest Hour SLUS 20725 UPS2Intro +
+
0.9.4
Call Of Duty 2 - Big Red One SLUS 21228 UPS2Intro +
+
0.9.4
Call Of Duty 3 SLUS 21426 UPS2Intro +
+
0.9.4
Capcom Classics Collection Volume 2 SLUS 21473 UPS2Playable +
+
0.9.4
Capcom Fighting Jam SLES 52854 EPS2Playable +
+
0.9.4
Capcom Fighting Jam SLPM 65794 JPS2Playable +
+
0.9.4
Capcom VS. SNK 2 PBPX 95201 JPS2Playable +
+
0.9.4
Card Captor Sakura SLPS 20408 JPS2Ingame +
+
0.9.2
Carmen Sandiego - The Secret of The Stolen Drums SLES 52143 EPS2Ingame +
+
0.9.2
Carol Vordermans Sudoku SLUS 21495 UPS2Intro +
+
0.9.4
CART Fury Championship Racing SLUS 20141 UPS2Ingame +
+
0.9.4
Cartoon Network Racing SLUS 21438 UPS2Menus +
+
0.9.4
Casper - Spirit Dimensions SLUS 20205 UPS2Ingame +
+
0.9.4
Castle Shikigami 2 SLUS 20962 UPS2Nothing +
+
0.9.4
Castlevania - Curse of Darkness SLES 53755 EPS2Playable +
+
0.9.4
Castlevania - Curse of Darkness SLUS 21168 UPS2Playable +
+
0.9.4
Castlevania - Lament of Innocence SLUS 20733 UPS2Playable +
+
0.9.4
Castlevania SLES 52118 EPS2Menus +
+
0.9.2
Castlevania SLPM 65444 JPS2Playable +
+
0.9.4
Castleween SLES 51249 EPS2Ingame +
+
0.9.4
Catwoman SLUS 20992 UPS2Menus +
+
0.9.4
Celebrity Deathmatch SLUS 20604 UPS2Ingame +
+
0.9.4
Champions - Return To Arms SLUS 20973 UPS2Ingame +
+
0.9.4
Champions of Norrath - Realms of Everquest SLUS 20565 UPS2Ingame +
+
0.9.4
Chaos Legion SLUS 20695 UPS2Ingame +
+
0.9.4
Charlie and The Chocolate Factory SLES 53386 EPS2Playable +
+
0.9.2
Charlie and The Chocolate Factory SLUS 21246 UPS2Playable +
+
0.9.4
Charlie's Angels SLES 51750 EPS2Ingame +
+
0.9.2
Chess Challenger SLES 51630 EPS2Playable +
+
0.9.2
Chessmasters SLUS 20637 UPS2Nothing +
+
0.9.4
Choro Q SLUS 20930 UPS2Ingame +
+
0.9.4
Chou Aniki: Sei Naru Protein Densetsu SLPM 62403 JPS2Ingame +
+
0.9.4
Chronicles of Narnia, The - The Lion, The Witch and The Wardrobe SLES 53706 EPS2Intro +
+
0.9.2
Chronicles of Narnia, The - The Lion, The Witch and The Wardrobe SLUS 21082 UPS2Intro +
+
0.9.4
Chulip SLPS 20230 JPS2Playable +
+
0.9.4
Chulip SLUS 20742 UPS2Playable +
+
0.9.4
Clock Tower 3 SLES 51619 EPS2Playable +
+
0.9.4
Clock Tower 3 SLPM 65221 JPS2Playable +
+
0.9.4
Codename: Kids Next Door - Operation V.I.D.E.O.G.A.M.E SLES 53685 EPS2Ingame +
+
0.9.2
Cold Fear SLES 53158 EPS2Intro +
+
0.9.2
Cold Fear SLUS 21047 UPS2Playable +
+
0.9.4
Cold Winter SLUS 20845 UPS2Nothing +
+
0.9.4
Colin McRae 2005 SLES 52636 EPS2Ingame +
+
0.9.4
Colin McRae Rally 3 SLUS 20502 UPS2Ingame +
+
0.9.4
College Hoops 2006 SLUS 21232 UPS2Intro +
+
0.9.2
Colosseum - Road To Freedom SLUS 21179 UPS2Playable +
+
0.9.4
Combat Queen SLPM 65169 JPS2Playable +
+
0.9.4
Commandos - Strike Force SLUS 21103 UPS2Intro +
+
0.9.4
Commandos 2 SLUS 20086 UPS2Ingame +
+
0.9.4
Conflict - Desert Storm SLUS 20549 UPS2Ingame +
+
0.9.4
Conflict - Global Terror SLUS 21172 UPS2Ingame +
+
0.9.4
Conflict Desert Storm 2 - Back To Baghdad SLES 51523 EPS2Playable +
+
0.9.4
Conflict Zone SLUS 20240 UPS2Ingame +
+
0.9.4
Constantine SLUS 21142 UPS2Ingame +
+
0.9.4
Contra - Shattered Soldier SLUS 20306 UPS2Playable +
+
0.9.4
Cool Shot SLES 51785 EPS2Playable +
+
0.9.4
Cool Shot SLPS 20256 JPS2Playable +
+
0.9.4
Corvette Evolution GT SLUS 21499 UPS2Nothing +
+
0.9.4
Corvette SLUS 20858 UPS2Playable +
+
0.9.4
Crash Bandicoot - The Wrath of Cortex SLUS 20238 UPS2Playable +
+
0.9.4
Crash N Burn SLES 52339 EPS2Ingame +
+
0.9.2
Crash Nitro Karts SLUS 20649 UPS2Intro +
+
0.9.4
Crash Of The Titans SLUS 21583 UPS2Playable +
+
0.9.4
Crash Tag Team Racing SLUS 21191 UPS2Ingame +
+
0.9.4
Crash Twinsanity SLES 52568 EPS2Ingame +
+
0.9.2
Crash Twinsanity SLUS 20909 UPS2Playable +
+
0.9.4
Crazy Frog Racer SLES 53869 EPS2Nothing +
+
0.9.2
Crazy Taxi SLES 50215 EPS2Playable +
+
0.9.4
Crazy Taxi SLUS 20202 UPS2Playable +
+
0.9.2
Crimson Sea 2 SLUS 20877 UPS2Intro +
+
0.9.4
Crimson Tears SLPM 65575 JPS2Ingame +
+
0.9.2
Crimson Tears SLUS 20948 UPS2Playable +
+
0.9.4
Crouching Tiger Hidden Dragon SLUS 20523 UPS2Playable +
+
0.9.4
CSI: 3 Dimensions Of Murder SLUS 21655 UPS2Intro +
+
0.9.4
Culdcept SLUS 20774 UPS2Playable +
+
0.9.4
Curry House Coco Ichibanya SLPS 20367 JPS2Playable +
+
0.9.4
Cy Girls SLUS 20697 UPS2Ingame +
+
0.9.4
Cyclone Circus - Power Sail Racing SLES 52684 EPS2Nothing +
+
0.9.2
D1 Grand Prix SLUS 21416 UPS2Playable +
+
0.9.4
Da Vinci Code, The SLUS 21443 UPS2Ingame +
+
0.9.4
Dakar 2 SLES 50879 EPS2Ingame +
+
0.9.2
Dance Dance Revolution - Extreme 2 SLUS 21174 UPS2Ingame +
+
0.9.4
Dance Dance Revolution - Extreme SLPM 65358 JPS2Playable +
+
0.9.4
Dance Dance Revolution - Extreme SLUS 20916 UPS2Playable +
+
0.9.4
Dance Dance Revolution - Supernova 2 SLUS 21608 UPS2Playable +
+
0.9.4
Dance Dance Revolution - Supernova SLUS 21377 UPS2Ingame +
+
0.9.4
Dance Factory SLUS 21296 UPS2Menus +
+
0.9.4
Dance Summit 2001 - Bust A Move PBPX 95201 JPS2Intro +
+
0.9.4
Dancing Stage Fever MegaMix SLES 51510 EPS2Playable +
+
0.9.2
Dancing Stage Fever SLES 51757 EPS2Playable +
+
0.9.2
Dancing Stage Fusion SLES 52598 EPS2Playable +
+
0.9.4
Dancing With The Stars SLUS 21676 UPS2Playable +
+
0.9.4
Dark Angel - Vampire Apocalypse SLUS 20131 UPS2Ingame +
+
0.9.4
Dark Chronicle SCES 51190 EPS2Ingame +
+
0.9.4
Dark Cloud 2 SCUS 97213 UPS2Ingame +
+
0.9.4
Dark Cloud SCES 50295 EPS2Playable +
+
0.9.4
Dark Cloud SCUS 97111 UPS2Ingame +
+
0.9.4
Dark Summit SLUS 20300 UPS2Menus +
+
0.9.2
Darkwatch SLUS 21042 UPS2Playable +
+
0.9.4
Das Ding aus einer anderen Welt SLES 50976 GPS2Ingame +
+
0.9.3
Dave Mira Freestyle BMX 2 SLUS 20159 UPS2Playable +
+
0.9.4
Dawn Of Mana SLUS 21574 UPS2Ingame +
+
0.9.4
DDR Festival SLPM 65775 JPS2Playable +
+
0.9.4
DDR Max 2 SLPM 65277 JPS2Playable +
+
0.9.4
DDR Max SLPM 62154 JPS2Playable +
+
0.9.4
DDR Party Collection SLPM 62427 JPS2Ingame +
+
0.9.2
DDR Strike SLPM 66242 JPS2Playable +
+
0.9.4
DDRmax1 - Dance Dance Revolution SLUS 20437 UPS2Playable +
+
0.9.2
DDRmax2 - Dance Dance Revolution SLUS 20711 UPS2Playable +
+
0.9.2
Dead or Alive 2 - Hardcore PBPX 95201 JPS2Nothing +
+
0.9.2
Dead or Alive 2 - Hardcore SLUS 20071 UPS2Nothing +
+
0.9.4
Dead or Alive 2 SCES 50003 EPS2Nothing +
+
0.9.4
Dead or Alive 2 SLPS 25002 JPS2Nothing +
+
0.9.4
Dead Or Alive 2 SLUS 20071 UPS2Nothing +
+
0.9.4
Dead To Rights 2 SLUS 20843 UPS2Ingame +
+
0.9.4
Dead To Rights SLES 51581 EPS2Playable +
+
0.9.2
Dead To Rights SLUS 20220 UPS2Intro +
+
0.9.4
Death By Degrees SLUS 20934 UPS2Playable +
+
0.9.4
Defender SLUS 20191 UPS2Ingame +
+
0.9.4
Delta Force - Black Hawk Down SLUS 21124 UPS2Intro +
+
0.9.4
Delta Force - Black Hawk Down - Team Sabre SLUS21414 UPS2Intro +
+
0.9.4
Demento SLPM 65913 JPS2Playable +
+
0.9.4
Demolition Girl SLES 53403 EPS2Ingame +
+
0.9.4
Demonstone SLES 52669 EPS2Playable +
+
0.9.2
Destroy All Humans 2 - Make War Not Love SLUS 21439 UPS2Ingame +
+
0.9.4
Destroy All Humans SLUS 20945 UPS2Menus +
+
0.9.4
Destruction Derby Arenas SCES 50781 EPS2Ingame +
+
0.9.3
Destruction Derby Arenas SLUS 20855 UPS2Ingame +
+
0.9.4
Detonator SLES 52041 EPS2Ingame +
+
0.9.4
Deus Ex - The Conspiracy SLUS 20111 UPS2Menus +
+
0.9.4
Devil Kings SLUS 21297 UPS2Playable +
+
0.9.4
Devil May Cry 2 SLES 82011 EPS2Ingame +
+
0.9.4
Devil May Cry 2 SLPM 65232 JPS2Ingame +
+
0.9.4
Devil May Cry 2 SLUS 20484 UPS2Ingame +
+
0.9.4
Devil May Cry 3 - Special Edition SLPM 66160 JPS2Playable +
+
0.9.2
Devil May Cry 3 - Special Edition SLUS 21361 UPS2Playable +
+
0.9.4
Devil May Cry 3 SLES 53038 EPS2Playable +
+
0.9.2
Devil May Cry 3 SLPM 65880 JPS2Playable +
+
0.9.4
Devil May Cry 3 SLUS 20964 UPS2Intro +
+
0.9.2
Devil May Cry SLES 50358 EPS2Menus +
+
0.9.4
Devil May Cry SLPM 65038 JPS2Menus +
+
0.9.2
Devil May Cry SLUS 20216 UPS2Intro +
+
0.9.4
Dice SLUS 21181 UPS2Ingame +
+
0.9.4
Die Hard - Vendetta SLES 51347 EPS2Playable +
+
0.9.4
Die Hard - Vendetta SLES 51348 EPS2Ingame +
+
0.9.2
Digimon World - Data Squad SLUS 21598 UPS2Playable +
+
0.9.4
Digimon World 4 SLUS 20836 UPS2Playable +
+
0.9.4
Dino Crisis - Gun Survivor 3 SLPS 25026 JPS2Menus +
+
0.9.2
Dino Stalker SLUS 20485 UPS2Playable +
+
0.9.4
Dirge Of Cerberus - Final Fantasy VII SLES 54185 EPS2Ingame +
+
0.9.3
Dirge Of Cerberus - Final Fantasy VII SLPM 66271 JPS2Menus +
+
0.9.4
Dirge Of Cerberus - Final Fantasy VII SLUS 21419 UPS2Intro +
+
0.9.4
Disaster Report SLUS 20561 UPS2Playable +
+
0.9.4
Disgaea - Hour of Darkness SLUS 20666 UPS2Playable +
+
0.9.4
Disgaea 2 - Cursed Memories SLPS 25608 JPS2Playable +
+
0.9.2
Disgaea 2 - Cursed Memories SLUS 21397 UPS2Playable +
+
0.9.4
Disney Golf SLUS 20532 UPS2Intro +
+
0.9.4
Disney Pixar - Cars - Mater-National SLUS 21637 UPS2Ingame +
+
0.9.4
Disney Pixar - Cars SLES 53624 EPS2Playable +
+
0.9.2
Disney Princess - Enchanted Journey SLUS 21660 UPS2Ingame +
+
0.9.4
Disney's Extreme Skate Adventure SLUS 20607 UPS2Intro +
+
0.9.4
Disney's Stitch: Experiment 626 SCES 50960 EPS2Playable +
+
0.9.3
Disneys Pixar Cars SLUS 21151 UPS2Playable +
+
0.9.4
DNA Dark Native Apostle SLES 50202 EPS2Ingame +
+
0.9.3
Dodgeball SLES 53367 EPS2Playable +
+
0.9.2
Dog's Life SLUS 21018 UPS2Ingame +
+
0.9.4
Donald Duck Phantomias Platyrhynchos Kineticus SLES 50773 EPS2Ingame +
+
0.9.2
Donald Duck Quack Attack SLES 50048 EPS2Intro +
+
0.9.4
Dot Hack - Infection Part 1 SLUS 20267 UPS2Ingame +
+
0.9.4
Dot Hack - Mutation Part 2 SLES 52467 EPS2Ingame +
+
0.9.4
Dot Hack - Mutation Part 2 SLUS 20562 UPS2Ingame +
+
0.9.4
Dot Hack - Outbreak Part 3 SLUS 20563 UPS2Ingame +
+
0.9.4
Dot Hack - Quarantine Part 4 SLES 52468 EPS2Ingame +
+
0.9.4
Dot Hack GU Volume 1 - Rebirth - Terminal Disc SLUS 21480 UPS2Playable +
+
0.9.4
Dot Hack GU Volume 1 - Rebirth SLUS 21258 UPS2Ingame +
+
0.9.4
Dot Hack GU Volume 2 - Remnisce SLUS 21488 UPS2Ingame +
+
0.9.4
Dot Hack GU Volume 3 - Redemption SLUS 21489 UPS2Ingame +
+
0.9.4
Downforce SLES 50842 EPS2Ingame +
+
0.9.2
Downhill Domination SCUS 97177 UPS2Playable +
+
0.9.4
Dr. Muto SLUS 20458 UPS2Intro +
+
0.9.4
Dragon Quest VIII - Journey of the Cursed King SLES 53974 EPS2Playable +
+
0.9.2
Dragon Quest VIII SLUS 21207 UPS2Playable +
+
0.9.4
Dragonball Z Budokai 2 SLES 51839 EPS2Playable +
+
0.9.4
Dragonball Z Budokai 3 SLES 52730 EPS2Playable +
+
0.9.4
Dragonball Z Budokai Tenkaichi 2 SLUS 21441 UPS2Ingame +
+
0.9.4
Dragonball Z Budokai Tenkaichi SLES 53200 EPS2Ingame +
+
0.9.4
Dragonball Z Budokai Tenkaichi SLUS 21227 UPS2Playable +
+
0.9.4
Dragonball Z Budokai SLES 51233 EPS2Playable +
+
0.9.2
Dragonball Z Budokai SLUS 20591 UPS2Playable +
+
0.9.4
Dragonball Z Sagas SLUS 20874 UPS2Ingame +
+
0.9.4
Drakan - The Ancients Gate SCUS 97128 UPS2Intro +
+
0.9.4
Drakengard 2 SLUS 21373 UPS2Playable +
+
0.9.4
Drakengard SLUS 20732 UPS2Ingame +
+
0.9.4
DreamMix TV - World Fighters SLPM 65384 JPS2Playable +
+
0.9.4
Drive To Survive SLUS 21109 UPS2Intro +
+
0.9.4
Driver - Parallel Lines SLUS 21271 UPS2Ingame +
+
0.9.4
Driver 3 SLUS 20587 UPS2Ingame +
+
0.9.4
Driving Emotion Type-S SLPS 20007 JPS2Ingame +
+
0.9.2
Driving Emotion Type-S SLUS 20113 UPS2Ingame +
+
0.9.4
Drome Racers SLUS 20577 UPS2Ingame +
+
0.9.4
Dropship - United Peace Force SCES 50459 EPS2Intro +
+
0.9.2
Dropship - United Peace Force SLUS 20463 UPS2Menus +
+
0.9.4
DT Racer SLUS 21095 UPS2Intro +
+
0.9.4
DTM Race Driver 2 SLES 52638 EPS2Ingame +
+
0.9.2
DTM Race Driver SLES 50816 EPS2Playable +
+
0.9.2
Dual Hearts SLUS 20475 UPS2Menus +
+
0.9.4
Duel Masters SLUS 20924 UPS2Playable +
+
0.9.4
Dukes of Hazard SLUS 20959 UPS2Intro +
+
0.9.4
Dynasty Tactics 2 SLUS 20761 UPS2Intro +
+
0.9.4
Dynasty Warriors 3 SLES 50641 EPS2Playable +
+
0.9.4
Dynasty Warriors 3 SLUS 20277 UPS2Ingame +
+
0.9.4
Dynasty Warriors 4 - Empires SLUS 20653 UPS2Playable +
+
0.9.4
Dynasty Warriors 4 Xtreme Legends SLUS 20812 UPS2Playable +
+
0.9.4
Dynasty Warriors 4 SLUS 20653 UPS2Playable +
+
0.9.4
Dynasty Warriors 5 - Empires SLUS 21398 UPS2Ingame +
+
0.9.4
Dynasty Warriors 5 Xtreme Legends SLUS 21299 UPS2Playable +
+
0.9.4
Dynasty Warriors 5 SLUS 21153 UPS2Playable +
+
0.9.4
EA Arena Football SLUS 21337 UPS2Ingame +
+
0.9.4
EA Sports cricket 2004 SLES 52123 EPS2Ingame +
+
0.9.3
Eagle Eye Golf SLUS 21486 UPS2Playable +
+
0.9.4
Ecco the Dolphin - Defender of the Future SCES 50499 EPS2Intro +
+
0.9.4
Ecco the Dolphin - Defender of the Future SLUS 20394 UPS2Intro +
+
0.9.4
Echo Night Beyond SLUS 20928 UPS2Ingame +
+
0.9.4
Egg Mania - Eggstreme Madness SLUS 20452 UPS2Menus +
+
0.9.4
Eggomania SLES 51101 EPS2Playable +
+
0.9.2
Endgame SLUS 20389 UPS2Nothing +
+
0.9.4
Energy Airforce SLPM 65177 JPS2Playable +
+
0.9.2
Energy Airforce SLPS 25026 JPS2Playable +
+
0.9.4
Enter The Matrix SLES 51203 EPS2Playable +
+
0.9.4
Enter The Matrix SLUS 20454 UPS2Intro +
+
0.9.4
Enthusia Professional Racing SLUS 20967 UPS2Ingame +
+
0.9.4
Ephemeral Fantasia SLUS 20169 UPS2Menus +
+
0.9.4
Ephemeral Phantasia SLES 50110 EPS2Ingame +
+
0.9.2
Eragon SLUS 21322 UPS2Ingame +
+
0.9.4
Erementar Gerad SLPM 62623 JPS2Ingame +
+
0.9.2
Escape From Monkey Island SLUS 20181 UPS2Ingame +
+
0.9.4
Espgaluda SLPS 25352 JPS2Playable +
+
0.9.4
ESPN International Track And Field SLUS 20041 UPS2Playable +
+
0.9.4
ESPN NBA 2Night SLUS 20261 UPS2Menus +
+
0.9.2
ESPN NFL 2K5 SLUS 20919 UPS2Intro +
+
0.9.4
ESPN NHL Hockey SLUS 20728 UPS2Menus +
+
0.9.2
ESPN Winter X Games Snowboarding 2002 SLUS 20321 UPS2Playable +
+
0.9.4
Eternal Quest SLES 51624 EPS2Playable +
+
0.9.2
Eternal Ring SLUS 20015 UPS2Ingame +
+
0.9.4
Eureka Seven Volume 1 - The New Wave SLUS 21353 UPS2Ingame +
+
0.9.4
Euro Rally Champion SLES 52378 EPS2Ingame +
+
0.9.2
Eve Of Destruction SLUS 20270 UPS2Ingame +
+
0.9.4
Everblue 2 SLUS 20598 UPS2Intro +
+
0.9.4
Everblue SLES 50639 EPS2Playable +
+
0.9.2
Everblue SLPM 62081 JPS2Playable +
+
0.9.4
Evergrace SLES 50050 EPS2Playable +
+
0.9.2
Evergrace SLUS 20016 UPS2Ingame +
+
0.9.4
Everquest Online Adventures SCES 51725 EPS2Menus +
+
0.9.2
Evil Dead - A Fistful of Boomstick SLUS 20403 UPS2Ingame +
+
0.9.4
Evil Dead - Regeneration SLUS 21048 UPS2Playable +
+
0.9.4
Evil Twin - Cyprien's Chronicles SLES 50201 EPS2Playable +
+
0.9.4
Evil Twin PBPX 95201 EPS2Playable +
+
0.9.2
Evolution Snowboarding SLUS 20546 UPS2Intro +
+
0.9.4
Extermination SCUS 97112 UPS2Playable +
+
0.9.4
Eyetoy - Kinetic SCES 52883 EPS2Intro +
+
0.9.2
EyeToy - Monkey Mania SCES 52930 EPS2Intro +
+
0.9.4
Eyetoy - Play 2 SCES 52748 EPS2Intro +
+
0.9.2
Eyetoy - Sega Superstars SLES 52834 EPS2Nothing +
+
0.9.2
Eyetoy Play 2 SCUS 97468 UPS2Intro +
+
0.9.4
Eyetoy Play SCUS 97319 UPS2Intro +
+
0.9.4
F1 2001 SLES 50423 EPS2Intro +
+
0.9.2
F1 Career Challenge SLES 51584 EPS2Menus +
+
0.9.2
F1 Championship Season 2000 SLES 50017 EPS2Playable +
+
0.9.2
F1 Championship Season 2000 SLUS 20103 UPS2Ingame +
+
0.9.2
F1 Racing Championship SLES 50046 EPS2Ingame +
+
0.9.2
Family Feud SLUS 21446 UPS2Nothing +
+
0.9.4
Family Guy SLUS 21560 UPS2Playable +
+
0.9.4
Fantastic 4 SLUS 20615 UPS2Ingame +
+
0.9.4
Fantastic Four - Rise Of The Silver Surfer SLUS 21544 UPS2Ingame +
+
0.9.4
Fantavision SCPS 11002 JPS2Playable +
+
0.9.4
Fantavision SLUS 97015 UPS2Playable +
+
0.9.4
Fast and The Furious, The SLUS 21449 UPS2Ingame +
+
0.9.4
Fatal Frame 2 SLUS 20766 UPS2Menus +
+
0.9.4
Fatal Frame 3 SLUS 21244 UPS2Menus +
+
0.9.4
Fatal Frame SLUS 20388 UPS2Ingame +
+
0.9.4
Fatal Fury - Battle Archives 1 SLPS 25664 JPS2Playable +
+
0.9.2
Fatal Fury - Battle Archives Volume 1 SLUS 21537 UPS2Playable +
+
0.9.4
FC Barcelona Club Football SLES 51078 EPS2Ingame +
+
0.9.4
Ferrari F355 Challenge SCES 50956 EPS2Playable +
+
0.9.2
FIFA 07 SLES 54240 EPS2Ingame +
+
0.9.4
Fifa 2002 SLUS 20280 UPS2Intro +
+
0.9.2
Fifa 2005 SLUS 21051 UPS2Ingame +
+
0.9.4
FIFA Football 2003 SLES 51197 EPS2Ingame +
+
0.9.2
FIFA Soccer 2004 SLKA 25087 JPS2Ingame +
+
0.9.3
FIFA Street 2 SLES 53796 EPS2Ingame +
+
0.9.4
Fifa Street SLES 53064 EPS2Nothing +
+
0.9.3
Fifa World Cup 2002 SLES 50798 EPS2Intro +
+
0.9.2
Fight Club SLUS 20857 UPS2Ingame +
+
0.9.4
Fight Night - Round 3 SLUS 21383 UPS2Playable +
+
0.9.4
Fighter Maker 2 SLUS 20524 UPS2Playable +
+
0.9.4
Fighting Angels SLES 53408 EPS2Ingame +
+
0.9.4
Final Fantasy X - International Version SLPS 25088 JPS2Playable +
+
0.9.4
Final Fantasy X SCES 50490 EPS2Playable +
+
0.9.4
Final Fantasy X SCES 50492 GPS2Playable +
+
0.9.2
Final Fantasy X SLPS 25050 JPS2Playable +
+
0.9.4
Final Fantasy X SLUS 20312 UPS2Playable +
+
0.9.4
Final Fantasy X-2 International Version + Last Mission SLPM 65478 JPS2Playable +
+
0.9.4
Final Fantasy X-2 SLES 51815 EPS2Playable +
+
0.9.4
Final Fantasy X-2 SLES 51817 GPS2Ingame +
+
0.9.2
Final Fantasy X-2 SLUS 20672 UPS2Playable +
+
0.9.4
Final Fantasy XI Expansion Pack SCUS 97269 UPS2Intro +
+
0.9.4
Final Fantasy XI SCUS 97266 UPS2Menus +
+
0.9.4
Final Fantasy XI SLPS 25200 JPS2Menus +
+
0.9.4
Final Fantasy XII SLES 54354 EPS2Ingame +
+
0.9.4
Final Fantasy XII SLPM 66320 JPS2Playable +
+
0.9.4
Final Fantasy XII SLUS 20963 UPS2Ingame +
+
0.9.4
Final Fight - Streetwise SLUS 21238 UPS2Intro +
+
0.9.4
Finny The Fish & The Seven Waters SLUS 21072 UPS2Playable +
+
0.9.4
Fire Pro Wrestling Z SLPM 62342 JPS2Playable +
+
0.9.2
Fireblade SLUS 20198 UPS2Ingame +
+
0.9.2
Firefighter F.D. 18 SLUS 20724 UPS2Ingame +
+
0.9.4
Fisherman's Challenge SLES 51418 EPS2Nothing +
+
0.9.3
Fisherman's Challenge SLUS 20553 UPS2Intro +
+
0.9.4
Fishermans Bass Club SLUS 20428 UPS2Playable +
+
0.9.4
Flatout 2 SLES 54002 EPS2Ingame +
+
0.9.2
Flatout 2 SLUS 21251 UPS2Ingame +
+
0.9.4
Flatout SLUS 20901 UPS2Intro +
+
0.9.4
Flipnic SCPS 15050 JPS2Ingame +
+
0.9.4
Flipnic SLUS 21157 UPS2Intro +
+
0.9.4
Flow - Urban Dance Uprising SLES 53848 EPS2Ingame +
+
0.9.4
Flow - Urban Dance Uprising SLUS 21319 UPS2Intro +
+
0.9.4
Flucht von Monkey Island SLES 50227 GPS2Ingame +
+
0.9.2
Flushed Away SLUS 21484 UPS2Ingame +
+
0.9.4
Forbidden Siren SCES 52328 GPS2Ingame +
+
0.9.2
Ford Mustang - The Legend Lives SLES 53296 EPS2Ingame +
+
0.9.2
Forever Kingdom SLUS 20343 UPS2Menus +
+
0.9.4
Forgotten Realms - Demon Stone SLUS 20804 UPS2Ingame +
+
0.9.4
Formula 1 2006 SCES 53950 EPS2Intro +
+
0.9.4
Formula One 2001 SCES 50004 EPS2Ingame +
+
0.9.2
Formula One 2003 SCES 51592 EPS2Ingame +
+
0.9.2
Formula One 2005 SCES 53033 EPS2Intro +
+
0.9.2
Freak Out SLES 50310 EPS2Ingame +
+
0.9.4
Freaky Flyers SLUS 20284 UPS2Nothing +
+
0.9.4
Freedom Fighters SLUS 20658 UPS2Ingame +
+
0.9.4
Freestyle SLUS 20367 UPS2Ingame +
+
0.9.4
Frequency SCES 50791 EPS2Playable +
+
0.9.4
Frequency SCUS 97125 UPS2Playable +
+
0.9.4
Friends - The One With All The Trivia SLUS 21313 UPS2Playable +
+
0.9.4
Frogger - Ancient Shadows SLUS 21098 UPS2Nothing +
+
0.9.4
Froggers Adventures - The Rescue SLUS 20734 UPS2Playable +
+
0.9.4
Front Mission 4 SLUS 20888 UPS2Playable +
+
0.9.4
Fu-Un Bakumatsu-Den SLPM 65813 JPS2Menus +
+
0.9.2
Fugitive Hunter - War On Terror SLUS 20464 UPS2Ingame +
+
0.9.4
Full Metal Alchemist 2 - Curse of the Crimson Elixir SLUS 21166 UPS2Playable +
+
0.9.4
Full Metal Alchemist and The Broken Angel SLUS 20994 UPS2Playable +
+
0.9.4
Full Spectrum Warriors - Ten Hammers SLUS 21250 UPS2Intro +
+
0.9.4
Fur Fighters - Viggos Revenge SLUS 20088 UPS2Playable +
+
0.9.4
Futurama SLUS 20439 UPS2Playable +
+
0.9.4
Future Tactics - The Uprising SLUS 20859 UPS2Playable +
+
0.9.4
Gadget Racer SLUS 20225 UPS2Intro +
+
0.9.4
Galactic Wrestling SLUS 20822 UPS2Playable +
+
0.9.4
Galerians - Ash SLUS 20560 UPS2Ingame +
+
0.9.4
Gallop Racer 2006 SLUS 21393 UPS2Playable +
+
0.9.4
Gantz SLPM 65950 JPS2Playable +
+
0.9.2
Garfield 2 - Tale of Two Kitties SLES 54172 EPS2Ingame +
+
0.9.3
Gauntlet - Dark Legacy SLES 50211 EPS2Ingame +
+
0.9.3
Gauntlet - Dark Legacy SLUS 20047 UPS2Intro +
+
0.9.4
Gauntlet - Seven Sorrows SLUS 21077 UPS2Ingame +
+
0.9.4
Gegege no Kitaro SLPM 65337 JPS2Playable +
+
0.9.4
Gekibo 2 SLPS 20091 JPS2Playable +
+
0.9.4
Genji - Dawn of The Samurai SCUS 97471 UPS2Nothing +
+
0.9.4
Getaway - Black Monday, The SCUS 97408 UPS2Ingame +
+
0.9.4
Getaway, The SCES 51159 EPS2Ingame +
+
0.9.2
Ghost In The Shell - Stand Alone Complex SLUS 21006 UPS2Ingame +
+
0.9.4
Ghost Recon - Advanced Warfighter SLUS 21422 UPS2Intro +
+
0.9.4
Ghost Recon - Jungle Storm SLUS 20820 UPS2Playable +
+
0.9.4
Ghost Rider SLUS 21306 UPS2Intro +
+
0.9.4
Ghosthunter SCES 51463 EPS2Ingame +
+
0.9.4
Ghosthunter SLUS 20993 UPS2Intro +
+
0.9.4
Giants - Citizen Kabuto SLUS 20178 UPS2Nothing +
+
0.9.4
Gift SLES 50296 EPS2Ingame +
+
0.9.4
Girl Zone SLES 53190 EPS2Playable +
+
0.9.4
Gitaroo Man SLPM 65012 JPS2Playable +
+
0.9.4
Gitaroo Man SLUS 20294 UPS2Playable +
+
0.9.4
Gladiator - Sword of Vengeance SLUS 20793 UPS2Intro +
+
0.9.4
Gladius SLUS 20490 UPS2Playable +
+
0.9.4
Glass Rose SLPM 65373 JPS2Playable +
+
0.9.4
Goblin Commander - Unleash The Horde SLUS 20792 UPS2Playable +
+
0.9.4
God Hand SLPM 66550 JPS2Ingame +
+
0.9.4
God Hand SLUS 21503 UPS2Playable +
+
0.9.4
God Of War 2 SCUS 97481 UPS2Nothing +
+
0.9.4
God of War SCES 53133 EPS2Menus +
+
0.9.4
God of War SCUS 97399 UPS2Ingame +
+
0.9.4
GoDai Elemental Force SLES 50704 EPS2Ingame +
+
0.9.2
Godfather, The SLUS 21385 UPS2Intro +
+
0.9.4
Godzilla - Save The Earth SLUS 20809 UPS2Intro +
+
0.9.4
Goemon SLPM 65014 JPS2Nothing +
+
0.9.4
Golden Eye - Rogue Agent SLUS 21064 UPS2Nothing +
+
0.9.4
Gottlieb Pinball Classics SLES 54181 EPS2Playable +
+
0.9.4
Gradius 3 & 4 SLES 50038 EPS2Playable +
+
0.9.2
Gradius 3 & 4 SLPM 62007 JPS2Playable +
+
0.9.4
Gradius 3 & 4 SLUS 20040 UPS2Playable +
+
0.9.4
Gradius 5 SLPM 62462 JPS2Playable +
+
0.9.4
Gradius 5 SLUS 20712 UPS2Playable +
+
0.9.4
Graffiti Kingdom SLUS 21136 UPS2Playable +
+
0.9.4
Gran Turismo 3 A-spec SCES 50294 EPS2Ingame +
+
0.9.4
Gran Turismo 3 A-spec SCUS 97102 UPS2Ingame +
+
0.9.4
Gran Turismo 4 First Preview PCPX 96649 JPS2Ingame +
+
0.9.2
Gran Turismo 4 Prologue PBPX 95524 JPS2Ingame +
+
0.9.2
Gran Turismo 4 Prologue SCES 52438 EPS2Ingame +
+
0.9.2
Gran Turismo 4 SCAJ 30007 JPS2Ingame +
+
0.9.2
Gran Turismo 4 SCES 51719 EPS2Ingame +
+
0.9.4
Gran Turismo 4 SCUS 97328 UPS2Intro +
+
0.9.4
Gran Turismo Concept 2001 Tokyo SCPS 15010 JPS2Ingame +
+
0.9.2
Gran Turismo Concept 2002 Tokyo-Geneva SCES 50858 EPS2Menus +
+
0.9.4
Grand Prix Challenge SLES 51296 EPS2Ingame +
+
0.9.2
Grand Prix Challenge SLUS 20630 UPS2Nothing +
+
0.9.4
Grand Theft Auto - Liberty City Stories SLES 54135 EPS2Ingame +
+
0.9.4
Grand Theft Auto - Liberty City Stories SLUS 21423 UPS2Intro +
+
0.9.4
Grand Theft Auto - San Andreas SLES 52541 EPS2Ingame +
+
0.9.4
Grand Theft Auto - San Andreas SLUS 20946 UPS2Playable +
+
0.9.4
Grand Theft Auto - Vice City Stories SLUS 21590 UPS2Ingame +
+
0.9.4
Grand Theft Auto - Vice City SLES 51061-T EPS2Intro +
+
0.9.2
Grand Theft Auto - Vice City SLES 51061 EPS2Intro +
+
0.9.3
Grand Theft Auto - Vice City SLUS 20552 UPS2Intro +
+
0.9.4
Grand Theft Auto III SLES 50330-T EPS2Ingame +
+
0.9.2
Grand Theft Auto III SLUS 20062 UPS2Intro +
+
0.9.4
Grand Tour Racing 400 SLES 52045 EPS2Ingame +
+
0.9.4
Grandia 3 SLUS 21334 UPS2Ingame +
+
0.9.4
Grandia II SLUS 20194 UPS2Ingame +
+
0.9.4
Grandia III SLPM 65976 JPS2Ingame +
+
0.9.4
Grandia Xtreme SLUS 20417 UPS2Playable +
+
0.9.4
Great Escape, The SLES 51315 EPS2Ingame +
+
0.9.2
Great Escape, The SLUS 20670 UPS2Ingame +
+
0.9.4
Greg Hastings Tournament Paintball Max'd SLUS 21539 UPS2Ingame +
+
0.9.4
Gretzky NHL 06 SCUS 97466 UPS2Ingame +
+
0.9.4
Grim Adventures Of Billy And Mandy SLUS 21451 UPS2Playable +
+
0.9.4
Grim Grimoire SLPS 25771 JPS2Playable +
+
0.9.4
Grim Grimoire SLUS 21604 UPS2Playable +
+
0.9.4
Growlanser - Heritage of War SLUS 21571 UPS2Playable +
+
0.9.4
Growlanser 3 - The Dual Darkness SLUS 20759 UPS2Ingame +
+
0.9.4
Growlanser Generation SLUS 20758 UPS2Intro +
+
0.9.4
GT Racers SLES 52602 EPS2Ingame +
+
0.9.2
GTC Africa SLES 50472 EPS2Playable +
+
0.9.2
Guilty Gear Isuka SLES 53284 EPS2Playable +
+
0.9.2
Guilty Gear X Plus "By Your Side" SLPM 64525 JPS2Playable +
+
0.9.2
Guilty Gear X2 Reload SLES 52967 EPS2Playable +
+
0.9.2
Guilty Gear X2 SLUS 20436 UPS2Playable +
+
0.9.4
Guilty Gear XX - Accent Core SLPM 66746 JPS2Playable +
+
0.9.4
Guilty Gear XX - Accent Core SLUS 21652 UPS2Playable +
+
0.9.4
Guilty Gear XX - Slash Midnight Carnival SLPM 66333 JPS2Playable +
+
0.9.4
Guilty Gear XX SLPS 25184 JPS2Playable +
+
0.9.4
Guitar Freak 3 & Drummania 2 SLPM 65011 JPS2Playable +
+
0.9.4
Guitar Freak 4 & Drummania 3 SLPM 65052 JPS2Playable +
+
0.9.4
Guitar Freak and Drummania - Masterpiece Silver SLPM 66484 JPS2Ingame +
+
0.9.4
Guitar Hero - Rocks The 80's SLUS 21586 UPS2Playable +
+
0.9.4
Guitar Hero II SLES 54442 EPS2Ingame +
+
0.9.2
Guitar Hero II SLUS 21447 UPS2Ingame +
+
0.9.4
Guitar Hero III - Legends Of Rock SLUS 21672 UPS2Intro +
+
0.9.4
Guitar Hero SLES 54132 EPS2Ingame +
+
0.9.2
Guitar Hero SLUS 21224 UPS2Ingame +
+
0.9.4
Gumball 3000 SLES 50984 EPS2Menus +
+
0.9.3
Gun SLUS 21139 UPS2Intro +
+
0.9.4
Gunbird 1 & 2 SLPM 62469 JPS2Playable +
+
0.9.2
Gunbird Special Edition SLES 53021 EPS2Playable +
+
0.9.2
Gundam - Journey To Jaburo SLUS 20175 UPS2Playable +
+
0.9.4
Gungrave Overdose SLUS 21020 UPS2Ingame +
+
0.9.4
Gungrave SLUS 20493 UPS2Playable +
+
0.9.4
Gungriffon Blaze SLUS 20080 UPS2Playable +
+
0.9.4
Guy Game, The SLUS 21074 UPS2Playable +
+
0.9.4
Habitrail Hamster Ball SLES 52749 EPS2Playable +
+
0.9.4
Half Life SLUS 20066 UPS2Playable +
+
0.9.4
Half-Life SLES 50504 EPS2Ingame +
+
0.9.2
Half-Life SLES 50505 GPS2Playable +
+
0.9.2
Hanjuku Hero 4 SLPM 65839 JPS2Ingame +
+
0.9.4
Hanjuku Hero vs 3D SLPM 65315 JPS2Ingame +
+
0.9.4
Happy Feet SLES 54421 EPS2Ingame +
+
0.9.4
Happy Feet SLUS 21455 UPS2Intro +
+
0.9.4
Hard Rock Casino SLUS 21102 UPS2Playable +
+
0.9.4
Harley Davidson SLUS 21573 UPS2Ingame +
+
0.9.4
Harry Potter And The Order Of The Phoenix SLUS 21619 UPS2Intro +
+
0.9.4
Harvest Fishing SLES 53282 EPS2Playable +
+
0.9.2
Harvest Moon - A Wonderful Life SLUS 21171 UPS2Ingame +
+
0.9.4
Harvest Moon - Save The Homeland SLUS 20251 UPS2Playable +
+
0.9.4
Haunted Mansion SLUS 20681 UPS2Intro +
+
0.9.4
Haunting Ground SLUS 21075 UPS2Playable +
+
0.9.4
Haven - Call of The King SLES 51209 EPS2Menus +
+
0.9.2
Haven - Call of The King SLUS 20517 UPS2Ingame +
+
0.9.4
Headhunter - Redemption SLUS 20817 UPS2Ingame +
+
0.9.4
Heatseeker SLUS 21570 UPS2Ingame +
+
0.9.4
Heavy Metal Thunder SLPM 66030 JPS2Ingame +
+
0.9.4
Herdy Gerdy SLES 50751 EPS2Intro +
+
0.9.4
Herdy Gerdy SLUS 20231 UPS2Ingame +
+
0.9.4
Heroes of Might and Magic - Quest for the Dragon Bone Staff SLUS 20158 UPS2Playable +
+
0.9.4
Hidden Invasion SLUS 20301 UPS2Ingame +
+
0.9.4
High Rollers Casino SLUS 21014 UPS2Playable +
+
0.9.4
Hisshou Pachinko Pachi-Slot Kouryoku Series Vol. 7 - Fever Powerful + Zero SLPS 25697PS2Ingame +
+
0.9.3
History Channel's Civil War - The Game - Great Battles SLUS 21474 UPS2Ingame +
+
0.9.4
Hitman - Blood Money SLUS 21108 UPS2Intro +
+
0.9.4
Hitman - Contracts SLUS 20882 UPS2Intro +
+
0.9.4
Hitman 2 - Silent Assassin SLES 50992 EPS2Ingame +
+
0.9.4
Hitman 2 - Silent Assassin SLUS 20374 UPS2Ingame +
+
0.9.4
Hobbit, The SLUS 20655 UPS2Playable +
+
0.9.4
Hokuto no Ken SLPM 66660 JPS2Ingame +
+
0.9.4
Homura SLPM 62685 JPS2Ingame +
+
0.9.2
Horsez SLUS 21563 UPS2Menus +
+
0.9.4
Hot Shots Golf - Fore! SCUS 97401 UPS2Ingame +
+
0.9.4
Hot Shots Golf 3 SCUS 97130 UPS2Menus +
+
0.9.4
Hot Shots Tennis SCUS 97610 UPS2Playable +
+
0.9.4
Hot Wheels - Beat That SLUS 21628 UPS2Playable +
+
0.9.4
Hot Wheels - Stunt Track Challenge SLUS 20954 UPS2Playable +
+
0.9.4
Hot Wheels Velocity X - Maximum Justice SLUS 20412 UPS2Ingame +
+
0.9.4
Hot Wheels World Race SLUS 20737 UPS2Nothing +
+
0.9.4
Hummer Badlands SLUS 21357 UPS2Ingame +
+
0.9.4
Hungry Ghosts SCPS 15046 JPS2Playable +
+
0.9.4
Hunter - The Reckoning Wayward SLUS 20511 UPS2Intro +
+
0.9.4
Hustle, The - Detroit Streets SLUS 21335 UPS2Playable +
+
0.9.4
I-Ninja SLUS 20705 UPS2Ingame +
+
0.9.4
Ice Age 2 - The Meltdown SLUS 21307 UPS2Intro +
+
0.9.4
Ico SCES 50760 EPS2Ingame +
+
0.9.4
Ico SCPS 11003 JPS2Ingame +
+
0.9.4
Ico SCUS 97113 UPS2Ingame +
+
0.9.4
IGPX SLUS 21430 UPS2Ingame +
+
0.9.4
IHRA Drag Racing - Sportsman Edition SLUS 21256 UPS2Ingame +
+
0.9.4
IHRA Motor Sports Drag Racing 2 SLUS 20586 UPS2Playable +
+
0.9.4
Ikusa Gami SLKA 25320 KPS2Intro +
+
0.9.4
In The Groove SLUS 21177 UPS2Playable +
+
0.9.4
Incredible Hulk, The - Ultimate Destruction SLUS 20941 UPS2Ingame +
+
0.9.4
Incredibles, The - Rise of The Underminers SLUS 21217 UPS2Ingame +
+
0.9.4
Indiana Jones and The Emporers Tomb SLUS 20508 UPS2Intro +
+
0.9.4
Indiana Jones und die Legende der Kaisergruft SLES 50838 GPS2Menus +
+
0.9.2
Indigo Prophecy SLUS 21196 UPS2Nothing +
+
0.9.4
IndyCar Series SLUS 20641 UPS2Ingame +
+
0.9.3
Inspector Gadget - Mad Robots Invasion SLES 51247 EPS2Playable +
+
0.9.4
International Track and Field PBPX 95201 EPS2Playable +
+
0.9.4
Inuyasha - Feudal Combat SLUS 21193 UPS2Intro +
+
0.9.4
Inuyasha: Secret of The Cursed Mask SLUS 20913 UPS2Playable +
+
0.9.4
IQ Remix Plus SCPS 11001 JPS2Playable +
+
0.9.4
Island Xtreme Stunts SLUS 20575 UPS2Ingame +
+
0.9.4
ISS Pro 2000 PBPX 95201 UPS2Ingame +
+
0.9.2
Itudaki Street Special SLPM 65797 JPS2Playable +
+
0.9.4
Jackass - The Game SLUS 21627 UPS2Ingame +
+
0.9.4
Jade Cocoon 2 SLES 50735 EPS2Playable +
+
0.9.3
Jade Cocoon 2 SLUS 20309 UPS2Playable +
+
0.9.4
Jak 1 - The Precursor Legacy SCUS 97124 UPS2Ingame +
+
0.9.4
Jak 2 SCUS 97265 UPS2Intro +
+
0.9.4
Jak 3 SCUS 97330 UPS2Intro +
+
0.9.4
Jak and Daxter: the Precursor Legacy SCES 50361 EPS2Intro +
+
0.9.2
Jak X Combat Racing SCUS 97429 UPS2Nothing +
+
0.9.4
James Bond 007 - Agent Under Fire SLUS 20265 UPS2Intro +
+
0.9.4
James Bond 007 - Everything or Nothing SLES 52005 EPS2Intro +
+
0.9.4
James Bond 007 - Everything or Nothing SLUS 20751 UPS2Ingame +
+
0.9.4
James Bond 007 - From Russia With Love SLUS 21282 UPS2Intro +
+
0.9.4
James Bond 007 - Nightfire SLUS 20579 UPS2Nothing +
+
0.9.4
James Bond 007 Nightfire SLES 51260 EPS2Ingame +
+
0.9.2
James Cameron's Dark Angel SLES 51333 EPS2Ingame +
+
0.9.2
James Pond - Codename Robocod SLES 53682 EPS2Playable +
+
0.9.4
Jaws Unleashed SLUS 21062 UPS2Intro +
+
0.9.4
Jeapordy SLUS 20789 UPS2Playable +
+
0.9.4
Jikkyou Powerful Pro Yakyuu 13 SLPM 66450 JPS2Playable +
+
0.9.2
Judge Dredd - Dredd VS Death SLUS 20869 UPS2Intro +
+
0.9.4
Juiced 2 - Hot Import Nights SLUS 21593 UPS2Ingame +
+
0.9.4
Juiced SLES 53044 EPS2Playable +
+
0.9.2
Juiced SLUS 20872 UPS2Ingame +
+
0.9.4
Jumanji SLES 54382 EPS2Playable +
+
0.9.4
Junior Board Games SLES 52776 EPS2Intro +
+
0.9.2
Just Cause SLUS 21436 UPS2Ingame +
+
0.9.4
Justice League Heroes SLUS 21304 UPS2Ingame +
+
0.9.4
K1 World Grand Prix SLUS 20682 UPS2Ingame +
+
0.9.4
Ka SCPS 11009 JPS2Ingame +
+
0.9.4
Kaena SLPS 25295 JPS2Playable +
+
0.9.2
Kamen Rider 555 SLPS 20329 JPS2Ingame +
+
0.9.2
Kamen Rider Blade SLPS 20402 JPS2Ingame +
+
0.9.2
Kao The Kangaroo - Round 2 SLUS 20296 UPS2Playable +
+
0.9.4
Karaoke Revolution - American Idol SLUS 21540 UPS2Menus +
+
0.9.4
Karaoke Revolution - Country SLUS 21329 UPS2Menus +
+
0.9.4
Karaoke Revolution - Love and Ballad SLPM 62382 JPS2Ingame +
+
0.9.4
Karaoke Revolution Party SLUS 21223 UPS2Menus +
+
0.9.4
Katamari Damacy SLPS 25360 JPS2Ingame +
+
0.9.4
Katamari Damacy SLUS 21008 UPS2Ingame +
+
0.9.4
Kelly Slater Pro Surfer SLUS 20334 UPS2Playable +
+
0.9.4
Kengo - Master of Bushido SLES 50114 EPS2Playable +
+
0.9.2
Kengo - Master of Bushido SLUS 20021 UPS2Playable +
+
0.9.4
Kengo 2 SLPS 25107 JPS2Nothing +
+
0.9.4
Kensetsu Juuki Kenka Battle - Buchigire Kongou!! SLPS 25004 JPS2Ingame +
+
0.9.4
Keroro Gunsou: MeroMero Battle Royale Z SLPS 25575 JPS2Intro +
+
0.9.2
Kessen 2 SLUS 20275 UPS2Playable +
+
0.9.4
Kessen 3 SLES 53121 GPS2Nothing +
+
0.9.2
Kessen 3 SLUS 21119 UPS2Playable +
+
0.9.4
Kessen SLUS 20098 UPS2Playable +
+
0.9.4
Kill Switch SLUS 20706 UPS2Nothing +
+
0.9.4
Killer 7 SLES 53366 EPS2Playable +
+
0.9.4
Killer 7 SLUS 21154 UPS2Playable +
+
0.9.4
Killzone SCUS 97402 UPS2Ingame +
+
0.9.4
Kinetica SCUS 97132 UPS2Nothing +
+
0.9.4
King Arthur SLUS 21046 UPS2Ingame +
+
0.9.4
King Kong SLES 53703 EPS2Intro +
+
0.9.4
King Kong SLUS 21311 UPS2Ingame +
+
0.9.4
King of Colosseum II SLPM 65602 JPS2Ingame +
+
0.9.4
King of Fighters - Maximum Impact SLUS 20923 UPS2Playable +
+
0.9.4
King of Fighters - Neowave SLPS 25525 JPS2Playable +
+
0.9.2
King of Fighters 2000 SLUS 20834 UPS2Playable +
+
0.9.2
King of Fighters 2001, The SLUS 20839 UPS2Playable +
+
0.9.2
King of Fighters 2006 SLUS 21365 UPS2Playable +
+
0.9.4
King of Fighters XI, The SLPS 25660 JPS2Playable +
+
0.9.4
King's Field - The Ancient City SLUS 20318 UPS2Playable +
+
0.9.4
King's Field 4 SLES 50920 EPS2Playable +
+
0.9.2
Kingdom Hearts - Final Mix SLPS 25198 JPS2Playable +
+
0.9.4
Kingdom Hearts II Final Mix SLPM 66675 JPS2Ingame +
+
0.9.4
Kingdom Hearts II SLES 54114 EPS2Ingame +
+
0.9.4
Kingdom Hearts II SLPM 66233 JPS2Ingame +
+
0.9.4
Kingdom Hearts II SLUS 21005 UPS2Ingame +
+
0.9.4
Kingdom Hearts SCES 50967 EPS2Playable +
+
0.9.2
Kingdom Hearts SCES 50968 FPS2Playable +
+
0.9.2
Kingdom Hearts SCES 50969 GPS2Playable +
+
0.9.2
Kingdom Hearts SLUS 20370 UPS2Playable +
+
0.9.4
Klonoa 2 - Lunatea's Veil SCES 50354 EPS2Playable +
+
0.9.4
Klonoa 2 - Lunatea's Veil SLUS 20151 UPS2Playable +
+
0.9.4
Knight Rider 2 SLES 52836 EPS2Playable +
+
0.9.4
Knight Rider SLES 51011 EPS2Playable +
+
0.9.4
Knights of the Temple II SLES 53645 EPS2Menus +
+
0.9.2
Knockout Kings 2002 SLUS 20369 UPS2Ingame +
+
0.9.4
Konohana 4 SLPM 65690 JPS2Playable +
+
0.9.2
Kunoichi SLPM 65447 JPS2Ingame +
+
0.9.4
Kuon SLPS 25329 JPS2Ingame +
+
0.9.4
Kuon SLUS 21007 UPS2Playable +
+
0.9.4
Kuri Kuri Mix SLES 50224 EPS2Playable +
+
0.9.4
Kya - Dark Lineage SLUS 20440 UPS2Intro +
+
0.9.4
L.A. Rush SLUS 21112 UPS2Ingame +
+
0.9.4
La Petite Princess - Twinkle Star Spirits SLPS 25534 JPS2Intro +
+
0.9.4
La Pucelle Tactics SLUS 20847 UPS2Playable +
+
0.9.4
Lake Masters EX SLES 50351 EPS2Playable +
+
0.9.4
Lara Croft Tomb Raider - The Angel of Darkness SLES 51227 EPS2Ingame +
+
0.9.4
Largo Winch SLES 51093 EPS2Nothing +
+
0.9.2
Last Blade 1 & 2 SLPS 25503 JPS2Intro +
+
0.9.4
Le Mans 24 Hours SLES 50131 EPS2Playable +
+
0.9.4
League Series Baseball SLES 51718 EPS2Playable +
+
0.9.2
Legaia 2 - Duel Saga SLUS 20414 UPS2Playable +
+
0.9.4
Legend Of Alon Dar SLUS 20045 UPS2Playable +
+
0.9.4
Legend of Kay, The SLES 52931 EPS2Menus +
+
0.9.2
Legend of Kay, The SLUS 21248 UPS2Ingame +
+
0.9.4
Legend of Spyro - A New Beginning SLUS 21372 UPS2Ingame +
+
0.9.4
Legend Of The Dragon SLUS 21612 UPS2Playable +
+
0.9.4
Legends Of Wrestling 2 SLUS 20507 UPS2Playable +
+
0.9.4
Legion - Legend of Excalibur SLUS 20048 UPS2Playable +
+
0.9.4
Lego Racers 2 SLUS 20042 UPS2Ingame +
+
0.9.4
Lego Soccer Mania SLUS 20456 UPS2Playable +
+
0.9.4
Lego Star Wars SLUS 21083 UPS2Ingame +
+
0.9.4
Leisure Suit Larry SLUS 20956 UPS2Playable +
+
0.9.4
Lemmings SCES 54145 EPS2Playable +
+
0.9.4
Lemony Snickets - A Series Of Unfortunate Events SLUS 21078 UPS2Intro +
+
0.9.4
Lethal Skies - Elite Pilot Team SW SLUS 20386 UPS2Playable +
+
0.9.4
Lethal Skies 2 SLUS 20735 UPS2Nothing +
+
0.9.4
Lifeline SLUS 20848 UPS2Intro +
+
0.9.4
London Racer - Police Madness SLES 53536 EPS2Playable +
+
0.9.4
London Racer 2 SLES 50955 EPS2Ingame +
+
0.9.2
Looney Tunes - Acme Arsenal SLUS 21636 UPS2Playable +
+
0.9.4
Looney Tunes - Back In Action SLUS 20853 UPS2Ingame +
+
0.9.4
Looney Tunes Back in Action SLES 51794 EPS2Ingame +
+
0.9.2
Looney Tunes Space Race SLES 50487 EPS2Playable +
+
0.9.4
Lord of the Rings - The Return of the King SLES 52017 EPS2Ingame +
+
0.9.4
Lord of the Rings - The Return of the King SLPM 65503 JPS2Ingame +
+
0.9.4
Lord of the Rings - The Two Towers SLES 51252 EPS2Ingame +
+
0.9.2
Lord of the Rings - The Two Towers SLPS 29004 JPS2Intro +
+
0.9.4
Lotus Challenge SLES 50230 EPS2Ingame +
+
0.9.2
Love Com - Punch de Court SLPM 66470 JPS2Playable +
+
0.9.4
Lowrider SLUS 20676 UPS2Ingame +
+
0.9.4
Lucinda Green's Equestrain Challenge SLUS 21401 UPS2Ingame +
+
0.9.4
Lumines Plus Puzzle Fusion SLUS 21553 UPS2Playable +
+
0.9.4
Lumines Plus SLES 54336 EPS2Playable +
+
0.9.4
Mace Griffin - Bounty Hunter SLUS 20505 UPS2Ingame +
+
0.9.4
Mad Maestro SLUS 20376 UPS2Playable +
+
0.9.4
Madagascar SLES 53226 EPS2Intro +
+
0.9.2
Madden NFL 2005 SLUS 21000 UPS2Ingame +
+
0.9.4
Madden NFL 2006 SLUS 21213 UPS2Ingame +
+
0.9.4
Madden NFL 2007 SLUS 21476 UPS2Ingame +
+
0.9.4
Madden NFL 2008 SLUS 21638 UPS2Ingame +
+
0.9.4
Made Men: Confessions Of The Family Blood SLUS 21587 UPS2Playable +
+
0.9.4
Mafia SLUS 20671 UPS2Nothing +
+
0.9.4
Magic Pengel - The Quest for Color SLUS 20593 UPS2Playable +
+
0.9.4
MAGIX Music Maker SLUS 20609 UPS2Intro +
+
0.9.4
Magna Carta - Tears of Blood SLUS 21221 UPS2Ingame +
+
0.9.4
Mahjong Party: Swim Suit Beauty SCAJ 10010 JPS2Playable +
+
0.9.4
Makai Kingdom SLUS 21170 UPS2Playable +
+
0.9.4
Malice SLES 52413 EPS2Ingame +
+
0.9.2
Malice SLUS 20358 UPS2Intro +
+
0.9.4
Manhunt 2 SLUS 21613 UPS2Ingame +
+
0.9.4
Manhunt SLUS 20827 UPS2Menus +
+
0.9.4
Marc Ecko's Getting Up - Contents Under Pressure SLUS 21032 UPS2Ingame +
+
0.9.4
Mark Of Kri SCUS 97140 UPS2Ingame +
+
0.9.4
Marvel - Ultimate Alliance SLUS 21374 UPS2Playable +
+
0.9.4
Marvel Nemesis - Rise of the Imperfects SLES 53585 EPS2Menus +
+
0.9.2
Marvel Nemesis - Rise of the Imperfects SLUS 21281 UPS2Menus +
+
0.9.4
Marvel vs Capcom 2 SLPM 62227 JPS2Playable +
+
0.9.4
Marvel vs Capcom 2 SLUS 20486 UPS2Playable +
+
0.9.4
Master Chess SLES 52295 EPS2Playable +
+
0.9.2
Masters of The Universe - He-Man - Defender of Grayskull SLES 53035 EPS2Menus +
+
0.9.2
Matching Maker SLPS 20469 JPS2Nothing +
+
0.9.4
Matrix, The - Path of Neo SLUS 21273 UPS2Ingame +
+
0.9.4
Matt Hoffman - Pro BMX 2 SLUS 20335 UPS2Playable +
+
0.9.4
Max Payne 2 - The Fall of Max Payne SLES 52336 EPS2Ingame +
+
0.9.2
Max Payne 2 - The Fall of Max Payne SLUS 20814 UPS2Intro +
+
0.9.4
Max Payne SLES 50325 EPS2Ingame +
+
0.9.2
Max Payne SLUS 20230 UPS2Playable +
+
0.9.4
Maximo - Ghosts To Glory SLUS 20017 UPS2Nothing +
+
0.9.4
Maximo Machine Monster no Yabou SLPM 65367 JPS2Ingame +
+
0.9.4
Maximo Vs Army of Zin SLUS 20722 UPS2Intro +
+
0.9.4
Maximo SLPM 62127 JPS2Ingame +
+
0.9.4
Medal Of Honor - European Assault SLUS 21199 UPS2Ingame +
+
0.9.4
Medal of Honor - Frontline SLUS 20368 UPS2Menus +
+
0.9.4
Medal Of Honor - Rising Sun SLES 51873 EPS2Ingame +
+
0.9.3
Medal Of Honor - Rising Sun SLUS 20753 UPS2Playable +
+
0.9.4
Medal Of Honor - Vanguard SLUS 21597 UPS2Ingame +
+
0.9.4
Meet The Robinsons SLUS 21453 UPS2Ingame +
+
0.9.4
Megaman Anniversary Collection SLUS 20833 UPS2Playable +
+
0.9.4
Megaman X - Command Mission SLUS 20903 UPS2Playable +
+
0.9.4
Megaman X Collection SLUS 21370 UPS2Playable +
+
0.9.4
Megaman X7 SLUS 20487 UPS2Intro +
+
0.9.2
Megaman X8 SLUS 20960 UPS2Playable +
+
0.9.4
Meiwaku Seijin - Panic Maker SLPM 65642 JPS2Playable +
+
0.9.4
Melty Blood - Act Cadenza - SLPM 66438 JPS2Playable +
+
0.9.4
Men In Black 2 - Alien Escape SLUS 20373 UPS2Intro +
+
0.9.4
Mercenaries - Playground of Destruction SLUS 20932 UPS2Intro +
+
0.9.4
Mercury Meltdown Remix SLES 54432 EPS2Playable +
+
0.9.4
Mercury Meltdown Remix SLUS 21548 UPS2Playable +
+
0.9.4
Metal Arms - Glitch In The System SLUS 20786 UPS2Nothing +
+
0.9.4
Metal Gear Sold 2, Document of SLES 82010 EPS2Ingame +
+
0.9.4
Metal Gear Sold 2, Document of SLUS 20543 UPS2Ingame +
+
0.9.4
Metal Gear Solid 2 - Sons of Liberty SLES 50383 EPS2Ingame +
+
0.9.3
Metal Gear Solid 2 - Sons of Liberty SLUS 20144 UPS2Ingame +
+
0.9.4
Metal Gear Solid 2 - Substance SLES 82009 EPS2Nothing +
+
0.9.4
Metal Gear Solid 2 - Substance SLUS 20554 UPS2Nothing +
+
0.9.4
Metal Gear Solid 3 - Snake Eater SLES 82013 EPS2Ingame +
+
0.9.4
Metal Gear Solid 3 - Snake Eater SLUS 20915 UPS2Ingame +
+
0.9.4
Metal Gear Solid 3 - Subsistence SLUS 21359 UPS2Nothing +
+
0.9.4
Metal Saga SLUS 21293 UPS2Playable +
+
0.9.4
Metal Slug 3 SLPS 25209 JPS2Playable +
+
0.9.2
Metal Slug 4 SLPS 25376 JPS2Playable +
+
0.9.4
Metal Slug 4 SLUS 20971 UPS2Playable +
+
0.9.4
Metal Slug 5 SLES 53383 EPS2Playable +
+
0.9.2
Metal Slug 5 SLPS 25495 JPS2Playable +
+
0.9.2
Metal Slug 5 SLUS 20990 UPS2Playable +
+
0.9.4
Metal Slug 6 SLPS 25674 JPS2Playable +
+
0.9.4
Metal Slug Anthology SLUS 21550 UPS2Playable +
+
0.9.4
Metal Slug SLPS 25650 JPS2Playable +
+
0.9.2
Metropolismania SLUS 20377 UPS2Playable +
+
0.9.4
Micro Machines V4 SLUS 21402 UPS2Intro +
+
0.9.4
Midnight Club 3 Dub Edition Remix SLUS 21355 UPS2Ingame +
+
0.9.4
Midnight Club 3 Dub Edition SLUS 21029 UPS2Ingame +
+
0.9.4
Midnight Club II SLES 51054 EPS2Menus +
+
0.9.3
Midnight Club II SLUS 20209 UPS2Ingame +
+
0.9.4
Midnight Club SLUS 20063 UPS2Playable +
+
0.9.4
Midway Arcade Treasures 2 SLUS 20997 UPS2Playable +
+
0.9.4
Midway Arcade Treasures 3 SLUS 21094 UPS2Menus +
+
0.9.4
Mike Tyson Heavy Weight Boxing SLES 50396 EPS2Ingame +
+
0.9.4
Minna Daisuki Katamari Damacy SLPS 25467 JPS2Ingame +
+
0.9.4
Minority Report - Everybody Runs SLUS 20331 UPS2Playable +
+
0.9.2
Mission Impossible - Operation Surma SLUS 20400 UPS2Ingame +
+
0.9.4
Mister Mosquito SLUS 20375 UPS2Ingame +
+
0.9.4
Mobile Light Force 2 SLUS 20608 UPS2Playable +
+
0.9.4
Mobile Suit Gundam - Encounters In Space SLUS 20740 UPS2Playable +
+
0.9.4
Mobile Suit Gundam - Gundam VS Zeta Gundam SLUS 20821 UPS2Playable +
+
0.9.4
Mobile Suit Gundam Seed - Never Ending Tomorrow SLUS 21140 UPS2Playable +
+
0.9.4
Mobile Suit Gundam Zeonic Front SLUS 20233 UPS2Playable +
+
0.9.4
Mojib-ribon SCPS 11033 JPS2Intro +
+
0.9.4
Mojo! SLES 52111 EPS2Ingame +
+
0.9.4
Monochrome SLPM 66195 JPS2Playable +
+
0.9.4
Monopoly Party SLUS 20348 UPS2Ingame +
+
0.9.4
Monster House SLES 54215 EPS2Ingame +
+
0.9.4
Monster House SLUS 21400 UPS2Intro +
+
0.9.4
Monster Hunter 2 SLPM 66280 JPS2Playable +
+
0.9.4
Monster Hunter G SLPM 65869 JPS2Playable +
+
0.9.4
Monster Hunter SLES 52707 EPS2Playable +
+
0.9.4
Monster Hunter SLUS 20896 UPS2Playable +
+
0.9.4
Monster Rancher 3 SLUS 20190 UPS2Playable +
+
0.9.4
Monster Rancher EVO SLUS 21330 UPS2Playable +
+
0.9.4
Monster Trux Extreme - Arena Edition SLES 52750 EPS2Playable +
+
0.9.4
Monster World Complete Collection SLPM 62760 JPS2Playable +
+
0.9.4
Monsters Inc SCUS 97123 UPS2Playable +
+
0.9.4
Mortal Kombat - Armageddon SLUS 21410 UPS2Playable +
+
0.9.4
Mortal Kombat - Deadly Alliance SLUS 20423 UPS2Playable +
+
0.9.2
Mortal Kombat - Deception SLES 52705 EPS2Ingame +
+
0.9.2
Mortal Kombat - Deception SLUS 20881 UPS2Playable +
+
0.9.4
Mortal Kombat - Shaolin Monks SLUS 21087 UPS2Menus +
+
0.9.4
Mortal Kombat 1 - Deception Premium Pack Bonus Disc SLUS 21081 UPS2Playable +
+
0.9.4
Motocross Mania 3 SLUS 21229 UPS2Ingame +
+
0.9.4
MotoGP 07 SLUS 21688 UPS2Nothing +
+
0.9.4
MotoGP 2 SLUS 20285 UPS2Ingame +
+
0.9.2
MotoGP 3 SCES 50982 EPS2Ingame +
+
0.9.2
MotoGP 4 SCES 52892 EPS2Ingame +
+
0.9.2
MotoGP 4 SLUS 21159 UPS2Playable +
+
0.9.4
MotoGP SCES 50034 EPS2Ingame +
+
0.9.2
Motorbike King SLES 52518 EPS2Playable +
+
0.9.2
Mouse Police, The SLES 52370 EPS2Playable +
+
0.9.2
MS Saga - A New Dawn SLUS 21270 UPS2Playable +
+
0.9.4
MTV Music Generator 3 SLUS 20861 UPS2Playable +
+
0.9.4
MTX Mototrax SLUS 20399 UPS2Intro +
+
0.9.4
Mummy Returns, The SLES 50510 EPS2Ingame +
+
0.9.2
Mummy Returns, The SLUS 20253 UPS2Ingame +
+
0.9.4
Muppets Party Cruise SLUS 20635 UPS2Intro +
+
0.9.4
Musashi Samurai Legend SLUS 20983 UPS2Intro +
+
0.9.4
Mushihimesama SLPM 66056 JPS2Playable +
+
0.9.2
MX 2002 SLUS 20072 UPS2Playable +
+
0.9.2
MX Unleashed SLUS 20767 UPS2Playable +
+
0.9.4
MX VS ATV Unleashed SLUS 21104 UPS2Playable +
+
0.9.4
My Street SCUS 97212 UPS2Intro +
+
0.9.4
Myst 3 - Exile SLUS 20434 UPS2Playable +
+
0.9.4
Mystic Heroes SLUS 20521 UPS2Menus +
+
0.9.4
Namco Museum - 50th Anniversary SLUS 20273 UPS2Playable +
+
0.9.4
Namco Museum SLUS 20273 UPS2Playable +
+
0.9.4
Namco X Capcom SLPS 25505 JPS2Playable +
+
0.9.2
Nano Breaker SLKA 25263 JPS2Ingame +
+
0.9.2
Nano Breaker SLUS 21010 UPS2Ingame +
+
0.9.4
Narc SLUS 20730 UPS2Intro +
+
0.9.4
Naruto - Narutimett Hero SLPS 25293 JPS2Ingame +
+
0.9.4
Naruto - Ultimate Ninja 2 SLUS 21575 UPS2Playable +
+
0.9.4
Naruto - Ultimate Ninja SLUS 21358 UPS2Ingame +
+
0.9.4
Naruto Uzumaki Chronicles SLUS 21498 UPS2Ingame +
+
0.9.4
Naruto Uzumaki Ninden SLPS 25542 JPS2Ingame +
+
0.9.4
Nascar - Dirt To Daytona SLPS 25026 UPS2Playable +
+
0.9.4
Nascar 08 SLUS 21639 UPS2Intro +
+
0.9.4
NASCAR 2001 SLUS 20101 UPS2Nothing +
+
0.9.2
Nascar 2006 - Total Team Control SLUS 21266 UPS2Ingame +
+
0.9.4
Nascar 2007 SLUS 21461 UPS2Ingame +
+
0.9.4
NASCAR Thunder 2002 SLUS 20266 UPS2Ingame +
+
0.9.2
Naval Ops - Warship Gunner SLUS 20663 UPS2Intro +
+
0.9.4
Naval Ops Commander SLUS 20871 UPS2Intro +
+
0.9.4
NBA Street 2 SLES 51481 EPS2Playable +
+
0.9.2
Nebula Echo Night SLPS 25314 JPS2Ingame +
+
0.9.4
Need For Speed - Carbon SLUS 21493 UPS2Menus +
+
0.9.4
Need For Speed - Hot Pursuit 2 SLES 50731 EPS2Ingame +
+
0.9.3
Need For Speed - Hot Pursuit 2 SLUS 20362 UPS2Ingame +
+
0.9.4
Need For Speed - Most Wanted Black Edition SLUS 21351 UPS2Ingame +
+
0.9.4
Need For Speed - Most Wanted SLES 53557 EPS2Ingame +
+
0.9.2
Need For Speed - Most Wanted SLUS 21267 UPS2Ingame +
+
0.9.4
Need For Speed Underground 2 SLES 52725 EPS2Ingame +
+
0.9.4
Need For Speed Underground 2 SLUS 21065 UPS2Menus +
+
0.9.4
Need For Speed Underground SLES 51967 EPS2Ingame +
+
0.9.3
Need For Speed Underground SLUS 20811 UPS2Nothing +
+
0.9.4
Neo Contra SLES 52510 EPS2Ingame +
+
0.9.4
Neo Contra SLUS 20961 UPS2Ingame +
+
0.9.4
Neon Genesis Evangelion - The Iron Maiden 2nd SLPM 65867 JPS2Playable +
+
0.9.2
Neopets - The Darkest Faerie SCUS 97367 UPS2Ingame +
+
0.9.4
NFL GameDay 2002 SCUS 97131 UPS2Ingame +
+
0.9.2
NFL Headcoach SLUS 21407 UPS2Ingame +
+
0.9.4
NFL Street 2 SLUS 21118 UPS2Intro +
+
0.9.4
NFL Street 3 SLUS 21482 UPS2Ingame +
+
0.9.4
NHL 2006 SLUS 21241 UPS2Ingame +
+
0.9.4
NHL 2007 SLUS 21458 UPS2Ingame +
+
0.9.4
NHL 2008 SLUS 21647 UPS2Intro +
+
0.9.4
NHL 2K6 SLUS 21234 UPS2Intro +
+
0.9.4
NHL 2K7 SLUS 21425 UPS2Intro +
+
0.9.4
NHL 2K8 SLUS 21632 UPS2Intro +
+
0.9.4
NHL Hitz 2003 SLUS 20438 UPS2Menus +
+
0.9.4
NHL Hitz Pro SLUS 20691 UPS2Ingame +
+
0.9.4
NHRA - Countdown To The Championship SLUS 21547 UPS2Menus +
+
0.9.4
NHRA Championship Drag Racing SLUS 21114 UPS2Playable +
+
0.9.4
Nicktoons - Battle For Volcano Island SLUS 21469 UPS2Ingame +
+
0.9.4
Nightmare Before Christmas - Oogie's Revenge SLPM 65739 JPS2Playable +
+
0.9.4
Nightmare Before Christmas - Oogie's Revenge SLUS 20860 UPS2Ingame +
+
0.9.4
Nightmare of Druaga - Fushigino Dungeon SLUS 21071 UPS2Playable +
+
0.9.4
Nightshade SLUS 20810 UPS2Nothing +
+
0.9.4
Ninja Assault SLUS 20492 UPS2Ingame +
+
0.9.4
Ninjabread Man SLES 53570 EPS2Ingame +
+
0.9.2
NRA Gun Club SLUS 21432 UPS2Menus +
+
0.9.4
NTRA Breeders Cup - World Thoroughbred Championship SLUS 21195 UPS2Intro +
+
0.9.4
Obscure 2 SLES 54782 EPS2Playable +
+
0.9.4
Obscure SLES 52738 EPS2Playable +
+
0.9.4
Obscure SLUS 20777 UPS2Playable +
+
0.9.4
Odin Sphere SLUS 21577 UPS2Playable +
+
0.9.4
Odin's Sphere SLPM 66474 JPS2Playable +
+
0.9.4
Off-Road Wide Open SLES 50232 EPS2Playable +
+
0.9.2
Okage - Shadow King SCUS 97129 UPS2Ingame +
+
0.9.2
Okami SLPM 66375 JPS2Ingame +
+
0.9.2
Okami SLUS 21115 UPS2Ingame +
+
0.9.4
One Piece - Grand Adventure SLUS 21435 UPS2Ingame +
+
0.9.4
One Piece - Grand Battle 3 SLPS 25315 JPS2Playable +
+
0.9.3
One Piece - Grand Battle SLUS 20975 UPS2Ingame +
+
0.9.4
One Piece - Pirates Carnival SLUS 21364 UPS2Ingame +
+
0.9.4
Oni PBPX 95201 UPS2Intro +
+
0.9.2
Oni SLES 50177 GPS2Ingame +
+
0.9.2
Onimusha - Blade Warriors SLUS 20710 UPS2Nothing +
+
0.9.4
Onimusha - Dawn of Dreams SLPM 66275 JPS2Playable +
+
0.9.3
Onimusha - Dawn of Dreams SLUS 21180 UPS2Ingame +
+
0.9.4
Onimusha - Warlords SLES 50247 EPS2Menus +
+
0.9.2
Onimusha 2 - Samurai's Destiny SLES 50978 EPS2Playable +
+
0.9.4
Onimusha 2 - Samurai's Destiny SLUS 20393 UPS2Playable +
+
0.9.4
Onimusha 3 - Demon Siege SLES 51914 EPS2Playable +
+
0.9.2
Onimusha 3 - Demon Siege SLUS 20694 UPS2Ingame +
+
0.9.4
Onimusha Buraiden SLPM 65411 JPS2Ingame +
+
0.9.4
Onimusha SLPM 65010 JPS2Ingame +
+
0.9.4
Onimusha SLUS 20018 UPS2Ingame +
+
0.9.4
Open Season SLUS 21467 UPS2Intro +
+
0.9.4
Operation Winback SLES 50155 EPS2Ingame +
+
0.9.2
Operative, The - No One Lives Forver SLUS 20028 UPS2Ingame +
+
0.9.4
Oretachi Geasen Zoku Sono 15: Akumajou Dracula SLPM 62729 JPS2Playable +
+
0.9.2
Orphen Scion Of Sorcery SLUS 20011 UPS2Nothing +
+
0.9.4
Otostaz SCPS 11024 JPS2Ingame +
+
0.9.4
Outlaw Golf 2 SLES 52965 EPS2Menus +
+
0.9.4
Outlaw Golf 2 SLUS 21030 UPS2Menus +
+
0.9.4
Outlaw Tennis SLUS 21190 UPS2Ingame +
+
0.9.4
Outlaw Volleyball Remix SLUS 21049 UPS2Ingame +
+
0.9.4
Outrun 2 SP SLPM 66628 JPS2Nothing +
+
0.9.4
Outrun 2006 - Coast 2 Coast SLUS 21274 UPS2Nothing +
+
0.9.4
Over the Hedge SLUS 21300 UPS2Intro +
+
0.9.4
Pac Man World 2 SLUS 20224 UPS2Playable +
+
0.9.4
Pac Man World 3 SLUS 21219 UPS2Ingame +
+
0.9.4
Pacman Fever SLUS 20197 UPS2Ingame +
+
0.9.4
Pacman World Rally SLUS 21328 UPS2Ingame +
+
0.9.4
Paparazzi SLES 53489 EPS2Playable +
+
0.9.2
Parappa The Rapper 2 SCES 50408 EPS2Playable +
+
0.9.4
Parappa The Rapper 2 SCUS 97167 UPS2Playable +
+
0.9.4
Paris-Dakar Rally SLES 50212 EPS2Menus +
+
0.9.2
Party Girls SLES 53406 EPS2Ingame +
+
0.9.4
PC Genjin SLPM 62418 JPS2Playable +
+
0.9.2
Peter Pan - The Legend Of Neverland SLES 50522 EPS2Ingame +
+
0.9.4
Phantasy Star Universe SLES 54308 EPS2Playable +
+
0.9.4
Phantasy Star Universe SLUS 21194 UPS2Ingame +
+
0.9.4
Phantom Brave SLES 52951 EPS2Playable +
+
0.9.2
Phantom Brave SLUS 20955 UPS2Playable +
+
0.9.4
Pinball Hall of Fame SLES 52863 EPS2Playable +
+
0.9.2
Pink Pong SLES 52519 EPS2Ingame +
+
0.9.4
Pipo Saru 2001 SCPS 11014 JPS2Intro +
+
0.9.4
Pirates - Legend of The Black Buccaneer SLUS 21478 UPS2Ingame +
+
0.9.4
Pirates - The Legend of Black Cat SLES 50680 EPS2Ingame +
+
0.9.2
Pirates - The Legend Of Black Kat SLUS 20365 UPS2Playable +
+
0.9.4
Pirates of the Caribbean - The Legend of Jack Sparrow SLES 54237 EPS2Ingame +
+
0.9.2
Pirates of The Caribbean - The Legend of Jack Sparrow SLUS 21110 UPS2Ingame +
+
0.9.4
Pitfall - The Lost Expedition SLUS 20408 UPS2Intro +
+
0.9.4
Pk - Out Of The Shadows SLUS 20478 UPS2Intro +
+
0.9.4
Play it Pinball SLES 51555 EPS2Playable +
+
0.9.4
Playboy: The Mansion SLUS 20988 UPS2Ingame +
+
0.9.4
Pochi to Nyaa SLPS 20323 JPS2Playable +
+
0.9.2
Polar Express SLUS 20989 UPS2Playable +
+
0.9.4
Police 24-7 SLES 50285 EPS2Ingame +
+
0.9.4
Pool Master SLES 50052 EPS2Ingame +
+
0.9.4
Pool Paradise SLES 52246 EPS2Playable +
+
0.9.2
Pool Paradise SLUS 20987 UPS2Ingame +
+
0.9.4
Pop'n Music 12 SLPM 66314 JPS2Playable +
+
0.9.4
Pop'n Music 14 - Forever SLPM 66742 JPS2Ingame +
+
0.9.4
Pop'n Taisen Puzzle-Dama Online SLPM 62464 JPS2Playable +
+
0.9.4
Portal Runner SLUS 20003 UPS2Ingame +
+
0.9.4
Power Rangers - Dino Thunder SLUS 20944 UPS2Playable +
+
0.9.4
Power Rangers - Super Legends SLUS 21679 UPS2Ingame +
+
0.9.4
Power Smash 2 SLPM 62236 JPS2Ingame +
+
0.9.2
Powerpuff Girls, The - Relish Rampage SLES 51134 EPS2Ingame +
+
0.9.2
PowerShot Pinball SLES 54084 EPS2Ingame +
+
0.9.2
Predator - Concrete Jungle SLUS 20875 UPS2Playable +
+
0.9.4
Primal SCES 51135 EPS2Ingame +
+
0.9.4
Primal SCUS 97142 UPS2Menus +
+
0.9.4
Prince of Persia - The Two Thrones SLES 53777 EPS2Ingame +
+
0.9.2
Prince of Persia - The Two Thrones SLUS 21287 UPS2Intro +
+
0.9.4
Prince of Persia - Warrior Within SLUS 21022 UPS2Menus +
+
0.9.4
Prince of Persia : Sands of Time SLES 51918 EPS2Ingame +
+
0.9.4
Pro Evolution Soccer 4 SLES 52760 EPS2Playable +
+
0.9.3
Pro Evolution Soccer 5 SLES 53544 EPS2Intro +
+
0.9.2
Pro Race Driver SLUS 20329 UPS2Playable +
+
0.9.4
Pro Rally 2002 SLES 50637 EPS2Playable +
+
0.9.2
Pro Stroke Golf - World Tour 2007 SLUS 21496 UPS2Ingame +
+
0.9.4
Project Altered Beast SLES 53024 EPS2Playable +
+
0.9.4
Project Eden SLES 50553 EPS2Playable +
+
0.9.2
Project Eden SLUS 20164 UPS2Intro +
+
0.9.4
Project Snowblind SLUS 21037 UPS2Menus +
+
0.9.4
Project Zero 3 - Shisei No Koe SLPS 25544 JPS2Menus +
+
0.9.4
Project Zero II - Akai Chou SLPS 25303 JPS2Menus +
+
0.9.2
Project Zero II - Crimson Butterfly SLES 52384 EPS2Menus +
+
0.9.2
Project Zero SLES 50821 EPS2Ingame +
+
0.9.2
Project Zero SLPS 25074 JPS2Ingame +
+
0.9.4
Pryzm - Chapter One - The Dark Unicorn SLES 50721 EPS2Playable +
+
0.9.2
Pryzm Chapter 1 - The Dark Unicorn SLUS 20172 UPS2Playable +
+
0.9.4
Psi-Ops - The Mindgate Conspiracy SLUS 20688 UPS2Ingame +
+
0.9.4
Psychonauts SLUS 21120 UPS2Playable +
+
0.9.4
Psyvariar Complete Edition SLPM 62139 JPS2Playable +
+
0.9.2
Psyvariar Revision SLPM 62371 JPS2Playable +
+
0.9.4
Pump It Up - Exceed SLUS 21131 UPS2Ingame +
+
0.9.4
Punisher, The SLUS 20864 UPS2Playable +
+
0.9.4
Puzzle Challenge - Crosswords and More SLUS 21339 UPS2Playable +
+
0.9.4
Q Ball - Billiards Master PBPX 95201 JPS2Playable +
+
0.9.4
Quake 3 - Revolution PBPX 95201 UPS2Intro +
+
0.9.4
R-type Final SLPS 25247 JPS2Playable +
+
0.9.4
R-Type Final SLUS 20780 UPS2Playable +
+
0.9.4
R: Racing Evolution SLUS 20721 UPS2Ingame +
+
0.9.4
R: Racing SLES 52309 EPS2Ingame +
+
0.9.4
Radiata Stories SLUS 21262 UPS2Nothing +
+
0.9.4
Radirgy Precious SLPM 66405 JPS2Playable +
+
0.9.4
Raiden III SLES 53829 EPS2Playable +
+
0.9.4
Raiden III SLUS 21465 UPS2Playable +
+
0.9.4
Rakugaki Onkoku SLPM 65097 JPS2Playable +
+
0.9.2
Rally Championship SLES 50763 EPS2Playable +
+
0.9.2
Rally Fusion SLUS 20361 UPS2Intro +
+
0.9.4
Rampage - Total Destruction SLUS 21323 UPS2Playable +
+
0.9.4
Ratatouille SLUS 21541 UPS2Ingame +
+
0.9.4
Ratchet - Deadlocked SCUS 97465 UPS2Ingame +
+
0.9.4
Ratchet And Clank 3 - Up Your Arsenal SCUS 97353 UPS2Ingame +
+
0.9.4
Raw Danger SLUS 21501 UPS2Nothing +
+
0.9.4
Rayman - Raving Rabbids SLUS 21576 UPS2Ingame +
+
0.9.4
Rayman 3 - Hoodlum Havoc SLUS 20601 UPS2Intro +
+
0.9.4
Rayman Arena SLUS 20272 UPS2Playable +
+
0.9.4
Rayman M SLES 50457 EPS2Playable +
+
0.9.4
Rayman Revolution SLUS 20138 UPS2Ingame +
+
0.9.4
RC Revenge Pro SLUS 20153 UPS2Menus +
+
0.9.4
Ready To Rumble - Round 2 SLUS 20054 UPS2Menus +
+
0.9.4
Real Fishing 3 SLUS 20555 UPS2Intro +
+
0.9.4
Rebel Raiders - Operation Nighthawk SLUS 21303 UPS2Playable +
+
0.9.4
Red Card Soccer 2003 SLUS 20354 UPS2Playable +
+
0.9.2
Red Dead Revolver SLUS 20500 UPS2Intro +
+
0.9.4
Red Faction 2 SLUS 20442 UPS2Ingame +
+
0.9.4
Red Faction II SLES 51133 EPS2Ingame +
+
0.9.2
Red Faction SLES 50279 GPS2Playable +
+
0.9.2
Red Faction SLUS 20073 UPS2Nothing +
+
0.9.4
Red Ninja - End of Honor SLUS 20714 UPS2Intro +
+
0.9.4
Red Star SLUS 20885 UPS2Ingame +
+
0.9.4
Reservoir Dogs SLUS 21479 UPS2Ingame +
+
0.9.4
Resident Evil - Code Veronica X SLES 50306 EPS2Ingame +
+
0.9.2
Resident Evil - Code Veronica X SLUS 20184 UPS2Playable +
+
0.9.4
Resident Evil 4 SLES 53702 EPS2Playable +
+
0.9.4
Resident Evil 4 SLUS 21134 UPS2Playable +
+
0.9.4
Resident Evil Dead Aim SLUS 20669 UPS2Playable +
+
0.9.4
Resident Evil Outbreak 2 SLUS 20984 UPS2Playable +
+
0.9.4
Resident Evil Outbreak SLUS 20765 UPS2Playable +
+
0.9.4
Resident Evil Survivor 2: Code Veronica SLES 50650 UPS2Playable +
+
0.9.4
Return To Castle Wolfenstein - Operation Resurrection SLUS 20297 UPS2Playable +
+
0.9.4
Rez SLPM 62101 JPS2Ingame +
+
0.9.4
Rez SLUS 20344 UPS2Ingame +
+
0.9.4
Ridge Racer 5 SCES 50000 EPS2Ingame +
+
0.9.3
Ridge Racer 5 SLUS 20002 UPS2Ingame +
+
0.9.4
Riding Spirits II SLES 52277 EPS2Playable +
+
0.9.3
Riding Spirits SLUS 20429 UPS2Playable +
+
0.9.4
Rig Racer 2 SLES 53572 EPS2Ingame +
+
0.9.2
Rimokokoron SCPS 11012 JPS2Nothing +
+
0.9.4
Ring Of Red SLUS 20145 UPS2Nothing +
+
0.9.4
Rise of the Kasai SCUS 97416 UPS2Ingame +
+
0.9.4
Rise To Honor SCUS 97279 UPS2Playable +
+
0.9.4
Risk - Global Domination SLUS 20390 UPS2Playable +
+
0.9.4
River King - A Wonderful Journey SLUS 21275 UPS2Playable +
+
0.9.4
Roadkill SLUS 20687 UPS2Playable +
+
0.9.4
Robin Hood - Defender Of The Crown SLES 51946 EPS2Intro +
+
0.9.4
Robocop SLES 51374 EPS2Playable +
+
0.9.4
Robot Alchemic Drive SLUS 20445 UPS2Playable +
+
0.9.4
Robotech Invasion SLUS 20823 UPS2Intro +
+
0.9.4
Robots SLUS 20942 UPS2Menus +
+
0.9.4
Rocket Power Beach Bandits SLUS 20473 UPS2Nothing +
+
0.9.4
Rockman 7 SLES 51885 EPS2Playable +
+
0.9.2
Rocky Legends SLES 52761 EPS2Playable +
+
0.9.2
Rocky Legends SLUS 20890 UPS2Ingame +
+
0.9.4
Rocky SLUS 20559 UPS2Menus +
+
0.9.4
Rogue Galaxy SCUS 97490 UPS2Ingame +
+
0.9.4
Rogue Trooper SLUS 21320 UPS2Intro +
+
0.9.4
Roland Garros 2005 SCES 53310 EPS2Ingame +
+
0.9.4
Romance of The Three Kingdoms IX SLUS 20879 UPS2Intro +
+
0.9.4
Romance of The Three Kingdoms VII SLUS 20720 UPS2Playable +
+
0.9.4
Romance of The Three Kingdoms X SLUS 21202 UPS2Intro +
+
0.9.4
Romance Of The Tree Kingdoms XI SLUS 21584 UPS2Playable +
+
0.9.4
Romancing Saga SLUS 21263 UPS2Menus +
+
0.9.4
RPG Maker 3 SLUS 21178 UPS2Playable +
+
0.9.4
RPM Tuning SLES 52190 EPS2Intro +
+
0.9.2
RTX Red Rock SLES 51071 EPS2Ingame +
+
0.9.2
Ruff Trigger - The Vanocore Conspiracy SLUS 21314 UPS2Nothing +
+
0.9.4
Rule of Rose SCPS 15093 JPS2Ingame +
+
0.9.4
Rule of Rose SLUS 21448 UPS2Menus +
+
0.9.4
Rumble Fish, The SLPM 65919 JPS2Playable +
+
0.9.4
Rumble Racing SLES 50120 EPS2Ingame +
+
0.9.4
Rumble Roses SLES 52535 EPS2Playable +
+
0.9.2
Rumble Roses SLUS 20970 UPS2Playable +
+
0.9.4
Run Like Hell SLUS 20037 UPS2Playable +
+
0.9.4
Rune - Viking Warlord SLUS 20109 UPS2Ingame +
+
0.9.4
Rygar - Legendary Adventure SLUS 20471 UPS2Ingame +
+
0.9.4
Saikyou Toudai Shogi Special SLPS 20161 JPS2Playable +
+
0.9.2
Saint Seiya Chapter Sanctuary SLES 53201 EPS2Playable +
+
0.9.2
Samurai Champloo - Sidetracked SLUS 21343 UPS2Playable +
+
0.9.4
Samurai Jack - The Shadow Of Aku SLUS 20899 UPS2Intro +
+
0.9.4
Samurai Showdown V SLES 53059 EPS2Playable +
+
0.9.4
Samurai Spirits Tenkaichi Kenkakuten SLPS 25559 JPS2Playable +
+
0.9.4
Samurai Warriors 2 SLUS 21462 UPS2Playable +
+
0.9.4
Samurai Warriors Xtreme Legends SLUS 21080 UPS2Playable +
+
0.9.2
Samurai Warriors SLUS 20878 UPS2Playable +
+
0.9.4
Samurai Western SLES 53234 EPS2Playable +
+
0.9.2
Samurai Western SLUS 21187 UPS2Playable +
+
0.9.4
Sanyo Pachinko Paradise 14 SLPS 25787 JPS2Nothing +
+
0.9.4
Saru Gechu 2 SCPS 15025 JPS2Ingame +
+
0.9.4
Savage Skies SLUS 20430 UPS2Menus +
+
0.9.4
Scaler SLES 52917 EPS2Ingame +
+
0.9.4
Scaler SLUS 20957 UPS2Ingame +
+
0.9.4
Scarface - The World Is Yours SLUS 21111 UPS2Ingame +
+
0.9.4
School Rumble 2 SLPS 25669 JPS2Playable +
+
0.9.4
Scooby Doo - Night Of 100 Frights SLUS 20349 UPS2Playable +
+
0.9.4
Scooby Doo - Unmasked SLUS 21091 UPS2Intro +
+
0.9.4
Scorpion King - Rise of the Akkadian SLUS 20424 UPS2Playable +
+
0.9.4
Second Sight SLES 52670 EPS2Ingame +
+
0.9.2
Second Sight SLUS 21033 UPS2Playable +
+
0.9.4
Secret Weapons Over Normandy SLUS 20762 UPS2Intro +
+
0.9.4
Seek And Destroy SLUS 20606 UPS2Playable +
+
0.9.4
SEGA Ages 2500 - Vol. 11 - Fist of the North Star SLPM 62462 JPS2Intro +
+
0.9.2
Sega Ages 2500 Volume 01 - Phantasy Star generation:1 SLPM 62362 JPS2Playable +
+
0.9.2
Sega Ages 2500 Volume 03 - Fantasy Zone SLPM 62366 JPS2Playable +
+
0.9.4
Sega Ages 2500 Volume 05 - Golden Axe SLPM 62385 JPS2Playable +
+
0.9.2
Sega Ages 2500 Volume 06 - Bonanza Bros SLPM 62433 JPS2Playable +
+
0.9.4
Sega Ages 2500 Volume 07 - Columns SLPM 62425 JPS2Playable +
+
0.9.4
Sega Ages 2500 Volume 08 - Virtua Racing - Flat Out SLPM 62443 JPS2Playable +
+
0.9.4
Sega Ages 2500 Volume 09 - Gain Ground SLPM 62445 JPS2Playable +
+
0.9.2
Sega Ages 2500 Volume 10 - After Burner II SLPM 62446 JPS2Playable +
+
0.9.4
Sega Ages 2500 Volume 16 - Virtua Fighter 2 SLPM 62547 JPS2Playable +
+
0.9.4
Sega Ages 2500 Volume 17 - Phantasy Star Generation 2 SLPM 62553 JPS2Playable +
+
0.9.4
Sega Ages 2500 Volume 20 - Space Harrier SLPM 62691 JPS2Menus +
+
0.9.2
Sega Bass Fishing Duel SLUS 20339 UPS2Playable +
+
0.9.4
Sega Classics Collection SLES 53461 EPS2Ingame +
+
0.9.4
Sega Classics Collection SLUS 21009 UPS2Playable +
+
0.9.4
Sega Genesis Collection SLUS 21542 UPS2Playable +
+
0.9.4
Sega Rally 2006 SLPM 66212 JPS2Playable +
+
0.9.2
Sega Rally 95 SLPM 62703 JPS2Nothing +
+
0.9.4
Sega Soccer Slam SLUS 20509 UPS2Ingame +
+
0.9.4
Sega Sports Tennis SLUS 20480 UPS2Playable +
+
0.9.4
Seitoushi Seiya Seiiki Juunikyuu Hen SLPS 25476 JPS2Playable +
+
0.9.4
Sengoku Basara 2 SLPM 66447 JPS2Playable +
+
0.9.2
Sensible Soccer 2006 SLES 53810 UPS2Ingame +
+
0.9.2
Serious Sam - Next Encounter SLUS 20907 UPS2Playable +
+
0.9.4
Seven - Molmorth no Kiheitai SLPS 25025 JPS2Intro +
+
0.9.4
Seven Samurai SLUS 20621 UPS2Playable +
+
0.9.4
Shadow Hearts - Covenant SLUS 21041 UPS2Nothing +
+
0.9.4
Shadow Hearts - From The New World SLPM 66071 JPS2Ingame +
+
0.9.2
Shadow Hearts - From The New World SLUS 21326 UPS2Ingame +
+
0.9.4
Shadow Hearts SLES 50677 EPS2Playable +
+
0.9.4
Shadow Hearts SLUS 20347 UPS2Playable +
+
0.9.4
Shadow Of Destiny SLUS 20146 UPS2Ingame +
+
0.9.4
Shadow of Memories SLPM 65013 JPS2Ingame +
+
0.9.4
Shadow of Rome SLUS 20902 UPS2Playable +
+
0.9.4
Shadow of The Colossus SCES 53326 EPS2Ingame +
+
0.9.2
Shadow of The Colossus SCUS 97472 UPS2Ingame +
+
0.9.4
Shadow The Hedgehog SLUS 21261 UPS2Ingame +
+
0.9.4
Shadow Tower Abyss SLPS 25217 JPS2Ingame +
+
0.9.2
Shadowman - 2econd Coming SLUS 20413 UPS2Intro +
+
0.9.4
Shadowman - The Second Coming SLES 50446 EPS2Menus +
+
0.9.4
Shaman King - Power Of Spirit SLUS 20953 UPS2Playable +
+
0.9.4
Shark Tales SLUS 20925 UPS2Intro +
+
0.9.4
Shellshock Nam '67 SLUS 20828 UPS2Ingame +
+
0.9.4
Shield, The SLUS 21040 UPS2Playable +
+
0.9.4
Shikigami no Shiro 2 SLPM 62461 JPS2Ingame +
+
0.9.4
Shikigami no Shiro Nanayozuki Genshou Kyoku SLPM 66069 JPS2Playable +
+
0.9.4
Shikigami no Shiro SLPM 62165 JPS2Playable +
+
0.9.2
Shin Megami Tensei - Devil Summoner SLUS 21431 UPS2Playable +
+
0.9.4
Shin Megami Tensei - Digital Devil Saga 2 SLUS 21152 UPS2Ingame +
+
0.9.4
Shin Megami Tensei - Digital Devil Saga SLES 53458 EPS2Playable +
+
0.9.2
Shin Megami Tensei - Digital Devil Saga SLUS 20974 UPS2Ingame +
+
0.9.4
Shin Megami Tensei - Nocturne SLUS 20911 UPS2Playable +
+
0.9.4
Shin Megami Tensei - Persona 3 SLMP 66445 JPS2Playable +
+
0.9.4
Shin Megami Tensei - Persona 3 SLUS 21569 UPS2Playable +
+
0.9.4
Shin Sangoku Musou 2 SLPM 65053 JPS2Playable +
+
0.9.2
Shining Force EXA SLUS 21567 UPS2Ingame +
+
0.9.4
Shining Force Neo SLUS 21206 UPS2Playable +
+
0.9.4
Shining Tears SLUS 21063 UPS2Playable +
+
0.9.4
Shinobi SLUS 20459 UPS2Nothing +
+
0.9.4
Shinobido - Way of The Ninja SCES 53931 EPS2Playable +
+
0.9.4
Shougi, The SLPM 62180 JPS2Playable +
+
0.9.2
Shox SLES 51144 EPS2Ingame +
+
0.9.3
Shox SLES 51250 EPS2Intro +
+
0.9.2
Shrek 2 SLUS 20745 UPS2Playable +
+
0.9.4
Shrek Smash N Crash Racing SLUS 21392 UPS2Intro +
+
0.9.4
Shrek Super Party SLUS 20516 UPS2Ingame +
+
0.9.4
Shrek Superslam SLUS 21197 UPS2Intro +
+
0.9.4
Shrek The Third SLUS 21454 UPS2Menus +
+
0.9.4
Sidewinder V SLPS 25271 JPS2Ingame +
+
0.9.3
Silent Hill 2 - Saigo no Uta SLPM 65098 JPS2Nothing +
+
0.9.2
Silent Hill 2 SLPM 65051 JPS2Nothing +
+
0.9.2
Silent Hill 3 SLPM 65257 JPS2Nothing +
+
0.9.2
Silent Hill 3 SLUS 20622 UPS2Ingame +
+
0.9.4
Silent Hill 4 - The Room SLES 52445 EPS2Playable +
+
0.9.4
Silent Hill 4 - The Room SLPM 65574 JPS2Ingame +
+
0.9.2
Silent Hill 4 - The Room SLUS 20873 UPS2Playable +
+
0.9.4
Silent Line - Armored Core SLES 52203 EPS2Playable +
+
0.9.2
Silent Scope 2 SLUS 20243 UPS2Nothing +
+
0.9.4
Silpheed - The Lost Planet SLES 50193 EPS2Playable +
+
0.9.2
Silpheed - The Lost Planet SLUS 20085 UPS2Playable +
+
0.9.4
Silpheed the Lost Planet SLPM 62015 JPS2Playable +
+
0.9.2
Simple 2000 Series - Vol. 81 - The Chikyuu Boueigun 2 SLPM 62652 JPS2Nothing +
+
0.9.4
Simple 2000 Series - Vol.69 - The Board Games Collection SLPM 62580 JPS2Playable +
+
0.9.2
Simple 2000 Series ULTIMATE - Vol. 22: Stylish Mahjongg SLPM 62571 JPS2Playable +
+
0.9.2
Simple Series 2000 - Vol. 116 - The Neko Mura no Ninnin SLPS 20493 JPS2Ingame +
+
0.9.4
Simple Series 2000 - Vol. 16 - The Sniper 2 SLPM 62459 JPS2Playable +
+
0.9.2
Simple Series 2000 - Vol. 55 - The Catfight - Joneko Densetsu SLPM 62494 JPS2Ingame +
+
0.9.4
Simple Series 2000 - Vol. 59 - The Uchuujin to Hanashi Sou! SLPM 62524 JPS2Playable +
+
0.9.2
Simple Series 2000 - Vol. 60 - The Tokusatsu Henshin Hero SLPM 62510 JPS2Ingame +
+
0.9.2
Simple Series 2000 - Vol. 61 - The Oane Chapara SLPM 62525 JPS2Ingame +
+
0.9.4
Simple Series 2000 - Vol. 63 - The Suieitaikai SLPM 62534 JPS2Ingame +
+
0.9.4
Simple Series 2000 - Vol. 64 - The Splatter Action SLPM 62545 JPS2Menus +
+
0.9.2
Simple Series 2000 - Vol. 91 - The ALL STAR Fighting SLPS 20430 JPS2Intro +
+
0.9.4
Simple Series 2000 - Vol. 99 - The Genshijin SLPS 20461 JPS2Nothing +
+
0.9.4
Simple Series 2000 - Vol.106 - The Block Kuzushi Quest SLPS 20468 JPS2Playable +
+
0.9.4
Simply... The Best For Less - Vol. 6 Ultimate Casino SLES 52515 EPS2Ingame +
+
0.9.3
Simpsons Hit & Run, The SLES 51897 EPS2Playable +
+
0.9.4
Simpsons Hit & Run, The SLUS 20624 UPS2Intro +
+
0.9.4
Simpsons Road Rage, The SLUS 20305 UPS2Ingame +
+
0.9.4
Simpsons Skateboarding SLUS 20114 UPS2Playable +
+
0.9.4
Sims - Bustin' Out, The SLUS 20842 UPS2Intro +
+
0.9.4
Sims - The Urbz, The SLES 52908 EPS2Playable +
+
0.9.2
Sims 2 - Castaway SLUS 21664 UPS2Nothing +
+
0.9.4
Sims 2 - Pets SLUS 21536 UPS2Nothing +
+
0.9.4
Sims 2 SLUS 21265 UPS2Ingame +
+
0.9.4
Sims Bustin Out, The SLES 52047 EPS2Playable +
+
0.9.2
Sims SLUS 20573 UPS2Intro +
+
0.9.4
Singstar 80's SCUS 97616 UPS2Menus +
+
0.9.4
Singstar Amped SCUS 97611 UPS2Menus +
+
0.9.4
Singstar Anthems SCES 54131 EPS2Menus +
+
0.9.2
Singstar Rocks SCUS 97571 UPS2Menus +
+
0.9.4
Siren 2 SCPS 15106 JPS2Ingame +
+
0.9.4
Siren SCPS 15053 JPS2Ingame +
+
0.9.4
Siren SCUS 97355 UPS2Playable +
+
0.9.4
Ski Doo Snow X Racing SLUS 21591 UPS2Intro +
+
0.9.4
Sky Odyssey SLUS 20134 UPS2Playable +
+
0.9.4
Skygunner SCPS 11006 JPS2Ingame +
+
0.9.2
Skygunner SLUS 20384 UPS2Playable +
+
0.9.4
Sled Storm SLUS 20363 UPS2Playable +
+
0.9.4
Slotter Up Core 4 SLPS 20378 JPS2Playable +
+
0.9.4
Sly 2 - Band of Thieves SCES 52529 EPS2Ingame +
+
0.9.2
Sly 2 - Band of Thieves SCUS 97316 UPS2Menus +
+
0.9.4
Sly 3 - Honor Among Thieves SCUS 97464 UPS2Ingame +
+
0.9.4
Sly Cooper And The Thievious Raccoonus SCUS 97198 UPS2Playable +
+
0.9.4
Smackdown - Here Comes The Pain SLUS 20787 UPS2Playable +
+
0.9.4
Smackdown - Just Bring It SLUS 20316 UPS2Playable +
+
0.9.4
Smackdown - Shut Your Mouth SLUS 20483 UPS2Ingame +
+
0.9.4
Smash Court Tennis Pro - Tournament 2 SLUS 20933 UPS2Ingame +
+
0.9.4
Smuggler's Run SLES 50061 EPS2Playable +
+
0.9.2
Smugglers Run 2 SLUS 20204 UPS2Menus +
+
0.9.4
Smugglers Run SLUS 2065 UPS2Playable +
+
0.9.4
Sniper 2, the SLES 51623 EPS2Playable +
+
0.9.3
Sniper Elite SLUS 21231 UPS2Ingame +
+
0.9.4
SNK vs Capcom - SVC Chaos SLES 53065 EPS2Ingame +
+
0.9.4
SNK vs Capcom - SVC Chaos SLPS 25316 JPS2Playable +
+
0.9.3
SnoCross 2 - Featuring Blair Morgan SLUS 21130 UPS2Ingame +
+
0.9.4
Snoopy Vs The Red Baron SLUS 21380 UPS2Ingame +
+
0.9.4
Socom - US Navy Seals 2 SCUS 97275 UPS2Ingame +
+
0.9.4
Socom - US Navy Seals 3 SCUS 97474 UPS2Ingame +
+
0.9.4
Socom US Navy Seals - Combined Assault SCES 54477 EPS2Ingame +
+
0.9.4
Socom US Navy Seals - Combined Assault SCUS 97545 UPS2Ingame +
+
0.9.4
Sol Divide SLES 53873 EPS2Ingame +
+
0.9.4
Soldier of Fortune 2 SLUS 20084 UPS2Ingame +
+
0.9.3
Sonic Gems Collection SLES 53350 EPS2Playable +
+
0.9.2
Sonic Gems Collection SLPM 66074 JPS2Playable +
+
0.9.4
Sonic Heroes SLUS 20718 UPS2Intro +
+
0.9.4
Sonic Mega Collection Plus SLES 52998 EPS2Playable +
+
0.9.2
Sonic Mega Collection SLUS 20917 UPS2Playable +
+
0.9.4
Sonic Riders SLES 53560 EPS2Playable +
+
0.9.4
Sonic Riders SLUS 21331 UPS2Playable +
+
0.9.4
Sopranos - Road To Respect SLUS 21388 UPS2Ingame +
+
0.9.4
Soul Calibur II SLES 51799 EPS2Menus +
+
0.9.4
Soul Calibur II SLPS 25230 JPS2Ingame +
+
0.9.2
Soul Calibur II SLUS 20643 UPS2Ingame +
+
0.9.4
Soul Calibur III SCES 53312 EPS2Ingame +
+
0.9.4
Soul Calibur III SLPS 25577 JPS2Intro +
+
0.9.2
Soul Calibur III SLUS 21216 UPS2Intro +
+
0.9.4
Soul Nomad SLUS 21603 UPS2Ingame +
+
0.9.4
Space Channel 5 - Special Edition SLUS 20806 UPS2Playable +
+
0.9.4
Space Channel 5 Part 2 SLPM 65096 JPS2Playable +
+
0.9.2
Space Fishermen SCPS 11025 JPS2Ingame +
+
0.9.2
Space Invaders - Invasion Day SLES 51746 EPS2Playable +
+
0.9.2
Spartan - Total Warrior SLUS 21212 UPS2Ingame +
+
0.9.4
Spawn - Armageddon SLUS 20707 UPS2Playable +
+
0.9.4
Speed Kings SLUS 20584 UPS2Intro +
+
0.9.4
Sphinx And The Cursed Mummy SLUS 20482 UPS2Playable +
+
0.9.4
Spider-Man SLES 50814 GPS2Playable +
+
0.9.2
Spiderman - Friend Or Foe SLUS 21600 UPS2Menus +
+
0.9.4
Spiderman 2 SLUS 20776 UPS2Menus +
+
0.9.4
Spiderman 3 SLES 54723 EPS2Intro +
+
0.9.4
Spiderman 3 SLUS 21552 UPS2Intro +
+
0.9.4
Spiderman SLUS 20336 UPS2Ingame +
+
0.9.4
Splashdown - Rides Gone Wild SLUS 20686 UPS2Ingame +
+
0.9.4
Splashdown SLUS 20223 UPS2Menus +
+
0.9.4
Splinter Cell - Chaos Theory SLUS 21137 UPS2Ingame +
+
0.9.4
Splinter Cell - Double Agent SLUS 21356 UPS2Intro +
+
0.9.4
Splinter Cell - Pandora Tomorrow SLUS 20958 UPS2Ingame +
+
0.9.4
Splinter Cell SLUS 20652 UPS2Ingame +
+
0.9.4
Spongebob Schwammkopf - Schlacht um Bikini Bottom SLES 51970 GPS2Playable +
+
0.9.2
Spongebob Squarepants - Battle For Bikini Bottom SLUS 20680 UPS2Playable +
+
0.9.4
Spongebob Squarepants - Creature From The Krusty Krab SLUS 21391 UPS2Ingame +
+
0.9.4
Spongebob Squarepants - Revenge of the Flying Dutchman SLES 51285 EPS2Menus +
+
0.9.2
Spongebob Squarepants - Revenge of the Flying Dutchman SLUS 20425 UPS2Intro +
+
0.9.4
Spongebobs Atlantis Squarepantis SLUS 21644 UPS2Ingame +
+
0.9.4
Sprint Cars - The Road to Knoxville SLUS 21418 UPS2Ingame +
+
0.9.4
Spy Fiction SLUS 20856 UPS2Playable +
+
0.9.4
Spy Hunter - Nowhere To Run SLUS 21421 UPS2Ingame +
+
0.9.4
Spy Hunter 2 SLUS 20590 UPS2Intro +
+
0.9.4
Spy VS Spy SLES 53078 EPS2Ingame +
+
0.9.4
Spyro - A Hero's Tale SLES 52569 EPS2Playable +
+
0.9.2
Spyro - A Hero's Tale SLUS 20884 UPS2Playable +
+
0.9.4
Spyro - Enter The Dragonfly SLUS 20315 UPS2Intro +
+
0.9.4
Spyro - The Eternal Night SLUS 21607 UPS2Ingame +
+
0.9.4
SSX - On Tour SLUS 21278 UPS2Ingame +
+
0.9.4
SSX 3 SLUS 20772 UPS2Ingame +
+
0.9.4
SSX Tricky SLUS 20326 UPS2Playable +
+
0.9.4
SSX PBPX 95201 UPS2Playable +
+
0.9.4
Stacked SLUS 21259 UPS2Intro +
+
0.9.4
Star Ocean - Till the End of Time SLUS 20488 UPS2Nothing +
+
0.9.2
Star Trek - Elite Force SLPS 25026 UPS2Intro +
+
0.9.4
Star Trek - Encounters SLUS 21396 UPS2Playable +
+
0.9.4
Star Trek - Shattered Universe SLUS 20112 UPS2Playable +
+
0.9.4
Star Trek Voyager - Elite Force SLES 50738 EPS2Intro +
+
0.9.2
Star Wars - Battlefront 2 SLUS 21240 UPS2Ingame +
+
0.9.4
Star Wars - Bounty Hunter SLUS 20420 UPS2Playable +
+
0.9.4
Star Wars - Clone Wars SLUS 20510 UPS2Ingame +
+
0.9.4
Star Wars - Jedi Starfighter SLUS 20293 UPS2Playable +
+
0.9.4
Star Wars - Racer Revenge SLUS 20268 UPS2Ingame +
+
0.9.4
Star Wars - Starfighter SLUS 20044 UPS2Ingame +
+
0.9.4
Star Wars - Super Bombad Racing PBPX 95201 UPS2Ingame +
+
0.9.4
Star Wars Episode 3 - Revenge of the Sith SLES 53155 EPS2Ingame +
+
0.9.2
Star Wars Episode 3 - Revenge of The Sith SLUS 21143 UPS2Ingame +
+
0.9.4
Starsky & Hutch SLES 51783 EPS2Ingame +
+
0.9.2
State of Emergency 2 SLUS 20966 UPS2Ingame +
+
0.9.4
State of Emergency SLUS 20214 UPS2Playable +
+
0.9.4
Steambot Chronicles SLUS 21344 UPS2Nothing +
+
0.9.4
Steamboy SLPS 25502 JPS2Playable +
+
0.9.2
Steel Dragon EX SLES 52482 EPS2Playable +
+
0.9.4
Steel Lancer Arena International SLUS 20969 UPS2Playable +
+
0.9.4
Stella Deus SLUS 21132 UPS2Playable +
+
0.9.4
Stitch - Experiment 626 SCUS 97145 UPS2Playable +
+
0.9.4
Stock Car Crash SLES 53972 EPS2Ingame +
+
0.9.2
Stolen SLES 52882 EPS2Menus +
+
0.9.4
Stolen SLUS 21099 UPS2Ingame +
+
0.9.4
Street Fighter - Alpha Anthology SLUS 21317 UPS2Ingame +
+
0.9.4
Street Fighter EX3 PBPX 95201 UPS2Playable +
+
0.9.2
Street Fighter EX3 SLES 50072 EPS2Ingame +
+
0.9.4
Street Fighter EX3 SLUS 20130 UPS2Playable +
+
0.9.4
Street Fighter Zero - Fighters Generation SLPM 66409 JPS2Playable +
+
0.9.2
Street Racing Syndicate SLES 53045 EPS2Menus +
+
0.9.4
Street Racing Syndicate SLUS 20582 UPS2Nothing +
+
0.9.4
Strikers 1945 I & II SLPM 62515 JPS2Playable +
+
0.9.2
Strikers 1945 III SLKA 15005 JPS2Playable +
+
0.9.2
Stuntman - Ignition SLUS 21626 UPS2Intro +
+
0.9.4
Stuntman Ignition SLES 54820 EPS2Ingame +
+
0.9.4
Stuntman SLES 50288 EPS2Ingame +
+
0.9.2
Stuntman SLPS 25026 UPS2Ingame +
+
0.9.4
Suffering, The - Ties That Bind SLUS 21189 UPS2Ingame +
+
0.9.4
Suffering, The SLUS 20636 UPS2Ingame +
+
0.9.4
Suikoden III SLUS 20387 UPS2Playable +
+
0.9.4
Suikoden IV SLES 52913 EPS2Playable +
+
0.9.2
Suikoden IV SLUS 20979 UPS2Playable +
+
0.9.4
Suikoden Tactics SLUS 21245 UPS2Ingame +
+
0.9.4
Suikoden V SLUS 21291 UPS2Ingame +
+
0.9.4
Sum of All Fears, The SLES 51180 EPS2Playable +
+
0.9.2
Summer Heat Beach Volleyball SLES 51778 EPS2Ingame +
+
0.9.2
Summer Heat Beach Volleyball SLUS 20634 UPS2Playable +
+
0.9.4
Summoner 1 SLES 82005 GPS2Ingame +
+
0.9.2
Summoner 1 SLUS 20074 UPS2Ingame +
+
0.9.4
Summoner 2 SLES 51142 GPS2Playable +
+
0.9.2
Sunny Garcia Surfing SLUS 20208 UPS2Nothing +
+
0.9.4
Super Bust-A-Move 2 SLUS 20460 UPS2Intro +
+
0.9.4
Super Bust-A-Move PBPX 95201 UPS2Playable +
+
0.9.4
Super Bust-A-Move SLES 50076 EPS2Playable +
+
0.9.2
Super Bust-A-Move SLUS 20115 UPS2Playable +
+
0.9.2
Super Dimensional Fortress Macross SLPM 65405 JPS2Nothing +
+
0.9.4
Super Dragon Ball Z SLUS 21442 UPS2Playable +
+
0.9.4
Super Monkey Ball Adventure SLES 53701 EPS2Intro +
+
0.9.2
Super Monkey Ball Adventure SLUS 21272 UPS2Ingame +
+
0.9.4
Super Puzzle Bobble SLPM 62016 JPS2Playable +
+
0.9.2
Super Robot Wars 3 SLPS 25537 JPS2Playable +
+
0.9.4
Super Shanghai 2005 SLPM 62552 JPS2Playable +
+
0.9.2
Super Trucks SLES 50897 EPS2Ingame +
+
0.9.3
Superior Defender Gundam Force Showdown! SLUS 20698 UPS2Ingame +
+
0.9.2
Superman - Shadow of Apokolips SLUS 20235 UPS2Ingame +
+
0.9.4
Superman Returns SLUS 21434 UPS2Ingame +
+
0.9.4
Surfs Up SLUS 21572 UPS2Playable +
+
0.9.4
Suzuki TT Superbikes SLUS 20912 UPS2Ingame +
+
0.9.4
Sven Göran Eriksson's World Manager 2002 SLES 50794 EPS2Ingame +
+
0.9.4
SWAT - Global Strike Team SLES 52097 EPS2Ingame +
+
0.9.2
SWAT - Global Strike Team SLUS 20433 UPS2Ingame +
+
0.9.4
Swing Away Golf SLUS 20096 UPS2Intro +
+
0.9.4
Switch SLPM 65121 JPS2Playable +
+
0.9.2
Sword of Etheria SLES 53768 EPS2Ingame +
+
0.9.2
Sword of The Berserk - Gut's Rage SLPM 65688 JPS2Playable +
+
0.9.2
Swords of Destiny SLES 53699 EPS2Playable +
+
0.9.2
SX Superstars SLES 51495 EPS2Ingame +
+
0.9.2
Syberia SLES 51393 EPS2Ingame +
+
0.9.3
Syphon Filter - Dark Mirror SCUS 97362 UPS2Ingame +
+
0.9.4
Syphon Filter - The Omega Strain SCES 52033 GPS2Menus +
+
0.9.2
Syphon Filter - The Omega Strain SCUS 97264 UPS2Ingame +
+
0.9.4
Taiko no Tatsujin: Doki! Shinkyoku Darake no Haru Matsuri SLPS 20272 JPS2Playable +
+
0.9.4
Taisho Mononoke Ibunroku SLPM 65228 JPS2Playable +
+
0.9.4
Taito Legends SLES 53438 EPS2Playable +
+
0.9.2
Tak - The Great Juju Challenge SLUS 21218 UPS2Ingame +
+
0.9.4
Tales of Legendia SLPS 25533 JPS2Ingame +
+
0.9.4
Tales of Legendia SLUS 21201 UPS2Ingame +
+
0.9.4
Tales of The Abyss SLUS 21386 UPS2Playable +
+
0.9.4
TamTam Paradise SLPM 62073 JPS2Ingame +
+
0.9.2
Tarzan Untamed SLUS 20076 UPS2Nothing +
+
0.9.4
Taz Wanted SLES 50649 EPS2Intro +
+
0.9.4
TD overdrive - The Brotherhood Of Speed SLES 50778 EPS2Intro +
+
0.9.2
Technic Beat SLUS 21019 UPS2Playable +
+
0.9.4
Technictix SLPS 20055 JPS2Playable +
+
0.9.4
Teen Titans SLUS 21183 UPS2Menus +
+
0.9.4
Teenage Mutant Ninja Turtles 2 - Battle Nemesis SLUS 20981 UPS2Ingame +
+
0.9.4
Teenage Mutant Ninja Turtles SLES 51931 EPS2Playable +
+
0.9.2
Teenage Mutant Ninja Turtles SLUS 21595 UPS2Intro +
+
0.9.4
Tekken 4 SCES 50878 EPS2Playable +
+
0.9.4
Tekken 4 SLUS 20328 UPS2Playable +
+
0.9.4
Tekken 5 SCES 53202 EPS2Intro +
+
0.9.2
Tekken 5 SLPS 25510 JPS2Ingame +
+
0.9.4
Tekken 5 SLUS 21059 UPS2Ingame +
+
0.9.4
Tekken Tag Tournament PBPX 95201 UPS2Playable +
+
0.9.4
Tekken Tag Tournament SCES 50001 EPS2Ingame +
+
0.9.4
Tekken Tag Tournament SLPS 20015 JPS2Ingame +
+
0.9.2
Tenchu - Fatal Shadows SLUS 21129 UPS2Ingame +
+
0.9.4
Tenchu - Wrath of Heaven SLES 51402 GPS2Ingame +
+
0.9.3
Tenchu - Wrath of Heaven SLUS 20397 UPS2Ingame +
+
0.9.4
Tennis Court Smash SLES 51860 EPS2Menus +
+
0.9.2
Terminator 3 - Rise Of The Machines SLUS 20799 UPS2Ingame +
+
0.9.4
Terminator 3 - The Redemption SLUS 20852 UPS2Intro +
+
0.9.4
Terminator, The - Dawn Of Fate SLUS 20391 UPS2Ingame +
+
0.9.4
Test Drive - Eve of Destruction SLUS 20910 UPS2Menus +
+
0.9.4
Test Drive Offroad - Wide Open SLUS 20177 UPS2Playable +
+
0.9.4
Test Drive Unlimited SLES 54466 EPS2Intro +
+
0.9.4
Test Drive Unlimited SLUS 21490 UPS2Nothing +
+
0.9.4
Test Drive SLUS 20213 UPS2Intro +
+
0.9.4
Tetris Worlds SLUS 20247 UPS2Playable +
+
0.9.4
Th3 Plan SLUS 21466 UPS2Playable +
+
0.9.4
Theme Park - Rollercoaster SLUS 20099 UPS2Ingame +
+
0.9.4
Theme Park World SLES 50032 EPS2Playable +
+
0.9.4
Thing, The SLES 50975 EPS2Menus +
+
0.9.4
Thing, The SLUS 20371 UPS2Menus +
+
0.9.4
Thrillville - Off The Rails SLUS 21611 UPS2Intro +
+
0.9.4
Thrillville SLUS 21413 UPS2Menus +
+
0.9.4
Tiger Woods PGA Tour 2003 SLUS 20572 UPS2Ingame +
+
0.9.4
Tiger Woods PGA Tour 2005 SLUS 21002 UPS2Ingame +
+
0.9.4
Tiger Woods PGA Tour 2006 SLUS 21264 UPS2Intro +
+
0.9.4
Tiger Woods PGA Tour 2007 SLUS 21483 UPS2Intro +
+
0.9.4
Tiger Woods PGA Tour 2008 SLUS 21646 UPS2Ingame +
+
0.9.4
Time Crisis - Crisis Zone SLUS 20927 UPS2Playable +
+
0.9.4
Time Crisis 2 SLUS 20219 UPS2Nothing +
+
0.9.4
Time Crisis 3 SLUS 20645 UPS2Nothing +
+
0.9.4
Timesplitters 2 SLES 50877 EPS2Nothing +
+
0.9.2
Timesplitters 2 SLUS 20314 UPS2Playable +
+
0.9.4
Timesplitters 3 SLUS 21148 UPS2Ingame +
+
0.9.4
Timesplitters PBPX 95201 UPS2Playable +
+
0.9.4
Timesplitters SLES 50078 EPS2Playable +
+
0.9.2
Timesplitters SLUS 20090 UPS2Playable +
+
0.9.4
Toca Race Driver 2 SLUS 21039 UPS2Ingame +
+
0.9.4
Toca Race Driver 3 SLUS 21182 UPS2Ingame +
+
0.9.4
Tokimeki Memorial 3 SLPM 65080 JPS2Ingame +
+
0.9.4
Tokobot Plus - Mysteries Of The Karakuri SLUS 21471 UPS2Ingame +
+
0.9.4
Tokyo Bus Guide 2 SLPM 65982 UPS2Intro +
+
0.9.4
Tokyo Etreme Racer Zero SLUS 20189 UPS2Ingame +
+
0.9.4
Tokyo Extreme Racer 3 SLUS 20831 UPS2Ingame +
+
0.9.4
Tokyo Extreme Racer Drift 2 SLUS 21394 UPS2Playable +
+
0.9.4
Tokyo Extreme Racer Drift SLUS 21236 UPS2Ingame +
+
0.9.4
Tokyo Road Race SLES 50954 EPS2Ingame +
+
0.9.2
Tom And Jerry - War Of The Whiskers SLUS 20355 UPS2Ingame +
+
0.9.4
Tom Clancy's Rainbow Six - Lockdown SLUS 21144 UPS2Ingame +
+
0.9.4
Tom Clancy's Rainbow Six 3 SLUS20883 UPS2Playable +
+
0.9.4
Tom Clancy's Splinter Cell Chaos Theory SLES 53007 EPS2Ingame +
+
0.9.2
Tomb Raider - Angel of Darkness SLUS 20467 UPS2Nothing +
+
0.9.4
Tomb Raider Anniversary SLUS 21555 UPS2Ingame +
+
0.9.4
Tomb Raider Legend SLES 53908 EPS2Ingame +
+
0.9.2
Tomb Raider Legend SLUS 21203 UPS2Ingame +
+
0.9.4
Tony Hawk's American Wasteland SLUS 21208 UPS2Intro +
+
0.9.4
Tony Hawk's Pro Skater 3 SLUS 20013 UPS2Ingame +
+
0.9.4
Tony Hawk's Underground 2 SLUS 20965 UPS2Intro +
+
0.9.4
Tony Hawk's Underground SLES 51852 GPS2Intro +
+
0.9.2
Tony Hawks Downhill Jam SLUS 21456 UPS2Intro +
+
0.9.4
Tony Hawks Project 8 SLUS 21444 UPS2Intro +
+
0.9.4
Tony Hawks Proving Ground SLUS 21616 UPS2Intro +
+
0.9.4
Tony Hawks Underground SLUS 20731 UPS2Intro +
+
0.9.4
Top Spin SLUS 21222 UPS2Intro +
+
0.9.4
Torino 2006 SLES 53901 EPS2Ingame +
+
0.9.2
Total Immersion Racing SLES 51128 EPS2Playable +
+
0.9.2
Total Immersion Racing SLUS 20409 UPS2Ingame +
+
0.9.4
Total Overdose - A Gunslinger's Tale In Mexico SLUS 21283 UPS2Ingame +
+
0.9.4
Tourist Trophy - Real Riding Simulator SCAJ 20170 JPS2Intro +
+
0.9.2
Tourist Trophy SCUS 97502 UPS2Ingame +
+
0.9.4
Transformers - The Game SLES 54755 EPS2Ingame +
+
0.9.4
Transformers - The Game SLUS 21602 UPS2Ingame +
+
0.9.4
Transformers Tataki SLPM 65407 JPS2Ingame +
+
0.9.4
Transformers SLES 52388 EPS2Ingame +
+
0.9.2
Transformers SLUS 20668 UPS2Intro +
+
0.9.4
Transworld Surf SLUS 20356 UPS2Playable +
+
0.9.4
Trapt SLUS 21255 UPS2Ingame +
+
0.9.4
Tribes - Aerial Assault SLUS 20149 UPS2Playable +
+
0.9.4
Trigger Man SLUS 20931 UPS2Playable +
+
0.9.4
Trivial Pursuit Unhinged SLUS 20791 UPS2Intro +
+
0.9.4
True Crime - New York City SLUS 21106 UPS2Menus +
+
0.9.4
True Crimes - Streets Of L.A. SLUS 20550 UPS2Menus +
+
0.9.4
Tsugunai Atonement SLUS 20292 UPS2Ingame +
+
0.9.4
Tsukiyo Ni Saraba SLPM 65826 JPS2Ingame +
+
0.9.2
Turok - Evolution SLES 51124 GPS2Ingame +
+
0.9.3
Turok - Evolution SLUS 20333 UPS2Ingame +
+
0.9.4
TVDJ SCPS 15002 JPS2Ingame +
+
0.9.4
Twenty 2 Party SLES 53369 EPS2Playable +
+
0.9.4
Twin Caliber SLES 51226 EPS2Playable +
+
0.9.2
Twisted Metal - Black SCUS 97101 UPS2Ingame +
+
0.9.4
Ty The Tasmanian Tiger SLUS 20571 UPS2Intro +
+
0.9.4
Ty The Tazmanian Tiger 3 SLUS 21253 UPS2Ingame +
+
0.9.4
UEFA Champions League 2006-2007 SLUS 21581 UPS2Ingame +
+
0.9.4
UFC - Sudden Impact SLUS 20596 UPS2Ingame +
+
0.9.4
UFC Throwdown SLUS 20252 UPS2Ingame +
+
0.9.4
Ultimate Board Game Collection SLUS 21366 UPS2Nothing +
+
0.9.4
Ultimate Pro pinball SLES 53508 EPS2Ingame +
+
0.9.4
Ultimate Spiderman SLUS 20870 UPS2Ingame +
+
0.9.4
Under The Skin SLES 52719 EPS2Playable +
+
0.9.4
Unison SLPS 25010 JPS2Playable +
+
0.9.4
Unlimited Saga SLUS 20678 UPS2Ingame +
+
0.9.4
Unreal Tournament PBPX 95201 JPS2Intro +
+
0.9.4
Unreal Tournament SLES 50074 EPS2Playable +
+
0.9.2
Unrun Quest SLPM 66028 JPS2Ingame +
+
0.9.4
Urban Chaos - Riot Response SLUS 21390 UPS2Ingame +
+
0.9.4
Urban Reign SCES 53688 EPS2Ingame +
+
0.9.4
Urban Reign SLUS 21209 UPS2Ingame +
+
0.9.4
Urbz - Sims In The City SLUS 21066 UPS2Ingame +
+
0.9.4
V-Rally 3 SLES 50725 EPS2Ingame +
+
0.9.2
Valkyrie Profile 2 - Silmeria SLPM 66419 JPS2Nothing +
+
0.9.2
Valkyrie Profile 2 - Slimeria SLUS 21452 UPS2Nothing +
+
0.9.4
Vampire Darkstalkers Collection SLPM 65998 JPS2Ingame +
+
0.9.4
Vampire Night SCES 50411 EPS2Playable +
+
0.9.4
Vampire Night SLPS 25077 JPS2Playable +
+
0.9.2
Vampire Night SLUS 20221 UPS2Playable +
+
0.9.4
Van Helsing SLES 51908 EPS2Ingame +
+
0.9.4
Van Helsing SLUS 20738 UPS2Playable +
+
0.9.4
Venus & Braves SLPS 25195 JPS2Ingame +
+
0.9.2
Vexx SLES 50481 EPS2Menus +
+
0.9.2
Vexx SLUS 20383 UPS2Ingame +
+
0.9.4
Victorious Boxers - Ippo's Road To Victory SLUS 20282 UPS2Playable +
+
0.9.4
Victorious Boxers 2 - Fighting Spirits SLUS 21204 UPS2Playable +
+
0.9.4
Vietcong - Purple Haze SLUS 21068 UPS2Ingame +
+
0.9.4
Viewtiful Joe 2 SLPM 65824 JPS2Playable +
+
0.9.2
Viewtiful Joe SLPM 65699 JPS2Playable +
+
0.9.2
Viewtiful Joe SLUS 20951 UPS2Playable +
+
0.9.4
Virtua Cop Elite Edition SLES 51229 EPS2Playable +
+
0.9.2
Virtua Cop Re-Birth SLPM 62205 JPS2Playable +
+
0.9.2
Virtua Fighter 4 - Evolution SLES 51616 EPS2Playable +
+
0.9.4
Virtua Fighter 4 - Evolution SLUS 20616 UPS2Playable +
+
0.9.2
Virtua Fighter 4 SCES 50759 EPS2Playable +
+
0.9.4
Virtua Fighter 4 SLPM 62130 JPS2Playable +
+
0.9.2
Virtua Fighter 4 SLUS 20323 UPS2Playable +
+
0.9.4
Virtua Quest SLUS 20977 UPS2Ingame +
+
0.9.4
Virtua Tennis 2 SLES 51232 EPS2Playable +
+
0.9.4
Virtual On - Mars SLUS 20674 UPS2Playable +
+
0.9.4
Volleyball Xciting SLES 51765 EPS2Playable +
+
0.9.4
Wakeboarding Unleashed SLUS 20418 UPS2Intro +
+
0.9.4
WakuWaku Volley 2 SLPM 62285 JPS2Playable +
+
0.9.4
Wallace & Gromit in Project Zoo SLES 51989 EPS2Ingame +
+
0.9.4
Wallace & Grommit in Projekt Zoo SLES 52026 GPS2Ingame +
+
0.9.3
Wanda to Kyozou SCPS 15097 JPS2Ingame +
+
0.9.4
War Of The Monsters SCUS 97197 UPS2Playable +
+
0.9.4
Warhammer 40,000 - Fire Warrior SLUS 20597 UPS2Ingame +
+
0.9.4
Warriors Orochi SLUS 21662 UPS2Playable +
+
0.9.4
Warriors, The SLUS 21215 UPS2Ingame +
+
0.9.4
Warship Gunner 2 SLUS 21387 UPS2Nothing +
+
0.9.4
Way of The Samurai SLUS 20407 UPS2Ingame +
+
0.9.4
Way of The Samurai 2 SLES 52275 EPS2Playable +
+
0.9.2
Way of The Samurai 2 SLUS 20893 UPS2Ingame +
+
0.9.4
We Love Katamari SLUS 21230 UPS2Ingame +
+
0.9.4
Wheel of Fortune SLUS 20790 UPS2Playable +
+
0.9.4
Whiplash SLES 51958 EPS2Ingame +
+
0.9.2
Whiplash SLUS 20684 UPS2Ingame +
+
0.9.4
Wild Arms 3 SCUS 97203 UPS2Ingame +
+
0.9.4
Wild Arms 4 SLUS 21292 UPS2Playable +
+
0.9.4
Wild Arms 5 SLUS 21615 UPS2Nothing +
+
0.9.4
Wild Arms Alter Code - F SLUS 20937 UPS2Playable +
+
0.9.4
Wild Wild Racing SLES 50009 EPS2Playable +
+
0.9.4
Winback 2 - Project Poseidon SLUS 20947 UPS2Ingame +
+
0.9.4
Winning Eleven 10 SLPM 66374 JPS2Playable +
+
0.9.3
Winning Eleven 6 SLPM 62268 JPS2Playable +
+
0.9.3
Winning Eleven 7 SLPM 62356 JPS2Playable +
+
0.9.3
Winning Eleven 9 International SLUS 21220 UPS2Playable +
+
0.9.4
Winning Eleven 9 SLPM 66009 JPS2Ingame +
+
0.9.2
Winning Eleven Pro Evolution Soccer 2007 SLUS 21464 UPS2Playable +
+
0.9.4
WipEout Fusion SCES 50005 EPS2Playable +
+
0.9.4
Wipeout Fusion SLUS 20462 UPS2Playable +
+
0.9.4
Without Warning SLUS 21156 UPS2Intro +
+
0.9.4
Wizardry - Tale Of The Forsaken Lands SLUS 20259 UPS2Ingame +
+
0.9.4
Woody Woodpecker - Escape From Buzz Buzzard Park SLUS 20341 UPS2Playable +
+
0.9.4
Woody Woodpecker PBPX 95201 JPS2Playable +
+
0.9.2
Woody Woodpecker SLES 50613 EPS2Ingame +
+
0.9.2
Worimagesoundplay SLPM 65484 JPS2Ingame +
+
0.9.4
World Championship Poker - All In SLUS 21412 UPS2Nothing +
+
0.9.4
World Championship Poker 2 SLUS 21176 UPS2Playable +
+
0.9.4
World Championship Pool 2004 SLUS 20760 UPS2Playable +
+
0.9.4
World Destruction League - Thunder Tanks PBPX 95201 UPS2Playable +
+
0.9.4
World Destruction League - War Jets SLUS 20007 UPS2Playable +
+
0.9.4
World Poker Tour SLUS 21333 UPS2Intro +
+
0.9.4
World Rally Championship 3 SLPM 65583 JPS2Ingame +
+
0.9.2
World Rally Championship 4 SCES 52389 EPS2Ingame +
+
0.9.3
World Rally Championship SCES 50139 EPS2Nothing +
+
0.9.2
World Series Of Poker - Battle For The Bracelets SLUS 21686 UPS2Ingame +
+
0.9.4
World Series of Poker - Tournament of Champions SLUS 21491 UPS2Playable +
+
0.9.4
World Series Of Poker SLUS 21301 UPS2Playable +
+
0.9.4
Worms 3D SLUS 20894 UPS2Playable +
+
0.9.4
Worms 4 - Mayhem SLES 53096 EPS2Ingame +
+
0.9.4
Worms Forts Under Siege SLES 52342 EPS2Playable +
+
0.9.3
Wrath Unleashed SLUS 20840 UPS2Intro +
+
0.9.4
WRC Rally Evolved SCES 53247 EPS2Ingame +
+
0.9.3
Wrestle Kingdom SLPM 66401 JPS2Playable +
+
0.9.4
WTA Tennis Tour USA SLPM 62046 JPS2Playable +
+
0.9.4
WWE Crush Hour SLUS 20385 UPS2Playable +
+
0.9.4
WWE Smackdown VS Raw 2006 SLES 53676 EPS2Playable +
+
0.9.3
WWE Smackdown VS Raw 2006 SLUS 21286 UPS2Playable +
+
0.9.4
WWE Smackdown VS Raw 2007 SLES 54489 EPS2Playable +
+
0.9.3
WWE Smackdown VS Raw 2007 SLUS 21427 UPS2Playable +
+
0.9.4
WWE Smackdown VS Raw SLUS 21060 UPS2Playable +
+
0.9.4
WWE Smackdown! Here Comes the Pain SLUS 20787 UPS2Playable +
+
0.9.2
X-Files, The - Resist or Serve SLUS 20179 UPS2Ingame +
+
0.9.4
X-Men 3 - The Official Game SLUS 21107 UPS2Ingame +
+
0.9.4
X-Men Legends 2 - Rise of The Apocalypse SLUS 21138 UPS2Playable +
+
0.9.4
X-Men Legends SLUS 20656 UPS2Playable +
+
0.9.4
X-Squad SLES 50031 EPS2Intro +
+
0.9.4
X-Treme Quads SLES 53141 EPS2Ingame +
+
0.9.4
X2 - Wolverine's Revenge SLUS 20337 UPS2Ingame +
+
0.9.4
Xena - Warrior Princess SLES 54541 EPS2Playable +
+
0.9.4
Xenosaga Episode 3 - Also Sprach Zarathustra SLPS 25640 JPS2Ingame +
+
0.9.2
Xenosaga Episode I - Der Wille zur Macht SCAJ 30001 JPS2Playable +
+
0.9.4
Xenosaga Episode I - Der Wille zur Macht SLUS 20469 UPS2Ingame +
+
0.9.4
Xenosaga Episode II - Jenseits von Gut und Bose SLUS 20892 UPS2Ingame +
+
0.9.4
Xenosaga Episode III - Also Sprach Zarathustra SLUS 21389 UPS2Ingame +
+
0.9.4
XGIII - Extreme G Racing SLES 50210 EPS2Ingame +
+
0.9.4
XGRA - Extreme G Racing Association SLUS 20632 UPS2Playable +
+
0.9.4
Xiaolin Showdown SLUS 21405 UPS2Ingame +
+
0.9.4
XIII SLES 51244 EPS2Ingame +
+
0.9.2
XIII SLUS 20677 UPS2Menus +
+
0.9.4
Yakuza SLES 54171 EPS2Menus +
+
0.9.2
Yakuza SLUS 21348 UPS2Ingame +
+
0.9.4
Yamasa Digi World SP Giant Pulsar SLPS 20475 JPS2Playable +
+
0.9.2
Yoshinoya SLPM 62489 JPS2Playable +
+
0.9.4
Ys 3 - Wanderer's From Ys SLPM 62532 JPS2Intro +
+
0.9.4
Ys: Ark Of Napishtim SLUS 20980 UPS2Playable +
+
0.9.4
Yu-Gi-Oh! Capsule Monster Coliseum SLUS 20940 UPS2Playable +
+
0.9.3
Yu-Gi-Oh! The Duelists of the Roses SLUS 20515 UPS2Playable +
+
0.9.4
Zatch Bell - Mamodo Battles SLUS 21254 UPS2Playable +
+
0.9.4
Zatch Bell - Mamodo Fury SLUS 21363 UPS2Nothing +
+
0.9.4
Zathura SLES 53696 EPS2Playable +
+
0.9.2
Zathura SLUS 21336 UPS2Playable +
+
0.9.4
Zettai Zetsumei Toshi SLPS 25113 JPS2Playable +
+
0.9.4
Zone Of The Enders - The 2nd Runner SLES 51113 EPS2Playable +
+
0.9.2
Zone Of The Enders - The 2nd Runner SLUS 20545 UPS2Nothing +
+
0.9.4
Zone of the Enders SLPM 65019 JPS2Ingame +
+
0.9.2
Zone of The Enders SLUS 20148 UPS2Ingame +
+
0.9.4
Zooo SLPM 62466 JPS2Ingame +
+
0.9.2
diff --git a/bin/compat_list/h_console.jpg b/bin/compat_list/h_console.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c589631fae6448736182f3030663a4d8a1e89ab6 GIT binary patch literal 544 zcmex=iF;o{=v;^GnD0RsUZK7IjyJ|1CV5fNcw z8EI*08F@HhWM^mR<>8eO5Ri}(6%>_%OAyQWe}F-dgMov=hM7^2fk}{&S&;Gn5r#wt zMj!yX5)N3Inb=W9KvDt>j4X`IKq;6yMkZz!RzU_KVMP%(Wl_V#LM5XhRY4S!KuZ7L zV&GwB1lq?e$Y9U#rbNHO>4Cv&%_Whd`ct(gE2@E z^lsi($k3Bv| zmQ^@8P%Qeq!?fCkv!(tsgzS3U$*H>|=452h#);P*`e)n=^KXrNW0`9$;-kB7tJQ1Y p^wxClXy*!fU&fky`fq=}ORR4Dll%USMa9S8>2+`ZY?1$e69BBKj57cL literal 0 HcmV?d00001 diff --git a/bin/compat_list/h_region.jpg b/bin/compat_list/h_region.jpg new file mode 100644 index 0000000000000000000000000000000000000000..515ea4f33bd96e9647997e24ca7ff5716ccda75f GIT binary patch literal 486 zcmex=iF;o{=v;^GnD0RsUZK7IjyJ|1CV5fNcw z8EI*08F@HhWM^mR<>8eO5Ri}(6%>_%OAyQWe}F-dgMouVpP5mRfk}{&S&;Gn5r!BB z24+T}Bar|z3p*p45Cek%0}~S?Gczk(9U~Jn3oC=5p`xR3qL7jZn;?dk|F;-;fMzoZ zG7B=;GhEAhv}O+1)7%wN`##0p30t#ug3VDzfsGNCFVD;EV#@q@+;XBM=WEvJOWKw1 zV%h>F5-%pVKinXbp>f*3MKb%fYDZ(p$}eGOX9pZhGE~|BcppDvdLD8NV@C-q!s8CIFiF;o{=v;^GnD0RsUZK7IjyJ|1CV5fNcw z8EI*08F@HhWM^mR<>8eO5Ri}(6%>_%OAyQWe}F-dgMouVmzhzJfk}{&S&;Gn5r!BB zMn*=UBN2d=g&j=(86|xzpEk#(7^*NiglYP$GKP&f>X|vZ8ImwR;oSJ>OX* z>5_Q(aB}+>2ipvx*}}={^Y&;Q7o9c1J-m6|-5r5_tx?g3{GKx3-t&TgtMxw3w<~SV zbw4^TIrZo|z4vXC9$k8WB0h7$qHEg9U3S-duj+CF)G)|FS0F%uS*@ZR!oxuE%7 c{`tPu2R{XVud+5iF;o{=v;^GnD0RsUZK7IjyJ|1CV5fNcw z8EI*08F@HhWM^mR<>8eO5Ri}(6%>_%OAyQWe}F-dgMov=l$lYGfk}{&S&;Gn5r#yd zvzZxzu7m?tW)?PfMie2CoB#t86B8pd6FW?Xk%^f_kX495QP_~pG0@0KSwxgw5GDyX z?f)$X9-#S5g3N*p_6#rYZ+4qfIjt)Ex%>M=Umt2u`XSN7>a)U@ZF}j0YxX-!JHi6C z-#UFOFsYO$$l!E?4C`U}?Xs-WRo2sMk8WyP6X?t8vZiC3Zd7}RVU>Q%yvXBqu`EK_ z4`=`RC);u?xQgY$q&1g%m{XcKc$mv9-e1t&Y_&c=*JS^cs|%tod!iF;o{=v;^GnD0RsUZK7IjyJ|1CV5fNcw z8EI*08F@HhWM^mR<>8eO5Ri}(6%>_%OAyQWe}F-dgMouVg_%*1fk}{&S&;Gn5r#wt z1}0{pBN2d+iJ6t1kr5#PVF@rWurf2Tvckm~1(}#xgoIfcL<}88*~AnR3yp#VF|_@^ z#lXYN2(*t`kinkeUQMy^)7@Tix8H0x`!2-w_Ena3`{C)lOj^vs8zrBobaxlY>^#-8 zWY^=84h0swF3$<=j2B#f8yHH+=sel6P3X@Izq6%zJ2nU`aCI_WnIgAm3-g}h87Dt% ze0O--<&91W+kRbnz1E~GOl9j9(T17~t@ma7{cX?aEsPA_eiF;o{=v;^GnD0RsUZK7IjyJ|1CV5fNcw z8EI*08F@HhWM^mR<>8eO5Ri}(6%>_%OAyQWe}F-dgMov=f|*f}fk}{&S&;Gn5r#wt zMj!yX5)N3InAuT9KvDt>jLb}|ER3u$b&O2REP|{IibBGohDIV{g-XiBs%(NNCV`ax zzs11A%m}oPS&+e=;q9FXU+cI}*d30wQ(L@0_V20ikIz3gXg+^?>}R1k`(u`6#hqb) zPWJ4Q=8{z1R@cRNnPKv^nc~)8963&~p^lrGC#*{|Iuah`_Pj$Ms78)xHD+_xMmO}6f{xlvyyu!Lz1fq@K zV_^6OhG#D30lApXXCMb?Y+#;H)BSpT0D%D*S@l9PX?Cj(n${XJcO1bPV;$<9{de}j}@5dr8q%6u@?(|r=(D-L0eILYcK`qY literal 0 HcmV?d00001 diff --git a/bin/compat_list/sq_intro.jpg b/bin/compat_list/sq_intro.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e7b8a3cc44c0567642c30c47ce6498590faeeb71 GIT binary patch literal 305 zcma)$O$x#=5QX2^G^uIYCZvU81ur6qAmYz0w60vZa^c>Cb>SiWS?B@8E5zVJP&a;$ zf#Dk%o;$i5^lUsGgIYlC19yiRZI|mE2n;~qs|V6yy;-bKH{OuDU=Pk13mFeX-sXPd zi1R2`QpQp$tzKxAW;#oiwRvuBx2mdc?+pnd5|xx%mz}~Co%iA2#WgSyd;&6P843)! g1LnT=CxYlpaiPBqV1A4{Lj~kN2L=O-Cdt*Eo}fV{4FCWD literal 0 HcmV?d00001 diff --git a/bin/compat_list/sq_menus.jpg b/bin/compat_list/sq_menus.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5f2defa6a2a5eabf284222fb90b9f35d91ba33a1 GIT binary patch literal 305 zcma)$Jqp4=5QX2E{fV13?y?3#B8c}8M38`b3yqb9m4&?rOW`5>X*_^|tI)W+{I zFnj~U^Cy3UnOCz4)B@@d_&e-$x7zGMU;suzJ&;c7?Q)H}35NU?2MER(XM%Il7GaWD zh&a(oB}!?dUl^TsO*hrf^?J_r%d+eb-jI~CqdU1Vd6C(ycpv^SyCVn;z|g1{qH(=nZ&5eKkiO#t&KPqTcO2g1 zW&)3MFA%;T_`Z$;%`b1Cy8Q(4g+{#|qn<-x=z1xt~w gNT0DVwZGv+J;hwvGl2Rr`UYW>`D`nUu|Gjy-+QDbpa1{> literal 0 HcmV?d00001 diff --git a/bin/patches/013E349D.pnach b/bin/patches/013E349D.pnach new file mode 100644 index 0000000000..ca24695584 --- /dev/null +++ b/bin/patches/013E349D.pnach @@ -0,0 +1,4 @@ +gametitle= Resident Evil 4 [SLUS 21134] (U) +comment= +//ZeroGS Patch - GAME_NOTARGETCLUT +zerogs=00001000 \ No newline at end of file diff --git a/bin/patches/015314A2.pnach b/bin/patches/015314A2.pnach new file mode 100644 index 0000000000..a03a6a2e3d --- /dev/null +++ b/bin/patches/015314A2.pnach @@ -0,0 +1,4 @@ +gametitle= Sims - The Urbz, The [SLES 52908] (E) +comment= +//ZeroGS Patch - GAME_NOQUICKRESOLVE +zerogs=00000800 \ No newline at end of file diff --git a/bin/patches/034836F8.pnach b/bin/patches/034836F8.pnach new file mode 100644 index 0000000000..6bbc24dcd6 --- /dev/null +++ b/bin/patches/034836F8.pnach @@ -0,0 +1,13 @@ +gametitle=Driving Emotion Type-S [SLPS 20007] (J) +comment= patches by Nachbrenner +//Skip OP_MOVIE +//patch=0,EE,001c1818,word,1000000c +//MTGS: fix GS_CSR in "sceGsExecStoreImage" +patch=0,EE,00276ad0,word,34420002 +patch=0,EE,00276aa0,word,34630002 +//MTGS: fix GS_CSR in custom function +patch=0,EE,00251ac4,word,00000000 +//disable BGM +patch=0,EE,00221914,word,24020000 +patch=0,EE,002218c8,word,03e00008 +patch=0,EE,002218cc,word,00000000 \ No newline at end of file diff --git a/bin/patches/0442B1BD.pnach b/bin/patches/0442B1BD.pnach new file mode 100644 index 0000000000..2d7a18a206 --- /dev/null +++ b/bin/patches/0442B1BD.pnach @@ -0,0 +1,4 @@ +gametitle=Samurai Warriors Xtreme Legends +comment=Skips Videos by bositman +//Skip Videos +patch=0,EE,00126bb0,word,24020001 \ No newline at end of file diff --git a/bin/patches/05177ECE.pnach b/bin/patches/05177ECE.pnach new file mode 100644 index 0000000000..ea287ab894 --- /dev/null +++ b/bin/patches/05177ECE.pnach @@ -0,0 +1,4 @@ +gametitle=Tomb Raider Legend [SLES 53908] (E) [05177ECE] +comment=Patch by CKemu +//Skip Videos (sceMpegIsEnd) +patch=0,EE,00369988,word,24020001 \ No newline at end of file diff --git a/bin/patches/0518D274.pnach b/bin/patches/0518D274.pnach new file mode 100644 index 0000000000..748dfdc23f --- /dev/null +++ b/bin/patches/0518D274.pnach @@ -0,0 +1,4 @@ +gametitle=Need For Speed Most Wanted Black Edition(SLUS_213.51) +comment=Skips Video - deny file By General Plot +//Skip Videos +patch=0,EE,00242418,word,00000000 \ No newline at end of file diff --git a/bin/patches/0518D275.pnach b/bin/patches/0518D275.pnach new file mode 100644 index 0000000000..89686a2c30 --- /dev/null +++ b/bin/patches/0518D275.pnach @@ -0,0 +1,4 @@ +gametitle=Need For Speed Most Wanted (SLUS_212.67) +comment=Skips Video - deny file By General Plot +//Skip Videos +patch=0,EE,00242414,word,00000000 \ No newline at end of file diff --git a/bin/patches/06DE61E0.pnach b/bin/patches/06DE61E0.pnach new file mode 100644 index 0000000000..c9dbb3bc87 --- /dev/null +++ b/bin/patches/06DE61E0.pnach @@ -0,0 +1,4 @@ +gametitle= Guilty Gear XX - Slash Midnight Carnival [SLPM 66333] (J) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,002743d8,word,24020001 \ No newline at end of file diff --git a/bin/patches/07AD79C9.pnach b/bin/patches/07AD79C9.pnach new file mode 100644 index 0000000000..74fa49547b --- /dev/null +++ b/bin/patches/07AD79C9.pnach @@ -0,0 +1,4 @@ +gametitle=Yu-Gi-Oh The Duelists Of The Roses (U) +comment= +//Skip Videos +patch=0,EE,0018ee60,word,24020001 \ No newline at end of file diff --git a/bin/patches/0838D766.pnach b/bin/patches/0838D766.pnach new file mode 100644 index 0000000000..79d1f54315 --- /dev/null +++ b/bin/patches/0838D766.pnach @@ -0,0 +1,6 @@ +gametitle=Raiden III [SLES 53829] (E) +comment=patches by Nachbrenner, made with Phaste 1.20 +//Infinite Lives +patch=1,EE,002d7458,byte,08 +//Infinite Bombs +patch=1,EE,002d74bc,byte,08 \ No newline at end of file diff --git a/bin/patches/086273D2.pnach b/bin/patches/086273D2.pnach new file mode 100644 index 0000000000..5abbf3cd0d --- /dev/null +++ b/bin/patches/086273D2.pnach @@ -0,0 +1,4 @@ +gametitle=Metal Gear Solid 3 - Snake Eater [SLES 82013] (E) [086273D2] +comment=Patch By CKemu +//ZeroGS Patch - Required fixes to visuals +zerogs=06018000 \ No newline at end of file diff --git a/bin/patches/08901101.pnach b/bin/patches/08901101.pnach new file mode 100644 index 0000000000..9041aa2124 --- /dev/null +++ b/bin/patches/08901101.pnach @@ -0,0 +1,13 @@ +gametitle=Neo Contra [SLUS 20961] (U) +comment= Patches By Nachbrenner +//disable BGM (for blockdump) +// patch=0,EE,0012bf20,word,03e00008 +// patch=0,EE,0012bf24,word,00000000 +//fix IPU busy! ingame +patch=0,EE,003a9538,word,03e00008 +patch=0,EE,003a953c,word,00000000 +patch=0,EE,003a9460,word,03e00008 +patch=0,EE,003a9464,word,00000000 +// +//no 3D = no DMA error +// patch=0,EE,00122b60,word,00000000 \ No newline at end of file diff --git a/bin/patches/08FB9DCF.pnach b/bin/patches/08FB9DCF.pnach new file mode 100644 index 0000000000..ac07a947a2 --- /dev/null +++ b/bin/patches/08FB9DCF.pnach @@ -0,0 +1,9 @@ +gametitle= Card Captor Sakura [SLPS 20408] (J) +comment=patches by Nachbrenner +//skip eyetoy check +patch=0,EE,013385b0,word,00000000 +//skip MC & Start screen +patch=0,EE,01337c20,word,00000000 +//activate 1st for food scene, both for telephone scene +// patch=0,EE,01337c2c,word,00000000 // food scene +// patch=0,EE,01337c3c,word,00000000 // telephone scene \ No newline at end of file diff --git a/bin/patches/0999F9DB.pnach b/bin/patches/0999F9DB.pnach new file mode 100644 index 0000000000..6492054e50 --- /dev/null +++ b/bin/patches/0999F9DB.pnach @@ -0,0 +1,13 @@ +gametitle=Rally Championship [SLES 50763] (E) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,001ADE48,word,03E00008 +patch=0,EE,001CC840,word,24020001 +//Skip sceGsExecStoreImage +patch=0,EE,001c4af0,word,03e00008 +patch=0,EE,001c4af4,word,00000000 +//Begin New Career With Loads Of Money +patch=0,EE,001A05C0,word,3C0505F5 +patch=0,EE,001A05DC,word,34A5E0FF +//Enable Cheat (ASM) +patch=0,EE,0018DEB8,word,24020001 \ No newline at end of file diff --git a/bin/patches/09D35D3F.pnach b/bin/patches/09D35D3F.pnach new file mode 100644 index 0000000000..4223ec165e --- /dev/null +++ b/bin/patches/09D35D3F.pnach @@ -0,0 +1,4 @@ +gametitle=The Urbz: Sims In The City [SLUS 21066] (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,004226d0,word,24020001 \ No newline at end of file diff --git a/bin/patches/0A342A88.pnach b/bin/patches/0A342A88.pnach new file mode 100644 index 0000000000..5d3ae4f4c9 --- /dev/null +++ b/bin/patches/0A342A88.pnach @@ -0,0 +1,4 @@ +gametitle=Futurama ( U ) +comment=patch by Refraction +//skip movies +patch=0,EE,002fc474,word,00000000 \ No newline at end of file diff --git a/bin/patches/0B359BBF.pnach b/bin/patches/0B359BBF.pnach new file mode 100644 index 0000000000..d3fd069c76 --- /dev/null +++ b/bin/patches/0B359BBF.pnach @@ -0,0 +1,5 @@ +gametitle= Kamen Rider 555 [SLPS 20329] (J) +comment=patches by Nachbrenner +//skip "PlayMpeg" +patch=0,EE,0022f590,word,03e00008 +patch=0,EE,0022f594,word,00000000 \ No newline at end of file diff --git a/bin/patches/0C370E94.pnach b/bin/patches/0C370E94.pnach new file mode 100644 index 0000000000..4e9ba42975 --- /dev/null +++ b/bin/patches/0C370E94.pnach @@ -0,0 +1,4 @@ +gametitle=Simple Series 2000 - Vol.106 - The Block Kuzushi Quest (J) +comment= patch by parotaku +//Skip Videos (sceMpegIsEnd) +patch=0,EE,0019a880,word,24020001 \ No newline at end of file diff --git a/bin/patches/0F6B6315.pnach b/bin/patches/0F6B6315.pnach new file mode 100644 index 0000000000..b3b8d33225 --- /dev/null +++ b/bin/patches/0F6B6315.pnach @@ -0,0 +1,5 @@ +gametitle= Kingdom Hearts [SLUS 20370] (U) +comment= +//ZeroGS Patch - GAME_QUICKRESOLVE1 +zerogs=00000400 +fastmemory diff --git a/bin/patches/1025D50A.pnach b/bin/patches/1025D50A.pnach new file mode 100644 index 0000000000..f705d0d512 --- /dev/null +++ b/bin/patches/1025D50A.pnach @@ -0,0 +1,7 @@ +gametitle= Godzilla - Save The Earth [SLUS 20809] (U) +comment=patches by nachbrenner +//EEREC fix: borked memory write +patch=0,EE,0028b2d0,word,00000000 +//DMA fix menus +patch=0,EE,0022c568,word,00000000 +patch=0,EE,0022c540,word,00000000 \ No newline at end of file diff --git a/bin/patches/116154AD.pnach b/bin/patches/116154AD.pnach new file mode 100644 index 0000000000..1798b7eedb --- /dev/null +++ b/bin/patches/116154AD.pnach @@ -0,0 +1,8 @@ +gametitle=CART Fury Championship Racing [SLUS 20141] (U) +comment=patches by Nachbrenner +//skip PSX2_Mpeg_PlayFile__FPc +patch=0,EE,00107a00,word,03e00008 +patch=0,EE,00107a04,word,24020001 +//skip SuperSync__Fi +patch=0,EE,00100eb0,word,03e00008 +patch=0,EE,00100eb4,word,24020001 \ No newline at end of file diff --git a/bin/patches/1248FE3A.pnach b/bin/patches/1248FE3A.pnach new file mode 100644 index 0000000000..0ebca0f2d4 --- /dev/null +++ b/bin/patches/1248FE3A.pnach @@ -0,0 +1,6 @@ +gametitle=Baphomets Fluch - Der Schlafende Drache [SLES 51978] (G) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,00243fa0,word,24020001 +//Fix obscure infinite loop +patch=0,EE,00216764,word,00000000 \ No newline at end of file diff --git a/bin/patches/129C8600.pnach b/bin/patches/129C8600.pnach new file mode 100644 index 0000000000..6c5e010aa0 --- /dev/null +++ b/bin/patches/129C8600.pnach @@ -0,0 +1,8 @@ +gametitle=DTM Race Driver [SLES 50816] (E) +comment=patches by nachbrenner +//Skip Videos +patch=0,EE,003876c8,word,24020001 +//All Boni unlocked +patch=1,EE,0046a120,word,0000ffff +patch=0,EE,00306e5c,word,3445ffff +patch=0,EE,00306e94,word,3446ffff \ No newline at end of file diff --git a/bin/patches/12AFF4D4.pnach b/bin/patches/12AFF4D4.pnach new file mode 100644 index 0000000000..376621e301 --- /dev/null +++ b/bin/patches/12AFF4D4.pnach @@ -0,0 +1,23 @@ +gametitle= Gunbird 1 & 2 [SLPM 62469] (J) +comment=patches by nachbrenner +//Disable Autosave (001E5AF2) +//patch=0,EE,00107f6c,word,a4400012 +// +//Gunbird 1 Codes +// +//Infinite Lives +patch=0,EE,0018b06c,word,24020005 +//Infinite Bombs +patch=0,EE,0018AF30,word,24020006 +//Invincible +patch=0,EE,0018AF60,word,340203E8 +patch=0,EE,00199b78,word,00000000 +// +//Gunbird 2 Codes +// +//Infinite Lives +patch=0,EE,00134900,word,34020008 +//Infinite Bombs +patch=0,EE,00133db4,word,34020009 +//Invincible +patch=0,EE,00133a60,word,340303E7 \ No newline at end of file diff --git a/bin/patches/1468C474.pnach b/bin/patches/1468C474.pnach new file mode 100644 index 0000000000..48e1348357 --- /dev/null +++ b/bin/patches/1468C474.pnach @@ -0,0 +1,4 @@ +gametitle=Madagascar +comment=Keep thread alive +//Sleepthread syscall +patch=0,EE,002f8e44,word,00000000 \ No newline at end of file diff --git a/bin/patches/1510E1D1.pnach b/bin/patches/1510E1D1.pnach new file mode 100644 index 0000000000..2210ccb0ca --- /dev/null +++ b/bin/patches/1510E1D1.pnach @@ -0,0 +1,7 @@ +gametitle=Crash Twinsanity [SLES 52568] (E) +comment= patches by Nachbrenner +//Skip Videos +patch=0,EE,002b85d0,word,24020001 +// fix VU0REC 17.10.06 (obsolete) +//patch=0,EE,00189ab4,word,00000000 +//patch=0,EE,00189b78,word,00000000 \ No newline at end of file diff --git a/bin/patches/1629D655.pnach b/bin/patches/1629D655.pnach new file mode 100644 index 0000000000..a4555a1026 --- /dev/null +++ b/bin/patches/1629D655.pnach @@ -0,0 +1,20 @@ +gametitle=Red Faction II [SLES 51133] (E) +comment=comment=patches by Nachbrenner +//Skip Videos +//patch=0,EE,001afbc8,word,24020001 +//Boot Language Modifier (use only one of these) +//English +//patch=0,EE,0015bca8,word,24020001 +//Français +//patch=0,EE,0015bca8,word,24020002 +//Deutsch +//patch=0,EE,0015bca8,word,24020004 +//obsolete: +//skip borked VU0 stuff +//patch=0,EE,0020a464,word,00000000 +//patch=0,EE,00180eb0,word,03e00008 //slerp_src +//patch=0,EE,00180eb4,word,00000000 +//new VU0 fix: ctc2 t0, CMSAR0 -> li t0,0 +//patch=0,EE,00180df8,word,24080000 +//patch=0,EE,00181738,word,24080000 +//patch=0,EE,001811ac,word,24080000 diff --git a/bin/patches/163F0461.pnach b/bin/patches/163F0461.pnach new file mode 100644 index 0000000000..4acce85108 --- /dev/null +++ b/bin/patches/163F0461.pnach @@ -0,0 +1,18 @@ +gametitle=V-Rally 3 [SLES 50725] (E) +comment= patches by Nachbrenner +// skip network check (obsolete) +//patch=0,EE,002a4278,word,00103528 // GameInit_edNet__Fv +//IPU DMA fix +patch=0,EE,0012655c,word,00000000 // gcc2_compiled. +//Skip Videos (obsolete) +//patch=0,EE,00267810,word,00000000 // FE_MovieRefresh__Fv +//patch=0,EE,002677d0,word,00000000 // FE_MovieInstall__Fv +//patch=0,EE,002677f0,word,00000000 // FE_MovieInit__Fv +// disable menu music ( Vr3SoundMidiInstall__FP10_vr3_midi_) +patch=0,EE,002698fc,word,00000000 +//skip edSoundFlush__Fv +patch=0,EE,00197530,word,03e00008 +patch=0,EE,00197534,word,00000000 +//skip edDspSetCommand__Fii +patch=0,EE,001ab6b0,word,03e00008 +patch=0,EE,001ab6b4,word,00000000 \ No newline at end of file diff --git a/bin/patches/1738A5B0.pnach b/bin/patches/1738A5B0.pnach new file mode 100644 index 0000000000..4a8e83d993 --- /dev/null +++ b/bin/patches/1738A5B0.pnach @@ -0,0 +1,13 @@ +gametitle=Super Shanghai 2005 [SLPM 62552] (J) +comment=patches by Nachbrenner +//Infinite time +patch=1,EE,00AADAD8,word,0000face +//Skip STAR.PSS +//patch=0,EE,001064c8,word,00000000 +//Skip TITLDEMO.PSS +//patch=0,EE,0010d01c,word,00000000 +//boot with BGM off +patch=0,EE,0011fdac,word,adcf0008 +//Skip BGM Start +patch=0,EE,00116d58,word,03e00008 +patch=0,EE,00116d5c,word,00000000 \ No newline at end of file diff --git a/bin/patches/19D145D7.pnach b/bin/patches/19D145D7.pnach new file mode 100644 index 0000000000..b8c6e6b9ad --- /dev/null +++ b/bin/patches/19D145D7.pnach @@ -0,0 +1,12 @@ +gametitle=Kuri Kuri Mix [SLES 50224] (E) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,001f41b4,word,00000000 +//Start with 600 seconds +patch=0,EE,0012cc60,word,24027530 +//Freeze countdown timer +patch=0,EE,0012d1f8,word,00000000 +//Don't lose time on getting hurt +patch=0,EE,0012e86c,word,00000000 +//Low total time +patch=0,EE,0012d1fc,word,24030001 \ No newline at end of file diff --git a/bin/patches/1CAC8A56.pnach b/bin/patches/1CAC8A56.pnach new file mode 100644 index 0000000000..96760c8036 --- /dev/null +++ b/bin/patches/1CAC8A56.pnach @@ -0,0 +1,4 @@ +gametitle=Venus & braves NTSC-J +comment= +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,003e86a0,word,24020001 \ No newline at end of file diff --git a/bin/patches/1D2818AF.pnach b/bin/patches/1D2818AF.pnach new file mode 100644 index 0000000000..9a4c6c0c78 --- /dev/null +++ b/bin/patches/1D2818AF.pnach @@ -0,0 +1,13 @@ +gametitle=Need For Speed - Hot Pursuit 2 [SLES 50731] (E) +comment=patches by Nachbrenner +//for blockdump: +//disable sound streaming +patch=0,EE,00274080,word,03e00008 +patch=0,EE,00274084,word,00000000 +//Skip Logo and Intro AVI +patch=0,EE,0016f094,word,00000000 +//Skip AVI streaming +patch=0,EE,0016ce30,word,03e00008 +patch=0,EE,0016ce34,word,00000000 +//American Mode (Skip language select menu) +patch=0,EE,0012EF78,word,00000000 \ No newline at end of file diff --git a/bin/patches/200BC0E6.pnach b/bin/patches/200BC0E6.pnach new file mode 100644 index 0000000000..76492e1ed3 --- /dev/null +++ b/bin/patches/200BC0E6.pnach @@ -0,0 +1,4 @@ +gametitle=XIII [SLES 51244] (E) +comment=patches by nachbrenner +//Skip Videos (sceMpegIsEnd) +patch=0,EE,001114a0,word,24020001 \ No newline at end of file diff --git a/bin/patches/2088950A.pnach b/bin/patches/2088950A.pnach new file mode 100644 index 0000000000..fc136e364b --- /dev/null +++ b/bin/patches/2088950A.pnach @@ -0,0 +1,4 @@ +gametitle= Xenosaga Episode III - Also Sprach Zarathustra [SLUS 21389] (U) +comment= +//ZeroGS Patch - GAME_VSSHACK|GAME_FULL16BITRES|GAME_NODEPTHRESOLVE +zerogs=00018000 \ No newline at end of file diff --git a/bin/patches/21068223.pnach b/bin/patches/21068223.pnach new file mode 100644 index 0000000000..c7e282630e --- /dev/null +++ b/bin/patches/21068223.pnach @@ -0,0 +1,5 @@ +gametitle=Okami [US] +//fix sceSifDmaStat loop +patch=0,EE,00214f90,word,24020001 +//ZeroGS Patch -GAME_FULL16BITRES|GAME_NODEPTHRESOLVE|GAME_FASTUPDATE +zerogs=00058000 \ No newline at end of file diff --git a/bin/patches/2251E14D.pnach b/bin/patches/2251E14D.pnach new file mode 100644 index 0000000000..63d63ecaf9 --- /dev/null +++ b/bin/patches/2251E14D.pnach @@ -0,0 +1,4 @@ +gametitle=Tekken 4 [SCES 50878] (E) [2251E14D] +comment=Patch By CKemu +//ZeroGS Patch - Stops characters appearing as wigs on GeForce 8x00 series cards (Disable Alpha Testing) +zerogs=00080000 \ No newline at end of file diff --git a/bin/patches/22C36E63.pnach b/bin/patches/22C36E63.pnach new file mode 100644 index 0000000000..e09b627f6f --- /dev/null +++ b/bin/patches/22C36E63.pnach @@ -0,0 +1,4 @@ +gametitle=Toca Race Driver 2 [SLUS 21039] (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,00452a48,word,24020001 \ No newline at end of file diff --git a/bin/patches/25433CBD.pnach b/bin/patches/25433CBD.pnach new file mode 100644 index 0000000000..5a151f529f --- /dev/null +++ b/bin/patches/25433CBD.pnach @@ -0,0 +1,9 @@ +gametitle=Flipnic [SCPS15050](J) +comment= patches by Nachbrenner +// fix D3_CHCR (might be redundant) +patch=0,EE,0011c1b8,word,00000000 +// skip sceIpuSync +patch=0,EE,00208eb0,word,03e00008 +patch=0,EE,00208eb4,word,00000000 +// fix sceIpuRestartDMA +patch=0,EE,00208e04,word,00000000 \ No newline at end of file diff --git a/bin/patches/27E54B37.pnach b/bin/patches/27E54B37.pnach new file mode 100644 index 0000000000..5768df2acc --- /dev/null +++ b/bin/patches/27E54B37.pnach @@ -0,0 +1,16 @@ +gametitle=Syphon Filter - The Omega Strain [SCES 52033] (G) +comment= patches by Nachbrenner +//Skip FMV (for blockdump) +patch=0,EE,0041743c,word,00000000 +patch=0,EE,00417444,word,00000000 +//Boot Language Fix +patch=0,EE,0042020c,word,24020004 //german +// Playername = PCSX2 +patch=1,EE,008b945c,word,58534350 +patch=1,EE,008b9460,word,00000032 +// +//brute force fix GS_CSR (obsolete) +//patch=0,EE,0039ca3c,word,00000000 +//patch=0,EE,0039caec,word,00000000 +//brute force fix VIF1_STAT (obsolete) +//patch=0,EE,0039c8e0,word,10000019 \ No newline at end of file diff --git a/bin/patches/280DAC56.pnach b/bin/patches/280DAC56.pnach new file mode 100644 index 0000000000..21507086eb --- /dev/null +++ b/bin/patches/280DAC56.pnach @@ -0,0 +1,16 @@ +gametitle=Super Trucks [SLES 50897] (E) +comment=patches by nachbrenner +//Skip Intro Video +patch=0,EE,00100060,word,00000000 +//always use music "BUG.SPU" (for blockdumping) +patch=0,EE,001b5308,word,00209460 +patch=0,EE,001b530c,word,00209460 +patch=0,EE,001b5310,word,00209460 +patch=0,EE,001b5314,word,00209460 +patch=0,EE,001b5318,word,00209460 +patch=0,EE,001b531c,word,00209460 +patch=0,EE,001b5320,word,00209460 +// Disable "PAD_CheckForUSBDevices" +//patch=0,EE,00135118,word,00000000 +//Disable "lgDevInit" +//patch=0,EE,001339fc,word,00000000 \ No newline at end of file diff --git a/bin/patches/295D2F96.pnach b/bin/patches/295D2F96.pnach new file mode 100644 index 0000000000..b2f9716540 --- /dev/null +++ b/bin/patches/295D2F96.pnach @@ -0,0 +1,6 @@ +gametitle=Spider-Man [SLES 50814] (G) +comment=patches by Nachbrenner +//Skip Videos +//patch=0,EE,0039A9D0,word,03E00008 +//patch=0,EE,0039A9D4,word,00000000 +//patch=0,EE,004a3540,word,24020001 \ No newline at end of file diff --git a/bin/patches/29D80A23.pnach b/bin/patches/29D80A23.pnach new file mode 100644 index 0000000000..db783032b1 --- /dev/null +++ b/bin/patches/29D80A23.pnach @@ -0,0 +1,9 @@ +gametitle=Sidewinder V [SLPS 25271] (J) +comment=patches by Nachbrenner +//Language = English +patch=1,EE,0039b484,word,00000001 +//Skip Videos (sceMpegIsEnd) +//patch=0,EE,00280398,word,24020001 +//Skip DualBgmOn +//patch=0,EE,002439f8,word,03e00008 +//patch=0,EE,002439fc,word,00000000 \ No newline at end of file diff --git a/bin/patches/2B2E1535.pnach b/bin/patches/2B2E1535.pnach new file mode 100644 index 0000000000..bb73e07db7 --- /dev/null +++ b/bin/patches/2B2E1535.pnach @@ -0,0 +1,5 @@ + +gametitle=Arc the Lad +comment=Skips Video by Rudy_x +//Skip introVideo(s) +patch=0,EE,001d7df8,word,24020001 diff --git a/bin/patches/2C728173.pnach b/bin/patches/2C728173.pnach new file mode 100644 index 0000000000..5580939418 --- /dev/null +++ b/bin/patches/2C728173.pnach @@ -0,0 +1,4 @@ +gametitle=Fast and The Furious +comment= Skip Videos By Refraction +//Skip Videos +patch=0,EE,003d2570,word,24020001 \ No newline at end of file diff --git a/bin/patches/2CFFFA50.pnach b/bin/patches/2CFFFA50.pnach new file mode 100644 index 0000000000..e513700e16 --- /dev/null +++ b/bin/patches/2CFFFA50.pnach @@ -0,0 +1,9 @@ +gametitle=Buffy: Chaos Bleeds [SLES 51890] (E) +comment= patches by Nachbrenner +//Skip Videos +// patch=0,EE,002A3960,word,24020001 +//Video modifier (use only one of these) +//INTRO1 -> SPIKE_VO +//patch=0,EE,002efb70,word,002efab0 +//INTRO1 -> OUTTAKES +//patch=0,EE,002efb70,word,002efaf0 \ No newline at end of file diff --git a/bin/patches/2D919421.pnach b/bin/patches/2D919421.pnach new file mode 100644 index 0000000000..2c754784a2 --- /dev/null +++ b/bin/patches/2D919421.pnach @@ -0,0 +1,15 @@ +Gametitle=Charlie's Angels [SLES 51750] (E) +comment=patches by nachbrenner +//Skip Delay on Screen 1+2 0x00208818 +patch=0,EE,001915b4,word,ad608818 +patch=0,EE,00191604,word,ac600100 +patch=0,EE,001916ac,word,ac408818 +patch=0,EE,001916c8,word,24020000 +patch=0,EE,0019592c,word,ae000100 +//Skip Videos +patch=0,EE,00191320,word,03E00008 +patch=0,EE,00191324,word,00000000 +patch=0,EE,001aa810,word,24020001 +//Unlock All Boni +patch=0,EE,00194a68,word,2401FFFF +patch=0,EE,00194a6c,word,FCE10148 \ No newline at end of file diff --git a/bin/patches/2F56CBC9.pnach b/bin/patches/2F56CBC9.pnach new file mode 100644 index 0000000000..c9e2bc1422 --- /dev/null +++ b/bin/patches/2F56CBC9.pnach @@ -0,0 +1,4 @@ +gametitle= Klonoa 2 - Lunatea's Veil [SLUS 20151] (U) +comment=patches by nachbrenner +//Skip sceIpuSync +patch=0,EE,00305b90,word,03e00008 \ No newline at end of file diff --git a/bin/patches/302797DF.pnach b/bin/patches/302797DF.pnach new file mode 100644 index 0000000000..068a62759b --- /dev/null +++ b/bin/patches/302797DF.pnach @@ -0,0 +1,5 @@ +gametitle=CYGirls +comment= +//patches by Refraction +//Skip Videos +patch=0,EE,00135130,word,00000000 \ No newline at end of file diff --git a/bin/patches/304C115C.pnach b/bin/patches/304C115C.pnach new file mode 100644 index 0000000000..e5294930ad --- /dev/null +++ b/bin/patches/304C115C.pnach @@ -0,0 +1,4 @@ +gametitle= Harvest Moon - A Wonderful Life [SLUS 21171] (U) +comment= +//ZeroGS Patch - GAME_FFXHACK +zerogs=00000080 \ No newline at end of file diff --git a/bin/patches/32629F36.pnach b/bin/patches/32629F36.pnach new file mode 100644 index 0000000000..fea149d6c3 --- /dev/null +++ b/bin/patches/32629F36.pnach @@ -0,0 +1,9 @@ +gametitle=Oni [SLES 50177] (G) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,001c67d8,word,03e00008 +patch=0,EE,001c67dc,word,00000000 +//skip "branch if copro 0 condition false" +patch=0,EE,001cef7c,word,00000000 // bc0f $001cef7c +//skip sceGsExecStoreImage +patch=0,EE,001d4454,word,00000000 \ No newline at end of file diff --git a/bin/patches/333F1F59.pnach b/bin/patches/333F1F59.pnach new file mode 100644 index 0000000000..e6593663a4 --- /dev/null +++ b/bin/patches/333F1F59.pnach @@ -0,0 +1,3 @@ +gametitle=Simple 2000: The neko mura NTSC JAP +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,00109420,word,24020001 \ No newline at end of file diff --git a/bin/patches/337B927C.pnach b/bin/patches/337B927C.pnach new file mode 100644 index 0000000000..fdd027f2f4 --- /dev/null +++ b/bin/patches/337B927C.pnach @@ -0,0 +1,4 @@ +gametitle=We Love Katamari (SLUS 21230) (U) +comment=Video Skip +//Skip Videos +patch=0,EE,0010ec5c,word,10000041 \ No newline at end of file diff --git a/bin/patches/339A0B8C.pnach b/bin/patches/339A0B8C.pnach new file mode 100644 index 0000000000..d3b94fa615 --- /dev/null +++ b/bin/patches/339A0B8C.pnach @@ -0,0 +1,4 @@ +gametitle= Fatal Frame [SLUS 20388] (U) NTSC +comment= Video Skip by Refraction +//Skip Videos (sceMpegIsEnd) +patch=0,EE,00232f00,word,24020001 \ No newline at end of file diff --git a/bin/patches/33F7D21A.pnach b/bin/patches/33F7D21A.pnach new file mode 100644 index 0000000000..866a7525d0 --- /dev/null +++ b/bin/patches/33F7D21A.pnach @@ -0,0 +1,14 @@ +gametitle= Dirge Of Cerberus - Final Fantasy VII [SLES 54185] (E) +comment=patches by nachbrenner +//Fix "**** SQTHREAD ERROR : BAD LOCK STATE" +patch=0,EE,0042d768,word,00000000 +//Skip mcDelayThread (calls SleepThread) +patch=0,EE,00458008,word,00000000 +//Skip Videos (for blockdump) +patch=0,EE,00241bac,word,00000000 +//override sceScfGetLanguage +//patch=0,EE,002486d8,word,24020000 //japanese +patch=0,EE,002486d8,word,24020001 //english +//patch=0,EE,002486d8,word,24020004 //german +//intro 3D - fix DMA0 vs. VU0REC +patch=0,EE,0026f960,word,10000020 \ No newline at end of file diff --git a/bin/patches/36FEEE3A.pnach b/bin/patches/36FEEE3A.pnach new file mode 100644 index 0000000000..a29efec175 --- /dev/null +++ b/bin/patches/36FEEE3A.pnach @@ -0,0 +1,19 @@ +gametitle=Knockout Kings 2002 [SLUS 20369] (U) +comment= patches by Nachbrenner +//fix DMA loop +patch=0,EE,001d8020,word,1000000f +//VU0 crash fix (obsolete) +//patch=0,EE,001fb144,word,00000000 +// +// for blockdump: +// +//Skip MPC videos +patch=0,EE,0018c0a8,word,03e00008 +patch=0,EE,0018c0ac,word,24020001 +//Fight commentary off +patch=1,EE,00273380,word,00000000 +//Music vol.=0 +patch=1,EE,002721A4,word,00000000 +//Skip sound streaming +patch=0,EE,00138b68,word,03e00008 +patch=0,EE,00138b6c,word,00000000 \ No newline at end of file diff --git a/bin/patches/37BA81B1.pnach b/bin/patches/37BA81B1.pnach new file mode 100644 index 0000000000..888d3499c3 --- /dev/null +++ b/bin/patches/37BA81B1.pnach @@ -0,0 +1,4 @@ +gametitle= Kamen Rider Blade [SLPS 20402] (J) +comment=patches by Nachbrenner +//skip "PlayMpeg" +patch=0,EE,00158dec,word,00000000 \ No newline at end of file diff --git a/bin/patches/37C07E96.pnach b/bin/patches/37C07E96.pnach new file mode 100644 index 0000000000..1fcc1626c7 --- /dev/null +++ b/bin/patches/37C07E96.pnach @@ -0,0 +1,3 @@ +gametitle=Simple2000 Vol.91 : The ALL STAR fighting [SLPS 20430] (J) +//Skip Movies +patch=0,EE,0010b720,word,24020001 \ No newline at end of file diff --git a/bin/patches/38F29E28.pnach b/bin/patches/38F29E28.pnach new file mode 100644 index 0000000000..920844b597 --- /dev/null +++ b/bin/patches/38F29E28.pnach @@ -0,0 +1,4 @@ +gametitle=7 cavalry of molmoth NTSC JAPAN +comment=skips pss intro +//Skip Videos +patch=0,EE,00284b58,word,24020001 \ No newline at end of file diff --git a/bin/patches/3BD85DA4.pnach b/bin/patches/3BD85DA4.pnach new file mode 100644 index 0000000000..34e39f52a9 --- /dev/null +++ b/bin/patches/3BD85DA4.pnach @@ -0,0 +1,7 @@ +gametitle=Ghosthunter [SCES 51463] (E) +comment=patches by nachbrenner +//fix VU0/DMA crap +patch=0,EE,003e8928,word,03e00008 +patch=0,EE,003e892c,word,00000000 +patch=0,EE,003831c8,word,00000000 +patch=0,EE,003831d4,word,00000000 \ No newline at end of file diff --git a/bin/patches/3CFE3B37.pnach b/bin/patches/3CFE3B37.pnach new file mode 100644 index 0000000000..4edde5e667 --- /dev/null +++ b/bin/patches/3CFE3B37.pnach @@ -0,0 +1,6 @@ +gametitle=Powershot Pinball [SLES 54084] (E) +comment=patches by auMatt +//Skip Video +patch=0,EE,0029c990,word,24020001 +//ZeroGS Patch - GAME_AUTORESET +zerogs=00000002 \ No newline at end of file diff --git a/bin/patches/3DB65A75.pnach b/bin/patches/3DB65A75.pnach new file mode 100644 index 0000000000..3fc6279161 --- /dev/null +++ b/bin/patches/3DB65A75.pnach @@ -0,0 +1,5 @@ +gametitle=Megaman Anniversary Collection [SLUS 20833] (U) +//fix GS_CSR +//patch=0,EE,0012d938,word,00000000 +//Skip videos +//patch=0,EE,0014e548,word,24020001 \ No newline at end of file diff --git a/bin/patches/3E0A256D.pnach b/bin/patches/3E0A256D.pnach new file mode 100644 index 0000000000..1cb94b5549 --- /dev/null +++ b/bin/patches/3E0A256D.pnach @@ -0,0 +1,4 @@ +//skip FMV (sceMpegIsEnd) +patch=0,EE,001eccc8,word,24020001 +//fix AudioReset (Peops SPU2 4.11.06) +patch=0,EE,00120a94,word,00000000 \ No newline at end of file diff --git a/bin/patches/3E68955A.pnach b/bin/patches/3E68955A.pnach new file mode 100644 index 0000000000..fffbc37805 --- /dev/null +++ b/bin/patches/3E68955A.pnach @@ -0,0 +1,5 @@ +gametitle=Kingdom Hearts - Final Mix [SLPS 25198] (J) +comment= +//ZeroGS Patch - GAME_QUICKRESOLVE1 +zerogs=00000400 +fastmemory \ No newline at end of file diff --git a/bin/patches/3EAD47FE.pnach b/bin/patches/3EAD47FE.pnach new file mode 100644 index 0000000000..b43294ef84 --- /dev/null +++ b/bin/patches/3EAD47FE.pnach @@ -0,0 +1,4 @@ +gametitle=Britney's Dance Beat [SLUS 20402] (U) +comment=Kill BNE +//Patch made by CKemu +patch=0,EE,0022581c,word,00000000 \ No newline at end of file diff --git a/bin/patches/3F0452DE.pnach b/bin/patches/3F0452DE.pnach new file mode 100644 index 0000000000..24e78de9a6 --- /dev/null +++ b/bin/patches/3F0452DE.pnach @@ -0,0 +1,4 @@ +gametitle= FlatOut 2 [SLES 54002] (E) +comment= Video Skip by Refraction +//Skip Videos (sceMpegIsEnd) +patch=0,EE,003dd1d0,word,24020001 \ No newline at end of file diff --git a/bin/patches/3FB69323.pnach b/bin/patches/3FB69323.pnach new file mode 100644 index 0000000000..83b25e97b5 --- /dev/null +++ b/bin/patches/3FB69323.pnach @@ -0,0 +1,8 @@ +gametitle=Gran Turismo 4 Prologue [SCES 52438] (E) +//fix IPU DMA +patch=1,EE,00175018,word,00000000 +patch=1,EE,0046bba8,word,00000000 +patch=1,EE,0046bbe4,word,00000000 +//Skip Videos +patch=1,EE,0020c790,word,03e00008 +patch=1,EE,0020c794,word,00000000 \ No newline at end of file diff --git a/bin/patches/4043F228.pnach b/bin/patches/4043F228.pnach new file mode 100644 index 0000000000..c0198bd179 --- /dev/null +++ b/bin/patches/4043F228.pnach @@ -0,0 +1,4 @@ +gametitle= PC Genjin [SLPM 62418] (J) [4043F228] +comment= +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,0020f9e0,word,24020001 \ No newline at end of file diff --git a/bin/patches/41A3191C.pnach b/bin/patches/41A3191C.pnach new file mode 100644 index 0000000000..2bcbb660f8 --- /dev/null +++ b/bin/patches/41A3191C.pnach @@ -0,0 +1,4 @@ +gametitle=Simple Series 2000 - Vol. 99 - The Genshijin [SLPS 20461] (J) +comment= patch by parotaku +//Skip Videos (sceMpegIsEnd) +patch=0,EE,00109e40,word,24020001 \ No newline at end of file diff --git a/bin/patches/4321A427.pnach b/bin/patches/4321A427.pnach new file mode 100644 index 0000000000..0ee28273ec --- /dev/null +++ b/bin/patches/4321A427.pnach @@ -0,0 +1,5 @@ +gametitle=World Soccer Winning Eleven 10 [SLPM 66374] (J) +comment=patches by fullcodes +//Skip PSS +// patch=0,EE,00131260,word,03e00008 +// patch=0,EE,00131264,word,00000000 \ No newline at end of file diff --git a/bin/patches/4334E17D.pnach b/bin/patches/4334E17D.pnach new file mode 100644 index 0000000000..d9455584c1 --- /dev/null +++ b/bin/patches/4334E17D.pnach @@ -0,0 +1,4 @@ +gametitle=Disgaea 2 - Cursed Memories [SLPS 25608] (J) +comment= +//ZeroGS Patch - GAME_NODEPTHRESOLVE +zerogs=00008000 \ No newline at end of file diff --git a/bin/patches/44A61C8F.pnach b/bin/patches/44A61C8F.pnach new file mode 100644 index 0000000000..cf21334ce4 --- /dev/null +++ b/bin/patches/44A61C8F.pnach @@ -0,0 +1,4 @@ +gametitle=Gran Turismo 4 [SCES 51719] (E) [44A61C8F] +comment=patches by nachbrenner +//skip Videos +patch=1,EE,00100D84,word,24100001 diff --git a/bin/patches/4691F6F7.pnach b/bin/patches/4691F6F7.pnach new file mode 100644 index 0000000000..e974348b3e --- /dev/null +++ b/bin/patches/4691F6F7.pnach @@ -0,0 +1,4 @@ +gametitle=The Sum Of All Fears [SLES 51180] (E) +comment= patches by Nachbrenner +//Skip sceIpuSync +//patch=0,EE,002aa098,word,03e00008 \ No newline at end of file diff --git a/bin/patches/46A7ECA4.pnach b/bin/patches/46A7ECA4.pnach new file mode 100644 index 0000000000..fc630c4cb4 --- /dev/null +++ b/bin/patches/46A7ECA4.pnach @@ -0,0 +1,4 @@ +gametitle=Guilty Gear Isuka [SLES 53284] (E) [46A7ECA4] +comment=Patch By CKemu +//Skip Videos +patch=0,EE,00107f10,word,24020001 \ No newline at end of file diff --git a/bin/patches/472C9E70.pnach b/bin/patches/472C9E70.pnach new file mode 100644 index 0000000000..a63e3a8719 --- /dev/null +++ b/bin/patches/472C9E70.pnach @@ -0,0 +1,6 @@ +gametitle=Kengo: Master Of Bushido [SLES 50114] (E) +comment=patches by nachbrenner +//start with 5 minutes time limit +patch=0,EE,002999b8,word,00003A98 //single_game_time_limit_table +//skip videos +patch=0,EE,0018d738,word,24020001 \ No newline at end of file diff --git a/bin/patches/48FE0C71.pnach b/bin/patches/48FE0C71.pnach new file mode 100644 index 0000000000..6e9b50c15a --- /dev/null +++ b/bin/patches/48FE0C71.pnach @@ -0,0 +1,20 @@ +gametitle=FFX-2 US +comment= +// InvalidDCache | SyncDCache +patch=0,EE,003498b0,word,03e00008 +patch=0,EE,003498b4,word,00000000 +//skip "INIT SOUND" +patch=0,EE,00132868,word,00000000 +//skip "INIT VOICE" +patch=0,EE,0013287c,word,00000000 +//skip AC3+PCM start (NOPs whole subroutine) +patch=0,EE,0017ac70,word,00000000 +//Skip Videos +//patch=0,EE,003338e0,word,24020001 +//skip "decode MPEG2" +patch=0,EE,0017aaa0,word,03e00008 +patch=0,EE,0017aaa4,word,00000000 +patch=0,EE,0017a9e8,word,03e00008 +patch=0,EE,0017a9ec,word,00000000 +//ZeroGS Patch - GAME_FFXHACK +zerogs=00000080 \ No newline at end of file diff --git a/bin/patches/49DA19CE.pnach b/bin/patches/49DA19CE.pnach new file mode 100644 index 0000000000..872ef01c18 --- /dev/null +++ b/bin/patches/49DA19CE.pnach @@ -0,0 +1,4 @@ +gametitle=Carmen Sandiego-The Secret Of The Stolen Drums [SLES52143] (E) +comment= patches by Nachbrenner +//Skip MpegPlay +patch=0,EE,0011efd0,word,03e00008 \ No newline at end of file diff --git a/bin/patches/4B6DDB6B.pnach b/bin/patches/4B6DDB6B.pnach new file mode 100644 index 0000000000..f98f21ff4c --- /dev/null +++ b/bin/patches/4B6DDB6B.pnach @@ -0,0 +1,3 @@ +gametitle=Guilty Gear XX NTSC JPN +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,00245418,word,24020001 \ No newline at end of file diff --git a/bin/patches/4C0C821D.pnach b/bin/patches/4C0C821D.pnach new file mode 100644 index 0000000000..5245a29414 --- /dev/null +++ b/bin/patches/4C0C821D.pnach @@ -0,0 +1,4 @@ +gametitle=Demolition Girl [SLES 53403] (E) [4C0C821D] +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,0020b5c0,word,24020001 \ No newline at end of file diff --git a/bin/patches/4C4D7072.pnach b/bin/patches/4C4D7072.pnach new file mode 100644 index 0000000000..202d920379 --- /dev/null +++ b/bin/patches/4C4D7072.pnach @@ -0,0 +1,11 @@ +gametitle=Tom Clancy's Splinter Cell Chaos Theory [SLES 53007] (E) +comment=patches by Nachbrenner +//fix sceIpuStopDMA +patch=0,EE,0020b884,word,00000000 +// +// load SC3_OFF.ELF manually to go ingame +// d7a4b7af.pnach +// gametitle=Tom Clancy's Splinter Cell Chaos Theory [SC3_OFF.ELF] (E) +// Skip sceIpuRestartDMA +// patch=1,EE,001116e0,word,03e00008 +// patch=1,EE,001116e4,word,00000000 \ No newline at end of file diff --git a/bin/patches/4C9EE7DF.pnach b/bin/patches/4C9EE7DF.pnach new file mode 100644 index 0000000000..ef6746e68d --- /dev/null +++ b/bin/patches/4C9EE7DF.pnach @@ -0,0 +1,20 @@ +gametitle=Crazy Taxi [SLUS 20202] (U) +comment= patches by Nachbrenner +//skip movie "PlaySega" +patch=0,EE,001a1950,word,03e00008 +patch=0,EE,001a1954,word,00000000 +//skip movie "PlayAcclaim" +patch=0,EE,001a1ae8,word,03e00008 +patch=0,EE,001a1aec,word,00000000 +//skip music.str (for blockdump) +// patch=0,EE,001a0bb0,word,0000000 +//skip voice.str (for blockdump) +// patch=0,EE,001a0b9c,word,0000000 +//Infinite arcade time +patch=1,EE,002F0C64,word,00000e00 +//Japanese mode +//patch=0,EE,00118514,word,af8095a4 +//controller setting B +patch=1,EE,002152DF,byte,01 +//skip nlWaitVSync / TaxiBlankInterrupt (obsolete) +//patch=0,EE,0019a590,word,03e00008 \ No newline at end of file diff --git a/bin/patches/4D228733.pnach b/bin/patches/4D228733.pnach new file mode 100644 index 0000000000..a91012bd45 --- /dev/null +++ b/bin/patches/4D228733.pnach @@ -0,0 +1,5 @@ +gametitle=Marvel vs Capcom 2 [SLUS 20486] (U) +comment= +//ZeroGS Patch - GAME_QUICKRESOLVE1 +zerogs=00000400 +fastmemory \ No newline at end of file diff --git a/bin/patches/4D6DBB75.pnach b/bin/patches/4D6DBB75.pnach new file mode 100644 index 0000000000..8533e66368 --- /dev/null +++ b/bin/patches/4D6DBB75.pnach @@ -0,0 +1,4 @@ +gametitle=bloody roar 3 NTSC JAPAN +comment=skips intro +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,001dc9f8,word,24020001 \ No newline at end of file diff --git a/bin/patches/4DAC50C2.pnach b/bin/patches/4DAC50C2.pnach new file mode 100644 index 0000000000..648401b2d9 --- /dev/null +++ b/bin/patches/4DAC50C2.pnach @@ -0,0 +1,5 @@ +gametitle=Starsky & Hutch DE/FR [SLES 51783] (E) +comment=patches by Nachbrenner +//Skip videos +//patch=1,EE,0021FAC4,word,00000000 +//patch=1,EE,00151750,word,24020001 \ No newline at end of file diff --git a/bin/patches/4F3D3CF0.pnach b/bin/patches/4F3D3CF0.pnach new file mode 100644 index 0000000000..36fa809d56 --- /dev/null +++ b/bin/patches/4F3D3CF0.pnach @@ -0,0 +1,5 @@ +gametitle=Simply... The Best For Less - Vol. 6 Ultimate Casino [SLES 52515] (E) +comment=patches by Nachbrenner +//Skip logo.pss +patch=0,EE,00115778,word,24020001 +patch=0,EE,0012eb90,word,00000000 \ No newline at end of file diff --git a/bin/patches/506644B3.pnach b/bin/patches/506644B3.pnach new file mode 100644 index 0000000000..ea628e26a5 --- /dev/null +++ b/bin/patches/506644B3.pnach @@ -0,0 +1,5 @@ +gametitle=Big Mutha Truckers [SLES 51355] (E) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,002d1438,word,24020001 //sceMpegIsEnd +patch=0,EE,001ca00c,word,00000000 //sceMpegGetPicture \ No newline at end of file diff --git a/bin/patches/512B5046.pnach b/bin/patches/512B5046.pnach new file mode 100644 index 0000000000..1833b8ba0a --- /dev/null +++ b/bin/patches/512B5046.pnach @@ -0,0 +1,3 @@ +gametitle=minna daisuki katamari damashi NTSC JAP +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,001ce5d8,word,24020001 \ No newline at end of file diff --git a/bin/patches/5162BCCA.pnach b/bin/patches/5162BCCA.pnach new file mode 100644 index 0000000000..692831fa11 --- /dev/null +++ b/bin/patches/5162BCCA.pnach @@ -0,0 +1,5 @@ + +gametitle=Beach King Stunts Racer +comment=Skips Video by Rudy_x(sceMpegIsEnd) +//Skip Video +patch=0,EE,00197988,word,24020001 diff --git a/bin/patches/51D8A6A9.pnach b/bin/patches/51D8A6A9.pnach new file mode 100644 index 0000000000..cd5c2b3d6b --- /dev/null +++ b/bin/patches/51D8A6A9.pnach @@ -0,0 +1,20 @@ +gametitle=GTC Africa [SLES 50472] (E) +comment=since beta 01/MAR/2004 PLAYABLE!!! +//Unlock All Challenges +patch=0,EE,0010a8bc,word,ac23a588 +patch=0,EE,0010a8d4,word,ac23a588 +//Unlock All Championships +//patch=0,EE,0010a8dc,word,ac23a594 +//Unlock All Single Races +patch=0,EE,0010a8e4,word,ac23a598 +//"-no_music" +//patch=0,EE,0010a7e4,word,ac20a534 +//@1234 PlayMPEGFile +patch=0,EE,0010a824,word,ac20a554 +//@1242 DrawDebugText +patch=0,EE,0010a87c,word,ac20a578 +//Disable HUDRender +patch=0,EE,001996c0,word,03e00008 +patch=0,EE,001996c4,word,00000000 +//wm: fix VIF0_STAT (obsolete) +//patch=0,EE,001f027c,word,00000000 \ No newline at end of file diff --git a/bin/patches/51F91783.pnach b/bin/patches/51F91783.pnach new file mode 100644 index 0000000000..b0e5d58aeb --- /dev/null +++ b/bin/patches/51F91783.pnach @@ -0,0 +1,4 @@ +gametitle=Kaena [SLPS 25295] (J) +comment= +//ZeroGS Patch - Fix refresh bug +zerogs=00000010 diff --git a/bin/patches/53A803AF.pnach b/bin/patches/53A803AF.pnach new file mode 100644 index 0000000000..f1247c1588 --- /dev/null +++ b/bin/patches/53A803AF.pnach @@ -0,0 +1,4 @@ +gametitle=Kessen 2 [SLUS 20275] (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos by General Plot +patch=0,EE,00108970,word,24020001 \ No newline at end of file diff --git a/bin/patches/53DF159B.pnach b/bin/patches/53DF159B.pnach new file mode 100644 index 0000000000..2e1120a337 --- /dev/null +++ b/bin/patches/53DF159B.pnach @@ -0,0 +1,16 @@ +gametitle=Max Payne MAIN_P.RUN [SLES 50325] (E) +comment=load ELF MAIN_P.RUN manually! +//deny video ELFs +patch=0,EE,0014BF5C,word,00000000 +patch=0,EE,0014E718,word,00000000 +//obsolete: +// don't flood console with bad VIF1_MARK messages +//patch=0,EE,0052f40c,word,00000000 +//WM: fix DMA1 +//patch=0,EE,00542ca8,word,1000000f +//skip bc0f +//patch=0,EE,00542e48,word,00000000 +//patch=0,EE,00542e74,word,00000000 +//WM: fix DMA9 +//patch=0,EE,005272e4,word,00000000 +//patch=0,EE,00527780,word,00000000 \ No newline at end of file diff --git a/bin/patches/54A548B4.pnach b/bin/patches/54A548B4.pnach new file mode 100644 index 0000000000..90b62039b1 --- /dev/null +++ b/bin/patches/54A548B4.pnach @@ -0,0 +1,6 @@ +gametitle= Crash N Burn +comment= Mpegisend +//Skip Videos +//patch=0,EE,0039fd50,word,24020001 +//ZeroGS Patch - GAME_EXACTCOLOR +zerogs=00000020 \ No newline at end of file diff --git a/bin/patches/54AD76D7.pnach b/bin/patches/54AD76D7.pnach new file mode 100644 index 0000000000..533d66fddc --- /dev/null +++ b/bin/patches/54AD76D7.pnach @@ -0,0 +1,8 @@ +gametitle=Lara Croft Tomb Raider - The Angel of Darkness [SLES 51227] (E) +comment= patches by nachbrenner +//Skip videos +patch=0,EE,00104de0,word,03E00008 +patch=0,EE,00104de4,word,24020001 +patch=0,EE,00104d60,word,03E00008 +patch=0,EE,00104d64,word,24020001 +patch=0,EE,0021bf68,word,24020001 \ No newline at end of file diff --git a/bin/patches/54D6BEE3.pnach b/bin/patches/54D6BEE3.pnach new file mode 100644 index 0000000000..9ec605ae7b --- /dev/null +++ b/bin/patches/54D6BEE3.pnach @@ -0,0 +1,4 @@ +gametitle=Swords of Destiny [SLES 53699] (E) +comment=patch by Refraction +//skip movies - brute force +patch=0,EE,00235440,word,10000003 diff --git a/bin/patches/561BE340.pnach b/bin/patches/561BE340.pnach new file mode 100644 index 0000000000..e6414cce66 --- /dev/null +++ b/bin/patches/561BE340.pnach @@ -0,0 +1,4 @@ +gametitle=Simple Series 2000 - Vol. 55 - The Catfight - Joneko Densetsu [SLPM 62494] (J) [561BE340] +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,00191560,word,24020001 \ No newline at end of file diff --git a/bin/patches/582EED0D.pnach b/bin/patches/582EED0D.pnach new file mode 100644 index 0000000000..d5ab4847f2 --- /dev/null +++ b/bin/patches/582EED0D.pnach @@ -0,0 +1,8 @@ +gametitle=Contra - Shattered Soldier [SLUS 20306] (U) +comment=patches by nachbrenner +//skip FMV (sceMpegIsEnd) +patch=0,EE,0063d950,word,24020001 +//suppress message "Gifstat write value = 4" +//patch=0,EE,0041be6c,word,00000000 +//suppress message "Gifstat write value = 0" +//patch=0,EE,0041bf1c,word,00000000 \ No newline at end of file diff --git a/bin/patches/586EA828.pnach b/bin/patches/586EA828.pnach new file mode 100644 index 0000000000..83aaf31ac8 --- /dev/null +++ b/bin/patches/586EA828.pnach @@ -0,0 +1,7 @@ +gametitle=MotoGP 2 [SLUS 20285] (U) +comment=patches by nachbrenner +//skip FMV +//patch=0,EE,00171fe8,word,24020001 +//All challenges complete +patch=0,EE,00133e54,word,20020002 +patch=0,EE,00133e58,word,a0a20000 \ No newline at end of file diff --git a/bin/patches/58EE1AFA.pnach b/bin/patches/58EE1AFA.pnach new file mode 100644 index 0000000000..5f35d8e6a1 --- /dev/null +++ b/bin/patches/58EE1AFA.pnach @@ -0,0 +1,4 @@ +gametitle=Breath Of Fire V: Drangon Quarter USA +comment=Skips videos +//Skip Videos +patch=0,EE,0010a758,word,24020001 \ No newline at end of file diff --git a/bin/patches/5A7635C1.pnach b/bin/patches/5A7635C1.pnach new file mode 100644 index 0000000000..84224a5e4d --- /dev/null +++ b/bin/patches/5A7635C1.pnach @@ -0,0 +1,4 @@ +gametitle=NRA Gun Club SLUS 21432 (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,001091c0,word,24020001 \ No newline at end of file diff --git a/bin/patches/5BBC2F40.pnach b/bin/patches/5BBC2F40.pnach new file mode 100644 index 0000000000..a8af9d2980 --- /dev/null +++ b/bin/patches/5BBC2F40.pnach @@ -0,0 +1,20 @@ +gametitle=Ridge Racer V [SCES 50000] (E) +comment= patches by Nachbrenner +// disable foreground 3D on car selection +//patch=0,EE,00201e3c,word,00000000 +// disable background 3D on car selection +//patch=0,EE,00227254,word,00000000 +//patch=0,EE,0027fac0,word,00000000 +//patch=0,EE,0027fbf4,word,00000000 +// +//skip VU0 Matrices (3D car select) +//patch=0,EE,0025a728,word,03e00008 +//patch=0,EE,0025a72c,word,00000000 +//patch=0,EE,0025aab8,word,03e00008 +//patch=0,EE,0025aabc,word,00000000 +//patch=0,EE,0025a6f8,word,03e00008 +//patch=0,EE,0025a6fc,word,00000000 +//patch=0,EE,00201540,word,03e00008 // ingame +//patch=0,EE,00201544,word,00000000 +//skip FlushCache before VU0 Sub/Scale/Copy Vector +//patch=0,EE,002a1298,word,00000000 \ No newline at end of file diff --git a/bin/patches/5BC8C9E8.pnach b/bin/patches/5BC8C9E8.pnach new file mode 100644 index 0000000000..a2c5166b69 --- /dev/null +++ b/bin/patches/5BC8C9E8.pnach @@ -0,0 +1,3 @@ +gametitle= Marvel Vs. Capcom 2 NTSC-J +comment= +fastmemory \ No newline at end of file diff --git a/bin/patches/5BE3F481.pnach b/bin/patches/5BE3F481.pnach new file mode 100644 index 0000000000..efa32a8924 --- /dev/null +++ b/bin/patches/5BE3F481.pnach @@ -0,0 +1,4 @@ +gametitle=James Cameron's Dark Angel [SLES 51333] (E) +comment= patches by Nachbrenner +//Skip FMV +// patch=0,EE,0021CC24,word,00000000 \ No newline at end of file diff --git a/bin/patches/5CCA0737.pnach b/bin/patches/5CCA0737.pnach new file mode 100644 index 0000000000..5e64e74a87 --- /dev/null +++ b/bin/patches/5CCA0737.pnach @@ -0,0 +1,7 @@ +gametitle=Barbarian [SLES 50972] (E) +comment=patches by Nachbrenner +//Skip SAMPLE.PSS +patch=0,EE,0010afc8,word,00000000 +//skip audio streams +//patch=0,EE,0016fb40,word,03e00008 +//patch=0,EE,0016fb44,word,00000000 \ No newline at end of file diff --git a/bin/patches/5D67AE48.pnach b/bin/patches/5D67AE48.pnach new file mode 100644 index 0000000000..5f01866529 --- /dev/null +++ b/bin/patches/5D67AE48.pnach @@ -0,0 +1,4 @@ +gametitle=Simple Series 2000 - Vol. 61 - The Oane Chapara [SLPM 62525] (J) [5D67AE48] +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,001de758,word,24020001 \ No newline at end of file diff --git a/bin/patches/5DFBE144.pnach b/bin/patches/5DFBE144.pnach new file mode 100644 index 0000000000..4ebdb28054 --- /dev/null +++ b/bin/patches/5DFBE144.pnach @@ -0,0 +1,8 @@ +gametitle=The Getaway [SCES 51159] (E) +comment= patches by Nachbrenner +//skip IPU videos (for blockdump) +patch=0,EE,00296c14,word,00000000 +patch=0,EE,00223bc8,word,03e00008 +patch=0,EE,00223bcc,word,00000000 +patch=0,EE,002218a8,word,03e00008 +patch=0,EE,002218ac,word,00000000 \ No newline at end of file diff --git a/bin/patches/5E115FB6.pnach b/bin/patches/5E115FB6.pnach new file mode 100644 index 0000000000..abc56f22b5 --- /dev/null +++ b/bin/patches/5E115FB6.pnach @@ -0,0 +1,3 @@ +gametitle=GTA3 +//Skip Intro Videos ("InitMPEGPlayer__Fv") +patch=0,EE,0027ef00,word,1000000b \ No newline at end of file diff --git a/bin/patches/5EB127E7.pnach b/bin/patches/5EB127E7.pnach new file mode 100644 index 0000000000..95d24184d0 --- /dev/null +++ b/bin/patches/5EB127E7.pnach @@ -0,0 +1,4 @@ +gametitle= Gradius 3 & 4 [SLPM 62007] (J) +comment= +//ZeroGS Patch - GAME_INTERLACE2X +zerogs=00000004 \ No newline at end of file diff --git a/bin/patches/5F2A0E36.pnach b/bin/patches/5F2A0E36.pnach new file mode 100644 index 0000000000..5e72933c51 --- /dev/null +++ b/bin/patches/5F2A0E36.pnach @@ -0,0 +1,7 @@ +gametitle= Armored Core 2 - Another Age [SLPS 25040] (J) +//Skip Videos (sceMpegIsEnd) +// patch=0,EE,002c8a10,word,24020001 +// Skip Intros +// patch=0,EE,00100230,word,00000000 +// Lots of credits +// patch=1,EE,00319f00,word,00C0FFEE \ No newline at end of file diff --git a/bin/patches/60013EBD.pnach b/bin/patches/60013EBD.pnach new file mode 100644 index 0000000000..4b38998bf3 --- /dev/null +++ b/bin/patches/60013EBD.pnach @@ -0,0 +1,5 @@ +gametitle= Gran Turismo Concept 2002 Tokyo-Geneva [SCES 50858] (E) +comment= patches by Nachbrenner +//Skip Video +patch=1,EE,00129600,word,03e00008 +patch=1,EE,00129604,word,00000000 \ No newline at end of file diff --git a/bin/patches/60FA8C69.pnach b/bin/patches/60FA8C69.pnach new file mode 100644 index 0000000000..4850027a5a --- /dev/null +++ b/bin/patches/60FA8C69.pnach @@ -0,0 +1,4 @@ +gametitle=Biohazard 4 [SLPM 66213] (J) +comment= +//ZeroGS Patch - GAME_NOTARGETCLUT +zerogs=00001000 \ No newline at end of file diff --git a/bin/patches/6175FE7D.pnach b/bin/patches/6175FE7D.pnach new file mode 100644 index 0000000000..7f432472ab --- /dev/null +++ b/bin/patches/6175FE7D.pnach @@ -0,0 +1,4 @@ +gametitle=Guilty Gear XX - Accent Core [SLPM 66746] (J) [6175FE7D] +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,002bcc48,word,24020001 \ No newline at end of file diff --git a/bin/patches/624F11F1.pnach b/bin/patches/624F11F1.pnach new file mode 100644 index 0000000000..ef96786c79 --- /dev/null +++ b/bin/patches/624F11F1.pnach @@ -0,0 +1,5 @@ +gametitle=Star Trek Voyager - Elite Force [SLES 50738] (E) +//Skip Videos +patch=0,EE,002945E8,word,24020001 +//suppress message: SPR0 stall 1 +patch=0,EE,0027fd18,byte,49 // 0027fd20: sw v0, $0000(v1) (D_CTRL) \ No newline at end of file diff --git a/bin/patches/625AF967.pnach b/bin/patches/625AF967.pnach new file mode 100644 index 0000000000..99e9073984 --- /dev/null +++ b/bin/patches/625AF967.pnach @@ -0,0 +1,4 @@ +gametitle= Nightmare Before Christmas - Oogie's Revenge [SLPM 65739] (J) +comment= +//ZeroGS Patch - GAME_TEXAHACK +zerogs=00000008 \ No newline at end of file diff --git a/bin/patches/627B8252.pnach b/bin/patches/627B8252.pnach new file mode 100644 index 0000000000..9941c7976f --- /dev/null +++ b/bin/patches/627B8252.pnach @@ -0,0 +1,9 @@ +gametitle=Garfield 2 - Tale of Two Kitties [SLES 54172] (E) +comment=patches by Nachbrenner +fastmemory +//Skip Videos (for blockdump) +patch=0,EE,00225bc0,word,03e00008 +patch=0,EE,00225bc4,word,00000000 +//Skip Music (for blockdump) +patch=0,EE,0033e270,word,03e00008 +patch=0,EE,0033e274,word,00000000 \ No newline at end of file diff --git a/bin/patches/62F6F886.pnach b/bin/patches/62F6F886.pnach new file mode 100644 index 0000000000..35787607bc --- /dev/null +++ b/bin/patches/62F6F886.pnach @@ -0,0 +1,4 @@ +gametitle=Die Hard - Vendetta Germany&Australia [SLES 51348] (E) +comment= patches by Nachbrenner +//Skip Videos +patch=0,EE,002570FC,word,00000000 \ No newline at end of file diff --git a/bin/patches/63F6B523.pnach b/bin/patches/63F6B523.pnach new file mode 100644 index 0000000000..6b4e3e6101 --- /dev/null +++ b/bin/patches/63F6B523.pnach @@ -0,0 +1,4 @@ +gametitle=Yu Gi Oh! Capsule Monster Coliseum (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,00280f30,word,24020001 \ No newline at end of file diff --git a/bin/patches/68EAF48F.pnach b/bin/patches/68EAF48F.pnach new file mode 100644 index 0000000000..6b3c4b1656 --- /dev/null +++ b/bin/patches/68EAF48F.pnach @@ -0,0 +1,8 @@ +gametitle=Clock tower 3 NTSC japan +comment= +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,01818cb8,word,24020001 +//skip MovieEndCheck +//patch=0,EE,019415c8,word,70001428 +//skip MoviePlayCheck +//patch=0,EE,019415cc,word,70001428 \ No newline at end of file diff --git a/bin/patches/6926B199.pnach b/bin/patches/6926B199.pnach new file mode 100644 index 0000000000..a17be646b6 --- /dev/null +++ b/bin/patches/6926B199.pnach @@ -0,0 +1,4 @@ +gametitle=7 Sins [SLES 53280] (E) +comment=patch by Nachbrenner & Refraction +//fix "SleepThread vs. RTFSSIOP.IRX" +patch=0,EE,004afc7c,word,00000000 \ No newline at end of file diff --git a/bin/patches/692CBA8E.pnach b/bin/patches/692CBA8E.pnach new file mode 100644 index 0000000000..4958322c75 --- /dev/null +++ b/bin/patches/692CBA8E.pnach @@ -0,0 +1,4 @@ +gametitle=Need For Speed Most Wanted (SLES_535.57) +comment=Skips Video - deny file By Refraction +//Skip Videos +patch=0,EE,00242414,word,00000000 \ No newline at end of file diff --git a/bin/patches/6A4EFE60.pnach b/bin/patches/6A4EFE60.pnach new file mode 100644 index 0000000000..42b16ef9b7 --- /dev/null +++ b/bin/patches/6A4EFE60.pnach @@ -0,0 +1,4 @@ +gametitle=Final Fantasy X [SLPS 25050] (J) +comment= +//ZeroGS Patch - GAME_FFXHACK +zerogs=00000080 \ No newline at end of file diff --git a/bin/patches/6ADBC24B.pnach b/bin/patches/6ADBC24B.pnach new file mode 100644 index 0000000000..f39a15aec0 --- /dev/null +++ b/bin/patches/6ADBC24B.pnach @@ -0,0 +1,6 @@ +gametitle=Gradius III & IV [SLES 50038] (E) +comment=patches by nachbrenner +//Skip videos +patch=0,EE,001009e4,word,00000000 +//ZeroGS Patch - GAME_INTERLACE2X +zerogs=00000004 \ No newline at end of file diff --git a/bin/patches/6BA2F6B9.pnach b/bin/patches/6BA2F6B9.pnach new file mode 100644 index 0000000000..e8914822f5 --- /dev/null +++ b/bin/patches/6BA2F6B9.pnach @@ -0,0 +1,4 @@ +gametitle=Resident Evil 4 [SLES 53702] (E) +comment= +//ZeroGS Patch - Speed Up +zerogs=00200010 \ No newline at end of file diff --git a/bin/patches/6D70F0E0.pnach b/bin/patches/6D70F0E0.pnach new file mode 100644 index 0000000000..105934e88f --- /dev/null +++ b/bin/patches/6D70F0E0.pnach @@ -0,0 +1,4 @@ +gametitle=Anubis 2 [SLES 53571] (E) [6D70F0E0] +comment=Patch by CKemu +//Skip Videos (scempegisend) +patch=0,EE,002DAFD0,word,24020001 \ No newline at end of file diff --git a/bin/patches/6EA9DDA9.pnach b/bin/patches/6EA9DDA9.pnach new file mode 100644 index 0000000000..7a0d5c8756 --- /dev/null +++ b/bin/patches/6EA9DDA9.pnach @@ -0,0 +1,4 @@ +gametitle= Resident Evil: Code Veronica X [SLES 50306] (E) +comment= patches by Nachbrenner +//Skip Videos (sceMpegIsEnd) +//patch=0,EE,00109d48,word,24020001 \ No newline at end of file diff --git a/bin/patches/6FB69282.pnach b/bin/patches/6FB69282.pnach new file mode 100644 index 0000000000..1b8b628cb4 --- /dev/null +++ b/bin/patches/6FB69282.pnach @@ -0,0 +1,3 @@ +gametitle=GodHand [SLUS 21503] (U) +//fix DMA loop +patch=0,EE,002be3d8,word,24020001 \ No newline at end of file diff --git a/bin/patches/7098BE76.pnach b/bin/patches/7098BE76.pnach new file mode 100644 index 0000000000..82add338fa --- /dev/null +++ b/bin/patches/7098BE76.pnach @@ -0,0 +1,6 @@ +gametitle=Nano Breaker [SLKA 25263] (J) +comment=patches by Nachbrenner +//Skip Videos (sceMpegIsEnd) +patch=0,EE,0010a330,word,24020001 +//fix psxUNK crash: skip RotateThreadReadyQueue +patch=0,EE,0019f708,word,00000000 \ No newline at end of file diff --git a/bin/patches/7130C553.pnach b/bin/patches/7130C553.pnach new file mode 100644 index 0000000000..2a8454c4eb --- /dev/null +++ b/bin/patches/7130C553.pnach @@ -0,0 +1,5 @@ +gametitle=Guitaroo man +comment=Skip Videos +roundmode=DOWN,CHOP +//Skip Videos +patch=0,EE,0012aa28,word,24020001 \ No newline at end of file diff --git a/bin/patches/71584BAC.pnach b/bin/patches/71584BAC.pnach new file mode 100644 index 0000000000..5872a1a294 --- /dev/null +++ b/bin/patches/71584BAC.pnach @@ -0,0 +1,4 @@ +gametitle=Blowout [SLUS 20850] (U) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,002c4a18,word,03e00008 \ No newline at end of file diff --git a/bin/patches/722BBD62.pnach b/bin/patches/722BBD62.pnach new file mode 100644 index 0000000000..f627600cba --- /dev/null +++ b/bin/patches/722BBD62.pnach @@ -0,0 +1,4 @@ +gametitle=FIFA Football 2003 [SLES 51197] (E) +comment=patches by nachbrenner +// fix WaitForDMACompletion__Q212EAGLInternal6DmaMgr +patch=0,EE,0037b310,word,00000000 \ No newline at end of file diff --git a/bin/patches/7377BC6F.pnach b/bin/patches/7377BC6F.pnach new file mode 100644 index 0000000000..1bb1c190da --- /dev/null +++ b/bin/patches/7377BC6F.pnach @@ -0,0 +1,4 @@ +gametitle= Naruto Ultimate Ninja [SLUS 21358] (U) +comment= Video Skip by Refraction +//Skip Videos (sceMpegIsEnd) +patch=0,EE,0011bbb0,word,24020001 diff --git a/bin/patches/763D3BF9.pnach b/bin/patches/763D3BF9.pnach new file mode 100644 index 0000000000..500291d0f6 --- /dev/null +++ b/bin/patches/763D3BF9.pnach @@ -0,0 +1,8 @@ +gametitle=Spongebob Schwammkopf - Schlacht um Bikini Bottom German [SLES 51970] (E) +comment= patches by Nachbrenner +// skip FMV (sceMpegIsEnd) +patch=0,EE,001284a0,word,24020001 +//Have All Tasks Completed +patch=0,EE,0019D9F8,word,8D810020 +patch=0,EE,0019D9FC,word,24060002 +patch=0,EE,00194A04,word,A4260014 \ No newline at end of file diff --git a/bin/patches/77ECAAA0.pnach b/bin/patches/77ECAAA0.pnach new file mode 100644 index 0000000000..18579d17bd --- /dev/null +++ b/bin/patches/77ECAAA0.pnach @@ -0,0 +1,4 @@ +gametitle= Freestyle [SLUS 20367] (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,00107808,word,24020001 \ No newline at end of file diff --git a/bin/patches/78168525.pnach b/bin/patches/78168525.pnach new file mode 100644 index 0000000000..fd4758e9d2 --- /dev/null +++ b/bin/patches/78168525.pnach @@ -0,0 +1,3 @@ +gametitle=Off-Road Wide Open [SLES 50232] (E) +//skip FMV +patch=0,EE,002953f0,word,24020001 \ No newline at end of file diff --git a/bin/patches/789D6B71.pnach b/bin/patches/789D6B71.pnach new file mode 100644 index 0000000000..a3db4c0e4b --- /dev/null +++ b/bin/patches/789D6B71.pnach @@ -0,0 +1,4 @@ +gametitle=The Thing/Das Ding [SLES 50976] (G) +comment= patches by nachbrenner +//Skip videos (for blockdump) +patch=0,EE,006ef130,word,03e00008 \ No newline at end of file diff --git a/bin/patches/78E20421.pnach b/bin/patches/78E20421.pnach new file mode 100644 index 0000000000..6ca4472d83 --- /dev/null +++ b/bin/patches/78E20421.pnach @@ -0,0 +1,7 @@ +gametitle=Crimson Tears [SLPM 65575] (J) +comment=patches by Nachbrenner, ported from CMX' US codes +//Funds Increase When Buying Items From Shop +patch=0,EE,0029b8fc,word,00641821 +//Infinite Health +patch=0,EE,0028581c,word,C60101FC +patch=0,EE,00285820,word,E6010200 \ No newline at end of file diff --git a/bin/patches/7ABDBB5E.pnach b/bin/patches/7ABDBB5E.pnach new file mode 100644 index 0000000000..8335357d52 --- /dev/null +++ b/bin/patches/7ABDBB5E.pnach @@ -0,0 +1,4 @@ +gametitle=Gran Turismo 4 Chinese [SCAJ 30007] (J) +comment=patches by nachbrenner +//skip Videos +patch=1,EE,00100D84,word,24100001 \ No newline at end of file diff --git a/bin/patches/7BA0128E.pnach b/bin/patches/7BA0128E.pnach new file mode 100644 index 0000000000..7c20610151 --- /dev/null +++ b/bin/patches/7BA0128E.pnach @@ -0,0 +1,3 @@ +gametitle=play it - Chess Challenger [SLES 51630] (E) +comment=patches by nachbrenner +//skip "cdrom0:\DATA\FW_LOGO.PSS \ No newline at end of file diff --git a/bin/patches/7CD1CDCD.pnach b/bin/patches/7CD1CDCD.pnach new file mode 100644 index 0000000000..53f92ce462 --- /dev/null +++ b/bin/patches/7CD1CDCD.pnach @@ -0,0 +1,12 @@ +gametitle=Simple 2000 Series - Vol. 81 - The Chikyuu Boueigun 2 [SLPM 62652] (J) +comment= patches by Nachbrenner +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,0015f990,word,24020001 +//fix "utPs2HgsControl::SynchronizeDma(Timeout...)" +patch=0,EE,0012d114,word,10000012 +//skip IPU dither +patch=0,EE,00131f30,word,00000000 +// skip sceIpuSync +patch=0,EE,00164570,word,03e00008 +// fix infinite loop (iFlushCache / D_STAT related) +patch=0,EE,00126b30,word,00000000 \ No newline at end of file diff --git a/bin/patches/7D8F539A.pnach b/bin/patches/7D8F539A.pnach new file mode 100644 index 0000000000..eee8a0702a --- /dev/null +++ b/bin/patches/7D8F539A.pnach @@ -0,0 +1,7 @@ +gametitle=Devil May Cry PAL [SLES 50358] (U) [7D8F539A] +comment= +//Skip Videos +patch=0,EE,0015BC78,word,00000000 +patch=0,EE,001CE340,word,03E00008 +patch=0,EE,001CE344,word,00000000 +patch=0,EE,001CE8EC,word,70001428 \ No newline at end of file diff --git a/bin/patches/7EBEEBBD.pnach b/bin/patches/7EBEEBBD.pnach new file mode 100644 index 0000000000..bbc25b42b9 --- /dev/null +++ b/bin/patches/7EBEEBBD.pnach @@ -0,0 +1,4 @@ +gametitle= Klonoa 2 - Lunatea's Veil [SCES 50354] (E) [7EBEEBBD] +comment=Patch By CKemu +//Skips IUP blitz +patch=0,EE,003078F4,word,00000000 \ No newline at end of file diff --git a/bin/patches/7F6319C7.pnach b/bin/patches/7F6319C7.pnach new file mode 100644 index 0000000000..e73287db0f --- /dev/null +++ b/bin/patches/7F6319C7.pnach @@ -0,0 +1,4 @@ +gametitle=James Pond (E) +comment= patches by parotaku +//Skip Videos (sceMpegIsEnd) +patch=0,EE,00137cf0,word,24020001 \ No newline at end of file diff --git a/bin/patches/7FD7A1B9.pnach b/bin/patches/7FD7A1B9.pnach new file mode 100644 index 0000000000..87fad752d5 --- /dev/null +++ b/bin/patches/7FD7A1B9.pnach @@ -0,0 +1,8 @@ +gametitle=Jade Cocoon 2 [SLES 50735] (E) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,00118110,word,24020001 +//Music off +patch=0,EE,00290eac,byte,00 +//Start new game with 10000 Yen +patch=0,EE,00291258,word,24022710 \ No newline at end of file diff --git a/bin/patches/83261085.pnach b/bin/patches/83261085.pnach new file mode 100644 index 0000000000..f7d0488c4d --- /dev/null +++ b/bin/patches/83261085.pnach @@ -0,0 +1,15 @@ +gametitle=Tenchu - Wrath Of Heaven [SLES 51402] (G) +comment= patches by Nachbrenner +//fix _waitIpuIdle for intro videos +patch=0,EE,00107a10,word,00000000 +//fix D3_CHCR for tutorial's mini-videos +patch=0,EE,00310dc4,word,00000000 +// +//obsolete: +//Skip sceIpuSync +//patch=0,EE,0010c810,word,03e00008 +//Skip sceDmaSendI | sceDmaSendN +//patch=0,EE,00176824,word,24020001 +//Skip Videos +//patch=0,EE,0010B288,word,24020001 //sceMpegIsEnd +//patch=0,EE,002fe2f0,word,00000000 //xref of sceMpegGetPicture \ No newline at end of file diff --git a/bin/patches/83D0CE43.pnach b/bin/patches/83D0CE43.pnach new file mode 100644 index 0000000000..d766b0efd6 --- /dev/null +++ b/bin/patches/83D0CE43.pnach @@ -0,0 +1,7 @@ +gametitle=Haven - Call of the King [SLES 51209] (E) +comment= patches by Nachbrenner +//disable 3D for menus and mashed music +patch=1,EE,00114c90,word,03e00008 +patch=1,EE,00114c94,word,00000000 +patch=1,EE,0010a980,word,03e00008 +patch=1,EE,0010a984,word,00000000 \ No newline at end of file diff --git a/bin/patches/844B58EE.pnach b/bin/patches/844B58EE.pnach new file mode 100644 index 0000000000..792dc2a316 --- /dev/null +++ b/bin/patches/844B58EE.pnach @@ -0,0 +1,4 @@ +gametitle=Tekken Tag Tournament (J) +comment= patch by parotaku +//Skip Videos (sceMpegIsEnd) +patch=0,EE,004b7220,word,24020001 \ No newline at end of file diff --git a/bin/patches/848C6247.pnach b/bin/patches/848C6247.pnach new file mode 100644 index 0000000000..733a947a4b --- /dev/null +++ b/bin/patches/848C6247.pnach @@ -0,0 +1,3 @@ +gametitle=Taiko no Tatsujin: Doki! NTSC JAP +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,001e7728,word,24020001 \ No newline at end of file diff --git a/bin/patches/85AE91B3.pnach b/bin/patches/85AE91B3.pnach new file mode 100644 index 0000000000..99915ba91f --- /dev/null +++ b/bin/patches/85AE91B3.pnach @@ -0,0 +1,5 @@ +gametitle= Gran Turismo 3 A-spec [SCUS 97102] (U) +comment=patches by nachbrenner +//Skip Opening Video +patch=1,EE,001287a8,word,03e00008 +patch=1,EE,001287ac,word,00000000 \ No newline at end of file diff --git a/bin/patches/85E92C92.pnach b/bin/patches/85E92C92.pnach new file mode 100644 index 0000000000..29e792e6e3 --- /dev/null +++ b/bin/patches/85E92C92.pnach @@ -0,0 +1,9 @@ +gametitle=The uchuujin NTSC J +comment= +//Skip Videos ("sceMpegIsEnd") +//patch=0,EE,00108fa0,word,24020001 +//Skip videos (custom) +patch=0,EE,001b4cd8,word,00000000 +//Skip videos (custom) +patch=0,EE,001b4c90,word,03e00008 +patch=0,EE,001b4c94,word,00000000 \ No newline at end of file diff --git a/bin/patches/8BC79F96.pnach b/bin/patches/8BC79F96.pnach new file mode 100644 index 0000000000..125fb2bb30 --- /dev/null +++ b/bin/patches/8BC79F96.pnach @@ -0,0 +1,9 @@ +gametitle=Formula One 2001 [SCES 50004] (E) +comment=patches by Nachbrenner +//Skip F1_Intro Video +//patch=0,EE,0010A1EC,word,00000000 +//Skip Credits Video +//patch=0,EE,0021A038,word,00000000 +//Skip FIA/ANIM256.FLC +//patch=0,EE,00162570,word,03e00008 +//patch=0,EE,00162574,word,00000000 \ No newline at end of file diff --git a/bin/patches/8BC95883.pnach b/bin/patches/8BC95883.pnach new file mode 100644 index 0000000000..f4c4125a6e --- /dev/null +++ b/bin/patches/8BC95883.pnach @@ -0,0 +1,4 @@ +gametitle= Sly 3 - Honor Among Thieves [SCUS 97464] (U) [8BC95883] +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,002aa040,word,24020001 \ No newline at end of file diff --git a/bin/patches/8BE3D7B2.pnach b/bin/patches/8BE3D7B2.pnach new file mode 100644 index 0000000000..ed2bf4d930 --- /dev/null +++ b/bin/patches/8BE3D7B2.pnach @@ -0,0 +1,4 @@ +gametitle= Shadow Hearts [SLUS 20347] (U) +comment= +//ZeroGS Patch - GAME_NODEPTHUPDATE|GAME_AUTORESET|GAME_NOQUICKRESOLVE +zerogs=00000A02 \ No newline at end of file diff --git a/bin/patches/8DC64680.pnach b/bin/patches/8DC64680.pnach new file mode 100644 index 0000000000..2660e812b7 --- /dev/null +++ b/bin/patches/8DC64680.pnach @@ -0,0 +1,30 @@ +gametitle=Red Faction 1 [SLES 50279] (G) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,0024b488,word,24020001 +patch=0,EE,002044d0,word,03e00008 +patch=0,EE,002044d4,word,24020001 +// skip IDEC error +patch=0,EE,002146b8,word,00000000 +//patch=0,EE,001b1d78,word,24020001 +//Skip THQ Logo +patch=0,EE,001B0A78,word,24020001 +//Skip RF Trailer +patch=0,EE,0027D19C,word,24020001 +// Unendlich Gesundheit (Inf. health) +patch=0,EE,200C0220,word,3C0142C8 +patch=0,EE,200C0224,word,44810000 +patch=0,EE,200C0228,word,03E00008 +patch=0,EE,200C022C,word,E6000034 +patch=0,EE,2016937C,word,0C030088 +// Unendlich Schutzanzug (Inf. flak jacket) +patch=0,EE,200C0230,word,3C0142C8 +patch=0,EE,200C0234,word,44811000 +patch=0,EE,200C0238,word,03E00008 +patch=0,EE,200C023C,word,E6020038 +patch=0,EE,201693E0,word,0C03008C +// Unendlich Munition (Inf. Ammo) +patch=0,EE,20154854,word,24630000 +patch=0,EE,20154890,word,24630000 +// Unendlich Sprengladungen (Inf. Explosives) +patch=0,EE,201A19BC,word,24420000 \ No newline at end of file diff --git a/bin/patches/8FDE8E16.pnach b/bin/patches/8FDE8E16.pnach new file mode 100644 index 0000000000..7a1202332a --- /dev/null +++ b/bin/patches/8FDE8E16.pnach @@ -0,0 +1,6 @@ +gametitle= Spongebob Squarepants - Revenge of the Flying Dutchman [SLES 51285] (E) +comment=patches by Nachbrenner +//skip "SBPlayLogoMovie" +//patch=0,EE,004531bc,word,00000000 +//skip "SBPlayDemoMovie" +//patch=0,EE,004531e0,word,00000000 \ No newline at end of file diff --git a/bin/patches/901AAC09.pnach b/bin/patches/901AAC09.pnach new file mode 100644 index 0000000000..8a6a0dcba5 --- /dev/null +++ b/bin/patches/901AAC09.pnach @@ -0,0 +1,4 @@ + +gametitle=Haunting Ground [SLUS 21075] (U) +comment=ZeroGS fixes +zerogs=00018000 \ No newline at end of file diff --git a/bin/patches/93223BE4.pnach b/bin/patches/93223BE4.pnach new file mode 100644 index 0000000000..42abd09e6c --- /dev/null +++ b/bin/patches/93223BE4.pnach @@ -0,0 +1,7 @@ +gametitle=Formula 1 2003 [SCES 51592] (E) +comment=patches by Nachbrenner +//skip sceIpuSync +patch=0,EE,00379f78,word,03e00008 +//skip Videos +patch=0,EE,00376f80,word,03e00008 +patch=0,EE,00376f84,word,00000000 \ No newline at end of file diff --git a/bin/patches/934F9081.pnach b/bin/patches/934F9081.pnach new file mode 100644 index 0000000000..ceea700199 --- /dev/null +++ b/bin/patches/934F9081.pnach @@ -0,0 +1,4 @@ +gametitle= Neopets - The Darkest Faerie [SCUS 97367] (U) +comment= +//ZeroGS Patch - GAME_RESOLVEPROMOTED|GAME_FULL16BITRES|GAME_NODEPTHRESOLVE +zerogs=00038000 \ No newline at end of file diff --git a/bin/patches/93F8A60B.pnach b/bin/patches/93F8A60B.pnach new file mode 100644 index 0000000000..eb5a07dc3c --- /dev/null +++ b/bin/patches/93F8A60B.pnach @@ -0,0 +1,4 @@ +gametitle=Kingdom Hearts II [SLPM 66233] (J) +// no depth resolves +zerogs=00008000 +fastmemory \ No newline at end of file diff --git a/bin/patches/941BB7D9.pnach b/bin/patches/941BB7D9.pnach new file mode 100644 index 0000000000..653d69738e --- /dev/null +++ b/bin/patches/941BB7D9.pnach @@ -0,0 +1,29 @@ +gametitle=Final Fantasy X Deutsch [SCES 50492] (G) +comment=patches by Nachbrenner +//crash fix: sceDmaSend +patch=0,EE,002dbacc,word,00000000 +patch=0,EE,002dbab8,word,00000000 +//fix DMA error: 0c0f0f00 - ipu1dma NULL! +patch=0,EE,0015ff68,word,00000000 +//Skip Videos +patch=0,EE,0015d1c0,word,100000b2 +//Skip sceIpuSync +//patch=0,EE,002e2530,word,03e00008 +//Use american savegames +//patch=0,EE,0024144c,word,24a56e60 +// +//Max GIL +patch=0,EE,201BE554,word,ACA3D218 +//Max HP +patch=0,EE,201C97B8,word,2402270F +patch=0,EE,201C97BC,word,AC620290 +//Max MP +patch=0,EE,201C97CC,word,2402270F +patch=0,EE,201C97D0,word,AC620294 +//No random battles +patch=0,EE,001be880,byte,00 +patch=0,EE,001bf2bc,byte,00 +//Hypermode +patch=0,EE,001164d0,word,3c014280 +//ZeroGS Patch - GAME_FFXHACK +zerogs=00000080 \ No newline at end of file diff --git a/bin/patches/94C56923.pnach b/bin/patches/94C56923.pnach new file mode 100644 index 0000000000..dbe41895dc --- /dev/null +++ b/bin/patches/94C56923.pnach @@ -0,0 +1,4 @@ +gametitle=Spyro: A Hero's Tale [SLUS 20884] (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,003faaf0,word,24020001 \ No newline at end of file diff --git a/bin/patches/950241D3.pnach b/bin/patches/950241D3.pnach new file mode 100644 index 0000000000..47a2ade1f5 --- /dev/null +++ b/bin/patches/950241D3.pnach @@ -0,0 +1,4 @@ +gametitle=Fu-Un Bakumatsu-Den [SLPM 65813] (J) +comment= patches by nachbrenner +//Skip Videos +// patch=0,EE,00112f20,word,24020001 \ No newline at end of file diff --git a/bin/patches/951555A0.pnach b/bin/patches/951555A0.pnach new file mode 100644 index 0000000000..d54d9c627e --- /dev/null +++ b/bin/patches/951555A0.pnach @@ -0,0 +1,4 @@ +gametitle=Disgaea 2 - Cursed Memories [SLUS 21397] (U) +comment= +//ZeroGS Patch - GAME_NODEPTHRESOLVE +zerogs=00008000 \ No newline at end of file diff --git a/bin/patches/95BB1901.pnach b/bin/patches/95BB1901.pnach new file mode 100644 index 0000000000..f9f3163c9c --- /dev/null +++ b/bin/patches/95BB1901.pnach @@ -0,0 +1,3 @@ +gametitle= MX VS ATV Unleashed [SLUS 21104] (U) +//Skip Movies +patch=0,EE,00339bc0,word,24020001 \ No newline at end of file diff --git a/bin/patches/96B2F56D.pnach b/bin/patches/96B2F56D.pnach new file mode 100644 index 0000000000..a58502713f --- /dev/null +++ b/bin/patches/96B2F56D.pnach @@ -0,0 +1,7 @@ +gametitle=Devil May Cry [SLPM 65038] (J) +comment=Skips Movies +//skip Movie_Set +patch=0,EE,001cdea0,word,03e00008 +patch=0,EE,001cdea4,word,03e00008 +//skip MovieEndCheck +patch=0,EE,001ce44c,word,70001428 \ No newline at end of file diff --git a/bin/patches/96B76E56.pnach b/bin/patches/96B76E56.pnach new file mode 100644 index 0000000000..219d549519 --- /dev/null +++ b/bin/patches/96B76E56.pnach @@ -0,0 +1,9 @@ +gametitle=SX Superstars [SLES 51495](E) +comment= patches by Nachbrenner +//Skip Intro Movies (for blockdump) +patch=0,EE,002f1088,word,03e00008 +patch=0,EE,002f108c,word,00000000 +//No ingame music (for blockdump) +patch=0,EE,00223cac,word,00000000 +//fix IPU DMA for "MAINMENU.IPU" (enable to go ingame) +patch=0,EE,003befe4,word,001003e0 \ No newline at end of file diff --git a/bin/patches/96C20D6F.pnach b/bin/patches/96C20D6F.pnach new file mode 100644 index 0000000000..6423d90a16 --- /dev/null +++ b/bin/patches/96C20D6F.pnach @@ -0,0 +1,4 @@ +gametitle=Dynasty Warriors 4 Xtreme Legends +comment=Skips Videos by bositman +//Skip Videos +patch=0,EE,0010ab70,word,24020001 diff --git a/bin/patches/9AAC5309.pnach b/bin/patches/9AAC5309.pnach new file mode 100644 index 0000000000..5b0e191e64 --- /dev/null +++ b/bin/patches/9AAC5309.pnach @@ -0,0 +1,4 @@ +gametitle=Final Fantasy X-2 [SLES 51815] (E) +comment= +//ZeroGS Patch - GAME_FFXHACK +zerogs=00000080 \ No newline at end of file diff --git a/bin/patches/9AAC530D.pnach b/bin/patches/9AAC530D.pnach new file mode 100644 index 0000000000..6a7d91c363 --- /dev/null +++ b/bin/patches/9AAC530D.pnach @@ -0,0 +1,6 @@ +gametitle=Final Fantasy X-2 [SLES 51817] (G) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,0017e1e4,word,1000000d +//ZeroGS Patch - GAME_FFXHACK +zerogs=00000080 \ No newline at end of file diff --git a/bin/patches/9AC65D6A.pnach b/bin/patches/9AC65D6A.pnach new file mode 100644 index 0000000000..536ad772cd --- /dev/null +++ b/bin/patches/9AC65D6A.pnach @@ -0,0 +1,4 @@ +gametitle=Atelier Iris 2 +comment=Skips Video by bositman +//Skip Videos +patch=0,EE,002bdbb8,word,24020001 \ No newline at end of file diff --git a/bin/patches/9B1EE9EB.pnach b/bin/patches/9B1EE9EB.pnach new file mode 100644 index 0000000000..f4047b5c24 --- /dev/null +++ b/bin/patches/9B1EE9EB.pnach @@ -0,0 +1,4 @@ +gametitle=DNA - Dark Native Apostle [SLES 50202] (E) +comment=patches by Nachbrenner +//Skip PSSPLAY.ELF +patch=1,EE,001691F0,word,00000000 \ No newline at end of file diff --git a/bin/patches/9BE3F92D.pnach b/bin/patches/9BE3F92D.pnach new file mode 100644 index 0000000000..1bec0f42d0 --- /dev/null +++ b/bin/patches/9BE3F92D.pnach @@ -0,0 +1,4 @@ +gametitle=Psyvariar Complete Edition [SLPM62139] (J) +comment=patch by Nachbrenner +//skip movie +patch=0,EE,0010019c,word,00000000 \ No newline at end of file diff --git a/bin/patches/9D6F46F0.pnach b/bin/patches/9D6F46F0.pnach new file mode 100644 index 0000000000..848d09fd9d --- /dev/null +++ b/bin/patches/9D6F46F0.pnach @@ -0,0 +1,4 @@ +gametitle=GoDai Elemental Force [SLES 50704] (E) +comment= patches by Nachbrenner +//Skip Videos +patch=0,EE,00211c90,word,24020001 \ No newline at end of file diff --git a/bin/patches/A029B109.pnach b/bin/patches/A029B109.pnach new file mode 100644 index 0000000000..f55485ad24 --- /dev/null +++ b/bin/patches/A029B109.pnach @@ -0,0 +1,12 @@ +gametitle=Grand Prix Challenge [SLES 51296] (E) +comment=patches by Nachbrenner +//g_dmaMaximumWaitTime (default=40400000) +patch=0,EE,00484f88,word,00000000 +// +//for blockdump: +//Don't stream videos (press 3 times X after FIA logo) +patch=0,EE,003a6fc0,word,03e00008 // UpdateInternal__12fmvStreamPS2Fv +patch=0,EE,003a6fc4,word,00000000 +//skip Play__13slMusicStreamFv +patch=0,EE,002ea300,word,03e00008 +patch=0,EE,002ea304,word,00000000 \ No newline at end of file diff --git a/bin/patches/A1B752C7.pnach b/bin/patches/A1B752C7.pnach new file mode 100644 index 0000000000..e5e65c99b7 --- /dev/null +++ b/bin/patches/A1B752C7.pnach @@ -0,0 +1,4 @@ +gametitle=Sensible Soccer 2006 +comment=Patch By Refraction +//Skip Videos +patch=0,EE,0036b0a0,word,24020001 \ No newline at end of file diff --git a/bin/patches/A36CFF6C.pnach b/bin/patches/A36CFF6C.pnach new file mode 100644 index 0000000000..7a37c561c3 --- /dev/null +++ b/bin/patches/A36CFF6C.pnach @@ -0,0 +1,3 @@ +gametitle=Castlevania NTSC JPN +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,007296b0,word,24020001 \ No newline at end of file diff --git a/bin/patches/A39517AB.pnach b/bin/patches/A39517AB.pnach new file mode 100644 index 0000000000..ecd2924513 --- /dev/null +++ b/bin/patches/A39517AB.pnach @@ -0,0 +1,16 @@ +gametitle=Final Fantasy X [SCES 50490] (E) [A39517AB] +comment=Patch By CKemu +// +//Emulation Fixes +//Skip Videos [No longer required: 0.9.2-0715-i] +//patch=0,EE,0015d1c0,word,100000b2 +//Kill INIT SOUND [No longer required: 0.9.2-0715-i] +//patch=0,EE,00126074,word,00000000 +//Kill INIT VOICE [No longer required: 0.9.2-0715-i] +//patch=0,EE,00126088,word,00000000 +// +//Misc Patches +//Use (U) gamesaves [SLUS 20312] +//patch=0,EE,00241434,word,24a56e60 +//ZeroGS Patch - GAME_FFXHACK +zerogs=00000080 \ No newline at end of file diff --git a/bin/patches/A3ACF3C7.pnach b/bin/patches/A3ACF3C7.pnach new file mode 100644 index 0000000000..4760dedd3d --- /dev/null +++ b/bin/patches/A3ACF3C7.pnach @@ -0,0 +1,8 @@ +gametitle=Project Eden [SLES 50553] (E) +comment=patches by Nachbrenner +//Skip EC_LOGO Video +patch=0,EE,0013af54,word,00000000 +patch=0,EE,0013af60,word,00000000 +//Skip INTRO Video +patch=0,EE,0013b060,word,00000000 +patch=0,EE,0013b06c,word,00000000 \ No newline at end of file diff --git a/bin/patches/A3D63039.pnach b/bin/patches/A3D63039.pnach new file mode 100644 index 0000000000..5e1f3c79ee --- /dev/null +++ b/bin/patches/A3D63039.pnach @@ -0,0 +1,2 @@ +gametitle=Xenosaga Episode I - Der Wille zur Macht [SCAJ 30001] (J) +fastmemory diff --git a/bin/patches/A4E2C043.pnach b/bin/patches/A4E2C043.pnach new file mode 100644 index 0000000000..e29021c245 --- /dev/null +++ b/bin/patches/A4E2C043.pnach @@ -0,0 +1,8 @@ +gametitle=Homura [SLPM 62685] (J) +comment=patches by Nachbrenner +//Infinite Lives 00310758 +patch=0,EE,00225714,word,24030005 +//Infinite Health +patch=0,EE,00213884,word,00000000 +//Infinite Bombs 0031075C +patch=0,EE,00227174,word,24030005 \ No newline at end of file diff --git a/bin/patches/A5BF36A8.pnach b/bin/patches/A5BF36A8.pnach new file mode 100644 index 0000000000..07eb85777c --- /dev/null +++ b/bin/patches/A5BF36A8.pnach @@ -0,0 +1,3 @@ +gametitle=Paris-Dakar Rally [SLES 50212] (E) +//Skip Videos +patch=0,EE,001098e8,word,24020001 \ No newline at end of file diff --git a/bin/patches/A63C896C.pnach b/bin/patches/A63C896C.pnach new file mode 100644 index 0000000000..c9ff6f79a6 --- /dev/null +++ b/bin/patches/A63C896C.pnach @@ -0,0 +1,6 @@ +gametitle=Tsukyo Ni Saraba [SLPM 65826] (J) +comment=patches by Nachbrenner +//infinite ammo +patch=0,EE,003620b4,word,24040000 +//crash fix (nukes AI) +patch=0,EE,0025328c,word,00000000 \ No newline at end of file diff --git a/bin/patches/A719D130.pnach b/bin/patches/A719D130.pnach new file mode 100644 index 0000000000..99254094af --- /dev/null +++ b/bin/patches/A719D130.pnach @@ -0,0 +1,4 @@ +gametitle=Dynasty Warriors 5 XL +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,0011a240,word,24020001 \ No newline at end of file diff --git a/bin/patches/A74F99CD.pnach b/bin/patches/A74F99CD.pnach new file mode 100644 index 0000000000..9022547630 --- /dev/null +++ b/bin/patches/A74F99CD.pnach @@ -0,0 +1,4 @@ +gametitle=Psyvariar Revision [SLPM 62371] (J) +comment= +//Skip Videos +patch=0,EE,00155938,word,00000000 \ No newline at end of file diff --git a/bin/patches/A7A2F7C5.pnach b/bin/patches/A7A2F7C5.pnach new file mode 100644 index 0000000000..6b647345a7 --- /dev/null +++ b/bin/patches/A7A2F7C5.pnach @@ -0,0 +1,16 @@ +gametitle=Disney's Stitch: Experiment 626 [SCES 50960] (E) +comment=patches by Nachbrenner +//Skip Videos +//patch=0,EE,001edbe4,word,00000000 +//Skip music +patch=0,EE,001d1de0,word,03e00008 +patch=0,EE,001d1de4,word,00000000 +patch=1,EE,00A02FAC,word,00000000 +//MTGS vs. GS_CSR +//patch=0,EE,00269e00,word,00000000 +//patch=0,EE,00269de4,word,00000000 +//patch=0,EE,002e3d54,word,00000000 +//patch=0,EE,00269fdc,word,00000000 +//patch=0,EE,0026a018,word,00000000 +//sceGsExecStoreImage +//patch=0,EE,002e22ec,word,00000000 \ No newline at end of file diff --git a/bin/patches/A8083AE6.pnach b/bin/patches/A8083AE6.pnach new file mode 100644 index 0000000000..018be7b1be --- /dev/null +++ b/bin/patches/A8083AE6.pnach @@ -0,0 +1,4 @@ +gametitle=RC Revenge Pro [SLUS 20153] (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,001ceaf8,word,24020001 \ No newline at end of file diff --git a/bin/patches/A88ACA28.pnach b/bin/patches/A88ACA28.pnach new file mode 100644 index 0000000000..e238e20ffe --- /dev/null +++ b/bin/patches/A88ACA28.pnach @@ -0,0 +1,9 @@ +gametitle=Codename: Kids Next Door - Operation V.I.D.E.O.G.A.M.E [SLES 53685] (E) +comment=patches by nachbrenner +// MMI fix: replace PINTH with PINTEH (obsolete) +//patch=0,EE,0037eef8,word,700212a9 +// skip SFD movies +patch=0,EE,00208220,word,03e00008 +patch=0,EE,00208224,word,00000000 +patch=0,EE,00208190,word,03e00008 +patch=0,EE,00208194,word,00000000 \ No newline at end of file diff --git a/bin/patches/A8A76AAC.pnach b/bin/patches/A8A76AAC.pnach new file mode 100644 index 0000000000..c62b917e8f --- /dev/null +++ b/bin/patches/A8A76AAC.pnach @@ -0,0 +1,4 @@ +gametitle=Guilty Gear XX - Accent Core [SLUS 21652] (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,002bdb88,word,24020001 \ No newline at end of file diff --git a/bin/patches/A929A697.pnach b/bin/patches/A929A697.pnach new file mode 100644 index 0000000000..fcd8217b47 --- /dev/null +++ b/bin/patches/A929A697.pnach @@ -0,0 +1,3 @@ +gametitle=Turok: Evolution [SLES 51124] (G) +//Skip Videos (sceMpegIsEnd) +patch=0,EE,001cf280,word,24020001 \ No newline at end of file diff --git a/bin/patches/A9759015.pnach b/bin/patches/A9759015.pnach new file mode 100644 index 0000000000..6ac48fade0 --- /dev/null +++ b/bin/patches/A9759015.pnach @@ -0,0 +1,9 @@ +gametitle=Flucht von Monkey Island [SLES 50227] (G) +comment=patches by nachbrenner +//Skip Videos +//patch=0,EE,001c5220,word,24020001 +//patch=0,EE,0018b850,word,03E00008 +//patch=0,EE,0018b854,word,00000000 +//fix MTGS loop at GS_CSR in sceGsExecStoreImage +//patch=0,EE,001bb858,word,34630002 +//patch=0,EE,001bb888,word,34420002 \ No newline at end of file diff --git a/bin/patches/AA31B5BF.pnach b/bin/patches/AA31B5BF.pnach new file mode 100644 index 0000000000..6f5c0148ea --- /dev/null +++ b/bin/patches/AA31B5BF.pnach @@ -0,0 +1,4 @@ +gametitle=Metal Gear Solid 3 - Snake Eater [SLES 82013] (E) [086273D2] +comment=Patch By CKemu +//ZeroGS Patch - Required fixes to visuals +zerogs=06018000 \ No newline at end of file diff --git a/bin/patches/AC7E88D9.pnach b/bin/patches/AC7E88D9.pnach new file mode 100644 index 0000000000..6946863eb5 --- /dev/null +++ b/bin/patches/AC7E88D9.pnach @@ -0,0 +1,18 @@ +gametitle=Silpheed - The Lost Planet [SLES 50193] (E) +comment=patches by Nachbrenner +//default player name = PCSX2 +patch=0,EE,008c7a48,word,58534350 +patch=0,EE,008c7a4c,word,00000032 +//Skip SOLONT.PSS +patch=0,EE,0019561c,word,00000000 +//Skip ?PART.MOV +patch=0,EE,0019556c,word,00000000 +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,0025c3f8,word,24020001 +//Have All Weapons +patch=0,EE,0015a488,word,340201ff +patch=0,EE,0015b2f4,word,340201ff +// Inf. Shield (ASM) +patch=0,EE,0013DA0C,word,00000000 +//BGM off (for blockdump) +//patch=0,EE,008ac334,word,00000000 diff --git a/bin/patches/ACB1989A.pnach b/bin/patches/ACB1989A.pnach new file mode 100644 index 0000000000..b7bc8831ba --- /dev/null +++ b/bin/patches/ACB1989A.pnach @@ -0,0 +1,10 @@ +gametitle=Midnight Club II [SLES 51054] (E) +comment=patches by Nachbrenner +//skip videos (for blockdump) +//patch=0,EE,0013DB58,word,00000000 +//skip network check "BT_ok_to_initialize_networking" +//patch=0,EE,00123efc,word,00000000 +//music off (for blockdump) +//patch=1,EE,004ABB1C,word,00000000 +//crash fix SVN november (obsolete) +//patch=0,EE,0016c9a4,word,10000068 \ No newline at end of file diff --git a/bin/patches/AD9D2B54.pnach b/bin/patches/AD9D2B54.pnach new file mode 100644 index 0000000000..275e20dbf6 --- /dev/null +++ b/bin/patches/AD9D2B54.pnach @@ -0,0 +1,4 @@ +gametitle=worldtvfighters NTSC JPN +comment=skip intro pss +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,002d8df8,word,24020001 \ No newline at end of file diff --git a/bin/patches/ADAA1256.pnach b/bin/patches/ADAA1256.pnach new file mode 100644 index 0000000000..9dcb3ed3bb --- /dev/null +++ b/bin/patches/ADAA1256.pnach @@ -0,0 +1,8 @@ +gametitle=Steel Dragon EX [SLES 52482] (E) +comment=patches by Nachbrenner +//Disable BGM +//patch=0,EE,0015bcf8,word,00000000 +//Disable SE +//patch=0,EE,0015bcf0,word,00000000 +//Skip Memory Card check +patch=0,EE,0015bd00,word,00000000 \ No newline at end of file diff --git a/bin/patches/AE0E098F.pnach b/bin/patches/AE0E098F.pnach new file mode 100644 index 0000000000..0f363aaff5 --- /dev/null +++ b/bin/patches/AE0E098F.pnach @@ -0,0 +1,6 @@ +gametitle=Indiana Jones und die Legende der Kaisergruft [SLES 50838] (G) +comment=patches by Nachbrenner +//Skip Videos +//patch=1,EE,00100624,word,24020000 // legal.pss +//patch=1,EE,0012c414,word,24020000 // leclogo.pss +//patch=1,EE,00100674,word,24020000 // cslogo.pss \ No newline at end of file diff --git a/bin/patches/AE3EAA05.pnach b/bin/patches/AE3EAA05.pnach new file mode 100644 index 0000000000..f49aa73313 --- /dev/null +++ b/bin/patches/AE3EAA05.pnach @@ -0,0 +1,14 @@ +gametitle=Kingdom Hearts German [SCES 50969] (G) +comment=patches by Nachbrenner +//Size modifier +// default = 1 (3f800000) +// patch=0,EE,001243BC,word,3c014000 //double size +//Hypermode +// default = 1.2 (3f999999) +// max =5 (40a00000) +//Don't use "Hypermode" in cutscenes. +//patch=0,EE,0011007c,word,3c0140a0 +//patch=0,EE,00110080,word,34210000 +//ZeroGS Patch - GAME_QUICKRESOLVE1 +zerogs=00000400 +fastmemory \ No newline at end of file diff --git a/bin/patches/AE9EB9A0.pnach b/bin/patches/AE9EB9A0.pnach new file mode 100644 index 0000000000..ce1dc32b43 --- /dev/null +++ b/bin/patches/AE9EB9A0.pnach @@ -0,0 +1,4 @@ +gametitle=TD overdrive - The Brotherhood Of Speed [SLES 50778] (E) +comment= patches by Nachbrenner +// skip FMV sceMpegIsEnd +// patch=1,EE,0030aad0,word,24020001 \ No newline at end of file diff --git a/bin/patches/AEDAEE99.pnach b/bin/patches/AEDAEE99.pnach new file mode 100644 index 0000000000..6430aa7a2f --- /dev/null +++ b/bin/patches/AEDAEE99.pnach @@ -0,0 +1,3 @@ +gametitle=GodHand [SLPM 66550] (J) +//fix DMA loop +patch=0,EE,002bdd28,word,24020001 \ No newline at end of file diff --git a/bin/patches/AFAC88EF.pnach b/bin/patches/AFAC88EF.pnach new file mode 100644 index 0000000000..4d1594a88e --- /dev/null +++ b/bin/patches/AFAC88EF.pnach @@ -0,0 +1,14 @@ +gametitle= Dirge Of Cerberus - Final Fantasy VII [SLPM 66271] (J) +comment=patches by Nachbrenner +//Fix "**** SQTHREAD ERROR : BAD LOCK STATE" +patch=0,EE,0047d228,word,00000000 +//Skip Videos +// patch=0,EE,00255264,word,00000000 +//fix ingame DMA0 +patch=0,EE,00283060,word,10000020 +//fix ingame DMA8 +//patch=0,EE,0031d714,word,00000000 +//fix ingame VIF1_STAT +//patch=0,EE,004a184c,word,00000000 +//Skip mcDelayThread +//patch=0,EE,004b7c88,word,00000000 \ No newline at end of file diff --git a/bin/patches/B0621C55.pnach b/bin/patches/B0621C55.pnach new file mode 100644 index 0000000000..44f0cf38cd --- /dev/null +++ b/bin/patches/B0621C55.pnach @@ -0,0 +1,4 @@ +gametitle=Fighting Angels [SLES 53408] (E) [B0621C55] +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,00192a38,word,24020001 \ No newline at end of file diff --git a/bin/patches/B0AE1898.pnach b/bin/patches/B0AE1898.pnach new file mode 100644 index 0000000000..4c5335f3f9 --- /dev/null +++ b/bin/patches/B0AE1898.pnach @@ -0,0 +1,6 @@ +gametitle=smackdown shut your mouth +comment=patched by prafull +//skip sceipusync +patch=0,EE,0010b880,word,00000000 +patch=0,EE,0010b798,word,00000000 +patch=0,EE,0010b7c8,word,00000000 \ No newline at end of file diff --git a/bin/patches/B130E5BA.pnach b/bin/patches/B130E5BA.pnach new file mode 100644 index 0000000000..96c8abad25 --- /dev/null +++ b/bin/patches/B130E5BA.pnach @@ -0,0 +1,3 @@ +gametitle=katamari damashi NTSC JAP +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,0018d798,word,24020001 \ No newline at end of file diff --git a/bin/patches/B1BE3E51.pnach b/bin/patches/B1BE3E51.pnach new file mode 100644 index 0000000000..47cf390a3d --- /dev/null +++ b/bin/patches/B1BE3E51.pnach @@ -0,0 +1,6 @@ +gametitle=Whiplash [SLES 51958] (E) +comment=patches by nachbrenner +//Skip error printing +//patch=0,EE,00252d88,word,00000000 +//fix jalr crash +patch=0,EE,0040db84,word,0010c370 \ No newline at end of file diff --git a/bin/patches/B234036E.pnach b/bin/patches/B234036E.pnach new file mode 100644 index 0000000000..c8d3f50980 --- /dev/null +++ b/bin/patches/B234036E.pnach @@ -0,0 +1,4 @@ +gametitle=Guilty Gear X2 Reload [SLES 52967] (E) [B234036E] +comment=Patch By CKemu +//Skip Videos +patch=0,EE,00250418,word,24020001 \ No newline at end of file diff --git a/bin/patches/B2BDE9F3.pnach b/bin/patches/B2BDE9F3.pnach new file mode 100644 index 0000000000..b3053be360 --- /dev/null +++ b/bin/patches/B2BDE9F3.pnach @@ -0,0 +1,6 @@ +gametitle=Hisshou Pachinko Pachi-Slot Kouryoku Series Vol. 7 - Fever Powerful Zero [SLPS 25697] (J) +//Skip sceIpuSync +patch=0,EE,00100dd8,word,03e00008 +//IPU VQ fix +patch=0,EE,0021b2cc,word,00000000 // ? +patch=0,EE,0021b08c,word,00000000 // _pictureData0 \ No newline at end of file diff --git a/bin/patches/B338676A.pnach b/bin/patches/B338676A.pnach new file mode 100644 index 0000000000..015616c412 --- /dev/null +++ b/bin/patches/B338676A.pnach @@ -0,0 +1,6 @@ +gametitle=Summoner 2 [SLES 51142] (G) +comment=patches by Nachbrenner +//skip FMV (sceMpegIsEnd) +// patch=0,EE,001c6638,word,24020001 +//brute force fix GS_CSR +// patch=0,EE,00188604,word,fc620000 // sd v0, $0000(v1) diff --git a/bin/patches/B34DA141.pnach b/bin/patches/B34DA141.pnach new file mode 100644 index 0000000000..b6bfb87561 --- /dev/null +++ b/bin/patches/B34DA141.pnach @@ -0,0 +1,8 @@ +gametitle=The Mouse Police [SLES 52370] (E) +comment=patches by Nachbrenner +//Skip LOGO.PSS +//patch=0,EE,0012945c,word,24020001 +//Skip INTRO.PSS +//patch=0,EE,001293f8,word,24020001 +//Skip DALAMATIO.PSS +//patch=0,EE,0012a69c,word,00000000 \ No newline at end of file diff --git a/bin/patches/B36EE21E.pnach b/bin/patches/B36EE21E.pnach new file mode 100644 index 0000000000..3bb63f76de --- /dev/null +++ b/bin/patches/B36EE21E.pnach @@ -0,0 +1,5 @@ +gametitle=IndyCar Series [SLUS 20641] (U) +//skip BjMediaVideoPlay +patch=0,EE,00204b38,word,24020001 +patch=0,EE,001ea500,word,03e00008 +patch=0,EE,001ea504,word,00000000 \ No newline at end of file diff --git a/bin/patches/B590CE04.pnach b/bin/patches/B590CE04.pnach new file mode 100644 index 0000000000..d26b8bbd29 --- /dev/null +++ b/bin/patches/B590CE04.pnach @@ -0,0 +1,5 @@ +gametitle=Gran Turismo 3 A-Spec [SCES 50294] (E) [B590CE04] +comment= patches by Nachbrenner +//Skip Intro Videos +patch=1,EE,00128c60,word,03e00008 +patch=1,EE,00128c64,word,00000000 \ No newline at end of file diff --git a/bin/patches/B82E4C4B.pnach b/bin/patches/B82E4C4B.pnach new file mode 100644 index 0000000000..f0e3ab08ef --- /dev/null +++ b/bin/patches/B82E4C4B.pnach @@ -0,0 +1,4 @@ +gametitle=Play it Pinball (E) +comment= patch by parotaku +//Skip Videos (sceMpegIsEnd) +patch=0,EE,002b29d0,word,24020001 \ No newline at end of file diff --git a/bin/patches/B884B6E9.pnach b/bin/patches/B884B6E9.pnach new file mode 100644 index 0000000000..788cbc1e06 --- /dev/null +++ b/bin/patches/B884B6E9.pnach @@ -0,0 +1,4 @@ +gametitle=Gottlieb Pinball Classics (E) +comment= patch by parotaku +//Skip Videos (sceMpegIsEnd) +patch=0,EE,00107ce0,word,24020001 \ No newline at end of file diff --git a/bin/patches/B99379B7.pnach b/bin/patches/B99379B7.pnach new file mode 100644 index 0000000000..d30bb15652 --- /dev/null +++ b/bin/patches/B99379B7.pnach @@ -0,0 +1,4 @@ +gametitle=Erementar Gerad [SLPM 62623] (J) +comment=patches by Nachbrenner +//Skip Videos (sceMpegIsEnd) +patch=0,EE,002861c8,word,24020001 \ No newline at end of file diff --git a/bin/patches/BB3D833A.pnach b/bin/patches/BB3D833A.pnach new file mode 100644 index 0000000000..b36fce72bc --- /dev/null +++ b/bin/patches/BB3D833A.pnach @@ -0,0 +1,4 @@ +gametitle= Final Fantasy X [SLUS 20312] (U) +comment= +//ZeroGS Patch - GAME_FFXHACK +zerogs=00000080 \ No newline at end of file diff --git a/bin/patches/BD3DBCF9.pnach b/bin/patches/BD3DBCF9.pnach new file mode 100644 index 0000000000..169e6b5ba6 --- /dev/null +++ b/bin/patches/BD3DBCF9.pnach @@ -0,0 +1,4 @@ +gametitle=Dynasty Warriors 4 - Empires +comment=Skips Video +//Skip Video +patch=0,EE,00211544,word,00000000 \ No newline at end of file diff --git a/bin/patches/BF5D9AEC.pnach b/bin/patches/BF5D9AEC.pnach new file mode 100644 index 0000000000..94f6564ce3 --- /dev/null +++ b/bin/patches/BF5D9AEC.pnach @@ -0,0 +1,4 @@ +gametitle=Gungrave US/C NTSC +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,001c07b0,word,24020001 \ No newline at end of file diff --git a/bin/patches/C04FB5FD.pnach b/bin/patches/C04FB5FD.pnach new file mode 100644 index 0000000000..640b1bb039 --- /dev/null +++ b/bin/patches/C04FB5FD.pnach @@ -0,0 +1,4 @@ +gametitle=Superior Defender Gundam Force Showdown! [SLUS 20698] (U) +comment= Captain Gundam The Secret Defender Of Neotopia +//Skip Videos +patch=0,EE,0010c660,word,24020001 \ No newline at end of file diff --git a/bin/patches/C0D6A139.pnach b/bin/patches/C0D6A139.pnach new file mode 100644 index 0000000000..29d1452360 --- /dev/null +++ b/bin/patches/C0D6A139.pnach @@ -0,0 +1,4 @@ +gametitle=Romance Of The Three Kingdoms XI [SLUS 21584] (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,0010b690,word,24020001 \ No newline at end of file diff --git a/bin/patches/C1625F14.pnach b/bin/patches/C1625F14.pnach new file mode 100644 index 0000000000..44adb215b1 --- /dev/null +++ b/bin/patches/C1625F14.pnach @@ -0,0 +1,9 @@ +gametitle=Midnight Club II [SLUS20209] (U) +comment=patches by Nachbrenner +//skip videos +patch=0,EE,0013D5E0,word,00000000 +//skip sceIpuSync +patch=0,EE,003ef4e8,word,03e00008 +patch=0,EE,003ef4ec,word,00000000 +//skip network check "BT_ok_to_initialize_networking" +patch=0,EE,00123c70,word,00000000 \ No newline at end of file diff --git a/bin/patches/C1767D64.pnach b/bin/patches/C1767D64.pnach new file mode 100644 index 0000000000..3498d7ece8 --- /dev/null +++ b/bin/patches/C1767D64.pnach @@ -0,0 +1,5 @@ +gametitle=Gitaroo man (J) +comment= patch by parotaku +//Skip Videos (sceMpegIsEnd) +patch=0,EE,0012ea48,word,24020001 +roundmode=DOWN,CHOP \ No newline at end of file diff --git a/bin/patches/C1B141D6.pnach b/bin/patches/C1B141D6.pnach new file mode 100644 index 0000000000..b75485c8f3 --- /dev/null +++ b/bin/patches/C1B141D6.pnach @@ -0,0 +1,7 @@ +gametitle= 25 To Life [SLUS 21016] (U) +comment=patches by Nachbrenner and Refraction +//Skip Videos (for blockdump) +// patch=0,EE,0040ade4,word,00000000 +//fix EEREC: LQ qw align +// patch=0,EE,0043b31c,word,78c40020 // intro (fixed) +patch=0,EE,0022d3d0,word,79050020 // ingame (still borked) \ No newline at end of file diff --git a/bin/patches/C220951A.pnach b/bin/patches/C220951A.pnach new file mode 100644 index 0000000000..64923c4f0c --- /dev/null +++ b/bin/patches/C220951A.pnach @@ -0,0 +1,5 @@ +gametitle= Gran Turismo Concept 2001 Tokyo [SCPS 15010] (J) +comment= patches by Nachbrenner +//Skip Video +patch=1,EE,001295b8,word,03e00008 +patch=1,EE,001295bc,word,00000000 \ No newline at end of file diff --git a/bin/patches/C398F477.pnach b/bin/patches/C398F477.pnach new file mode 100644 index 0000000000..3a722e70d3 --- /dev/null +++ b/bin/patches/C398F477.pnach @@ -0,0 +1,4 @@ +gametitle=Kingdom Hearts II [SLES 54114] (E) +// no depth resolves +zerogs=00008000 +fastmemory \ No newline at end of file diff --git a/bin/patches/C3D28EB9.pnach b/bin/patches/C3D28EB9.pnach new file mode 100644 index 0000000000..15b7fcfeae --- /dev/null +++ b/bin/patches/C3D28EB9.pnach @@ -0,0 +1,4 @@ +gametitle=Shadow Hearts - From The New World +comment=Skips Video,by bositman +//Skip Videos +patch=0,EE,00208b68,word,24020001 \ No newline at end of file diff --git a/bin/patches/C4467D30.pnach b/bin/patches/C4467D30.pnach new file mode 100644 index 0000000000..35049d9921 --- /dev/null +++ b/bin/patches/C4467D30.pnach @@ -0,0 +1,4 @@ +gametitle=World Rally Championship 3 [SLPM 65583] (J) +comment= patch by Nachbrenner +//Skip sceIpuSync +patch=1,EE,0027c808,word,03e00008 \ No newline at end of file diff --git a/bin/patches/C488EC04.pnach b/bin/patches/C488EC04.pnach new file mode 100644 index 0000000000..3be542cd3f --- /dev/null +++ b/bin/patches/C488EC04.pnach @@ -0,0 +1,8 @@ +gametitle=Ninjabread Man [SLES 53570] (E) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,00169e78,word,03e00008 +//Skip Music (for blockdumps) +//patch=0,EE,00106d6c,word,00000000 +//patch=0,EE,0014352c,word,00000000 +//patch=0,EE,00151738,word,00000000 \ No newline at end of file diff --git a/bin/patches/C4A60986.pnach b/bin/patches/C4A60986.pnach new file mode 100644 index 0000000000..76e3f134a8 --- /dev/null +++ b/bin/patches/C4A60986.pnach @@ -0,0 +1,4 @@ +gametitle=Sega Ages 2500 Volume 01 - Phantasy Star generation:1 (J) +comment=patches by Nachbrenner +//Get money for buying wares +patch=0,EE,0010e690,word,00a2282d \ No newline at end of file diff --git a/bin/patches/C502AD6E.pnach b/bin/patches/C502AD6E.pnach new file mode 100644 index 0000000000..ee561afdf0 --- /dev/null +++ b/bin/patches/C502AD6E.pnach @@ -0,0 +1,8 @@ +gametitle=Wallace & Grommit in Projekt Zoo [SLES 52026] (G) +comment=patches by Nachbrenner +//skip FMV (for blockdump) +//patch=0,EE,0046bf70,word,24020001 +//skip sceIpuSync +patch=0,EE,00472c78,word,03e00008 +//Disable Music & Speech (for blockdump) +//patch=0,EE,005767ac,word,001001d0 \ No newline at end of file diff --git a/bin/patches/C5DEFEA0.pnach b/bin/patches/C5DEFEA0.pnach new file mode 100644 index 0000000000..93734b5964 --- /dev/null +++ b/bin/patches/C5DEFEA0.pnach @@ -0,0 +1,7 @@ +gametitle=Okami [SLPM 66375] (J) +//fix sceSifDmaStat loop +patch=0,EE,00212a78,word,24020001 +//ZeroGS Patch - GAME_FULL16BITRES|GAME_NODEPTHRESOLVE|GAME_FASTUPDATE +zerogs=00058000 +//alternate ZeroGS Patch for speed VS pretty GFX - disable all lighting|blur effects +//zerogs=01000000 \ No newline at end of file diff --git a/bin/patches/C9246E9C.pnach b/bin/patches/C9246E9C.pnach new file mode 100644 index 0000000000..cb4b99874f --- /dev/null +++ b/bin/patches/C9246E9C.pnach @@ -0,0 +1,4 @@ +gametitle=Tetris Worlds NTSC +comment=Skips videos +//Skip Videos +patch=0,EE,002cea30,word,24020001 \ No newline at end of file diff --git a/bin/patches/C9C145BF.pnach b/bin/patches/C9C145BF.pnach new file mode 100644 index 0000000000..0c9ae7878a --- /dev/null +++ b/bin/patches/C9C145BF.pnach @@ -0,0 +1,10 @@ +gametitle=Crazy Taxi [SLES 50215] (E) +comment= patches by Nachbrenner +//skip movie "PlaySega" +patch=0,EE,001a42d0,word,03e00008 +patch=0,EE,001a42d4,word,00000000 +//skip movie "PlayAcclaim" +patch=0,EE,001a4468,word,03e00008 +patch=0,EE,001a446c,word,00000000 +//skip nlWaitVSync / TaxiBlankInterrupt +//patch=0,EE,0019cf10,word,03e00008 \ No newline at end of file diff --git a/bin/patches/CA295E61.pnach b/bin/patches/CA295E61.pnach new file mode 100644 index 0000000000..01f54701d2 --- /dev/null +++ b/bin/patches/CA295E61.pnach @@ -0,0 +1,4 @@ +gametitle= Atelier Iris Grand Phantasm [SLPM 66436] (J) +comment= Video Skip before MCD screen by Refraction +//Nop out IPU routine +patch=0,EE,00135cdc,word,00000000 \ No newline at end of file diff --git a/bin/patches/CA6243B9.pnach b/bin/patches/CA6243B9.pnach new file mode 100644 index 0000000000..83ae2208da --- /dev/null +++ b/bin/patches/CA6243B9.pnach @@ -0,0 +1,9 @@ +gametitle=Gran Turismo 4 Prologue China [PBPX 95524] (J) +comment=patches by Nachbrenner +//fix IPU DMA +patch=1,EE,00166814,word,00000000 +patch=1,EE,003a36b8,word,00000000 +patch=1,EE,003a36f4,word,00000000 +//Skip Videos +patch=1,EE,001e3718,word,03e00008 +patch=1,EE,001e371c,word,00000000 diff --git a/bin/patches/CB4EBD11.pnach b/bin/patches/CB4EBD11.pnach new file mode 100644 index 0000000000..ec94b28e1a --- /dev/null +++ b/bin/patches/CB4EBD11.pnach @@ -0,0 +1,7 @@ +gametitle=Dakar 2 [SLES 50879] (E) +comment= patches by Nachbrenner +//Skip Intro Videos +patch=0,EE,00139188,word,10000029 +//KOSMOS fix +patch=0,EE,00171874,word,00000000 // D1_CHCR +patch=0,EE,001718c4,word,00000000 // GS_CSR \ No newline at end of file diff --git a/bin/patches/CBBC2E7F.pnach b/bin/patches/CBBC2E7F.pnach new file mode 100644 index 0000000000..9b587c326d --- /dev/null +++ b/bin/patches/CBBC2E7F.pnach @@ -0,0 +1,15 @@ +gametitle= WRC Rally Evolved [SCES 53247] (E) +comment=patches by Nachbrenner +//fix delay slot violation (obsolete) +//patch=1,EE,003cc890,word,19c0004d +//patch=1,EE,003cc894,word,01ad6826 +//patch=1,EE,003cbc78,word,01ce7026 +//patch=1,EE,003cbc7c,word,19a0006c +//patch=1,EE,003cc528,word,01ce7026 +//patch=1,EE,003cc52c,word,19a0000c +//patch=1,EE,003cc46c,word,01ce7026 +//patch=1,EE,003cc470,word,19a0000f +//patch=1,EE,003cc704,word,01ce7026 +//patch=1,EE,003cc708,word,19a0000f +//patch=1,EE,003ccbb4,word,01ce7026 +//patch=1,EE,003ccbb8,word,19a00011 \ No newline at end of file diff --git a/bin/patches/CC4B9CDE.pnach b/bin/patches/CC4B9CDE.pnach new file mode 100644 index 0000000000..f1cd3dc816 --- /dev/null +++ b/bin/patches/CC4B9CDE.pnach @@ -0,0 +1,6 @@ +gametitle=Samurai Warriors 2 [SLUS 21462] (U) +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,0013CD10,word,24020001 +//ZeroGS patches +zerogs=00000002 \ No newline at end of file diff --git a/bin/patches/CC6AA742.pnach b/bin/patches/CC6AA742.pnach new file mode 100644 index 0000000000..5f5e24886b --- /dev/null +++ b/bin/patches/CC6AA742.pnach @@ -0,0 +1,4 @@ +gametitle=FIFA Soccer 2004 [SLKA 25087] (J) +comment=patches by Nachbrenner +//boot with english texts +patch=0,EE,00329b7c,word,24709430 \ No newline at end of file diff --git a/bin/patches/CDE7C999.pnach b/bin/patches/CDE7C999.pnach new file mode 100644 index 0000000000..d44a0fea0b --- /dev/null +++ b/bin/patches/CDE7C999.pnach @@ -0,0 +1,22 @@ +gametitle= World Rally Championship 4 [SCES 52389] (E) +comment=patches by Nachbrenner +//fix caching issue +patch=1,EE,004007e0,word,0080f809 +// +//skip STR videos (for blockdump) +patch=1,EE,00354850,word,00000000 +//Skip sceIpuSync +patch=1,EE,003811f0,word,03e00008 +//Disable video rendering (for speedy menus) +//patch=1,EE,00353960,word,00000000 +//fix delay slot violations (obsolete) +//patch=1,EE,002d8328,word,01ce7026 +//patch=1,EE,002d832c,word,19a0006c +//patch=1,EE,002d8c8c,word,01ce7026 +//patch=1,EE,002d8c90,word,19a0000f +//patch=1,EE,002d8f14,word,01ce7026 +//patch=1,EE,002d8f18,word,19a0000f +//patch=1,EE,002d8d48,word,01ce7026 +//patch=1,EE,002d8d4c,word,19a0000c +//patch=1,EE,002d9770,word,01ce7026 +//patch=1,EE,002d9774,word,19a00011 \ No newline at end of file diff --git a/bin/patches/CF11CD83.pnach b/bin/patches/CF11CD83.pnach new file mode 100644 index 0000000000..a05bc62ee9 --- /dev/null +++ b/bin/patches/CF11CD83.pnach @@ -0,0 +1,6 @@ +gametitle=Samurai Warriors +comment=Skips Video,by bositman +//Skip Videos +patch=0,EE,00126af0,word,24020001 +//ZeroGS patch - GAME_AUTORESET +zerogs=00000002 \ No newline at end of file diff --git a/bin/patches/CFB873AD.pnach b/bin/patches/CFB873AD.pnach new file mode 100644 index 0000000000..a1f4bfc877 --- /dev/null +++ b/bin/patches/CFB873AD.pnach @@ -0,0 +1,6 @@ +gametitle=Need For Speed Underground 2 [SLES52725] (E) +comment= patches by Nachbrenner +//Skip videos +patch=0,EE,0042b400,word,24020001 +//skip "RCMPDecodeBuffer" causing cputlbmiss +patch=0,EE,0025a970,word,00000000 \ No newline at end of file diff --git a/bin/patches/D03BEF2A.pnach b/bin/patches/D03BEF2A.pnach new file mode 100644 index 0000000000..f7d3ae2ad3 --- /dev/null +++ b/bin/patches/D03BEF2A.pnach @@ -0,0 +1,13 @@ +gametitle=Max Payne 2 - The Fall Of Max Payne [SLES 52336] (E) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,002c8650,word,03e00008 +patch=0,EE,002c8654,word,00000000 +//fix obscure inf. loop +patch=0,EE,001aa610,word,03e00008 +patch=0,EE,001aa614,word,24020000 +//Language Modifier (sceScfGetLanguage) +patch=0,EE,00445ce4,word,24020001 // english +//patch=0,EE,00445ce4,word,24020004 // deutsch +//fix "SleepThread vs. RTFSSIOP.IRX" +patch=0,EE,001b3208,word,00000000 \ No newline at end of file diff --git a/bin/patches/D08648B6.pnach b/bin/patches/D08648B6.pnach new file mode 100644 index 0000000000..e6d7127a74 --- /dev/null +++ b/bin/patches/D08648B6.pnach @@ -0,0 +1,3 @@ +gametitle=Robocop [SLES 51374] (E) +//Skip Videos +patch=0,EE,002ebf70,word,24020001 \ No newline at end of file diff --git a/bin/patches/D0E17D26.pnach b/bin/patches/D0E17D26.pnach new file mode 100644 index 0000000000..054c61e9e9 --- /dev/null +++ b/bin/patches/D0E17D26.pnach @@ -0,0 +1,4 @@ +gametitle=rtype final japan +comment= +//Skip Videos ("sceMpegIsEnd") +patch=0,EE,00116b88,word,24020001 \ No newline at end of file diff --git a/bin/patches/D1ACD489.pnach b/bin/patches/D1ACD489.pnach new file mode 100644 index 0000000000..f571d8e3d9 --- /dev/null +++ b/bin/patches/D1ACD489.pnach @@ -0,0 +1,5 @@ +gametitle=Syberia [SLES 51393] (E) +//Skip Videos +// patch=0,EE,0013E608,word,24020001 +//skip video "INTRO_MICROIDS.BIK" +// patch=0,EE,00297780,word,00000000 \ No newline at end of file diff --git a/bin/patches/D4781770.pnach b/bin/patches/D4781770.pnach new file mode 100644 index 0000000000..349fdaeb04 --- /dev/null +++ b/bin/patches/D4781770.pnach @@ -0,0 +1,6 @@ +gametitle=Donald Duck Phantomias Platyrhynchos Kineticus [SLES 50773] (E) +comment=patches by Nachbrenner +//Skip Videos +// patch=0,EE,00108ef0,word,24020001 // (sceMpegIsEnd) +//fix inf. loop on video playback +patch=0,EE,002d8b94,word,00000000 \ No newline at end of file diff --git a/bin/patches/D48A92E1.pnach b/bin/patches/D48A92E1.pnach new file mode 100644 index 0000000000..18b0cf3d89 --- /dev/null +++ b/bin/patches/D48A92E1.pnach @@ -0,0 +1,10 @@ +gametitle=Lotus Challenge [SLES 50230] (E) +comment=patches by nachbrenner +//skip Videos +//patch=0,EE,001cc5dc,word,00000000 +//recheck_wheel_connection__10cPauseMenuFv +patch=0,EE,001fef50,word,03e00008 +patch=0,EE,001fef54,word,00000000 +//getClosestPoint__FP11sGameSplineP8sVector4PiPScPi +patch=0,EE,00198d40,word,03e00008 +patch=0,EE,00198d44,word,00000000 \ No newline at end of file diff --git a/bin/patches/D4FB6049.pnach b/bin/patches/D4FB6049.pnach new file mode 100644 index 0000000000..ecbe60f9d3 --- /dev/null +++ b/bin/patches/D4FB6049.pnach @@ -0,0 +1,3 @@ +gametitle=Pryzm - Chapter One: The Dark Unicorn PAL +//Skip Videos +patch=0,EE,0036aa60,word,24020001 \ No newline at end of file diff --git a/bin/patches/D79F697A.pnach b/bin/patches/D79F697A.pnach new file mode 100644 index 0000000000..8a6c607417 --- /dev/null +++ b/bin/patches/D79F697A.pnach @@ -0,0 +1,4 @@ +gametitle=Capcom VS. SNK 2 [PBPX 95201] (J) [D79F697A] +comment= +//Fix for PCSX2 VM Build Speed Issue +fastmemory \ No newline at end of file diff --git a/bin/patches/DA0535FD.pnach b/bin/patches/DA0535FD.pnach new file mode 100644 index 0000000000..8e8053c59c --- /dev/null +++ b/bin/patches/DA0535FD.pnach @@ -0,0 +1,4 @@ +gametitle=Kingdom Hearts II [SLUS 21005] (U) +// no depth resolves +zerogs=00008000 +fastmemory \ No newline at end of file diff --git a/bin/patches/DAC14B26.pnach b/bin/patches/DAC14B26.pnach new file mode 100644 index 0000000000..7b3786a230 --- /dev/null +++ b/bin/patches/DAC14B26.pnach @@ -0,0 +1,7 @@ +gametitle=SWAT - Global Strike Team Germany/Australia [SLES 52097] (E) +comment=patches by nachbrenner +//multi-ELF fix / skip videos +patch=0,EE,004721B4,word,00100100 +patch=0,EE,004738C4,word,00100100 +//Enable Gore +patch=0,EE,00274AAC,word,24040001 \ No newline at end of file diff --git a/bin/patches/DB719F5C.pnach b/bin/patches/DB719F5C.pnach new file mode 100644 index 0000000000..3f1c3ff476 --- /dev/null +++ b/bin/patches/DB719F5C.pnach @@ -0,0 +1,7 @@ +gametitle=Gumball 3000 [SLES 50984] (E) +comment=patches by Nachbrenner +//skip FMV +// patch=0,EE,00209c00,word,03e00008 +// patch=0,EE,00209c04,word,00000000 +//go ingame w/o 3D +//patch=0,EE,00167da0,word,00000000 \ No newline at end of file diff --git a/bin/patches/DC85FC8F.pnach b/bin/patches/DC85FC8F.pnach new file mode 100644 index 0000000000..c615044aab --- /dev/null +++ b/bin/patches/DC85FC8F.pnach @@ -0,0 +1,4 @@ +gametitle=Worms 4 - Mayhem [SLES 53096] (E) [DC85FC8F] +comment=Patch by CKemu +//Skip SceMpegIsEnd +patch=0,EE,00520d40,word,24020001 \ No newline at end of file diff --git a/bin/patches/DCC4EEEA.pnach b/bin/patches/DCC4EEEA.pnach new file mode 100644 index 0000000000..793e3feccb --- /dev/null +++ b/bin/patches/DCC4EEEA.pnach @@ -0,0 +1,10 @@ +gametitle=Primal [SCES 51135] (E) +comment=patches by Nachbrenner +//Skip Videos +patch=0,EE,003ec7d4,word,00000000 +patch=0,EE,003ec4e0,word,00000000 +//Skip custom IPU routine +patch=0,EE,003c44ac,word,00000000 +patch=0,EE,003c4568,word,00000000 +//fix VU wm 25.06.06 +patch=0,EE,00391efc,word,00000000 \ No newline at end of file diff --git a/bin/patches/DDA2FA6A.pnach b/bin/patches/DDA2FA6A.pnach new file mode 100644 index 0000000000..66b2077f2b --- /dev/null +++ b/bin/patches/DDA2FA6A.pnach @@ -0,0 +1,4 @@ +gametitle=Rayman M [SLES 50457] (E) [DDA2FA6A] +comment=Patch by CKemu +//Kill '002FA6A0 1060FFFA: beq v1, zero, 0x002FA68C' (IPU) +patch=0,EE,002FA6A0,word,00000000 \ No newline at end of file diff --git a/bin/patches/DEFA4763.pnach b/bin/patches/DEFA4763.pnach new file mode 100644 index 0000000000..91645d6612 --- /dev/null +++ b/bin/patches/DEFA4763.pnach @@ -0,0 +1,5 @@ + +gametitle=Shadow Hearts [SLES 50677] (E) +comment= +//ZeroGS patches +zerogs=00008812 \ No newline at end of file diff --git a/bin/patches/E0127F2D.pnach b/bin/patches/E0127F2D.pnach new file mode 100644 index 0000000000..a0549a59b7 --- /dev/null +++ b/bin/patches/E0127F2D.pnach @@ -0,0 +1,5 @@ +gametitle=Flatout +comment=Skip Videos sceMpegIsEnd +//patch by refraction +//Skip Videos +patch=0,EE,002b2490,word,24020001 \ No newline at end of file diff --git a/bin/patches/E0426FC6.pnach b/bin/patches/E0426FC6.pnach new file mode 100644 index 0000000000..c82fdc79f0 --- /dev/null +++ b/bin/patches/E0426FC6.pnach @@ -0,0 +1,8 @@ +gametitle=Okage Shadow King [SCUS 97129](U) +comment= patches by Nachbrenner +// kill menu 3D (obsolete) +//patch=0,EE,00100fb8,word,24030000 +//LP=99 +patch=1,EE,005c1be0,word,00000063 +//Lots of Money +patch=1,EE,005c1b6c,word,07F819A0 \ No newline at end of file diff --git a/bin/patches/E0DADD1A.pnach b/bin/patches/E0DADD1A.pnach new file mode 100644 index 0000000000..c1ad2d82bb --- /dev/null +++ b/bin/patches/E0DADD1A.pnach @@ -0,0 +1,5 @@ + +gametitle=ESPN NBA 2night +comment=Skips Video by Rudy_x (sceMpegIsEnd) +//Skip Videos +patch=0,EE,00129c60,word,24020001 diff --git a/bin/patches/E138094A.pnach b/bin/patches/E138094A.pnach new file mode 100644 index 0000000000..383f714d96 --- /dev/null +++ b/bin/patches/E138094A.pnach @@ -0,0 +1,7 @@ +gametitle=F1 Championship Season 2000 [SLUS 20103] (U) +comment=patches by nachbrenner +//Skip Intro Videos +patch=0,EE,00221a14,word,00000000 +//Skip All Videos +patch=0,EE,00241bc0,word,03E00008 +patch=0,EE,00241bc4,word,00000000 \ No newline at end of file diff --git a/bin/patches/E1C5F607.pnach b/bin/patches/E1C5F607.pnach new file mode 100644 index 0000000000..1d50d09442 --- /dev/null +++ b/bin/patches/E1C5F607.pnach @@ -0,0 +1,6 @@ +gametitle= Pirates of the Caribbean +comment=by Refraction +//"Data/Movies/%s" +patch=0,EE,0031ed84,word,00000000 +//"Data/Movies/%s/%s" +patch=0,EE,0031edac,word,00000000 diff --git a/bin/patches/E1FD9A2D.pnach b/bin/patches/E1FD9A2D.pnach new file mode 100644 index 0000000000..e6231813d9 --- /dev/null +++ b/bin/patches/E1FD9A2D.pnach @@ -0,0 +1,6 @@ +gametitle=FFX-2 International + Last Mission +comment=Skip videos by General Plot +//Skip Videos +patch=0,EE,003338a0,word,24020001 +//ZeroGS Patch - GAME_FFXHACK +zerogs=00000080 \ No newline at end of file diff --git a/bin/patches/E2F1DB6B.pnach b/bin/patches/E2F1DB6B.pnach new file mode 100644 index 0000000000..7c1ad233ef --- /dev/null +++ b/bin/patches/E2F1DB6B.pnach @@ -0,0 +1,6 @@ +gametitle=GT Racers [SLES 52602] (E) +comment=patches by nachbrenner +//Skip INTRO.PSS +patch=0,EE,00160a2c,word,00000000 +//fix "sound end command" +patch=0,EE,00167f14,word,00000000 \ No newline at end of file diff --git a/bin/patches/E4A275B2.pnach b/bin/patches/E4A275B2.pnach new file mode 100644 index 0000000000..4c8f12f583 --- /dev/null +++ b/bin/patches/E4A275B2.pnach @@ -0,0 +1,4 @@ +gametitle=Glass rose (J) +comment= patch by parotaku +//Skip Videos (sceMpegIsEnd) +patch=0,EE,0010c1c0,word,24020001 diff --git a/bin/patches/E677B8F1.pnach b/bin/patches/E677B8F1.pnach new file mode 100644 index 0000000000..7a4406b314 --- /dev/null +++ b/bin/patches/E677B8F1.pnach @@ -0,0 +1,7 @@ +gametitle=Pirates - The Legend Of Black Cat [SLES 50680] (E) +comment= patches by Nachbrenner +//skip MPC videos +//patch=0,EE,00101504,word,24020000 // intro videos +//patch=0,EE,00100aa4,word,10000013 // amulett video +//fix IPU Wait Time Out +//patch=0,EE,001a7854,word,00000000 \ No newline at end of file diff --git a/bin/patches/E7A35274.pnach b/bin/patches/E7A35274.pnach new file mode 100644 index 0000000000..ef59911b2a --- /dev/null +++ b/bin/patches/E7A35274.pnach @@ -0,0 +1,4 @@ +gametitle=WE7 +comment= patch by Prafull +//skip movie "WE7_OP" +patch=0,EE,012ba4a0,word,00000000 \ No newline at end of file diff --git a/bin/patches/E906EA37.pnach b/bin/patches/E906EA37.pnach new file mode 100644 index 0000000000..097dfd2cfb --- /dev/null +++ b/bin/patches/E906EA37.pnach @@ -0,0 +1,7 @@ +gametitle=Gran Turismo 4 First Preview [PCPX 96649] (J) +comment=patches by mdr61 +//Timelimit counter stop +patch=0,EE,002EF740,word,00000000 +//Pause counter stop +patch=0,EE,002C7394,word,00000000 +patch=0,EE,002F1F40,word,00000000 \ No newline at end of file diff --git a/bin/patches/EAD76247.pnach b/bin/patches/EAD76247.pnach new file mode 100644 index 0000000000..28d8035b42 --- /dev/null +++ b/bin/patches/EAD76247.pnach @@ -0,0 +1,19 @@ +gametitle= Transformers [SLES 52388] (E) +comment=patches by Nachbrenner +//skip loops: DMA1 vs. VIF1_FBRST vs. mfc0 v0, Count +patch=0,EE,0025d660,word,00000000 +patch=0,EE,0025d7bc,word,00000000 +patch=0,EE,0025d75c,word,00000000 +//skip loop at "warp gate animation" (cop2 BC2T called) +patch=0,EE,0026eee8,word,00000000 +// +//skip FMV (for blockdump) +patch=0,EE,00134070,word,24020001 +patch=0,EE,0034d630,word,00000000 +//skip sound streaming partially (for blockdump) +patch=0,EE,002e75c0,word,03e00008 +patch=0,EE,002e75c4,word,00000000 +patch=0,EE,00281780,word,03e00008 +patch=0,EE,00281784,word,00000000 +patch=0,EE,001f05f8,word,00000000 +patch=1,EE,008F9AC0,word,00000000 \ No newline at end of file diff --git a/bin/patches/EADE437E.pnach b/bin/patches/EADE437E.pnach new file mode 100644 index 0000000000..8f339ed4ec --- /dev/null +++ b/bin/patches/EADE437E.pnach @@ -0,0 +1,4 @@ +//fix inf. $GP loop +//patch=0,EE,00165068,word,24020000 +//fix GS_IMR crash = no 3D gfx +//patch=0,EE,00174360,word,3403ff00 \ No newline at end of file diff --git a/bin/patches/EDD7E0FF.pnach b/bin/patches/EDD7E0FF.pnach new file mode 100644 index 0000000000..a30078988f --- /dev/null +++ b/bin/patches/EDD7E0FF.pnach @@ -0,0 +1,4 @@ +gametitle=F1 Racing Championship DE/FR [SLES 50046] (E) +comment= patches by Nachbrenner +//Skip Videos +patch=0,EE,0024ddc0,word,24020001 \ No newline at end of file diff --git a/bin/patches/EE838B5C.pnach b/bin/patches/EE838B5C.pnach new file mode 100644 index 0000000000..c16a57f4c0 --- /dev/null +++ b/bin/patches/EE838B5C.pnach @@ -0,0 +1,7 @@ +gametitle= Boku to mao (aka Okage Shadow King) [SCPS 11008] (J) +comment= patches by Nachbrenner +// ee838b5c.pnach +//Skip OP.PSS +patch=0,EE,001d6258,word,24020001 +// Money=9999 +patch=0,EE,00124bf4,word,2403270f \ No newline at end of file diff --git a/bin/patches/EEC3B310.pnach b/bin/patches/EEC3B310.pnach new file mode 100644 index 0000000000..7248440e28 --- /dev/null +++ b/bin/patches/EEC3B310.pnach @@ -0,0 +1,9 @@ +gametitle=Shin Sangoku Musou 2 [SLPM 65053] (J) +comment=patches by nachbrenner +//Skip Videos +patch=0,EE,0019d360,word,24020001 +//Skip Intro +patch=0,EE,00160ac0,word,03E00008 +patch=0,EE,00160ac4,word,00000000 +//GS_CSR fix +patch=0,EE,0017647c,word,34840001 diff --git a/bin/patches/EEE2F6A3.pnach b/bin/patches/EEE2F6A3.pnach new file mode 100644 index 0000000000..d7b600b0e3 --- /dev/null +++ b/bin/patches/EEE2F6A3.pnach @@ -0,0 +1,4 @@ +gametitle=Neo Contra [SLES 52510] (E) [EEE2F6A3] +comment=Skips Video (sceMpegIsEnd) +//Skip Videos +patch=0,EE,003b8050,word,24020001 \ No newline at end of file diff --git a/bin/patches/EF5B6AAD.pnach b/bin/patches/EF5B6AAD.pnach new file mode 100644 index 0000000000..90d038e1fc --- /dev/null +++ b/bin/patches/EF5B6AAD.pnach @@ -0,0 +1,4 @@ +gametitle= Graffiti Kingdom [SLUS 21136] (U) +comment= Video Skip by Refraction +//Skip Videos (sceMpegIsEnd) +patch=0,EE,00108590,word,24020001 \ No newline at end of file diff --git a/bin/patches/EF9E43EF.pnach b/bin/patches/EF9E43EF.pnach new file mode 100644 index 0000000000..d540d2e8ce --- /dev/null +++ b/bin/patches/EF9E43EF.pnach @@ -0,0 +1,5 @@ +gametitle= Ys: Ark Of Napishtim [SLUS 20980] (U) +comment=Skips Videos +//patches by Refraction +//Skip Videos +patch=0,EE,00224620,word,00000000 \ No newline at end of file diff --git a/bin/patches/F1370E83.pnach b/bin/patches/F1370E83.pnach new file mode 100644 index 0000000000..9c02d65fd6 --- /dev/null +++ b/bin/patches/F1370E83.pnach @@ -0,0 +1,4 @@ +gametitle=Enter the matrix +comment=Skips Video (sceMpegIsEnd)/bored +//Skip Videos +patch=0,EE,003d07c0,word,24020001 \ No newline at end of file diff --git a/bin/patches/F22CDDAF.pnach b/bin/patches/F22CDDAF.pnach new file mode 100644 index 0000000000..a989ec5514 --- /dev/null +++ b/bin/patches/F22CDDAF.pnach @@ -0,0 +1,4 @@ +gametitle= Gradius 3 & 4 [SLUS 20040] (U) +comment= +//ZeroGS Patch - GAME_INTERLACE2X +zerogs=00000004 \ No newline at end of file diff --git a/bin/patches/F266B00B.pnach b/bin/patches/F266B00B.pnach new file mode 100644 index 0000000000..386f8c2cdb --- /dev/null +++ b/bin/patches/F266B00B.pnach @@ -0,0 +1,4 @@ +gametitle=Kingdom Hearts II Final Mix [SLPM 66675] (J) +// no depth resolves +zerogs=00008000 +fastmemory \ No newline at end of file diff --git a/bin/patches/F4992CC1.pnach b/bin/patches/F4992CC1.pnach new file mode 100644 index 0000000000..dc79ad2d3f --- /dev/null +++ b/bin/patches/F4992CC1.pnach @@ -0,0 +1,4 @@ +gametitle=switch (J) +comment= patch by parotaku +//Skip Videos (sceMpegIsEnd) +patch=0,EE,00108ce8,word,24020001 \ No newline at end of file diff --git a/bin/patches/F52FB2BE.pnach b/bin/patches/F52FB2BE.pnach new file mode 100644 index 0000000000..d260513ef5 --- /dev/null +++ b/bin/patches/F52FB2BE.pnach @@ -0,0 +1,5 @@ +gametitle=Kingdom Hearts [SCES 50967] (E) +comment= +//ZeroGS Patch - GAME_QUICKRESOLVE1 +zerogs=00000400 +fastmemory \ No newline at end of file diff --git a/bin/patches/F56C7948.pnach b/bin/patches/F56C7948.pnach new file mode 100644 index 0000000000..830b3ecea5 --- /dev/null +++ b/bin/patches/F56C7948.pnach @@ -0,0 +1,4 @@ +gametitle=heavy metal thunder (J) +comment= patch by parotaku +//Skip Videos (sceMpegIsEnd) +patch=0,EE,00108890,word,24020001 diff --git a/bin/patches/F5C7B45F.pnach b/bin/patches/F5C7B45F.pnach new file mode 100644 index 0000000000..7bff741c3e --- /dev/null +++ b/bin/patches/F5C7B45F.pnach @@ -0,0 +1,4 @@ +gametitle=Need For Speed Underground 2 +comment=Video Skip by Refraction +//Skip videos +patch=0,EE,00293538,word,00000000 \ No newline at end of file diff --git a/bin/patches/F6DC728D.pnach b/bin/patches/F6DC728D.pnach new file mode 100644 index 0000000000..ed5a878393 --- /dev/null +++ b/bin/patches/F6DC728D.pnach @@ -0,0 +1,5 @@ +gametitle= Kingdom Hearts [SCES 50968] (F) +comment= +//ZeroGS Patch - GAME_QUICKRESOLVE1 +zerogs=00000400 +fastmemory \ No newline at end of file diff --git a/bin/patches/F6F9A91D.pnach b/bin/patches/F6F9A91D.pnach new file mode 100644 index 0000000000..99ad41eea7 --- /dev/null +++ b/bin/patches/F6F9A91D.pnach @@ -0,0 +1,4 @@ +gametitle= The Thing (pal) +comment= +//Skip Videos +patch=0,EE,00131dc0,word,24020001 \ No newline at end of file diff --git a/bin/patches/F758234F.pnach b/bin/patches/F758234F.pnach new file mode 100644 index 0000000000..a791dff918 --- /dev/null +++ b/bin/patches/F758234F.pnach @@ -0,0 +1,7 @@ +//skip BGM (for blockdump) +patch=0,EE,002cc1f0,word,03e00008 +patch=0,EE,002cc1f4,word,00000000 +patch=0,EE,002cc610,word,03e00008 +patch=0,EE,002cc614,word,00000000 +patch=0,EE,002cc430,word,03e00008 +patch=0,EE,002cc434,word,00000000 \ No newline at end of file diff --git a/bin/patches/F9E575D0.pnach b/bin/patches/F9E575D0.pnach new file mode 100644 index 0000000000..54c3edce90 --- /dev/null +++ b/bin/patches/F9E575D0.pnach @@ -0,0 +1,4 @@ +gametitle=Die Hard - Vendetta [SLES 51348] (E) +comment= patches by Nachbrenner +//Skip Videos +patch=0,EE,002570FC,word,00000000 \ No newline at end of file diff --git a/bin/patches/FA7E3081.pnach b/bin/patches/FA7E3081.pnach new file mode 100644 index 0000000000..a252a14454 --- /dev/null +++ b/bin/patches/FA7E3081.pnach @@ -0,0 +1,7 @@ +gametitle=kata +comment= +//Skip Intro Videos +patch=0,EE,0018e9e8,word,24020001 +//0018e4c8 +//0018e11c +//0018e020 \ No newline at end of file diff --git a/bin/patches/FAC64195.pnach b/bin/patches/FAC64195.pnach new file mode 100644 index 0000000000..19cd26fbc5 --- /dev/null +++ b/bin/patches/FAC64195.pnach @@ -0,0 +1,3 @@ +gametitle=Dead To Rights [SLES 51581] (E) +//Skip Movies +// patch=0,EE,002201a0,word,24020001 \ No newline at end of file diff --git a/bin/patches/FB0E6D72.pnach b/bin/patches/FB0E6D72.pnach new file mode 100644 index 0000000000..600a838630 --- /dev/null +++ b/bin/patches/FB0E6D72.pnach @@ -0,0 +1,6 @@ +gametitle=God Of War [SCES 53133] (E) +comment=patches by Nachbrenner +//D_STAT fix for Kosmos 0.95.7+ +patch=0,EE,0026dce8,word,00000000 +//crash fix ingame (breaks hero's legs) +patch=0,EE,00111604,word,24060000 \ No newline at end of file diff --git a/bin/patches/FC618D82.pnach b/bin/patches/FC618D82.pnach new file mode 100644 index 0000000000..feec7d2025 --- /dev/null +++ b/bin/patches/FC618D82.pnach @@ -0,0 +1,8 @@ +gametitle=Energy Airforce [SLPM 65177] (J) +comment=patches by Nachbrenner +//Skip Videos +//patch=0,EE,00109f48,word,24020001 +//Lots of fuel +patch=0,EE,002ef328,word,47110000 +//Infinite fuel +patch=0,EE,00195964,word,00000000 \ No newline at end of file diff --git a/bin/patches/FEA030CB.pnach b/bin/patches/FEA030CB.pnach new file mode 100644 index 0000000000..70dc28e417 --- /dev/null +++ b/bin/patches/FEA030CB.pnach @@ -0,0 +1,23 @@ +gametitle=Le Mans 24 Hours (24 Heures Du Mans) [SLES 50131] (E) +comment=patches by Nachbrenner +//Skip Video Mode Menu +patch=0,EE,00248238,word,24020000 +//Skip Videos +patch=0,EE,001152c8,word,24020001 +//g_showOnScreenRenderStats (debug mode) +//patch=0,EE,0024875c,word,24020001 +//RenderFrameRateIndicator__Fv +//patch=0,EE,0033d35c,word,00000000 +//g_ps2RenderTestPattern +//patch=1,EE,004123d4,word,00000001 +//Boot Language Modifier (use only one of these) +//English +patch=0,EE,001fe060,word,24020001 +//Francais +//patch=0,EE,001fe060,word,24020002 +//Espanol +//patch=0,EE,001fe060,word,24020003 +//Deutsch +//patch=0,EE,001fe060,word,24020004 +//Italiano +//patch=0,EE,001fe060,word,24020005 \ No newline at end of file diff --git a/bin/patches/FF920E90.pnach b/bin/patches/FF920E90.pnach new file mode 100644 index 0000000000..ac5e9e7bef --- /dev/null +++ b/bin/patches/FF920E90.pnach @@ -0,0 +1,8 @@ +gametitle=Pro Rally 2002 [SLES 50637] (E) +comment= patches by nachbrenner +//Skip Videos (sceMpegIsEnd) +//patch=0,EE,0010cbd0,word,24020001 +//Skip sceIpuSync +//patch=1,EE,0010e174,word,00000000 +//Skip "HX sound" IOP modules (obsolete) +// patch=1,EE,002029e0,word,00000000 \ No newline at end of file diff --git a/bin/patches/default.xml b/bin/patches/default.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bin/snaps/1.jpg b/bin/snaps/1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..df37c0048db688475b71b7e76ed4ce5e4d734116 GIT binary patch literal 71955 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 TdEW?+!E^x6N8mrh9QVNOou@d@qku_Ptt*YfK0J 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�zKhz||d(~5eoV*sez>z-&4 z8!7>SfC78e1#n0lk6O0TFF@_bKt^h`gzm{dUMWcSGTHn3k$uyG25DmsSP_ioox7iu zek4?ok>&yqKT3{=r(JSN#fVY<)SH0=aOx|i(cyKz1Qh^2de$^>yGSG(jtJ}0x(g^} z`#?Q@eDtnb??Ne5ce&l^E)v~?1k0b!v#;mO(y$}YVv<>&Ac^yQsn@49lY4McK7P2* zTJbq9?tNZd)TOCf`sAF7r5xL(2Q@X(HO2ww zih?~uVd@4e9`5k@cr0>ifVjhhxZ<^^hrZ_?BNHB9boD<9>IpXE=63Y0$!>YuyXjtQ z;eAX)a+&COHLc=}eCgU<#lnN;Ds7BU!0zwwi94q1uJq6is;};GZ$ZPt^6U zBgNMGVhd{sEh0;XW4cmN3pAnx`6kxBmbo)bO!h(k;#sv5TU3*HhDPP23kO zlQQgNl|v(U>Ds!M_^UBCgHY1qc-BzP=_yb^`i?7^z0)ieAqL7e-6E-A&Fl52$D`>- zN0Mm~l!PsXbI-RquT9sXD@nziOB-JP^`EKUYF`&LLum2aEO9)E!?PFL>)RE^S@?#^ z_(}FS{KU=+ecWcFyYL>5J&ZRH8?`cRI5LcL_*Y5b4+QD)>C?zsUoZreg359STyaCp z^6Wf0+IBi&m|~-)G?A02Np*UbIyy{5S8a(H1CbaRBk{&7aKIwl98t1Fg9-q^0=4{Q zquA&dnog%;tk&{K5zCz5IN)6*w#+1q(gZyxq!W$;+`dpeeY%+#dfDpwV(Y|rabmNsZWP)SY|0z{W5DP*HCv( z#|;V2ocW^lJmyP%R>tA)ZY?3WSr8gFl^i!!y&~vFy>$F*LN6rz?GUHu+1W&3eb~qd<0_ zsUxLr={g>W@Ww9e}Xj* zj^}CjHRZ~)Z${1}iV^r~c*bG5$n?%?#9C$KI4`lswOCs#xOs|jzQg@8)@{b+Iod^S7$SL%+igfYZD2Agc@hh?I}gk|S1ldS&B((Jm^{_nd$Ruk zGLl?&=xUuSYKEC5?WC~3%BPP?v0~q8hsc{M>&lvXT|B9i^O4jZqLma0L#V;`tAlHD zCc2dGtVkbp=CLo}a0>0y4!u;4^>?!~O!I#b$t9hkl@(c; z)e|R;%)v%~5-Zn!8~7T>!w|LH@(bNwbsl}wFaA2C_*8zi<~|~`Qua2|!p>&YEz<-3 zx&!?y-k>}R`W&*}&zq>xaX22;lj83a=)M)wn^M-VBe#TbqpL{Wl}A!*8{*H5*T9|! zy72+EYp0EtIO7d~S&jiG(!5hov(&szs4s~;NqRL4rclxD{{T^Hr;sC4-}SFwM6S-P zHW}7!w`8|s_}g36{vus1)~+Pd{5uKE8h-pdr?bj?clm2P>ycSRVAF&UMHs-^265~= zR;Hz)!F@TB>hkHJQV57JRpZwJvFEVTp_qM+(HC#=o=N;o7Qe(@Y)Vn~RCJHx&UaPS zJWsE)TG_+)dqr#+rTI&GoOLyqsQ6ezY@%sq)U_L#RvTzX2wjc`$~z9V-1M%K;68|a zN8{^{5YBATZL-Pxxc;aA0Iiyq?gyXzMAkpF^`it$n|p7OeF^Ssfbmy{biWZ; z8*N4a#B!1^42RSj*(WDCCp_fSU3Vc|k&%umCf33}de(HESHicA<_5U1-a<~R#ye-w z)w?UWZsQFb4EGi8cNSKY**%=r@yP>>GBV^Jt$8Pn{vTfG^2w@rY&5pH-1gb|ll|XO zQ&x;?E9}pIF3u%N(5tHaOgpZc+^iIFw<;ai4Hf$T^kd*r5SF(@@!RTw|&j{*mW3O2>gzZVtkb84qNJS#b zfz`4I?NL&jrpl*LO2qZtmJ&kV5;)^XeN;g`BxKtl5 zIWBtuJDTV&t>L=1dwJtoB9LumVUR_6Mc$G9o#WVSbx9t3PbYqvzwQPHkVkeEN=j<- z>G+;zHoUMjrBVv(@-?iI{{U3f4!fzUiAwFagQ&Md+x_F*)|ht+a-3(UN_a*ig#h;* zsrV&6b=n79wRu%4I+EsRz}Brg({r1}+aV{=WKaYp-CL7`?zpD0Io*OY>BU%3dbFT@MrpDZ92MLs0Uc-l79e({ zA+v&5`ux;Lz%B<-*ysFchBqHJXlm%`!qxV55W*=7Mot)oBjpFR6sbCr^j#oWvI=2clHem z-0Bw@!#F>>i#^%DrFUK#)9pMVrAc+DOp;$*Ig;W~Idj=p*jFAlq;Syw``LB%HH4hy zeuDUd>Pa;jj2N16#%^ueooo*PMY$CHuC9WXhnG1-|V9#b8_ z^gTs8#TU(MXe}i{BF7jl>I*Of^v!#4cc~bo%(6+gcA9+aCWCGYWGr|DdWz}(8%DvM zP8mc15nLC9E|zN-iYYVYzui8En(urHlD4GogJfu!li$!+hndHgoZ`xixtcmp6KHn2 z=A9m!IB4y|E0Tu{3Y?5q9--iCE#_S|>gvK?MyNOz}RQ9_g(UQGq%0Z9Z8)6+hCGOV`!pFvT>B zBA=Qm0astZ*L9|9_8NplLpa_2#^C-{nXhOXZm_@UaAH;alOfw8eF(2oj&ASCiHFU9 zk%aJ(wcyJ3y3NJ`Ryh@~Y2pTNmf|qOzD01FhKb@G7cyJ7_Pwh10OUX$zs4s~^yt+N zm#;;7S~Qnzsl&qHtFP2osR&Z99%^kh*xwg_5c#x!eB$WkU%YB>*1OO{GhK&_C2>u`IP!m=E3@ z{cBp@_ECoOH&;BPIGU!J1h%&*eq~`u7Da{K)aRhhZ26{m@-e&8qqza15Rfy9j(2-9 z($wWHTm#4h)~MWEMhWfot!+XT49M$-^{i{leYxaUT{|5tUl`|e=6!wL|nTovbZJ!2<|G}Qi*_)J{)xwl?>MkFiUOx>gBcEh~Vu* z^%$YWitN&gW>6dquW(OFzYOZ_w6_PKJ*tBUSwzY>Z@dBGr;0UX%Jm&87Ne29V>_q^ zZ?P3}Q5><5NB;m?v9#&S4h~4*)`~d#X0@`h&fS^I_^QLo)_gxIvZ}VP21)iL{{Tw# z^anM>_?t~#S4W=FP9$aB6aN5mLCUw_Yg6IgzIE*Z9^eGOwqtL39(hSugZcIRYuU?W zy`L{*Xgi`$h@K9y@y~~Uu{9ayir>vyPnLR-oCEF7e+uUOcj0I>Yb!}K)Y)YcEIwH1 znCO2R^tig#UyWz9i^F$f?*&;ftK7G2SF;zPcXqMjml64}vS6QJahk?>boQJ;w_a4l zeaEfZ%hz<+uOss|0_)Eme-Cf?AUjsBdj}ZJolTo{s?ljL2aTfmo;<<79?H|s*j`yp2D*#uS z{8!X`?M59v8!H0tfA^3709w5ZRlb8xu$J=Sw#ORe`}zv-KNjoJrNzdhbm2^_TLkp< z6$l4N=!#j`0qlVmx4r_?M@oMTOMYCCMWKM{RaDB5?(jOFG+!JeJE#L`)W#H~U zm0Y8-+Rfq!PyzrP}>wA%YW=PIHZ%?4Dy(hw6 z7qoERTHMN8acMG38Z<#;%zRf8c!yJ4tpiJ$E|_kV#+X*mO#9Ufd)v)E=;`{r5?ju_ zLyQ{wBHq^C-8MrCMl+l$)<=u{FQ#~w#KC(ISwKEi>OOQo*XcymDlSg_Yv; z?0Gb9u7Q+-4LY;6?IT(6b?A!uOxA4Jj&&a^eSHmDv$~4rF!8WH)#p~BDth!j&k2sD zijCm)>`z4$S2@~eYIa54ZlE8&LJmK@iuOMW>pw~9oO1EI>E_0N1j(BE0nH`*PVg`;`)kK8!z z^dpL^;mv7Te~18fx;G4m>0Lp%h+93z@7=1FKR`26tRxat>(X(S%;Eeue=f13!+9BS z&6SaJ_Z)Q{|o*S4Ld}aQ^_-yzE3H3bKq( z%xWbWw1OuiI63+XONl0IZDz-DO6-hsvi#WSDls!71z^peO2o`XX&iu59CkG+3P;a{ z_QgbJ8;IKAk7{X^SdYD*dIVV_2*^D^^x~u4<`QBZ{{X#A+D`$AC$&hS&~6IfOi(+D zWNp~VTn=(GPsqk`oYWy|%SVUu?N({S9K=kTJ!CEJn0k@TjrbHF(I)Oc_?5BbuG z6e!6#_ssyeAz;OiIUR;7j-{oG>zF6|Ot8-mLOLis9ta@y6?$nvZ-eZNAdKc#i>&Au4J ze7`8>pwy91%&njJ6)0mWC#pT%KM><7zFuqkoIL&@xriOs(IaHA^Eajs;wvI;Y1pd0 zoDyM5{E`ArYq`0NWJ0cEBhb|uG|f6;iEXWj{{XIHx@qF$d$fAe;u?*6&`jnc)pYmy zrO^N&V=AE48>_t(rX$hi-I9Cr!LG(l14utIS|B*;xmv289MZRA%W?kzZJYdSc;e~b zTbR`13N2bx{{SP7xWCgQk9p9DRZKSaIQrLF;NJsF;)o*DukF`M(%Zyd_SW|OFlbNSb(_=jD!(QQSout%IO5;f?fwtvFByx%vS zHn8|#jqdw@!7{0ijv_wfc~{~^E9l-6($?0>-Y66dBvFn}x&Htf;x%1L=Te-@JCl#P zy_8jJ%c<_}8heQgyB;V4&_19MRms7|if)*+`1n@sP>|AyFoaVg~Pw<|l3@TpLrhIbl!*~AxTBjfE zV?W+WU$LAUwuXCMU0#6EGsdT>m+ukuq>A1+oW2Q=^>#TXtlU~j1g#S*Mrt^_ zFv7WcmA&1H^&cCR2^7UL^Vrt3>RFk}H#jScH)E9N+ci{09E8T;s|xCGEgs%% zh06NXE8RUOUpgW4cg1(csn5uA^4q*mk+^*-)wbb>EJ*hmtq~AV7F5Wkn!rT9Yyh-v z_lKox?Gw?HV#S-TKH}i@#}%b-8V3xefITuRCdo`}7D5bX?w?B9wpSQjWFCwK^v^^1S4hs;`?#(*;^~%O1>8W3`=^lv^KIO1(>%xrsIJ@;+$$?}&`@?~ zwaY5O14$_XRAid+KNoBBYd37F=j_PGU>*JH$AWxEb>j~QSZKm<_wdX1Jw`-q;ZT39 z`Zxmz)YTsq=#Qc5CJgR@!Dw4NmGm|A*0zU1k5qN*{{ZamDtnNnzu4Bi(AWbVydwY3D+a?=S1IiBOlXnA@_?hAZd3f4&v*zC4@8kI2{{XFC z52l07Pjg(puW=+2Gu{#i_Q({|une$B zH0x!y)9vE6i9YC6=kYzwJ?4@A^RfJDv{$-q#9<7dipjLoXNEI_Xw3fr@7%EG@Dy$Z z`%6h~TM96}$*rqPIBk?38N0ErVB2akuo1kZ4ly33un&9?E27i9KVsHqO-kp)GO=H| zX>wm5)YT>CI}=H$SVwX8X)kTkY;M@n0NC}#65m^*mh)AIv8jAH@H4}nC%d0k)lRb= zxxzq`%2rSZbDn)`rLy=3;5g+m$9=0fU%wE(CnL~-Lr`5p$Czq*knup5!`e-<+$4>% z^6}6#R_WX+JQw~|!+77}-lgHUI*x#4yRaO|7g-}8)K|Lr&%kHE4<@1L2vdKlb_PJrzIwozpEa0J$o86sTXYn z!?rQl=$3ZM?ix5$VD#x%%xrlG0rkdnQa!r^m2iE5r1GB!Dt^1G$CR9;8XuH-*7 zZIFa|;8g1<2XM}P4L8bQGry0{kz#f^JP%rd5{BH}6Q1Ig;tP$wRWR<)1-}YklrBpj z#86NK!|vL-KixFvSpeW z%?iX03xD4<0M}(7!nBG}x~U%EQWsQihaQz7k#_D8XX{iu3@ILP8}y`0H7&S#jrdSx zexVUXIyq%0tAD^~3ko9g&)s9}O(Se$^JP7NrE9B&Y!KUj0Zn;jf4T@B^aX*I+$rVR zy8yJn<#)Z&$7BdG?FS&MhYCTj^hVW(Ra|bqWD@}PRLIs%i9KT zmkW|bb-D+FG}r@4CDqK12M2gs(S;T?Skwho7z3~s=UT6cZ*|WT-CNpEB52kp$;4+L zXq0{8Na_bnSCK*D^BKxC<;xYXpz(DfL31>0_?p|o>kRgG_9s!i!B9)HggEM0oPJf! z+Ah6gsLOfh+B`Buk$E0Y3Mz~MtZP((wysOcAibuqROOW=1_m7V~EP=)@|LU`&g=ZgyXt9-x=7)bpq-z zfE@-LuXXjVMYjQl8`rU@be&66@aOJONvY_~!`r)NNCE!o?!LyEeRt*Ci_Jb6?`tIU%0Hx0*(e!Q?@)g&}qwzAin2U7_{n?{9^lE$ZQ>tA0+^Q2 z#Bz~^^r?KE1*xH@Ur(o8t;7sL9Y0O7zX|k7) z9x=850Ht|(l@;v$s@)nxS7F`)DQcSCh~0}@_ej)#3LZcoopp-Jv8-vFoD*JqXR1f0 zd|=fyYoC=hIH8&*JvW6bgY^K{p~VZCHF3I@9#8wAt>Foc#@BDtC#MMRs(8V&mmFVcXWLPUY0@18}Hd zyJsF`XsmmRm86DY^PyAhYYtZv7Rnc2INItlS#reizUcB-(-pTgF}T2Q&Z$ouCmm~O zNP|6+Ih65_Vk)dMJ-nV-s zv*Dd$Y;pmu1eO_N03VolscnefOGTXnCJ5|moyDAyu^VvRkF73~d*wHl$BNk99m;KP z)o`4mWi@k}ofw0)NO9QL0r7q1Zv$!yhE!p*K3ib0+%mt9O7$D~jAv=cJ(yPm@pjbR z_&VaX%>Mw{_8lil@iw&EpthinrDIk0JQui;%(8{$rqVtse~=K9zd5F=OB&U54Cz&EYdYGQ^8bLsUODO zPTA7;RMyO)+OPZ-*V9AzfvNab_E`372ko{_e)(_!c***5E5^J-@lGkOp3_DT913vP zMl#HQ-5=vrWY@eir^g<#W2x%5J|MS|E-mK!IUl@JoHH>WFJq3?@5T*i%ul(GHRT>V z@a64=w;=Q}$2EB2hd>xBLGpW_}g%+0a1M`Gq^Bj9A>t1`N z+nYT;-qH{@K^mX{`V3btGZE}ym9KK?;_6|g&rR8o=zkG(ySW4xOxDwm8abC?ADwD= zgGKQF0F10Wo1{q?496<{^!Em`bzLV))YE1DpA3!t;yz*YJ!_tl!u!Hf{{U&|w-*;y zYJYucBW@_Zs%w^&YIApO;LbnAKf8aFf7zN5;egdhm&p9XPYDm&ezq`TqGz^$BI0ixv zJ+V&AC+@1^m^}Pp?Z?y_V@Oosp#p#$N)v@ZKU#3|hu+8KQ7gnwce0=NYQ#xw!!Ij0 z;XoBimK-)dqKs@AcL(cCkV;!50ZecR>A3sV6ii>t`B4?U#Yq%pdd2?$k5x#;!C(LZ zf7umh%K>vC|0XY0A2$EqM#JQP{2Vqp2L7bNadzy2*1^F@f znx%0pjCQjkiV`|0#knF$Za5(kKDAFuvwe5Qnr@QHOfWawE`2ts`BeQ+@~5=%K^sWS zaHAySpsnwO;cWC9{{R?j0FUh%u^$|)N_MFHc{PSEv%}$~QakLuU-a4P!j!<(=5G6Y6UJ0EqP;?LA6KP>~Mfuzy<3BD}m7 z9Z{yQxz#CLN-03C4FgP@Rn$__Ka?I_vG-TGu9-7ky>YLQ`{*A zW9W(1tHY`FoWe;{kGs(6q~jgOd{NhA(zHdev3zPef%0MXLHtkn4A&o_K^~uD6{IBX zjAQD1n$20YJy%qHZ&_e&U3Nhp0P>^s$FHqi6FPCXHT3u_G;nlPC9B=PKllaXa20A& zbs9QrOSMt4(XiZPHCCPOQbUE>J?kRgM`j0cA4=Ma=rlkQOQ`-X;oNAo#VN7aB%WAaDB~F zoEWZEe}#qxTobZSbEs=5^{xK^hw}M)%f@<18T~5>f!JFdFDKfxJSj5AuUa@cS8bsB z=ZeP_4tg?0#o3m;Z=x=v>BEv6u=8c0P2l1|#!5YZaw1$rHj5>{@n|rwZ zBwpj#^&eVqilf=6Xt%&*!FjW%X#r2>MPhiy6QKBp+r-xS7WWLc+JZ0f0&tIyP)s-~FznCyj9qV=p7JdN> zApL83#iJiGp-FA0=)>vWuPZF8xDpLT=azFE+#be;cY*$83_WUH<+;wu#EKUOIjZwE zLCC96&ut*)J*U=^Xku}MYRdL{l44w*yjFCwFwQuwo4Y9hBplWq58_RVR^8Kw!PiuD^PQ`E;j#T672>mI4XuUBmv?w(dP+38R`0RNf)~zM9 zQTY2#c4s#Cp_SAz3+5-xj7Q*WcjBFxz3^qyk&=a9`hQyU@pbugdRUE|zu}JhTF|lb z{{VypM(SwVH6fEck3(G>*;+XHt)f3w70+m~HLkcLVT|rt>TB)s58k>tPRrmgiY@#l z;)ygH+2gm;b#~nWcEZDO2i^1mhw3ZoHDz@Sq!30y9mRZWa|AaL80U#3L)BPhS7+hx z7kHQ9_kstBQt)b81UDkt^xJ}W#ttxJe%*Q=D@fw$u2|G|ZP|JD8jE`$OS-EmRsg9O zAoMlmUOe#3nq9+RS*C2{Za^RE&-hnG;4g`u7x98e9i_`zU%CeS71;9SAoo%HL)>P& z8B#f8G02MO!v$Dm8tZAIPXHeMXyDhWc$2|8WyP%b7EAV7!WK}cmazf#A z^b`QTC7m?i4P82rHbmI>IH
  • Ka~$;LSf&)+E_$Zx5PK479JDswnPQ@rvhuChCQJ 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 literal 0 HcmV?d00001 diff --git a/bin/snaps/2.jpg b/bin/snaps/2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ae31bf78286e5806dcc124bb33d7bdb38fa3c5f8 GIT binary patch literal 68304 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%0AN` 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 E*{hophyVZp literal 0 HcmV?d00001 diff --git a/bin/snaps/3.jpg b/bin/snaps/3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bf61a765f073cf9a6f927e138968178395181f45 GIT binary patch literal 51374 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 literal 0 HcmV?d00001 diff --git a/bin/snaps/4.jpg b/bin/snaps/4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2830e0559d0ec6d680f1bf8f1892e1f7f313107c GIT binary patch literal 92740 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 literal 0 HcmV?d00001 diff --git a/build.sh b/build.sh new file mode 100644 index 0000000000..b90c6a607f --- /dev/null +++ b/build.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +# Usage: sh build.sh [option] +# option can be all (rebuilds everything), clean, or nothing (incremental build) +# Modify the individual build.sh for specific program options like debug symbols +# Uncomment if building by itself, rather then with all the plugins + +#Normal +export PCSX2OPTIONS="--enable-sse3 --enable-sse4 --prefix `pwd`" + + +#Optimized, but a devbuild +#export PCSX2OPTIONS="--enable-sse3 --enable-sse4 --enable-devbuild --prefix `pwd`" + +#Debug / Devbuild version +#export PCSX2OPTIONS="--enable-debug --enable-devbuild --enable-sse3 --prefix `pwd`" + +# Make sure we have plugins, and bring the normal plugins in. +sh fetch.sh + +option=$@ +export PCSX2PLUGINS="`pwd`/bin/plugins" +curdir=`pwd` + +cd ${curdir}/plugins +sh build.sh $option + +if [ $? -ne 0 ] +then +echo Error with building plugins +exit 1 +fi + +cd ${curdir}/pcsx2 +sh build.sh $option + +if [ $? -ne 0 ] +then +echo Error with building pcsx2 +exit 1 +fi diff --git a/common/includes/PS2Edefs.h b/common/includes/PS2Edefs.h new file mode 100644 index 0000000000..1f65fc02b9 --- /dev/null +++ b/common/includes/PS2Edefs.h @@ -0,0 +1,885 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +#ifndef __PS2EDEFS_H__ +#define __PS2EDEFS_H__ + +/* + * PS2E Definitions v0.6.2 (beta) + * + * Author: linuzappz@hotmail.com + * shadowpcsx2@yahoo.gr + * florinsasu@hotmail.com + */ + +/* + Notes: + * Since this is still beta things may change. + + * OSflags: + __LINUX__ (linux OS) + _WIN32 (win32 OS) + + * common return values (for ie. GSinit): + 0 - success + -1 - error + + * reserved keys: + F1 to F10 are reserved for the emulator + + * plugins should NOT change the current + working directory. + (on win32, add flag OFN_NOCHANGEDIR for + GetOpenFileName) + +*/ + +#include "PS2Etypes.h" + + +/* common defines */ +#ifndef C_ASSERT +#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +#endif + +#if defined(GSdefs) || defined(PADdefs) || defined(SIOdefs) || \ + defined(SPU2defs) || defined(CDVDdefs) || defined(DEV9defs) || \ + defined(USBdefs) || defined(FWdefs) +#define COMMONdefs +#endif + +// PS2EgetLibType returns (may be OR'd) +#define PS2E_LT_GS 0x01 +#define PS2E_LT_PAD 0x02 // -=[ OBSOLETE ]=- +#define PS2E_LT_SPU2 0x04 +#define PS2E_LT_CDVD 0x08 +#define PS2E_LT_DEV9 0x10 +#define PS2E_LT_USB 0x20 +#define PS2E_LT_FW 0x40 +#define PS2E_LT_SIO 0x80 + +// PS2EgetLibVersion2 (high 16 bits) +#define PS2E_GS_VERSION 0x0006 +#define PS2E_PAD_VERSION 0x0002 // -=[ OBSOLETE ]=- +#define PS2E_SPU2_VERSION 0x0005 +#define PS2E_CDVD_VERSION 0x0005 +#define PS2E_DEV9_VERSION 0x0003 +#define PS2E_USB_VERSION 0x0003 +#define PS2E_FW_VERSION 0x0002 +#define PS2E_SIO_VERSION 0x0001 +#ifdef COMMONdefs + +u32 CALLBACK PS2EgetLibType(void); +u32 CALLBACK PS2EgetLibVersion2(u32 type); +char* CALLBACK PS2EgetLibName(void); + +#endif + +// key values: +/* key values must be OS dependant: + win32: the VK_XXX will be used (WinUser) + linux: the XK_XXX will be used (XFree86) +*/ + +// event values: +#define KEYPRESS 1 +#define KEYRELEASE 2 + +typedef struct _keyEvent { + u32 key; + u32 evt; +} keyEvent; + +// for 64bit compilers +typedef char __keyEvent_Size__[(sizeof(keyEvent) == 8)?1:-1]; + +// plugin types +#define SIO_TYPE_PAD 0x00000001 +#define SIO_TYPE_MTAP 0x00000004 +#define SIO_TYPE_RM 0x00000040 +#define SIO_TYPE_MC 0x00000100 + +typedef int (CALLBACK * SIOchangeSlotCB)(int slot); + +typedef struct _cdvdSubQ { + u8 ctrl:4; // control and mode bits + u8 mode:4; // control and mode bits + u8 trackNum; // current track number (1 to 99) + u8 trackIndex; // current index within track (0 to 99) + u8 trackM; // current minute location on the disc (BCD encoded) + u8 trackS; // current sector location on the disc (BCD encoded) + u8 trackF; // current frame location on the disc (BCD encoded) + u8 pad; // unused + u8 discM; // current minute offset from first track (BCD encoded) + u8 discS; // current sector offset from first track (BCD encoded) + u8 discF; // current frame offset from first track (BCD encoded) +} cdvdSubQ; + +typedef struct _cdvdTD { // NOT bcd coded + u32 lsn; + u8 type; +} cdvdTD; + +typedef struct _cdvdTN { + u8 strack; //number of the first track (usually 1) + u8 etrack; //number of the last track +} cdvdTN; + +// CDVDreadTrack mode values: +#define CDVD_MODE_2352 0 // full 2352 bytes +#define CDVD_MODE_2340 1 // skip sync (12) bytes +#define CDVD_MODE_2328 2 // skip sync+head+sub (24) bytes +#define CDVD_MODE_2048 3 // skip sync+head+sub (24) bytes +#define CDVD_MODE_2368 4 // full 2352 bytes + 16 subq + +// CDVDgetDiskType returns: +#define CDVD_TYPE_ILLEGAL 0xff // Illegal Disc +#define CDVD_TYPE_DVDV 0xfe // DVD Video +#define CDVD_TYPE_CDDA 0xfd // Audio CD +#define CDVD_TYPE_PS2DVD 0x14 // PS2 DVD +#define CDVD_TYPE_PS2CDDA 0x13 // PS2 CD (with audio) +#define CDVD_TYPE_PS2CD 0x12 // PS2 CD +#define CDVD_TYPE_PSCDDA 0x11 // PS CD (with audio) +#define CDVD_TYPE_PSCD 0x10 // PS CD +#define CDVD_TYPE_UNKNOWN 0x05 // Unknown +#define CDVD_TYPE_DETCTDVDD 0x04 // Detecting Dvd Dual Sided +#define CDVD_TYPE_DETCTDVDS 0x03 // Detecting Dvd Single Sided +#define CDVD_TYPE_DETCTCD 0x02 // Detecting Cd +#define CDVD_TYPE_DETCT 0x01 // Detecting +#define CDVD_TYPE_NODISC 0x00 // No Disc + +// CDVDgetTrayStatus returns: +#define CDVD_TRAY_CLOSE 0x00 +#define CDVD_TRAY_OPEN 0x01 + +// cdvdTD.type (track types for cds) +#define CDVD_AUDIO_TRACK 0x01 +#define CDVD_MODE1_TRACK 0x41 +#define CDVD_MODE2_TRACK 0x61 + +#define CDVD_AUDIO_MASK 0x00 +#define CDVD_DATA_MASK 0x40 +// CDROM_DATA_TRACK 0x04 //do not enable this! (from linux kernel) + +typedef void (*DEV9callback)(int cycles); +typedef int (*DEV9handler)(void); + +typedef void (*USBcallback)(int cycles); +typedef int (*USBhandler)(void); + +// freeze modes: +#define FREEZE_LOAD 0 +#define FREEZE_SAVE 1 +#define FREEZE_SIZE 2 + +typedef struct _GSdriverInfo { + char name[8]; + void *common; +} GSdriverInfo; + +#ifdef _WINDOWS_ +typedef struct _winInfo { // unsupported values must be set to zero + HWND hWnd; + HMENU hMenu; + HWND hStatusWnd; +} winInfo; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* GS plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef GSdefs + +// basic funcs + +s32 CALLBACK GSinit(); +s32 CALLBACK GSopen(void *pDsp, char *Title, int multithread); +void CALLBACK GSclose(); +void CALLBACK GSshutdown(); +void CALLBACK GSvsync(int field); +void CALLBACK GSgifTransfer1(u32 *pMem, u32 addr); +void CALLBACK GSgifTransfer2(u32 *pMem, u32 size); +void CALLBACK GSgifTransfer3(u32 *pMem, u32 size); +void CALLBACK GSgetLastTag(u64* ptag); // returns the last tag processed (64 bits) +void CALLBACK GSgifSoftReset(u32 mask); +void CALLBACK GSreadFIFO(u64 *mem); +void CALLBACK GSreadFIFO2(u64 *mem, int qwc); + +// extended funcs + +// GSkeyEvent gets called when there is a keyEvent from the PAD plugin +void CALLBACK GSkeyEvent(keyEvent *ev); +void CALLBACK GSchangeSaveState(int, const char* filename); +void CALLBACK GSmakeSnapshot(char *path); +void CALLBACK GSmakeSnapshot2(char *pathname, int* snapdone, int savejpg); +void CALLBACK GSirqCallback(void (*callback)()); +void CALLBACK GSprintf(int timeout, char *fmt, ...); +void CALLBACK GSsetBaseMem(void*); +void CALLBACK GSsetGameCRC(int crc, int gameoptions); + +// controls frame skipping in the GS, if this routine isn't present, frame skipping won't be done +void CALLBACK GSsetFrameSkip(int frameskip); + +// if start is 1, starts recording spu2 data, else stops +// returns a non zero value if successful +// for now, pData is not used +int CALLBACK GSsetupRecording(int start, void* pData); + +void CALLBACK GSreset(); +void CALLBACK GSwriteCSR(u32 value); +void CALLBACK GSgetDriverInfo(GSdriverInfo *info); +#ifdef _WIN32 +s32 CALLBACK GSsetWindowInfo(winInfo *info); +#endif +s32 CALLBACK GSfreeze(int mode, freezeData *data); +void CALLBACK GSconfigure(); +void CALLBACK GSabout(); +s32 CALLBACK GStest(); + +#endif + +/* PAD plugin API -=[ OBSOLETE ]=- */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef PADdefs + +// basic funcs + +s32 CALLBACK PADinit(u32 flags); +s32 CALLBACK PADopen(void *pDsp); +void CALLBACK PADclose(); +void CALLBACK PADshutdown(); +// PADkeyEvent is called every vsync (return NULL if no event) +keyEvent* CALLBACK PADkeyEvent(); +u8 CALLBACK PADstartPoll(int pad); +u8 CALLBACK PADpoll(u8 value); +// returns: 1 if supported pad1 +// 2 if supported pad2 +// 3 if both are supported +u32 CALLBACK PADquery(); + +// call to give a hint to the PAD plugin to query for the keyboard state. A +// good plugin will query the OS for keyboard state ONLY in this function. +// This function is necessary when multithreading because otherwise +// the PAD plugin can get into deadlocks with the thread that really owns +// the window (and input). Note that PADupdate can be called from a different +// thread than the other functions, so mutex or other multithreading primitives +// have to be added to maintain data integrity. +void CALLBACK PADupdate(int pad); + +// extended funcs + +void CALLBACK PADgsDriverInfo(GSdriverInfo *info); +void CALLBACK PADconfigure(); +void CALLBACK PADabout(); +s32 CALLBACK PADtest(); + +#endif + +/* SIO plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef SIOdefs + +// basic funcs + +s32 CALLBACK SIOinit(u32 port, u32 slot, SIOchangeSlotCB f); +s32 CALLBACK SIOopen(void *pDsp); +void CALLBACK SIOclose(); +void CALLBACK SIOshutdown(); +u8 CALLBACK SIOstartPoll(u8 value); +u8 CALLBACK SIOpoll(u8 value); +// returns: SIO_TYPE_{PAD,MTAP,RM,MC} +u32 CALLBACK SIOquery(); + +// extended funcs + +void CALLBACK SIOconfigure(); +void CALLBACK SIOabout(); +s32 CALLBACK SIOtest(); + +#endif + +/* SPU2 plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef SPU2defs + +// basic funcs + +s32 CALLBACK SPU2init(); +s32 CALLBACK SPU2open(void *pDsp); +void CALLBACK SPU2close(); +void CALLBACK SPU2shutdown(); +void CALLBACK SPU2write(u32 mem, u16 value); +u16 CALLBACK SPU2read(u32 mem); +void CALLBACK SPU2readDMA4Mem(u16 *pMem, int size); +void CALLBACK SPU2writeDMA4Mem(u16 *pMem, int size); +void CALLBACK SPU2interruptDMA4(); +void CALLBACK SPU2readDMA7Mem(u16* pMem, int size); +void CALLBACK SPU2writeDMA7Mem(u16 *pMem, int size); + +// all addresses passed by dma will be pointers to the array starting at baseaddr +// This function is necessary to successfully save and reload the spu2 state +void CALLBACK SPU2setDMABaseAddr(uptr baseaddr); + +void CALLBACK SPU2interruptDMA7(); +u32 CALLBACK SPU2ReadMemAddr(int core); +void CALLBACK SPU2WriteMemAddr(int core,u32 value); +void CALLBACK SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)()); + +// extended funcs +// if start is 1, starts recording spu2 data, else stops +// returns a non zero value if successful +// for now, pData is not used +int CALLBACK SPU2setupRecording(int start, void* pData); + +void CALLBACK SPU2setClockPtr(u32* ptr); +void CALLBACK SPU2setTimeStretcher(short int enable); + +void CALLBACK SPU2async(u32 cycles); +s32 CALLBACK SPU2freeze(int mode, freezeData *data); +void CALLBACK SPU2configure(); +void CALLBACK SPU2about(); +s32 CALLBACK SPU2test(); + +#endif + +/* CDVD plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef CDVDdefs + +// basic funcs + +s32 CALLBACK CDVDinit(); +s32 CALLBACK CDVDopen(const char* pTitleFilename); +void CALLBACK CDVDclose(); +void CALLBACK CDVDshutdown(); +s32 CALLBACK CDVDreadTrack(u32 lsn, int mode); + +// return can be NULL (for async modes) +u8* CALLBACK CDVDgetBuffer(); + +s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ* subq);//read subq from disc (only cds have subq data) +s32 CALLBACK CDVDgetTN(cdvdTN *Buffer); //disk information +s32 CALLBACK CDVDgetTD(u8 Track, cdvdTD *Buffer); //track info: min,sec,frame,type +s32 CALLBACK CDVDgetTOC(void* toc); //gets ps2 style toc from disc +s32 CALLBACK CDVDgetDiskType(); //CDVD_TYPE_xxxx +s32 CALLBACK CDVDgetTrayStatus(); //CDVD_TRAY_xxxx +s32 CALLBACK CDVDctrlTrayOpen(); //open disc tray +s32 CALLBACK CDVDctrlTrayClose(); //close disc tray + +// extended funcs + +void CALLBACK CDVDconfigure(); +void CALLBACK CDVDabout(); +s32 CALLBACK CDVDtest(); +void CALLBACK CDVDnewDiskCB(void (*callback)()); + +#endif + +/* DEV9 plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef DEV9defs + +// basic funcs + +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +s32 CALLBACK DEV9init(); +s32 CALLBACK DEV9open(void *pDsp); +void CALLBACK DEV9close(); +void CALLBACK DEV9shutdown(); +u8 CALLBACK DEV9read8(u32 addr); +u16 CALLBACK DEV9read16(u32 addr); +u32 CALLBACK DEV9read32(u32 addr); +void CALLBACK DEV9write8(u32 addr, u8 value); +void CALLBACK DEV9write16(u32 addr, u16 value); +void CALLBACK DEV9write32(u32 addr, u32 value); +void CALLBACK DEV9readDMA8Mem(u32 *pMem, int size); +void CALLBACK DEV9writeDMA8Mem(u32 *pMem, int size); +// cycles = IOP cycles before calling callback, +// if callback returns 1 the irq is triggered, else not +void CALLBACK DEV9irqCallback(DEV9callback callback); +DEV9handler CALLBACK DEV9irqHandler(void); + +// extended funcs + +s32 CALLBACK DEV9freeze(int mode, freezeData *data); +void CALLBACK DEV9configure(); +void CALLBACK DEV9about(); +s32 CALLBACK DEV9test(); + +#endif + +/* USB plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef USBdefs + +// basic funcs + +s32 CALLBACK USBinit(); +s32 CALLBACK USBopen(void *pDsp); +void CALLBACK USBclose(); +void CALLBACK USBshutdown(); +u8 CALLBACK USBread8(u32 addr); +u16 CALLBACK USBread16(u32 addr); +u32 CALLBACK USBread32(u32 addr); +void CALLBACK USBwrite8(u32 addr, u8 value); +void CALLBACK USBwrite16(u32 addr, u16 value); +void CALLBACK USBwrite32(u32 addr, u32 value); +void CALLBACK USBasync(u32 cycles); + +// cycles = IOP cycles before calling callback, +// if callback returns 1 the irq is triggered, else not +void CALLBACK USBirqCallback(USBcallback callback); +USBhandler CALLBACK USBirqHandler(void); +void CALLBACK USBsetRAM(void *mem); + +// extended funcs + +s32 CALLBACK USBfreeze(int mode, freezeData *data); +void CALLBACK USBconfigure(); +void CALLBACK USBabout(); +s32 CALLBACK USBtest(); + +#endif + +/* FW plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef FWdefs +// basic funcs + +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +s32 CALLBACK FWinit(); +s32 CALLBACK FWopen(void *pDsp); +void CALLBACK FWclose(); +void CALLBACK FWshutdown(); +u32 CALLBACK FWread32(u32 addr); +void CALLBACK FWwrite32(u32 addr, u32 value); +void CALLBACK FWirqCallback(void (*callback)()); + +// extended funcs + +s32 CALLBACK FWfreeze(int mode, freezeData *data); +void CALLBACK FWconfigure(); +void CALLBACK FWabout(); +s32 CALLBACK FWtest(); +#endif + +// might be useful for emulators +#ifdef PLUGINtypedefs + +typedef u32 (CALLBACK* _PS2EgetLibType)(void); +typedef u32 (CALLBACK* _PS2EgetLibVersion2)(u32 type); +typedef char*(CALLBACK* _PS2EgetLibName)(void); + +// GS +// NOTE: GSreadFIFOX/GSwriteCSR functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _GSinit)(); +typedef s32 (CALLBACK* _GSopen)(void *pDsp, char *Title, int multithread); +typedef void (CALLBACK* _GSclose)(); +typedef void (CALLBACK* _GSshutdown)(); +typedef void (CALLBACK* _GSvsync)(int field); +typedef void (CALLBACK* _GSgifTransfer1)(u32 *pMem, u32 addr); +typedef void (CALLBACK* _GSgifTransfer2)(u32 *pMem, u32 size); +typedef void (CALLBACK* _GSgifTransfer3)(u32 *pMem, u32 size); +typedef void (CALLBACK* _GSgetLastTag)(u64* ptag); // returns the last tag processed (64 bits) +typedef void (CALLBACK* _GSgifSoftReset)(u32 mask); +typedef void (CALLBACK* _GSreadFIFO)(u64 *pMem); +typedef void (CALLBACK* _GSreadFIFO2)(u64 *pMem, int qwc); + +typedef void (CALLBACK* _GSkeyEvent)(keyEvent* ev); +typedef void (CALLBACK* _GSchangeSaveState)(int, const char* filename); +typedef void (CALLBACK* _GSirqCallback)(void (*callback)()); +typedef void (CALLBACK* _GSprintf)(int timeout, char *fmt, ...); +typedef void (CALLBACK* _GSsetBaseMem)(void*); +typedef void (CALLBACK* _GSsetGameCRC)(int, int); +typedef void (CALLBACK* _GSsetFrameSkip)(int frameskip); +typedef int (CALLBACK* _GSsetupRecording)(int, void*); +typedef void (CALLBACK* _GSreset)(); +typedef void (CALLBACK* _GSwriteCSR)(u32 value); +typedef void (CALLBACK* _GSgetDriverInfo)(GSdriverInfo *info); +#ifdef _WINDOWS_ +typedef s32 (CALLBACK* _GSsetWindowInfo)(winInfo *info); +#endif +typedef void (CALLBACK* _GSmakeSnapshot)(const char *path); +typedef void (CALLBACK* _GSmakeSnapshot2)(const char *path, int*, int); +typedef s32 (CALLBACK* _GSfreeze)(int mode, freezeData *data); +typedef void (CALLBACK* _GSconfigure)(); +typedef s32 (CALLBACK* _GStest)(); +typedef void (CALLBACK* _GSabout)(); + +// PAD +typedef s32 (CALLBACK* _PADinit)(u32 flags); +typedef s32 (CALLBACK* _PADopen)(void *pDsp); +typedef void (CALLBACK* _PADclose)(); +typedef void (CALLBACK* _PADshutdown)(); +typedef keyEvent* (CALLBACK* _PADkeyEvent)(); +typedef u8 (CALLBACK* _PADstartPoll)(int pad); +typedef u8 (CALLBACK* _PADpoll)(u8 value); +typedef u32 (CALLBACK* _PADquery)(); +typedef void (CALLBACK* _PADupdate)(int pad); + +typedef void (CALLBACK* _PADgsDriverInfo)(GSdriverInfo *info); +typedef void (CALLBACK* _PADconfigure)(); +typedef s32 (CALLBACK* _PADtest)(); +typedef void (CALLBACK* _PADabout)(); + +// SIO +typedef s32 (CALLBACK* _SIOinit)(u32 port, u32 slot, SIOchangeSlotCB f); +typedef s32 (CALLBACK* _SIOopen)(void *pDsp); +typedef void (CALLBACK* _SIOclose)(); +typedef void (CALLBACK* _SIOshutdown)(); +typedef u8 (CALLBACK* _SIOstartPoll)(u8 value); +typedef u8 (CALLBACK* _SIOpoll)(u8 value); +typedef u32 (CALLBACK* _SIOquery)(); + +typedef void (CALLBACK* _SIOconfigure)(); +typedef s32 (CALLBACK* _SIOtest)(); +typedef void (CALLBACK* _SIOabout)(); + +// SPU2 +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _SPU2init)(); +typedef s32 (CALLBACK* _SPU2open)(void *pDsp); +typedef void (CALLBACK* _SPU2close)(); +typedef void (CALLBACK* _SPU2shutdown)(); +typedef void (CALLBACK* _SPU2write)(u32 mem, u16 value); +typedef u16 (CALLBACK* _SPU2read)(u32 mem); +typedef void (CALLBACK* _SPU2readDMA4Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2writeDMA4Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2interruptDMA4)(); +typedef void (CALLBACK* _SPU2readDMA7Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2writeDMA7Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2setDMABaseAddr)(uptr baseaddr); +typedef void (CALLBACK* _SPU2interruptDMA7)(); +typedef void (CALLBACK* _SPU2irqCallback)(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)()); +typedef int (CALLBACK* _SPU2setupRecording)(int, void*); + +typedef void (CALLBACK* _SPU2setClockPtr)(u32*ptr); +typedef void (CALLBACK* _SPU2setTimeStretcher)(short int enable); + +typedef u32 (CALLBACK* _SPU2ReadMemAddr)(int core); +typedef void (CALLBACK* _SPU2WriteMemAddr)(int core,u32 value); +typedef void (CALLBACK* _SPU2async)(u32 cycles); +typedef s32 (CALLBACK* _SPU2freeze)(int mode, freezeData *data); +typedef void (CALLBACK* _SPU2configure)(); +typedef s32 (CALLBACK* _SPU2test)(); +typedef void (CALLBACK* _SPU2about)(); + + +// CDVD +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _CDVDinit)(); +typedef s32 (CALLBACK* _CDVDopen)(const char* pTitleFilename); +typedef void (CALLBACK* _CDVDclose)(); +typedef void (CALLBACK* _CDVDshutdown)(); +typedef s32 (CALLBACK* _CDVDreadTrack)(u32 lsn, int mode); +typedef u8* (CALLBACK* _CDVDgetBuffer)(); +typedef s32 (CALLBACK* _CDVDreadSubQ)(u32 lsn, cdvdSubQ* subq); +typedef s32 (CALLBACK* _CDVDgetTN)(cdvdTN *Buffer); +typedef s32 (CALLBACK* _CDVDgetTD)(u8 Track, cdvdTD *Buffer); +typedef s32 (CALLBACK* _CDVDgetTOC)(void* toc); +typedef s32 (CALLBACK* _CDVDgetDiskType)(); +typedef s32 (CALLBACK* _CDVDgetTrayStatus)(); +typedef s32 (CALLBACK* _CDVDctrlTrayOpen)(); +typedef s32 (CALLBACK* _CDVDctrlTrayClose)(); + +typedef void (CALLBACK* _CDVDconfigure)(); +typedef s32 (CALLBACK* _CDVDtest)(); +typedef void (CALLBACK* _CDVDabout)(); +typedef void (CALLBACK* _CDVDnewDiskCB)(void (*callback)()); + +// DEV9 +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _DEV9init)(); +typedef s32 (CALLBACK* _DEV9open)(void *pDsp); +typedef void (CALLBACK* _DEV9close)(); +typedef void (CALLBACK* _DEV9shutdown)(); +typedef u8 (CALLBACK* _DEV9read8)(u32 mem); +typedef u16 (CALLBACK* _DEV9read16)(u32 mem); +typedef u32 (CALLBACK* _DEV9read32)(u32 mem); +typedef void (CALLBACK* _DEV9write8)(u32 mem, u8 value); +typedef void (CALLBACK* _DEV9write16)(u32 mem, u16 value); +typedef void (CALLBACK* _DEV9write32)(u32 mem, u32 value); +typedef void (CALLBACK* _DEV9readDMA8Mem)(u32 *pMem, int size); +typedef void (CALLBACK* _DEV9writeDMA8Mem)(u32 *pMem, int size); +typedef void (CALLBACK* _DEV9irqCallback)(DEV9callback callback); +typedef DEV9handler (CALLBACK* _DEV9irqHandler)(void); + +typedef s32 (CALLBACK* _DEV9freeze)(int mode, freezeData *data); +typedef void (CALLBACK* _DEV9configure)(); +typedef s32 (CALLBACK* _DEV9test)(); +typedef void (CALLBACK* _DEV9about)(); + +// USB +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _USBinit)(); +typedef s32 (CALLBACK* _USBopen)(void *pDsp); +typedef void (CALLBACK* _USBclose)(); +typedef void (CALLBACK* _USBshutdown)(); +typedef u8 (CALLBACK* _USBread8)(u32 mem); +typedef u16 (CALLBACK* _USBread16)(u32 mem); +typedef u32 (CALLBACK* _USBread32)(u32 mem); +typedef void (CALLBACK* _USBwrite8)(u32 mem, u8 value); +typedef void (CALLBACK* _USBwrite16)(u32 mem, u16 value); +typedef void (CALLBACK* _USBwrite32)(u32 mem, u32 value); +typedef void (CALLBACK* _USBasync)(u32 cycles); + + +typedef void (CALLBACK* _USBirqCallback)(USBcallback callback); +typedef USBhandler (CALLBACK* _USBirqHandler)(void); +typedef void (CALLBACK* _USBsetRAM)(void *mem); + +typedef s32 (CALLBACK* _USBfreeze)(int mode, freezeData *data); +typedef void (CALLBACK* _USBconfigure)(); +typedef s32 (CALLBACK* _USBtest)(); +typedef void (CALLBACK* _USBabout)(); + +//FW +typedef s32 (CALLBACK* _FWinit)(); +typedef s32 (CALLBACK* _FWopen)(void *pDsp); +typedef void (CALLBACK* _FWclose)(); +typedef void (CALLBACK* _FWshutdown)(); +typedef u32 (CALLBACK* _FWread32)(u32 mem); +typedef void (CALLBACK* _FWwrite32)(u32 mem, u32 value); +typedef void (CALLBACK* _FWirqCallback)(void (*callback)()); + +typedef s32 (CALLBACK* _FWfreeze)(int mode, freezeData *data); +typedef void (CALLBACK* _FWconfigure)(); +typedef s32 (CALLBACK* _FWtest)(); +typedef void (CALLBACK* _FWabout)(); + +#endif + +#ifdef PLUGINfuncs + +// GS +extern _GSinit GSinit; +extern _GSopen GSopen; +extern _GSclose GSclose; +extern _GSshutdown GSshutdown; +extern _GSvsync GSvsync; +extern _GSgifTransfer1 GSgifTransfer1; +extern _GSgifTransfer2 GSgifTransfer2; +extern _GSgifTransfer3 GSgifTransfer3; +extern _GSgetLastTag GSgetLastTag; +extern _GSgifSoftReset GSgifSoftReset; +extern _GSreadFIFO GSreadFIFO; +extern _GSreadFIFO2 GSreadFIFO2; + +extern _GSkeyEvent GSkeyEvent; +extern _GSchangeSaveState GSchangeSaveState; +extern _GSmakeSnapshot GSmakeSnapshot; +extern _GSmakeSnapshot2 GSmakeSnapshot2; +extern _GSirqCallback GSirqCallback; +extern _GSprintf GSprintf; +extern _GSsetBaseMem GSsetBaseMem; +extern _GSsetGameCRC GSsetGameCRC; +extern _GSsetFrameSkip GSsetFrameSkip; +extern _GSsetupRecording GSsetupRecording; +extern _GSreset GSreset; +extern _GSwriteCSR GSwriteCSR; +extern _GSgetDriverInfo GSgetDriverInfo; +#ifdef _WINDOWS_ +extern _GSsetWindowInfo GSsetWindowInfo; +#endif +extern _GSfreeze GSfreeze; +extern _GSconfigure GSconfigure; +extern _GStest GStest; +extern _GSabout GSabout; + +// PAD1 +extern _PADinit PAD1init; +extern _PADopen PAD1open; +extern _PADclose PAD1close; +extern _PADshutdown PAD1shutdown; +extern _PADkeyEvent PAD1keyEvent; +extern _PADstartPoll PAD1startPoll; +extern _PADpoll PAD1poll; +extern _PADquery PAD1query; +extern _PADupdate PAD1update; + +extern _PADgsDriverInfo PAD1gsDriverInfo; +extern _PADconfigure PAD1configure; +extern _PADtest PAD1test; +extern _PADabout PAD1about; + +// PAD2 +extern _PADinit PAD2init; +extern _PADopen PAD2open; +extern _PADclose PAD2close; +extern _PADshutdown PAD2shutdown; +extern _PADkeyEvent PAD2keyEvent; +extern _PADstartPoll PAD2startPoll; +extern _PADpoll PAD2poll; +extern _PADquery PAD2query; +extern _PADupdate PAD2update; + +extern _PADgsDriverInfo PAD2gsDriverInfo; +extern _PADconfigure PAD2configure; +extern _PADtest PAD2test; +extern _PADabout PAD2about; + +// SIO[2] +extern _SIOinit SIOinit[2][9]; +extern _SIOopen SIOopen[2][9]; +extern _SIOclose SIOclose[2][9]; +extern _SIOshutdown SIOshutdown[2][9]; +extern _SIOstartPoll SIOstartPoll[2][9]; +extern _SIOpoll SIOpoll[2][9]; +extern _SIOquery SIOquery[2][9]; + +extern _SIOconfigure SIOconfigure[2][9]; +extern _SIOtest SIOtest[2][9]; +extern _SIOabout SIOabout[2][9]; + +// SPU2 +extern _SPU2init SPU2init; +extern _SPU2open SPU2open; +extern _SPU2close SPU2close; +extern _SPU2shutdown SPU2shutdown; +extern _SPU2write SPU2write; +extern _SPU2read SPU2read; +extern _SPU2readDMA4Mem SPU2readDMA4Mem; +extern _SPU2writeDMA4Mem SPU2writeDMA4Mem; +extern _SPU2interruptDMA4 SPU2interruptDMA4; +extern _SPU2readDMA7Mem SPU2readDMA7Mem; +extern _SPU2writeDMA7Mem SPU2writeDMA7Mem; +extern _SPU2setDMABaseAddr SPU2setDMABaseAddr; +extern _SPU2interruptDMA7 SPU2interruptDMA7; +extern _SPU2ReadMemAddr SPU2ReadMemAddr; +extern _SPU2setupRecording SPU2setupRecording; +extern _SPU2WriteMemAddr SPU2WriteMemAddr; +extern _SPU2irqCallback SPU2irqCallback; + +extern _SPU2setClockPtr SPU2setClockPtr; +extern _SPU2setTimeStretcher SPU2setTimeStretcher; + +extern _SPU2async SPU2async; +extern _SPU2freeze SPU2freeze; +extern _SPU2configure SPU2configure; +extern _SPU2test SPU2test; +extern _SPU2about SPU2about; + +// CDVD +extern _CDVDinit CDVDinit; +extern _CDVDopen CDVDopen; +extern _CDVDclose CDVDclose; +extern _CDVDshutdown CDVDshutdown; +extern _CDVDreadTrack CDVDreadTrack; +extern _CDVDgetBuffer CDVDgetBuffer; +extern _CDVDreadSubQ CDVDreadSubQ; +extern _CDVDgetTN CDVDgetTN; +extern _CDVDgetTD CDVDgetTD; +extern _CDVDgetTOC CDVDgetTOC; +extern _CDVDgetDiskType CDVDgetDiskType; +extern _CDVDgetTrayStatus CDVDgetTrayStatus; +extern _CDVDctrlTrayOpen CDVDctrlTrayOpen; +extern _CDVDctrlTrayClose CDVDctrlTrayClose; + +extern _CDVDconfigure CDVDconfigure; +extern _CDVDtest CDVDtest; +extern _CDVDabout CDVDabout; +extern _CDVDnewDiskCB CDVDnewDiskCB; + +// DEV9 +extern _DEV9init DEV9init; +extern _DEV9open DEV9open; +extern _DEV9close DEV9close; +extern _DEV9shutdown DEV9shutdown; +extern _DEV9read8 DEV9read8; +extern _DEV9read16 DEV9read16; +extern _DEV9read32 DEV9read32; +extern _DEV9write8 DEV9write8; +extern _DEV9write16 DEV9write16; +extern _DEV9write32 DEV9write32; +extern _DEV9readDMA8Mem DEV9readDMA8Mem; +extern _DEV9writeDMA8Mem DEV9writeDMA8Mem; +extern _DEV9irqCallback DEV9irqCallback; +extern _DEV9irqHandler DEV9irqHandler; + +extern _DEV9configure DEV9configure; +extern _DEV9freeze DEV9freeze; +extern _DEV9test DEV9test; +extern _DEV9about DEV9about; + +// USB +extern _USBinit USBinit; +extern _USBopen USBopen; +extern _USBclose USBclose; +extern _USBshutdown USBshutdown; +extern _USBread8 USBread8; +extern _USBread16 USBread16; +extern _USBread32 USBread32; +extern _USBwrite8 USBwrite8; +extern _USBwrite16 USBwrite16; +extern _USBwrite32 USBwrite32; +extern _USBasync USBasync; + +extern _USBirqCallback USBirqCallback; +extern _USBirqHandler USBirqHandler; +extern _USBsetRAM USBsetRAM; + +extern _USBconfigure USBconfigure; +extern _USBfreeze USBfreeze; +extern _USBtest USBtest; +extern _USBabout USBabout; + +// FW +extern _FWinit FWinit; +extern _FWopen FWopen; +extern _FWclose FWclose; +extern _FWshutdown FWshutdown; +extern _FWread32 FWread32; +extern _FWwrite32 FWwrite32; +extern _FWirqCallback FWirqCallback; + +extern _FWconfigure FWconfigure; +extern _FWfreeze FWfreeze; +extern _FWtest FWtest; +extern _FWabout FWabout; +#endif + +#ifdef __cplusplus +} // End extern "C" +#endif + +#endif /* __PS2EDEFS_H__ */ diff --git a/common/includes/PS2Etypes.h b/common/includes/PS2Etypes.h new file mode 100644 index 0000000000..59be1c3de4 --- /dev/null +++ b/common/includes/PS2Etypes.h @@ -0,0 +1,219 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +#ifndef __PS2ETYPES_H__ +#define __PS2ETYPES_H__ + +#if defined (__linux__) && !defined(__LINUX__) // some distributions are lower case +#define __LINUX__ +#endif + +#ifdef __CYGWIN__ +#define __LINUX__ +#endif + +#ifndef ARRAYSIZE +#define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0])) +#endif + +#ifdef __LINUX__ +#define CALLBACK +#else +#define CALLBACK __stdcall +#endif + + +// jASSUME - give hints to the optimizer +// This is primarily useful for the default case switch optimizer, which enables VC to +// generate more compact switches. + +#ifdef NDEBUG +# define jBREAKPOINT() ((void) 0) +# ifdef _MSC_VER +# define jASSUME(exp) (__assume(exp)) +# else +# define jASSUME(exp) ((void) sizeof(exp)) +# endif +#else +# if defined(_MSC_VER) +# define jBREAKPOINT() do { __asm int 3 } while(0) +# else +# define jBREAKPOINT() ((void) *(volatile char *) 0) +# endif +# define jASSUME(exp) if(exp) ; else jBREAKPOINT() +#endif + +// disable the default case in a switch +#define jNO_DEFAULT \ +{ \ + break; \ + \ +default: \ + jASSUME(0); \ + break; \ +} + + +// Basic types +#if defined(_MSC_VER) + +typedef __int8 s8; +typedef __int16 s16; +typedef __int32 s32; +typedef __int64 s64; + +typedef unsigned __int8 u8; +typedef unsigned __int16 u16; +typedef unsigned __int32 u32; +typedef unsigned __int64 u64; + +typedef unsigned int uint; + +#define PCSX2_ALIGNED(alig,x) __declspec(align(alig)) x +#define PCSX2_ALIGNED16(x) __declspec(align(16)) x +#define PCSX2_ALIGNED16_DECL(x) __declspec(align(16)) x + +#define __naked __declspec(naked) + +#else // _MSC_VER + +#ifdef __LINUX__ + +#ifdef HAVE_STDINT_H +#include "stdint.h" + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef uintptr_t uptr; +typedef intptr_t sptr; + +#else // HAVE_STDINT_H + +typedef char s8; +typedef short s16; +typedef int s32; +typedef long long s64; + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +#endif // HAVE_STDINT_H + +typedef unsigned int uint; + +#define LONG long +typedef union _LARGE_INTEGER +{ + long long QuadPart; +} LARGE_INTEGER; + +#define __fastcall __attribute__((fastcall)) +#define __unused __attribute__((unused)) +#define _inline __inline__ __attribute__((unused)) +#define __forceinline __attribute__((always_inline,unused)) +#define __naked // GCC lacks the naked specifier + +#endif // __LINUX__ + +#define PCSX2_ALIGNED(alig,x) x __attribute((aligned(alig))) +#define PCSX2_ALIGNED16(x) x __attribute((aligned(16))) + +#define PCSX2_ALIGNED16_DECL(x) x + +#endif // _MSC_VER + +#if !defined(__LINUX__) || !defined(HAVE_STDINT_H) +#if defined(__x86_64__) +typedef u64 uptr; +typedef s64 sptr; +#else +typedef u32 uptr; +typedef s32 sptr; +#endif +#endif + +// A rough-and-ready cross platform 128-bit datatype, Non-SSE style. +#ifdef __cplusplus +struct u128 +{ + u64 lo; + u64 hi; + + // Implicit conversion from u64 + u128( u64 src ) : + lo( src ) + , hi( 0 ) {} + + // Implicit conversion from u32 + u128( u32 src ) : + lo( src ) + , hi( 0 ) {} +}; + +struct s128 +{ + s64 lo; + s64 hi; + + // Implicit conversion from u64 + s128( s64 src ) : + lo( src ) + , hi( 0 ) {} + + // Implicit conversion from u32 + s128( s32 src ) : + lo( src ) + , hi( 0 ) {} +}; + +#else + +typedef union _u128_t +{ + u64 lo; + u64 hi; +} u128; + +typedef union _s128_t +{ + s64 lo; + s64 hi; +} s128; + +#endif + +typedef struct { + int size; + s8 *data; +} freezeData; + +/* common defines */ +#ifndef C_ASSERT +#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +#endif + +#endif /* __PS2ETYPES_H__ */ diff --git a/common/tsvnrev/svnrev_template.h b/common/tsvnrev/svnrev_template.h new file mode 100644 index 0000000000..50327f3f30 --- /dev/null +++ b/common/tsvnrev/svnrev_template.h @@ -0,0 +1,18 @@ +// svnrev_template.h --> svnrev.h +// +// This file acts as a template for the automatic SVN revision/version tag. +// It is used by the utility SubWCrev.exe to create an "svnrev.h" file for +// whichever project is being compiled (as indicated by command line options +// passed to SubWCRev.exe during the project's pre-build step). +// +// The SubWCRev.exe utility is part of TortoiseSVN and requires several DLLs +// installed by TortoiseSVN, so it will only be available if you have TortoiseSVN +// installed on your system. If you do not have it installed, a generic template +// is used instead (see svnrev_generic.h). Having TortoiseSVN is handy but not +// necessary. If you do not have it installed, everything will still compile +// fine except without the SVN revision tagged to the application/dll version. +// +// TortoiseSVN can be downloaded from http://tortoisesvn.tigris.org + +#define SVN_REV $WCREV$ +#define SVN_MODS $WCMODS?1:0$ \ No newline at end of file diff --git a/common/tsvnrev/svnrev_unknown.h b/common/tsvnrev/svnrev_unknown.h new file mode 100644 index 0000000000..a2a3703186 --- /dev/null +++ b/common/tsvnrev/svnrev_unknown.h @@ -0,0 +1,23 @@ +// svnrev_genric.h --> svnrev.h +// +// This file acts as a placebo for people who do not have TortoiseSVN installed. +// It provides "empty" revision information to the Pcsx2 Playground projects in +// the absence of real revisions derived from the repository being built. +// +// This file does not affect application/dll builds in any significant manner, +// other than the lack of automatic revision tags inserted into the app (which +// is very convenient but hardly necessary). +// +// See svn_template.h for more information on how the process of revision +// templating works. +// +// If you would like to enable automatic revisin tagging, TortoiseSVN can be +// downloaded from http://tortoisesvn.tigris.org + +#define SVN_REV_UNKNOWN + +// The following defines are included so that code will still compile even if it +// doesn't check for the SVN_REV_UNKNOWN define. + +#define SVN_REV 0 +#define SVN_MODS "" \ No newline at end of file diff --git a/common/tsvnrev/updateRevision.cmd b/common/tsvnrev/updateRevision.cmd new file mode 100644 index 0000000000..0e192e7a9b --- /dev/null +++ b/common/tsvnrev/updateRevision.cmd @@ -0,0 +1,8 @@ +@echo off +SubWCRev.exe %1 %2\svnrev_template.h %1\svnrev.h +if not ERRORLEVEL 0 ( + echo Automatic revision update unavailable, using generic template instead. + echo You can safely ignore this message - see svnrev.h for details. + copy /Y %3\svnrev_unknown.h %2\svnrev.h +) +set ERRORLEVEL=0 diff --git a/fetch.sh b/fetch.sh new file mode 100644 index 0000000000..d14488c68f --- /dev/null +++ b/fetch.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Convenience script for obtaining plugins, +# Use at your own risk. Assumes that it is being run in the plugins directory. + +#Running this script should do the following: +#1) Check to see if the 6 needed plugin folders exist in the current directory. +#2) If they aren't. create a temp directory, download the plugins to it from +# the official pcsx2 svn, move them into the plugin directory, and then delete +# the temp directory. + +#3) Check for copies of certain plugins in the current directory, and copy them to +# the right place if they exist, assuming that they are modified copies of those plugins. + +cd plugins +sh fetch.sh +cd .. \ No newline at end of file diff --git a/pcsx2/3rdparty/zlib/ChangeLog b/pcsx2/3rdparty/zlib/ChangeLog new file mode 100644 index 0000000000..7f6869d323 --- /dev/null +++ b/pcsx2/3rdparty/zlib/ChangeLog @@ -0,0 +1,855 @@ + + ChangeLog file for zlib + +Changes in 1.2.3 (18 July 2005) +- Apply security vulnerability fixes to contrib/infback9 as well +- Clean up some text files (carriage returns, trailing space) +- Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant] + +Changes in 1.2.2.4 (11 July 2005) +- Add inflatePrime() function for starting inflation at bit boundary +- Avoid some Visual C warnings in deflate.c +- Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit + compile +- Fix some spelling errors in comments [Betts] +- Correct inflateInit2() error return documentation in zlib.h +- Added zran.c example of compressed data random access to examples + directory, shows use of inflatePrime() +- Fix cast for assignments to strm->state in inflate.c and infback.c +- Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer] +- Move declarations of gf2 functions to right place in crc32.c [Oberhumer] +- Add cast in trees.c t avoid a warning [Oberhumer] +- Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer] +- Update make_vms.com [Zinser] +- Initialize state->write in inflateReset() since copied in inflate_fast() +- Be more strict on incomplete code sets in inflate_table() and increase + ENOUGH and MAXD -- this repairs a possible security vulnerability for + invalid inflate input. Thanks to Tavis Ormandy and Markus Oberhumer for + discovering the vulnerability and providing test cases. +- Add ia64 support to configure for HP-UX [Smith] +- Add error return to gzread() for format or i/o error [Levin] +- Use malloc.h for OS/2 [Necasek] + +Changes in 1.2.2.3 (27 May 2005) +- Replace 1U constants in inflate.c and inftrees.c for 64-bit compile +- Typecast fread() return values in gzio.c [Vollant] +- Remove trailing space in minigzip.c outmode (VC++ can't deal with it) +- Fix crc check bug in gzread() after gzungetc() [Heiner] +- Add the deflateTune() function to adjust internal compression parameters +- Add a fast gzip decompressor, gun.c, to examples (use of inflateBack) +- Remove an incorrect assertion in examples/zpipe.c +- Add C++ wrapper in infback9.h [Donais] +- Fix bug in inflateCopy() when decoding fixed codes +- Note in zlib.h how much deflateSetDictionary() actually uses +- Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used) +- Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer] +- Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer] +- Add gzdirect() function to indicate transparent reads +- Update contrib/minizip [Vollant] +- Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer] +- Add casts in crc32.c to avoid warnings [Oberhumer] +- Add contrib/masmx64 [Vollant] +- Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant] + +Changes in 1.2.2.2 (30 December 2004) +- Replace structure assignments in deflate.c and inflate.c with zmemcpy to + avoid implicit memcpy calls (portability for no-library compilation) +- Increase sprintf() buffer size in gzdopen() to allow for large numbers +- Add INFLATE_STRICT to check distances against zlib header +- Improve WinCE errno handling and comments [Chang] +- Remove comment about no gzip header processing in FAQ +- Add Z_FIXED strategy option to deflateInit2() to force fixed trees +- Add updated make_vms.com [Coghlan], update README +- Create a new "examples" directory, move gzappend.c there, add zpipe.c, + fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html. +- Add FAQ entry and comments in deflate.c on uninitialized memory access +- Add Solaris 9 make options in configure [Gilbert] +- Allow strerror() usage in gzio.c for STDC +- Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer] +- Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant] +- Use z_off_t for adler32_combine() and crc32_combine() lengths +- Make adler32() much faster for small len +- Use OS_CODE in deflate() default gzip header + +Changes in 1.2.2.1 (31 October 2004) +- Allow inflateSetDictionary() call for raw inflate +- Fix inflate header crc check bug for file names and comments +- Add deflateSetHeader() and gz_header structure for custom gzip headers +- Add inflateGetheader() to retrieve gzip headers +- Add crc32_combine() and adler32_combine() functions +- Add alloc_func, free_func, in_func, out_func to Z_PREFIX list +- Use zstreamp consistently in zlib.h (inflate_back functions) +- Remove GUNZIP condition from definition of inflate_mode in inflate.h + and in contrib/inflate86/inffast.S [Truta, Anderson] +- Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson] +- Update projects/README.projects and projects/visualc6 [Truta] +- Update win32/DLL_FAQ.txt [Truta] +- Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta] +- Deprecate Z_ASCII; use Z_TEXT instead [Truta] +- Use a new algorithm for setting strm->data_type in trees.c [Truta] +- Do not define an exit() prototype in zutil.c unless DEBUG defined +- Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta] +- Add comment in zlib.h for Z_NO_FLUSH parameter to deflate() +- Fix Darwin build version identification [Peterson] + +Changes in 1.2.2 (3 October 2004) +- Update zlib.h comments on gzip in-memory processing +- Set adler to 1 in inflateReset() to support Java test suite [Walles] +- Add contrib/dotzlib [Ravn] +- Update win32/DLL_FAQ.txt [Truta] +- Update contrib/minizip [Vollant] +- Move contrib/visual-basic.txt to old/ [Truta] +- Fix assembler builds in projects/visualc6/ [Truta] + +Changes in 1.2.1.2 (9 September 2004) +- Update INDEX file +- Fix trees.c to update strm->data_type (no one ever noticed!) +- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown] +- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE) +- Add limited multitasking protection to DYNAMIC_CRC_TABLE +- Add NO_vsnprintf for VMS in zutil.h [Mozilla] +- Don't declare strerror() under VMS [Mozilla] +- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize +- Update contrib/ada [Anisimkov] +- Update contrib/minizip [Vollant] +- Fix configure to not hardcode directories for Darwin [Peterson] +- Fix gzio.c to not return error on empty files [Brown] +- Fix indentation; update version in contrib/delphi/ZLib.pas and + contrib/pascal/zlibpas.pas [Truta] +- Update mkasm.bat in contrib/masmx86 [Truta] +- Update contrib/untgz [Truta] +- Add projects/README.projects [Truta] +- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta] +- Update win32/DLL_FAQ.txt [Truta] +- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta] +- Remove an unnecessary assignment to curr in inftrees.c [Truta] +- Add OS/2 to exe builds in configure [Poltorak] +- Remove err dummy parameter in zlib.h [Kientzle] + +Changes in 1.2.1.1 (9 January 2004) +- Update email address in README +- Several FAQ updates +- Fix a big fat bug in inftrees.c that prevented decoding valid + dynamic blocks with only literals and no distance codes -- + Thanks to "Hot Emu" for the bug report and sample file +- Add a note to puff.c on no distance codes case. + +Changes in 1.2.1 (17 November 2003) +- Remove a tab in contrib/gzappend/gzappend.c +- Update some interfaces in contrib for new zlib functions +- Update zlib version number in some contrib entries +- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta] +- Support shared libraries on Hurd and KFreeBSD [Brown] +- Fix error in NO_DIVIDE option of adler32.c + +Changes in 1.2.0.8 (4 November 2003) +- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas +- Add experimental NO_DIVIDE #define in adler32.c + - Possibly faster on some processors (let me know if it is) +- Correct Z_BLOCK to not return on first inflate call if no wrap +- Fix strm->data_type on inflate() return to correctly indicate EOB +- Add deflatePrime() function for appending in the middle of a byte +- Add contrib/gzappend for an example of appending to a stream +- Update win32/DLL_FAQ.txt [Truta] +- Delete Turbo C comment in README [Truta] +- Improve some indentation in zconf.h [Truta] +- Fix infinite loop on bad input in configure script [Church] +- Fix gzeof() for concatenated gzip files [Johnson] +- Add example to contrib/visual-basic.txt [Michael B.] +- Add -p to mkdir's in Makefile.in [vda] +- Fix configure to properly detect presence or lack of printf functions +- Add AS400 support [Monnerat] +- Add a little Cygwin support [Wilson] + +Changes in 1.2.0.7 (21 September 2003) +- Correct some debug formats in contrib/infback9 +- Cast a type in a debug statement in trees.c +- Change search and replace delimiter in configure from % to # [Beebe] +- Update contrib/untgz to 0.2 with various fixes [Truta] +- Add build support for Amiga [Nikl] +- Remove some directories in old that have been updated to 1.2 +- Add dylib building for Mac OS X in configure and Makefile.in +- Remove old distribution stuff from Makefile +- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X +- Update links in README + +Changes in 1.2.0.6 (13 September 2003) +- Minor FAQ updates +- Update contrib/minizip to 1.00 [Vollant] +- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta] +- Update POSTINC comment for 68060 [Nikl] +- Add contrib/infback9 with deflate64 decoding (unsupported) +- For MVS define NO_vsnprintf and undefine FAR [van Burik] +- Add pragma for fdopen on MVS [van Burik] + +Changes in 1.2.0.5 (8 September 2003) +- Add OF to inflateBackEnd() declaration in zlib.h +- Remember start when using gzdopen in the middle of a file +- Use internal off_t counters in gz* functions to properly handle seeks +- Perform more rigorous check for distance-too-far in inffast.c +- Add Z_BLOCK flush option to return from inflate at block boundary +- Set strm->data_type on return from inflate + - Indicate bits unused, if at block boundary, and if in last block +- Replace size_t with ptrdiff_t in crc32.c, and check for correct size +- Add condition so old NO_DEFLATE define still works for compatibility +- FAQ update regarding the Windows DLL [Truta] +- INDEX update: add qnx entry, remove aix entry [Truta] +- Install zlib.3 into mandir [Wilson] +- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta] +- Adapt the zlib interface to the new DLL convention guidelines [Truta] +- Introduce ZLIB_WINAPI macro to allow the export of functions using + the WINAPI calling convention, for Visual Basic [Vollant, Truta] +- Update msdos and win32 scripts and makefiles [Truta] +- Export symbols by name, not by ordinal, in win32/zlib.def [Truta] +- Add contrib/ada [Anisimkov] +- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta] +- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant] +- Add contrib/masm686 [Truta] +- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm + [Truta, Vollant] +- Update contrib/delphi; rename to contrib/pascal; add example [Truta] +- Remove contrib/delphi2; add a new contrib/delphi [Truta] +- Avoid inclusion of the nonstandard in contrib/iostream, + and fix some method prototypes [Truta] +- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip + [Truta] +- Avoid the use of backslash (\) in contrib/minizip [Vollant] +- Fix file time handling in contrib/untgz; update makefiles [Truta] +- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines + [Vollant] +- Remove contrib/vstudio/vc15_16 [Vollant] +- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta] +- Update README.contrib [Truta] +- Invert the assignment order of match_head and s->prev[...] in + INSERT_STRING [Truta] +- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings + [Truta] +- Compare function pointers with 0, not with NULL or Z_NULL [Truta] +- Fix prototype of syncsearch in inflate.c [Truta] +- Introduce ASMINF macro to be enabled when using an ASM implementation + of inflate_fast [Truta] +- Change NO_DEFLATE to NO_GZCOMPRESS [Truta] +- Modify test_gzio in example.c to take a single file name as a + parameter [Truta] +- Exit the example.c program if gzopen fails [Truta] +- Add type casts around strlen in example.c [Truta] +- Remove casting to sizeof in minigzip.c; give a proper type + to the variable compared with SUFFIX_LEN [Truta] +- Update definitions of STDC and STDC99 in zconf.h [Truta] +- Synchronize zconf.h with the new Windows DLL interface [Truta] +- Use SYS16BIT instead of __32BIT__ to distinguish between + 16- and 32-bit platforms [Truta] +- Use far memory allocators in small 16-bit memory models for + Turbo C [Truta] +- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in + zlibCompileFlags [Truta] +- Cygwin has vsnprintf [Wilson] +- In Windows16, OS_CODE is 0, as in MSDOS [Truta] +- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson] + +Changes in 1.2.0.4 (10 August 2003) +- Minor FAQ updates +- Be more strict when checking inflateInit2's windowBits parameter +- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well +- Add gzip wrapper option to deflateInit2 using windowBits +- Add updated QNX rule in configure and qnx directory [Bonnefoy] +- Make inflate distance-too-far checks more rigorous +- Clean up FAR usage in inflate +- Add casting to sizeof() in gzio.c and minigzip.c + +Changes in 1.2.0.3 (19 July 2003) +- Fix silly error in gzungetc() implementation [Vollant] +- Update contrib/minizip and contrib/vstudio [Vollant] +- Fix printf format in example.c +- Correct cdecl support in zconf.in.h [Anisimkov] +- Minor FAQ updates + +Changes in 1.2.0.2 (13 July 2003) +- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons +- Attempt to avoid warnings in crc32.c for pointer-int conversion +- Add AIX to configure, remove aix directory [Bakker] +- Add some casts to minigzip.c +- Improve checking after insecure sprintf() or vsprintf() calls +- Remove #elif's from crc32.c +- Change leave label to inf_leave in inflate.c and infback.c to avoid + library conflicts +- Remove inflate gzip decoding by default--only enable gzip decoding by + special request for stricter backward compatibility +- Add zlibCompileFlags() function to return compilation information +- More typecasting in deflate.c to avoid warnings +- Remove leading underscore from _Capital #defines [Truta] +- Fix configure to link shared library when testing +- Add some Windows CE target adjustments [Mai] +- Remove #define ZLIB_DLL in zconf.h [Vollant] +- Add zlib.3 [Rodgers] +- Update RFC URL in deflate.c and algorithm.txt [Mai] +- Add zlib_dll_FAQ.txt to contrib [Truta] +- Add UL to some constants [Truta] +- Update minizip and vstudio [Vollant] +- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h +- Expand use of NO_DUMMY_DECL to avoid all dummy structures +- Added iostream3 to contrib [Schwardt] +- Replace rewind() with fseek() for WinCE [Truta] +- Improve setting of zlib format compression level flags + - Report 0 for huffman and rle strategies and for level == 0 or 1 + - Report 2 only for level == 6 +- Only deal with 64K limit when necessary at compile time [Truta] +- Allow TOO_FAR check to be turned off at compile time [Truta] +- Add gzclearerr() function [Souza] +- Add gzungetc() function + +Changes in 1.2.0.1 (17 March 2003) +- Add Z_RLE strategy for run-length encoding [Truta] + - When Z_RLE requested, restrict matches to distance one + - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE +- Correct FASTEST compilation to allow level == 0 +- Clean up what gets compiled for FASTEST +- Incorporate changes to zconf.in.h [Vollant] + - Refine detection of Turbo C need for dummy returns + - Refine ZLIB_DLL compilation + - Include additional header file on VMS for off_t typedef +- Try to use _vsnprintf where it supplants vsprintf [Vollant] +- Add some casts in inffast.c +- Enchance comments in zlib.h on what happens if gzprintf() tries to + write more than 4095 bytes before compression +- Remove unused state from inflateBackEnd() +- Remove exit(0) from minigzip.c, example.c +- Get rid of all those darn tabs +- Add "check" target to Makefile.in that does the same thing as "test" +- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in +- Update contrib/inflate86 [Anderson] +- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant] +- Add msdos and win32 directories with makefiles [Truta] +- More additions and improvements to the FAQ + +Changes in 1.2.0 (9 March 2003) +- New and improved inflate code + - About 20% faster + - Does not allocate 32K window unless and until needed + - Automatically detects and decompresses gzip streams + - Raw inflate no longer needs an extra dummy byte at end + - Added inflateBack functions using a callback interface--even faster + than inflate, useful for file utilities (gzip, zip) + - Added inflateCopy() function to record state for random access on + externally generated deflate streams (e.g. in gzip files) + - More readable code (I hope) +- New and improved crc32() + - About 50% faster, thanks to suggestions from Rodney Brown +- Add deflateBound() and compressBound() functions +- Fix memory leak in deflateInit2() +- Permit setting dictionary for raw deflate (for parallel deflate) +- Fix const declaration for gzwrite() +- Check for some malloc() failures in gzio.c +- Fix bug in gzopen() on single-byte file 0x1f +- Fix bug in gzread() on concatenated file with 0x1f at end of buffer + and next buffer doesn't start with 0x8b +- Fix uncompress() to return Z_DATA_ERROR on truncated input +- Free memory at end of example.c +- Remove MAX #define in trees.c (conflicted with some libraries) +- Fix static const's in deflate.c, gzio.c, and zutil.[ch] +- Declare malloc() and free() in gzio.c if STDC not defined +- Use malloc() instead of calloc() in zutil.c if int big enough +- Define STDC for AIX +- Add aix/ with approach for compiling shared library on AIX +- Add HP-UX support for shared libraries in configure +- Add OpenUNIX support for shared libraries in configure +- Use $cc instead of gcc to build shared library +- Make prefix directory if needed when installing +- Correct Macintosh avoidance of typedef Byte in zconf.h +- Correct Turbo C memory allocation when under Linux +- Use libz.a instead of -lz in Makefile (assure use of compiled library) +- Update configure to check for snprintf or vsnprintf functions and their + return value, warn during make if using an insecure function +- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that + is lost when library is used--resolution is to build new zconf.h +- Documentation improvements (in zlib.h): + - Document raw deflate and inflate + - Update RFCs URL + - Point out that zlib and gzip formats are different + - Note that Z_BUF_ERROR is not fatal + - Document string limit for gzprintf() and possible buffer overflow + - Note requirement on avail_out when flushing + - Note permitted values of flush parameter of inflate() +- Add some FAQs (and even answers) to the FAQ +- Add contrib/inflate86/ for x86 faster inflate +- Add contrib/blast/ for PKWare Data Compression Library decompression +- Add contrib/puff/ simple inflate for deflate format description + +Changes in 1.1.4 (11 March 2002) +- ZFREE was repeated on same allocation on some error conditions. + This creates a security problem described in + http://www.zlib.org/advisory-2002-03-11.txt +- Returned incorrect error (Z_MEM_ERROR) on some invalid data +- Avoid accesses before window for invalid distances with inflate window + less than 32K. +- force windowBits > 8 to avoid a bug in the encoder for a window size + of 256 bytes. (A complete fix will be available in 1.1.5). + +Changes in 1.1.3 (9 July 1998) +- fix "an inflate input buffer bug that shows up on rare but persistent + occasions" (Mark) +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) +- fix gzseek(..., SEEK_SET) in write mode +- fix crc check after a gzeek (Frank Faubert) +- fix miniunzip when the last entry in a zip file is itself a zip file + (J Lillge) +- add contrib/asm586 and contrib/asm686 (Brian Raiter) + See http://www.muppetlabs.com/~breadbox/software/assembly.html +- add support for Delphi 3 in contrib/delphi (Bob Dellaca) +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) +- added a FAQ file + +- Support gzdopen on Mac with Metrowerks (Jason Linhart) +- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) +- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) +- avoid some warnings with Borland C (Tom Tanner) +- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) +- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) +- allow several arguments to configure (Tim Mooney, Frodo Looijaard) +- use libdir and includedir in Makefile.in (Tim Mooney) +- support shared libraries on OSF1 V4 (Tim Mooney) +- remove so_locations in "make clean" (Tim Mooney) +- fix maketree.c compilation error (Glenn, Mark) +- Python interface to zlib now in Python 1.5 (Jeremy Hylton) +- new Makefile.riscos (Rich Walker) +- initialize static descriptors in trees.c for embedded targets (Nick Smith) +- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) +- add the OS/2 files in Makefile.in too (Andrew Zabolotny) +- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) +- fix maketree.c to allow clean compilation of inffixed.h (Mark) +- fix parameter check in deflateCopy (Gunther Nikl) +- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) +- Many portability patches by Christian Spieler: + . zutil.c, zutil.h: added "const" for zmem* + . Make_vms.com: fixed some typos + . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists + . msdos/Makefile.msc: remove "default rtl link library" info from obj files + . msdos/Makefile.*: use model-dependent name for the built zlib library + . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: + new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) +- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) +- replace __far with _far for better portability (Christian Spieler, Tom Lane) +- fix test for errno.h in configure (Tim Newsham) + +Changes in 1.1.2 (19 March 98) +- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) + See http://www.winimage.com/zLibDll/unzip.html +- preinitialize the inflate tables for fixed codes, to make the code + completely thread safe (Mark) +- some simplifications and slight speed-up to the inflate code (Mark) +- fix gzeof on non-compressed files (Allan Schrum) +- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) +- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) +- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) +- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) +- do not wrap extern "C" around system includes (Tom Lane) +- mention zlib binding for TCL in README (Andreas Kupries) +- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) +- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) +- allow "configure --prefix $HOME" (Tim Mooney) +- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) +- move Makefile.sas to amiga/Makefile.sas + +Changes in 1.1.1 (27 Feb 98) +- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) +- remove block truncation heuristic which had very marginal effect for zlib + (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the + compression ratio on some files. This also allows inlining _tr_tally for + matches in deflate_slow. +- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) + +Changes in 1.1.0 (24 Feb 98) +- do not return STREAM_END prematurely in inflate (John Bowler) +- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) +- compile with -DFASTEST to get compression code optimized for speed only +- in minigzip, try mmap'ing the input file first (Miguel Albrecht) +- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain + on Sun but significant on HP) + +- add a pointer to experimental unzip library in README (Gilles Vollant) +- initialize variable gcc in configure (Chris Herborth) + +Changes in 1.0.9 (17 Feb 1998) +- added gzputs and gzgets functions +- do not clear eof flag in gzseek (Mark Diekhans) +- fix gzseek for files in transparent mode (Mark Diekhans) +- do not assume that vsprintf returns the number of bytes written (Jens Krinke) +- replace EXPORT with ZEXPORT to avoid conflict with other programs +- added compress2 in zconf.h, zlib.def, zlib.dnt +- new asm code from Gilles Vollant in contrib/asm386 +- simplify the inflate code (Mark): + . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() + . ZALLOC the length list in inflate_trees_fixed() instead of using stack + . ZALLOC the value area for huft_build() instead of using stack + . Simplify Z_FINISH check in inflate() + +- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 +- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) +- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with + the declaration of FAR (Gilles VOllant) +- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) +- read_buf buf parameter of type Bytef* instead of charf* +- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) +- do not redeclare unlink in minigzip.c for WIN32 (John Bowler) +- fix check for presence of directories in "make install" (Ian Willis) + +Changes in 1.0.8 (27 Jan 1998) +- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) +- fix gzgetc and gzputc for big endian systems (Markus Oberhumer) +- added compress2() to allow setting the compression level +- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) +- use constant arrays for the static trees in trees.c instead of computing + them at run time (thanks to Ken Raeburn for this suggestion). To create + trees.h, compile with GEN_TREES_H and run "make test". +- check return code of example in "make test" and display result +- pass minigzip command line options to file_compress +- simplifying code of inflateSync to avoid gcc 2.8 bug + +- support CC="gcc -Wall" in configure -s (QingLong) +- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) +- fix test for shared library support to avoid compiler warnings +- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) +- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) +- do not use fdopen for Metrowerks on Mac (Brad Pettit)) +- add checks for gzputc and gzputc in example.c +- avoid warnings in gzio.c and deflate.c (Andreas Kleinert) +- use const for the CRC table (Ken Raeburn) +- fixed "make uninstall" for shared libraries +- use Tracev instead of Trace in infblock.c +- in example.c use correct compressed length for test_sync +- suppress +vnocompatwarnings in configure for HPUX (not always supported) + +Changes in 1.0.7 (20 Jan 1998) +- fix gzseek which was broken in write mode +- return error for gzseek to negative absolute position +- fix configure for Linux (Chun-Chung Chen) +- increase stack space for MSC (Tim Wegner) +- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) +- define EXPORTVA for gzprintf (Gilles Vollant) +- added man page zlib.3 (Rick Rodgers) +- for contrib/untgz, fix makedir() and improve Makefile + +- check gzseek in write mode in example.c +- allocate extra buffer for seeks only if gzseek is actually called +- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) +- add inflateSyncPoint in zconf.h +- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def + +Changes in 1.0.6 (19 Jan 1998) +- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and + gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) +- Fix a deflate bug occurring only with compression level 0 (thanks to + Andy Buckler for finding this one). +- In minigzip, pass transparently also the first byte for .Z files. +- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() +- check Z_FINISH in inflate (thanks to Marc Schluper) +- Implement deflateCopy (thanks to Adam Costello) +- make static libraries by default in configure, add --shared option. +- move MSDOS or Windows specific files to directory msdos +- suppress the notion of partial flush to simplify the interface + (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) +- suppress history buffer provided by application to simplify the interface + (this feature was not implemented anyway in 1.0.4) +- next_in and avail_in must be initialized before calling inflateInit or + inflateInit2 +- add EXPORT in all exported functions (for Windows DLL) +- added Makefile.nt (thanks to Stephen Williams) +- added the unsupported "contrib" directory: + contrib/asm386/ by Gilles Vollant + 386 asm code replacing longest_match(). + contrib/iostream/ by Kevin Ruland + A C++ I/O streams interface to the zlib gz* functions + contrib/iostream2/ by Tyge Løvset + Another C++ I/O streams interface + contrib/untgz/ by "Pedro A. Aranda Guti\irrez" + A very simple tar.gz file extractor using zlib + contrib/visual-basic.txt by Carlos Rios + How to use compress(), uncompress() and the gz* functions from VB. +- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression + level) in minigzip (thanks to Tom Lane) + +- use const for rommable constants in deflate +- added test for gzseek and gztell in example.c +- add undocumented function inflateSyncPoint() (hack for Paul Mackerras) +- add undocumented function zError to convert error code to string + (for Tim Smithers) +- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code. +- Use default memcpy for Symantec MSDOS compiler. +- Add EXPORT keyword for check_func (needed for Windows DLL) +- add current directory to LD_LIBRARY_PATH for "make test" +- create also a link for libz.so.1 +- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) +- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) +- added -soname for Linux in configure (Chun-Chung Chen, +- assign numbers to the exported functions in zlib.def (for Windows DLL) +- add advice in zlib.h for best usage of deflateSetDictionary +- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) +- allow compilation with ANSI keywords only enabled for TurboC in large model +- avoid "versionString"[0] (Borland bug) +- add NEED_DUMMY_RETURN for Borland +- use variable z_verbose for tracing in debug mode (L. Peter Deutsch). +- allow compilation with CC +- defined STDC for OS/2 (David Charlap) +- limit external names to 8 chars for MVS (Thomas Lund) +- in minigzip.c, use static buffers only for 16-bit systems +- fix suffix check for "minigzip -d foo.gz" +- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) +- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) +- added makelcc.bat for lcc-win32 (Tom St Denis) +- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) +- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion. +- check for unistd.h in configure (for off_t) +- remove useless check parameter in inflate_blocks_free +- avoid useless assignment of s->check to itself in inflate_blocks_new +- do not flush twice in gzclose (thanks to Ken Raeburn) +- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h +- use NO_ERRNO_H instead of enumeration of operating systems with errno.h +- work around buggy fclose on pipes for HP/UX +- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) +- fix configure if CC is already equal to gcc + +Changes in 1.0.5 (3 Jan 98) +- Fix inflate to terminate gracefully when fed corrupted or invalid data +- Use const for rommable constants in inflate +- Eliminate memory leaks on error conditions in inflate +- Removed some vestigial code in inflate +- Update web address in README + +Changes in 1.0.4 (24 Jul 96) +- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF + bit, so the decompressor could decompress all the correct data but went + on to attempt decompressing extra garbage data. This affected minigzip too. +- zlibVersion and gzerror return const char* (needed for DLL) +- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) +- use z_error only for DEBUG (avoid problem with DLLs) + +Changes in 1.0.3 (2 Jul 96) +- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS + small and medium models; this makes the library incompatible with previous + versions for these models. (No effect in large model or on other systems.) +- return OK instead of BUF_ERROR if previous deflate call returned with + avail_out as zero but there is nothing to do +- added memcmp for non STDC compilers +- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) +- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) +- better check for 16-bit mode MSC (avoids problem with Symantec) + +Changes in 1.0.2 (23 May 96) +- added Windows DLL support +- added a function zlibVersion (for the DLL support) +- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) +- Bytef is define's instead of typedef'd only for Borland C +- avoid reading uninitialized memory in example.c +- mention in README that the zlib format is now RFC1950 +- updated Makefile.dj2 +- added algorithm.doc + +Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] +- fix array overlay in deflate.c which sometimes caused bad compressed data +- fix inflate bug with empty stored block +- fix MSDOS medium model which was broken in 0.99 +- fix deflateParams() which could generated bad compressed data. +- Bytef is define'd instead of typedef'ed (work around Borland bug) +- added an INDEX file +- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), + Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) +- speed up adler32 for modern machines without auto-increment +- added -ansi for IRIX in configure +- static_init_done in trees.c is an int +- define unlink as delete for VMS +- fix configure for QNX +- add configure branch for SCO and HPUX +- avoid many warnings (unused variables, dead assignments, etc...) +- no fdopen for BeOS +- fix the Watcom fix for 32 bit mode (define FAR as empty) +- removed redefinition of Byte for MKWERKS +- work around an MWKERKS bug (incorrect merge of all .h files) + +Changes in 0.99 (27 Jan 96) +- allow preset dictionary shared between compressor and decompressor +- allow compression level 0 (no compression) +- add deflateParams in zlib.h: allow dynamic change of compression level + and compression strategy. +- test large buffers and deflateParams in example.c +- add optional "configure" to build zlib as a shared library +- suppress Makefile.qnx, use configure instead +- fixed deflate for 64-bit systems (detected on Cray) +- fixed inflate_blocks for 64-bit systems (detected on Alpha) +- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) +- always return Z_BUF_ERROR when deflate() has nothing to do +- deflateInit and inflateInit are now macros to allow version checking +- prefix all global functions and types with z_ with -DZ_PREFIX +- make falloc completely reentrant (inftrees.c) +- fixed very unlikely race condition in ct_static_init +- free in reverse order of allocation to help memory manager +- use zlib-1.0/* instead of zlib/* inside the tar.gz +- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith + -Wconversion -Wstrict-prototypes -Wmissing-prototypes" +- allow gzread on concatenated .gz files +- deflateEnd now returns Z_DATA_ERROR if it was premature +- deflate is finally (?) fully deterministic (no matches beyond end of input) +- Document Z_SYNC_FLUSH +- add uninstall in Makefile +- Check for __cpluplus in zlib.h +- Better test in ct_align for partial flush +- avoid harmless warnings for Borland C++ +- initialize hash_head in deflate.c +- avoid warning on fdopen (gzio.c) for HP cc -Aa +- include stdlib.h for STDC compilers +- include errno.h for Cray +- ignore error if ranlib doesn't exist +- call ranlib twice for NeXTSTEP +- use exec_prefix instead of prefix for libz.a +- renamed ct_* as _tr_* to avoid conflict with applications +- clear z->msg in inflateInit2 before any error return +- initialize opaque in example.c, gzio.c, deflate.c and inflate.c +- fixed typo in zconf.h (_GNUC__ => __GNUC__) +- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) +- fix typo in Make_vms.com (f$trnlnm -> f$getsyi) +- in fcalloc, normalize pointer if size > 65520 bytes +- don't use special fcalloc for 32 bit Borland C++ +- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc... +- use Z_BINARY instead of BINARY +- document that gzclose after gzdopen will close the file +- allow "a" as mode in gzopen. +- fix error checking in gzread +- allow skipping .gz extra-field on pipes +- added reference to Perl interface in README +- put the crc table in FAR data (I dislike more and more the medium model :) +- added get_crc_table +- added a dimension to all arrays (Borland C can't count). +- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast +- guard against multiple inclusion of *.h (for precompiled header on Mac) +- Watcom C pretends to be Microsoft C small model even in 32 bit mode. +- don't use unsized arrays to avoid silly warnings by Visual C++: + warning C4746: 'inflate_mask' : unsized array treated as '__far' + (what's wrong with far data in far model?). +- define enum out of inflate_blocks_state to allow compilation with C++ + +Changes in 0.95 (16 Aug 95) +- fix MSDOS small and medium model (now easier to adapt to any compiler) +- inlined send_bits +- fix the final (:-) bug for deflate with flush (output was correct but + not completely flushed in rare occasions). +- default window size is same for compression and decompression + (it's now sufficient to set MAX_WBITS in zconf.h). +- voidp -> voidpf and voidnp -> voidp (for consistency with other + typedefs and because voidnp was not near in large model). + +Changes in 0.94 (13 Aug 95) +- support MSDOS medium model +- fix deflate with flush (could sometimes generate bad output) +- fix deflateReset (zlib header was incorrectly suppressed) +- added support for VMS +- allow a compression level in gzopen() +- gzflush now calls fflush +- For deflate with flush, flush even if no more input is provided. +- rename libgz.a as libz.a +- avoid complex expression in infcodes.c triggering Turbo C bug +- work around a problem with gcc on Alpha (in INSERT_STRING) +- don't use inline functions (problem with some gcc versions) +- allow renaming of Byte, uInt, etc... with #define. +- avoid warning about (unused) pointer before start of array in deflate.c +- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c +- avoid reserved word 'new' in trees.c + +Changes in 0.93 (25 June 95) +- temporarily disable inline functions +- make deflate deterministic +- give enough lookahead for PARTIAL_FLUSH +- Set binary mode for stdin/stdout in minigzip.c for OS/2 +- don't even use signed char in inflate (not portable enough) +- fix inflate memory leak for segmented architectures + +Changes in 0.92 (3 May 95) +- don't assume that char is signed (problem on SGI) +- Clear bit buffer when starting a stored block +- no memcpy on Pyramid +- suppressed inftest.c +- optimized fill_window, put longest_match inline for gcc +- optimized inflate on stored blocks. +- untabify all sources to simplify patches + +Changes in 0.91 (2 May 95) +- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h +- Document the memory requirements in zconf.h +- added "make install" +- fix sync search logic in inflateSync +- deflate(Z_FULL_FLUSH) now works even if output buffer too short +- after inflateSync, don't scare people with just "lo world" +- added support for DJGPP + +Changes in 0.9 (1 May 95) +- don't assume that zalloc clears the allocated memory (the TurboC bug + was Mark's bug after all :) +- let again gzread copy uncompressed data unchanged (was working in 0.71) +- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented +- added a test of inflateSync in example.c +- moved MAX_WBITS to zconf.h because users might want to change that. +- document explicitly that zalloc(64K) on MSDOS must return a normalized + pointer (zero offset) +- added Makefiles for Microsoft C, Turbo C, Borland C++ +- faster crc32() + +Changes in 0.8 (29 April 95) +- added fast inflate (inffast.c) +- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this + is incompatible with previous versions of zlib which returned Z_OK. +- work around a TurboC compiler bug (bad code for b << 0, see infutil.h) + (actually that was not a compiler bug, see 0.81 above) +- gzread no longer reads one extra byte in certain cases +- In gzio destroy(), don't reference a freed structure +- avoid many warnings for MSDOS +- avoid the ERROR symbol which is used by MS Windows + +Changes in 0.71 (14 April 95) +- Fixed more MSDOS compilation problems :( There is still a bug with + TurboC large model. + +Changes in 0.7 (14 April 95) +- Added full inflate support. +- Simplified the crc32() interface. The pre- and post-conditioning + (one's complement) is now done inside crc32(). WARNING: this is + incompatible with previous versions; see zlib.h for the new usage. + +Changes in 0.61 (12 April 95) +- workaround for a bug in TurboC. example and minigzip now work on MSDOS. + +Changes in 0.6 (11 April 95) +- added minigzip.c +- added gzdopen to reopen a file descriptor as gzFile +- added transparent reading of non-gziped files in gzread. +- fixed bug in gzread (don't read crc as data) +- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose). +- don't allocate big arrays in the stack (for MSDOS) +- fix some MSDOS compilation problems + +Changes in 0.5: +- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but + not yet Z_FULL_FLUSH. +- support decompression but only in a single step (forced Z_FINISH) +- added opaque object for zalloc and zfree. +- added deflateReset and inflateReset +- added a variable zlib_version for consistency checking. +- renamed the 'filter' parameter of deflateInit2 as 'strategy'. + Added Z_FILTERED and Z_HUFFMAN_ONLY constants. + +Changes in 0.4: +- avoid "zip" everywhere, use zlib instead of ziplib. +- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush + if compression method == 8. +- added adler32 and crc32 +- renamed deflateOptions as deflateInit2, call one or the other but not both +- added the method parameter for deflateInit2. +- added inflateInit2 +- simplied considerably deflateInit and inflateInit by not supporting + user-provided history buffer. This is supported only in deflateInit2 + and inflateInit2. + +Changes in 0.3: +- prefix all macro names with Z_ +- use Z_FINISH instead of deflateEnd to finish compression. +- added Z_HUFFMAN_ONLY +- added gzerror() diff --git a/pcsx2/3rdparty/zlib/Makefile.am b/pcsx2/3rdparty/zlib/Makefile.am new file mode 100644 index 0000000000..ff26496e22 --- /dev/null +++ b/pcsx2/3rdparty/zlib/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = libpcsx2zlib.a + +libpcsx2zlib_a_SOURCES = \ +adler32.c crc32.c deflate.h inffast.c inflate.c inftrees.h trees.h zlib.h \ +crc32.h gzio.c inffast.h inflate.h uncompr.c zutil.c \ +compress.c deflate.c infback.c inffixed.h inftrees.c trees.c zconf.h zutil.h diff --git a/pcsx2/3rdparty/zlib/README b/pcsx2/3rdparty/zlib/README new file mode 100644 index 0000000000..758cc50020 --- /dev/null +++ b/pcsx2/3rdparty/zlib/README @@ -0,0 +1,125 @@ +ZLIB DATA COMPRESSION LIBRARY + +zlib 1.2.3 is a general purpose data compression library. All the code is +thread safe. The data format used by the zlib library is described by RFCs +(Request for Comments) 1950 to 1952 in the files +http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) +and rfc1952.txt (gzip format). These documents are also available in other +formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example +of the library is given in the file example.c which also tests that the library +is working correctly. Another example is given in the file minigzip.c. The +compression library itself is composed of all source files except example.c and +minigzip.c. + +To compile all files and run the test program, follow the instructions given at +the top of Makefile. In short "make test; make install" should work for most +machines. For Unix: "./configure; make test; make install". For MSDOS, use one +of the special makefiles such as Makefile.msc. For VMS, use make_vms.com. + +Questions about zlib should be sent to , or to Gilles Vollant + for the Windows DLL version. The zlib home page is +http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem, +please check this site to verify that you have the latest version of zlib; +otherwise get the latest version and check whether the problem still exists or +not. + +PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking +for help. + +Mark Nelson wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://dogma.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.2.3 are documented in the file ChangeLog. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit +http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html +See the zlib home page http://www.zlib.org for details. + +A Perl interface to zlib written by Paul Marquess is in the +CPAN (Comprehensive Perl Archive Network) sites +http://www.cpan.org/modules/by-module/Compress/ + +A Python interface to zlib written by A.M. Kuchling is +available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries is +availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html + +An experimental package to read and write files in .zip format, written on top +of zlib by Gilles Vollant , is available in the +contrib/minizip directory of zlib. + + +Notes for some targets: + +- For Windows DLL versions, please see win32/DLL_FAQ.txt + +- For 64-bit Irix, deflate.c must be compiled without any optimization. With + -O, one libpng test fails. The test works in 32 bit mode (with the -n32 + compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works + when compiled with cc. + +- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is + necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with + other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For PalmOs, see http://palmzlib.sourceforge.net/ + +- When building a shared, i.e. dynamic library on Mac OS X, the library must be + installed before testing (do "make install" before "make test"), since the + library location is specified in the library. + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-2004 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. Please +read the FAQ for more information on the distribution of modified source +versions. diff --git a/pcsx2/3rdparty/zlib/adler32.c b/pcsx2/3rdparty/zlib/adler32.c new file mode 100644 index 0000000000..f201d6701e --- /dev/null +++ b/pcsx2/3rdparty/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/pcsx2/3rdparty/zlib/compress.c b/pcsx2/3rdparty/zlib/compress.c new file mode 100644 index 0000000000..d37e84f5e3 --- /dev/null +++ b/pcsx2/3rdparty/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/pcsx2/3rdparty/zlib/crc32.c b/pcsx2/3rdparty/zlib/crc32.c new file mode 100644 index 0000000000..32814c20c8 --- /dev/null +++ b/pcsx2/3rdparty/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/pcsx2/3rdparty/zlib/crc32.h b/pcsx2/3rdparty/zlib/crc32.h new file mode 100644 index 0000000000..5de49bc978 --- /dev/null +++ b/pcsx2/3rdparty/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/pcsx2/3rdparty/zlib/deflate.c b/pcsx2/3rdparty/zlib/deflate.c new file mode 100644 index 0000000000..529f716b7a --- /dev/null +++ b/pcsx2/3rdparty/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/pcsx2/3rdparty/zlib/deflate.h b/pcsx2/3rdparty/zlib/deflate.h new file mode 100644 index 0000000000..222c53e043 --- /dev/null +++ b/pcsx2/3rdparty/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/pcsx2/3rdparty/zlib/gzio.c b/pcsx2/3rdparty/zlib/gzio.c new file mode 100644 index 0000000000..2de9a36441 --- /dev/null +++ b/pcsx2/3rdparty/zlib/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id$ */ + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_BEST_SPEED; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/pcsx2/3rdparty/zlib/infback.c b/pcsx2/3rdparty/zlib/infback.c new file mode 100644 index 0000000000..1e03e1bab0 --- /dev/null +++ b/pcsx2/3rdparty/zlib/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/pcsx2/3rdparty/zlib/inffast.c b/pcsx2/3rdparty/zlib/inffast.c new file mode 100644 index 0000000000..fa31cad905 --- /dev/null +++ b/pcsx2/3rdparty/zlib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/pcsx2/3rdparty/zlib/inffast.h b/pcsx2/3rdparty/zlib/inffast.h new file mode 100644 index 0000000000..614fa7877d --- /dev/null +++ b/pcsx2/3rdparty/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/pcsx2/3rdparty/zlib/inffixed.h b/pcsx2/3rdparty/zlib/inffixed.h new file mode 100644 index 0000000000..423d5c5b50 --- /dev/null +++ b/pcsx2/3rdparty/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/pcsx2/3rdparty/zlib/inflate.c b/pcsx2/3rdparty/zlib/inflate.c new file mode 100644 index 0000000000..33ea902928 --- /dev/null +++ b/pcsx2/3rdparty/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/pcsx2/3rdparty/zlib/inflate.h b/pcsx2/3rdparty/zlib/inflate.h new file mode 100644 index 0000000000..fbbc871432 --- /dev/null +++ b/pcsx2/3rdparty/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/pcsx2/3rdparty/zlib/inftrees.c b/pcsx2/3rdparty/zlib/inftrees.c new file mode 100644 index 0000000000..38ded81c36 --- /dev/null +++ b/pcsx2/3rdparty/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/pcsx2/3rdparty/zlib/inftrees.h b/pcsx2/3rdparty/zlib/inftrees.h new file mode 100644 index 0000000000..dc0fd567ea --- /dev/null +++ b/pcsx2/3rdparty/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/pcsx2/3rdparty/zlib/trees.c b/pcsx2/3rdparty/zlib/trees.c new file mode 100644 index 0000000000..7a04802862 --- /dev/null +++ b/pcsx2/3rdparty/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/pcsx2/3rdparty/zlib/trees.h b/pcsx2/3rdparty/zlib/trees.h new file mode 100644 index 0000000000..1ca868b848 --- /dev/null +++ b/pcsx2/3rdparty/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/pcsx2/3rdparty/zlib/uncompr.c b/pcsx2/3rdparty/zlib/uncompr.c new file mode 100644 index 0000000000..ad6db0a67c --- /dev/null +++ b/pcsx2/3rdparty/zlib/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/pcsx2/3rdparty/zlib/zconf.h b/pcsx2/3rdparty/zlib/zconf.h new file mode 100644 index 0000000000..e3b0c962e3 --- /dev/null +++ b/pcsx2/3rdparty/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/pcsx2/3rdparty/zlib/zlib.h b/pcsx2/3rdparty/zlib/zlib.h new file mode 100644 index 0000000000..62d0e4675b --- /dev/null +++ b/pcsx2/3rdparty/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/pcsx2/3rdparty/zlib/zutil.c b/pcsx2/3rdparty/zlib/zutil.c new file mode 100644 index 0000000000..0f4bd7871d --- /dev/null +++ b/pcsx2/3rdparty/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/pcsx2/3rdparty/zlib/zutil.h b/pcsx2/3rdparty/zlib/zutil.h new file mode 100644 index 0000000000..0e3a9a0638 --- /dev/null +++ b/pcsx2/3rdparty/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 9 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/pcsx2/AlignedMalloc.cpp b/pcsx2/AlignedMalloc.cpp new file mode 100644 index 0000000000..7ab8ca4e98 --- /dev/null +++ b/pcsx2/AlignedMalloc.cpp @@ -0,0 +1,72 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// This module contains implementations of _aligned_malloc for platforms that don't have +// it built into their CRT/libc. + +#include "PrecompiledHeader.h" +#include "System.h" + + +struct AlignedMallocHeader +{ + u32 size; // size of the allocated buffer (minus alignment and header) + void* baseptr; // offset of the original allocated pointer +}; + +static const uint headsize = sizeof(AlignedMallocHeader); + +void* __fastcall pcsx2_aligned_malloc(size_t size, size_t align) +{ + jASSUME( align < 0x10000 ); + + u8* p = (u8*)malloc(size+align+headsize); + + // start alignment calculations from past the header. + uptr pasthead = (uptr)(p+headsize); + uptr aligned = (pasthead + align-1) & ~(align-1); + + AlignedMallocHeader* header = (AlignedMallocHeader*)(aligned-headsize); + jASSUME( (uptr)header >= (uptr)p ); + + header->baseptr = p; + header->size = size; + + return (void*)aligned; +} + +void* __fastcall pcsx2_aligned_realloc(void* handle, size_t size, size_t align) +{ + if( handle == NULL ) return NULL; + jASSUME( align < 0x10000 ); + + AlignedMallocHeader* header = (AlignedMallocHeader*)((uptr)handle - headsize); + + void* newbuf = pcsx2_aligned_malloc( size, align ); + memcpy_fast( newbuf, handle, std::min( size, header->size ) ); + + free( header->baseptr ); + return newbuf; +} + +__forceinline void pcsx2_aligned_free(void* pmem) +{ + if( pmem == NULL ) return; + AlignedMallocHeader* header = (AlignedMallocHeader*)((uptr)pmem - headsize); + free( header->baseptr ); +} diff --git a/pcsx2/CDVD.cpp b/pcsx2/CDVD.cpp new file mode 100644 index 0000000000..fa06c19367 --- /dev/null +++ b/pcsx2/CDVD.cpp @@ -0,0 +1,2171 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include "PsxCommon.h" +#include "CDVDiso.h" + +static cdvdStruct cdvd; +static int cdCaseopen; + +/* +Interrupts - values are flag bits. + +0x00 No interrupt +0x01 Data Ready +0x02 Command Complete +0x03 Acknowledge (reserved) +0x04 End of Data Detected +0x05 Error Detected +0x06 Drive Not Ready + +In limited experimentation I found that PS2 apps respond actively to use of the +'Data Ready' flag -- in that they'll almost immediately initiate a DMA transfer +after receiving an Irq with that as the cause. But the question is, of course, +*when* to use it. Adding it into some locations of CDVD reading only slowed +games down and broke things. + +Using Drive Not Ready also invokes basic error handling from the Iop Bios, but +without proper emulation of the cdvd status flag it also tends to break things. + +*/ + +enum CdvdIrqId +{ + Irq_None = 0 +, Irq_DataReady = 0 +, Irq_CommandComplete +, Irq_Acknowledge +, Irq_EndOfData +, Irq_Error +, Irq_NotReady + +}; + +/* is cdvd.Status only for NCMDS? (linuzappz) */ +enum cdvdStatus +{ + CDVD_STATUS_NONE = 0x00, // not sure ;) + CDVD_STATUS_SEEK_COMPLETE = 0x0A, +}; + +// Cdvd actions tell the emulator how and when to respond to certain requests. +// Actions are handled by the cdvdInterrupt() +enum cdvdActions +{ + cdvdAction_None = 0 +, cdvdAction_Seek +, cdvdAction_Standby +, cdvdAction_Stop +, cdvdAction_Break +, cdvdAction_Read // note: not used yet. +}; + +////////////////////////////////////////////////////////////////////////// +// -- Cdvd Block Read Cycle Timings -- +// These timings are based on a median average block read speed. In theory the read +// speeds differ based on the location of the sector being read (outer rings have +// a different read speed from inner rings). But for our purposes an average is good +// enough, since all of Pcsx2's instruction cycle counting is hardly accurate anyway. + +// Morale of the story: don't get too caught up micro-managing your cycle timings. :) + +// Note: DVD read times are modified to be faster, because games seem to be a lot more +// concerned with accurate(ish) seek delays and less concerned with actual block read speeds. + +static const uint PSX_CD_READSPEED = 153600; // 1 Byte Time @ x1 (150KB = cd x 1) +static const uint PSX_DVD_READSPEED = 1382400 + 256000; // normal is 1 Byte Time @ x1 (1350KB = dvd x 1). + +enum CDVD_MODE_TYPE +{ + MODE_DVDROM, + MODE_CDROM +}; + +// if a seek is within this many blocks, read instead of seek. +// I picked 9 as an arbitrary value. Not sure what the real PS2 uses. +static const int Cdvd_Contigious_Seek = 9; +static const uint Cdvd_Avg_SeekCycles = (PSXCLK*40) / 1000; // average number of cycles per seek (40ms) + + + +static const char *mg_zones[8] = {"Japan", "USA", "Europe", "Oceania", "Asia", "Russia", "China", "Mexico"}; + +static const char *nCmdName[0x100]= { + "CdSync", "CdNop", "CdStandby", "CdStop", + "CdPause", "CdSeek", "CdRead", "CdReadCDDA", + "CdReadDVDV", "CdGetToc", "", "NCMD_B", + "CdReadKey", "", "sceCdReadXCDDA", "sceCdChgSpdlCtrl", +}; + +static const char *sCmdName[0x100]= { + "", "sceCdGetDiscType", "sceCdReadSubQ", "subcommands",//sceCdGetMecaconVersion, read/write console id, read renewal date + "", "sceCdTrayState", "sceCdTrayCtrl", "", + "sceCdReadClock", "sceCdWriteClock", "sceCdReadNVM", "sceCdWriteNVM", + "sceCdSetHDMode", "", "", "sceCdPowerOff", + "", "", "sceCdReadILinkID", "sceCdWriteILinkID", /*10*/ + "sceAudioDigitalOut", "sceForbidDVDP", "sceAutoAdjustCtrl", "sceCdReadModelNumber", + "sceWriteModelNumber", "sceCdForbidCD", "sceCdBootCertify", "sceCdCancelPOffRdy", + "sceCdBlueLEDCtl", "", "sceRm2Read", "sceRemote2_7",//Rm2PortGetConnection? + "sceRemote2_6", "sceCdWriteWakeUpTime", "sceCdReadWakeUpTime", "", /*20*/ + "sceCdRcBypassCtl", "", "", "", + "", "sceCdNoticeGameStart", "", "", + "sceCdXBSPowerCtl", "sceCdXLEDCtl", "sceCdBuzzerCtl", "", + "", "sceCdSetMediumRemoval", "sceCdGetMediumRemoval", "sceCdXDVRPReset", /*30*/ + "", "", "__sceCdReadRegionParams", "__sceCdReadMAC", + "__sceCdWriteMAC", "", "", "", + "", "", "__sceCdWriteRegionParams", "", + "sceCdOpenConfig", "sceCdReadConfig", "sceCdWriteConfig", "sceCdCloseConfig", /*40*/ + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", /*50*/ + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", /*60*/ + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", /*70*/ + "", "", "", "", + "", "", "", "", + "", "", "", "", + "mechacon_auth_0x80", "mechacon_auth_0x81", "mechacon_auth_0x82", "mechacon_auth_0x83", /*80*/ + "mechacon_auth_0x84", "mechacon_auth_0x85", "mechacon_auth_0x86", "mechacon_auth_0x87", + "mechacon_auth_0x88", "", "", "", + "", "sceMgWriteData", "sceMgReadData", "mechacon_auth_0x8F", + "sceMgWriteHeaderStart", "sceMgReadBITLength", "sceMgWriteDatainLength", "sceMgWriteDataoutLength", /*90*/ + "sceMgReadKbit", "sceMgReadKbit2", "sceMgReadKcon", "sceMgReadKcon2", + "sceMgReadIcvPs2", "", "", "", + "", "", "", "", + /*A0, no sCmds above?*/ +}; + +// NVM (eeprom) layout info +struct NVMLayout { + u32 biosVer; // bios version that this eeprom layout is for + s32 config0; // offset of 1st config block + s32 config1; // offset of 2nd config block + s32 config2; // offset of 3rd config block + s32 consoleId; // offset of console id (?) + s32 ilinkId; // offset of ilink id (ilink mac address) + s32 modelNum; // offset of ps2 model number (eg "SCPH-70002") + s32 regparams; // offset of RegionParams for PStwo + s32 mac; // offset of the value written to 0xFFFE0188 and 0xFFFE018C on PStwo +}; + +#define NVM_FORMAT_MAX 2 +NVMLayout nvmlayouts[NVM_FORMAT_MAX] = +{ + {0x000, 0x280, 0x300, 0x200, 0x1C8, 0x1C0, 0x1A0, 0x180, 0x198}, // eeproms from bios v0.00 and up + {0x146, 0x270, 0x2B0, 0x200, 0x1C8, 0x1E0, 0x1B0, 0x180, 0x198}, // eeproms from bios v1.70 and up +}; + +#define btoi(b) ((b)/16*10 + (b)%16) /* BCD to u_char */ +#define itob(i) ((i)/10*16 + (i)%10) /* u_char to BCD */ + +#define SetResultSize(size) \ + cdvd.ResultC = size; cdvd.ResultP = 0; \ + cdvd.sDataIn&=~0x40; + +#define CDVDREAD_INT(eCycle) PSX_INT(IopEvt_CdvdRead, eCycle) +static void CDVD_INT(int eCycle) +{ + if( eCycle == 0 ) + cdvdActionInterrupt(); + else + PSX_INT(IopEvt_Cdvd, eCycle); +} + +// Sets the cdvd IRQ and the reason for the IRQ, and signals the IOP for a branch +// test (which will cause the exception to be handled). +static void cdvdSetIrq( uint id = (1< 0) { if (ptr[i] == '.') break; i--; } + ptr[i+1] = '\0'; + strcat(file, "MEC"); + + // if file doesnt exist, create empty one + fd = fopen(file, "r+b"); + if (fd == NULL) { + SysPrintf("MEC File Not Found , Creating Blank File\n"); + fd = fopen(file, "wb"); + if (fd == NULL) { + Msgbox::Alert("_cdvdOpenMechaVer: Error creating %s", params file); + exit(1); + } + fputc(0x03, fd); + fputc(0x06, fd); + fputc(0x02, fd); + fputc(0x00, fd); + } + return fd; +} + +s32 cdvdGetMechaVer(u8* ver) +{ + FILE* fd = _cdvdOpenMechaVer(); + if (fd == NULL) return 1; + fseek(fd, 0, SEEK_SET); + fread(ver, 1, 4, fd); + fclose(fd); + return 0; +} + +FILE *_cdvdOpenNVM() { + char *ptr; + int i; + string Bios; + char file[g_MaxPath]; + FILE* fd; + + // get the name of the bios file + Path::Combine( Bios, Config.BiosDir, Config.Bios ); + + // use the bios filename to get the name of the nvm file + // [TODO] : Upgrade this to use std::string! + + strcpy( file, Bios.c_str() ); + ptr = file; i = (int)strlen(file); + while (i > 0) { if (ptr[i] == '.') break; i--; } + ptr[i+1] = '\0'; + strcat(file, "NVM"); + + // if file doesnt exist, create empty one + fd = fopen(file, "r+b"); + if (fd == NULL) { + SysPrintf("NVM File Not Found , Creating Blank File\n"); + fd = fopen(file, "wb"); + if (fd == NULL) { + Msgbox::Alert("_cdvdOpenNVM: Error creating %s", params file); + exit(1); + } + for (i=0; i<1024; i++) fputc(0, fd); + } + return fd; +} + +// +// the following 'cdvd' functions all return 0 if successful +// + +s32 cdvdReadNVM(u8 *dst, int offset, int bytes) { + FILE* fd = _cdvdOpenNVM(); + if (fd == NULL) return 1; + fseek(fd, offset, SEEK_SET); + fread(dst, 1, bytes, fd); + fclose(fd); + return 0; +} +s32 cdvdWriteNVM(const u8 *src, int offset, int bytes) { + FILE* fd = _cdvdOpenNVM(); + if (fd == NULL) return 1; + fseek(fd, offset, SEEK_SET); + fwrite(src, 1, bytes, fd); + fclose(fd); + return 0; +} + +#define GET_NVM_DATA(buff, offset, size, fmtOffset) getNvmData(buff, offset, size, BiosVersion, offsetof(NVMLayout, fmtOffset)) +#define SET_NVM_DATA(buff, offset, size, fmtOffset) setNvmData(buff, offset, size, BiosVersion, offsetof(NVMLayout, fmtOffset)) + +s32 getNvmData(u8* buffer, s32 offset, s32 size, u32 biosVersion, s32 fmtOffset) +{ + // find the correct bios version + NVMLayout* nvmLayout = NULL; + s32 nvmIdx; + for(nvmIdx=0; nvmIdx= cdvd.CNumBlocks) + return 1; + else if( + ((cdvd.COffset == 0) && (cdvd.CBlockIndex >= 4))|| + ((cdvd.COffset == 1) && (cdvd.CBlockIndex >= 2))|| + ((cdvd.COffset == 2) && (cdvd.CBlockIndex >= 7)) + ) + { + memzero_ptr<16>(config); + return 0; + } + + // get config data + if(cdvd.COffset == 0) + return GET_NVM_DATA(config, (cdvd.CBlockIndex++)*16, 16, config0); else + if(cdvd.COffset == 2) + return GET_NVM_DATA(config, (cdvd.CBlockIndex++)*16, 16, config2); + else + return GET_NVM_DATA(config, (cdvd.CBlockIndex++)*16, 16, config1); +} +s32 cdvdWriteConfig(const u8* config) +{ + // make sure its in write mode + if(cdvd.CReadWrite != 1) + return 1; + // check if block index is in bounds + else if(cdvd.CBlockIndex >= cdvd.CNumBlocks) + return 1; + else if( + ((cdvd.COffset == 0) && (cdvd.CBlockIndex >= 4))|| + ((cdvd.COffset == 1) && (cdvd.CBlockIndex >= 2))|| + ((cdvd.COffset == 2) && (cdvd.CBlockIndex >= 7)) + ) + return 0; + + // get config data + if(cdvd.COffset == 0) + return SET_NVM_DATA(config, (cdvd.CBlockIndex++)*16, 16, config0); else + if(cdvd.COffset == 2) + return SET_NVM_DATA(config, (cdvd.CBlockIndex++)*16, 16, config2); + else + return SET_NVM_DATA(config, (cdvd.CBlockIndex++)*16, 16, config1); +} + + +void cdvdReadKey(u8 arg0, u16 arg1, u32 arg2, u8* key) { + char str[g_MaxPath]; + int numbers; + int letters; + unsigned int key_0_3; + unsigned char key_4; + unsigned char key_14; + char exeName[12]; + + // get main elf name + GetPS2ElfName(str); + sprintf(exeName, "%c%c%c%c%c%c%c%c%c%c%c",str[8],str[9],str[10],str[11],str[12],str[13],str[14],str[15],str[16],str[17],str[18]); + DevCon::Notice("exeName = %s", params &str[8]); + + // convert the number characters to a real 32bit number + numbers = ((((exeName[5] - '0'))*10000) + + (((exeName[ 6] - '0'))*1000) + + (((exeName[ 7] - '0'))*100) + + (((exeName[ 9] - '0'))*10) + + (((exeName[10] - '0'))*1) ); + + // combine the lower 7 bits of each char + // to make the 4 letters fit into a single u32 + letters = (int)((exeName[3]&0x7F)<< 0) | + (int)((exeName[2]&0x7F)<< 7) | + (int)((exeName[1]&0x7F)<<14) | + (int)((exeName[0]&0x7F)<<21); + + // calculate magic numbers + key_0_3 = ((numbers & 0x1FC00) >> 10) | ((0x01FFFFFF & letters) << 7); // numbers = 7F letters = FFFFFF80 + key_4 = ((numbers & 0x0001F) << 3) | ((0x0E000000 & letters) >> 25); // numbers = F8 letters = 07 + key_14 = ((numbers & 0x003E0) >> 2) | 0x04; // numbers = F8 extra = 04 unused = 03 + + // clear key values + memzero_ptr<16>(key); + + // store key values + key[ 0] = (key_0_3&0x000000FF)>> 0; + key[ 1] = (key_0_3&0x0000FF00)>> 8; + key[ 2] = (key_0_3&0x00FF0000)>>16; + key[ 3] = (key_0_3&0xFF000000)>>24; + key[ 4] = key_4; + + if(arg2 == 75) + { + key[14] = key_14; + key[15] = 0x05; + } + else if(arg2 == 3075) + { + key[15] = 0x01; + } + else if(arg2 == 4246) + { + // 0x0001F2F707 = sector 0x0001F2F7 dec 0x07 + key[ 0] = 0x07; + key[ 1] = 0xF7; + key[ 2] = 0xF2; + key[ 3] = 0x01; + key[ 4] = 0x00; + key[15] = 0x01; + } + else + { + key[15] = 0x01; + } + + Console::WriteLn( "CDVD.KEY = %02X,%02X,%02X,%02X,%02X,%02X,%02X", params + cdvd.Key[0],cdvd.Key[1],cdvd.Key[2],cdvd.Key[3],cdvd.Key[4],cdvd.Key[14],cdvd.Key[15] ); + + // Now's a good time to reload the ELF info... + if( ElfCRC == 0 ) + { + ElfCRC = loadElfCRC( str ); + ElfApplyPatches(); + LoadGameSpecificSettings(); + } +} + +s32 cdvdGetToc(void* toc) +{ + s32 ret = CDVDgetTOC(toc); + if(ret == -1) ret = 0x80; + return ret; +/* + cdvdTN diskInfo; + cdvdTD trackInfo; + u8 _time[3]; + u32 type; + int i, err; + + //Param[0] is 0 for CdGetToc and any value for cdvdman_call19 + //the code below handles only CdGetToc! + //if(cdvd.Param[0]==0x01) + //{ + SysPrintf("CDGetToc Param[0]=%d, Param[1]=%d\n",cdvd.Param[0],cdvd.Param[1]); + //} + type = CDVDgetDiskType(); + if (CDVDgetTN(&diskInfo) == -1) { diskInfo.etrack = 0;diskInfo.strack = 1; } + if (CDVDgetTD(0, &trackInfo) == -1) trackInfo.lsn = 0; + + if (type == CDVD_TYPE_CDDA) { + PSXMu8(HW_DMA3_MADR+ 0) = 0x01; + } else + if (type == CDVD_TYPE_PS2DVD) { + if (trackInfo.lsn >= (2048*1024)) { // dual sided + PSXMu8(HW_DMA3_MADR+ 0) = 0x24; + } else { + PSXMu8(HW_DMA3_MADR+ 0) = 0x04; + } + } else + if (type == CDVD_TYPE_PS2CD) { + PSXMu8(HW_DMA3_MADR+ 0) = 0x41; + } + + if (PSXMu8(HW_DMA3_MADR+ 0) & 0x04) { + PSXMu8(HW_DMA3_MADR+ 1) = 0x02; + PSXMu8(HW_DMA3_MADR+ 2) = 0xF2; + PSXMu8(HW_DMA3_MADR+ 3) = 0x00; + + if (PSXMu8(HW_DMA3_MADR+ 0) & 0x20) { + PSXMu8(HW_DMA3_MADR+ 4) = 0x41; + PSXMu8(HW_DMA3_MADR+ 5) = 0x95; + } else { + PSXMu8(HW_DMA3_MADR+ 4) = 0x86; + PSXMu8(HW_DMA3_MADR+ 5) = 0x72; + } + PSXMu8(HW_DMA3_MADR+ 6) = 0x00; + PSXMu8(HW_DMA3_MADR+ 7) = 0x00; + PSXMu8(HW_DMA3_MADR+ 8) = 0x00; + PSXMu8(HW_DMA3_MADR+ 9) = 0x00; + PSXMu8(HW_DMA3_MADR+10) = 0x00; + PSXMu8(HW_DMA3_MADR+11) = 0x00; + + PSXMu8(HW_DMA3_MADR+12) = 0x00; + PSXMu8(HW_DMA3_MADR+13) = 0x00; + PSXMu8(HW_DMA3_MADR+14) = 0x00; + PSXMu8(HW_DMA3_MADR+15) = 0x00; + + PSXMu8(HW_DMA3_MADR+16) = 0x00; + PSXMu8(HW_DMA3_MADR+17) = 0x03; + PSXMu8(HW_DMA3_MADR+18) = 0x00; + PSXMu8(HW_DMA3_MADR+19) = 0x00; + + } else { + PSXMu8(HW_DMA3_MADR+ 1) = 0x00; + PSXMu8(HW_DMA3_MADR+ 2) = 0xA0; + PSXMu8(HW_DMA3_MADR+ 7) = itob(diskInfo.strack);//Number of FirstTrack + + PSXMu8(HW_DMA3_MADR+12) = 0xA1; + PSXMu8(HW_DMA3_MADR+17) = itob(diskInfo.etrack);//Number of LastTrack + + PSXMu8(HW_DMA3_MADR+22) = 0xA2;//DiskLength + LSNtoMSF(_time, trackInfo.lsn); + PSXMu8(HW_DMA3_MADR+27) = itob(_time[2]); + PSXMu8(HW_DMA3_MADR+28) = itob(_time[1]); + + for (i=diskInfo.strack; i<=diskInfo.etrack; i++) { + err=CDVDgetTD(i, &trackInfo); + LSNtoMSF(_time, trackInfo.lsn); + PSXMu8(HW_DMA3_MADR+i*10+30) = trackInfo.type; + PSXMu8(HW_DMA3_MADR+i*10+32) = err == -1 ? 0 : itob(i); //number + PSXMu8(HW_DMA3_MADR+i*10+37) = itob(_time[2]); + PSXMu8(HW_DMA3_MADR+i*10+38) = itob(_time[1]); + PSXMu8(HW_DMA3_MADR+i*10+39) = itob(_time[0]); + } + } +*/ +} + +s32 cdvdReadSubQ(s32 lsn, cdvdSubQ* subq) +{ + s32 ret = CDVDreadSubQ(lsn, subq); + if(ret == -1) ret = 0x80; + return ret; +} + +s32 cdvdCtrlTrayOpen() +{ + s32 ret = CDVDctrlTrayOpen(); + if(ret == -1) ret = 0x80; + return ret; +} + +s32 cdvdCtrlTrayClose() +{ + s32 ret = CDVDctrlTrayClose(); + if(ret == -1) ret = 0x80; + return ret; +} + +// Modified by (efp) - 16/01/2006 +// checks if tray was opened since last call to this func +s32 cdvdGetTrayStatus() +{ + s32 ret = CDVDgetTrayStatus(); + // get current tray state + if (cdCaseopen) return(CDVD_TRAY_OPEN); + + + if (ret == -1) return(CDVD_TRAY_CLOSE); + return(ret); +} + +// Note: Is tray status being kept as a var here somewhere? +// cdvdNewDiskCB() can update it's status as well... + +// Modified by (efp) - 16/01/2006 +__forceinline void cdvdGetDiskType() +{ + // defs 0.9.0 + if(CDVDnewDiskCB || cdvd.Type != CDVD_TYPE_NODISC) return; + + // defs.0.8.1 + if(cdvdGetTrayStatus() == CDVD_TRAY_OPEN) + { + cdvd.Type = CDVD_TYPE_NODISC; + return; + } + + cdvd.Type = CDVDgetDiskType(); + if (cdvd.Type == CDVD_TYPE_PS2CD) // && needReset == 1) + { + char str[g_MaxPath]; + if (GetPS2ElfName(str) == 1) + { + cdvd.Type = CDVD_TYPE_PSCD; + } // ENDIF- Does the SYSTEM.CNF file only say "BOOT="? PS1 CD then. + } // ENDIF- Is the type listed as a PS2 CD? +} // END cdvdGetDiskType() + + +// check whether disc is single or dual layer +// if its dual layer, check what the disctype is and what sector number +// layer1 starts at +// +// args: gets value for dvd type (0=single layer, 1=ptp, 2=otp) +// gets value for start lsn of layer1 +// returns: 1 if on dual layer disc +// 0 if not on dual layer disc +s32 cdvdReadDvdDualInfo(s32* dualType, u32* layer1Start) +{ + u8 toc[2064]; + *dualType = 0; + *layer1Start = 0; + + // if error getting toc, settle for single layer disc ;) + if(cdvdGetToc(toc)) + return 0; + if(toc[14] & 0x60) + { + if(toc[14] & 0x10) + { + // otp dvd + *dualType = 2; + *layer1Start = (toc[25]<<16) + (toc[26]<<8) + (toc[27]) - 0x30000 + 1; + } + else + { + // ptp dvd + *dualType = 1; + *layer1Start = (toc[21]<<16) + (toc[22]<<8) + (toc[23]) - 0x30000 + 1; + } + } + else + { + // single layer dvd + *dualType = 0; + *layer1Start = (toc[21]<<16) + (toc[22]<<8) + (toc[23]) - 0x30000 + 1; + } + + return 1; +} + + +#include + +static uint cdvdBlockReadTime( CDVD_MODE_TYPE mode ) +{ + return (PSXCLK * cdvd.BlockSize) / (((mode==MODE_CDROM) ? PSX_CD_READSPEED : PSX_DVD_READSPEED) * cdvd.Speed); +} + +void cdvdReset() +{ +#ifdef _WIN32 + SYSTEMTIME st; + //Get and set the internal clock to time + GetSystemTime(&st); +#else + time_t traw; + struct tm* ptlocal; + time(&traw); + ptlocal = localtime(&traw); +#endif + + memzero_obj(cdvd); + + cdvd.Type = CDVD_TYPE_NODISC; + cdvd.Spinning = false; + + cdvd.sDataIn = 0x40; + cdvd.Ready = 0x4e; + cdCaseopen = 0; + cdvd.Speed = 4; + cdvd.BlockSize = 2064; + cdvd.Action = cdvdAction_None; + cdvd.ReadTime = cdvdBlockReadTime( MODE_DVDROM ); + + // any random valid date will do + cdvd.RTC.hour = 1; + cdvd.RTC.day = 25; + cdvd.RTC.month = 5; + cdvd.RTC.year = 7; //2007 + +#ifndef _DEBUG +#ifdef _WIN32 + cdvd.RTC.second = (u8)(st.wSecond); + cdvd.RTC.minute = (u8)(st.wMinute); + cdvd.RTC.hour = (u8)(st.wHour+1)%24; + cdvd.RTC.day = (u8)(st.wDay); + cdvd.RTC.month = (u8)(st.wMonth); + cdvd.RTC.year = (u8)(st.wYear - 2000); +#else + cdvd.RTC.second = ptlocal->tm_sec; + cdvd.RTC.minute = ptlocal->tm_min; + cdvd.RTC.hour = ptlocal->tm_hour; + cdvd.RTC.day = ptlocal->tm_mday; + cdvd.RTC.month = ptlocal->tm_mon; + cdvd.RTC.year = ptlocal->tm_year; +#endif +#endif + +} + +struct Freeze_v10Compat +{ + u8 Action; + u32 SeekToSector; + u32 ReadTime; + bool Spinning; +}; + +void SaveState::cdvdFreeze() +{ + if( GetVersion() <= 0x10 ) + { + // the old cdvd struct didn't save the last few items. + FreezeLegacy( cdvd, sizeof(Freeze_v10Compat) ); + cdvd.SeekToSector = cdvd.Sector; + cdvd.Action = cdvdAction_None; + cdvd.ReadTime = cdvdBlockReadTime( MODE_DVDROM ); + cdvd.Spinning = true; + } + else + Freeze( cdvd ); + + if( IsLoading() ) + { + // Make sure the Cdvd plugin has the expected track loaded into the buffer. + // If cdvd.Readed is cleared it means we need to load the SeekToSector (ie, a + // seek is in progress!) + + if( cdvd.Reading ) + cdvd.RErr = CDVDreadTrack( cdvd.Readed ? cdvd.Sector : cdvd.SeekToSector, cdvd.ReadMode); + } +} + +// Modified by (efp) - 16/01/2006 +void cdvdNewDiskCB() +{ + cdvd.Type = CDVDgetDiskType(); + if(cdvd.Type == CDVD_TYPE_PS2CD) { + char str[g_MaxPath]; + if(GetPS2ElfName(str) == 1) { + cdvd.Type = CDVD_TYPE_PSCD; + } // ENDIF- Does the SYSTEM.CNF file only say "BOOT="? PS1 CD then. + } +} + +void mechaDecryptBytes(unsigned char* buffer, int size) +{ + int i; + + int shiftAmount = (cdvd.decSet>>4) & 7; + int doXor = (cdvd.decSet) & 1; + int doShift = (cdvd.decSet) & 2; + + for (i=0; i>shiftAmount) | (buffer[i]<<(8-shiftAmount)); + } +} + +int cdvdReadSector() { + s32 bcr; + + CDR_LOG("SECTOR %d (BCR %x;%x)\n", cdvd.Sector, HW_DMA3_BCR_H16, HW_DMA3_BCR_L16); + + bcr = (HW_DMA3_BCR_H16 * HW_DMA3_BCR_L16) *4; + if (bcr < cdvd.BlockSize) { + CDR_LOG( "READBLOCK: bcr < cdvd.BlockSize; %x < %x\n", bcr, cdvd.BlockSize ); + if (HW_DMA3_CHCR & 0x01000000) { + HW_DMA3_CHCR &= ~0x01000000; + psxDmaInterrupt(3); + } + return -1; + } + + const u32 madr = HW_DMA3_MADR; + + // if raw dvd sector 'fill in the blanks' + if (cdvd.BlockSize == 2064) { + // get info on dvd type and layer1 start + u32 layer1Start; + s32 dualType; + s32 layerNum; + u32 lsn = cdvd.Sector; + + cdvdReadDvdDualInfo(&dualType, &layer1Start); + + if((dualType == 1) && (lsn >= layer1Start)) { + // dual layer ptp disc + layerNum = 1; + lsn = lsn-layer1Start + 0x30000; + } else if((dualType == 2) && (lsn >= layer1Start)) { + // dual layer otp disc + layerNum = 1; + lsn = ~(layer1Start+0x30000 - 1); + } else { + // single layer disc + // or on first layer of dual layer disc + layerNum = 0; + lsn += 0x30000; + } // ENDLONGIF- Assumed the other dualType is 0. + + PSXMu8(madr+0) = 0x20 | layerNum; + PSXMu8(madr+1) = (u8)(lsn >> 16); + PSXMu8(madr+2) = (u8)(lsn >> 8); + PSXMu8(madr+3) = (u8)(lsn ); + + // sector IED (not calculated at present) + PSXMu8(madr+4) = 0; + PSXMu8(madr+5) = 0; + + // sector CPR_MAI (not calculated at present) + PSXMu8(madr+ 6) = 0; + PSXMu8(madr+ 7) = 0; + PSXMu8(madr+ 8) = 0; + PSXMu8(madr+ 9) = 0; + PSXMu8(madr+10) = 0; + PSXMu8(madr+11) = 0; + + // normal 2048 bytes of sector data + memcpy_fast(PSXM(madr+12), cdr.pTransfer, 2048); + + // 4 bytes of edc (not calculated at present) + PSXMu8(madr+2060) = 0; + PSXMu8(madr+2061) = 0; + PSXMu8(madr+2062) = 0; + PSXMu8(madr+2063) = 0; + } else { + // normal read + memcpy_fast((u8*)PSXM(madr), cdr.pTransfer, cdvd.BlockSize); + } + // decrypt sector's bytes + if(cdvd.decSet) + mechaDecryptBytes((u8*)PSXM(madr), cdvd.BlockSize); + + // Added a clear after memory write .. never seemed to be necessary before but *should* + // be more correct. (air) + psxCpu->Clear( madr, cdvd.BlockSize/4); + +// SysPrintf("sector %x;%x;%x\n", PSXMu8(madr+0), PSXMu8(madr+1), PSXMu8(madr+2)); + + HW_DMA3_BCR_H16-= (cdvd.BlockSize / (HW_DMA3_BCR_L16*4)); + HW_DMA3_MADR+= cdvd.BlockSize; + + return 0; +} + +// inlined due to being referenced in only one place. +__forceinline void cdvdActionInterrupt() +{ + switch( cdvd.Action ) + { + case cdvdAction_Seek: + case cdvdAction_Standby: + cdvd.Spinning = true; + cdvd.Ready = 0x40; + cdvd.Sector = cdvd.SeekToSector; + cdvd.Status = CDVD_STATUS_SEEK_COMPLETE; + break; + + case cdvdAction_Stop: + cdvd.Spinning = false; + cdvd.Ready = 0x40; + cdvd.Sector = 0; + cdvd.Status = 0; + break; + + case cdvdAction_Break: + // Make sure the cdvd action state is pretty well cleared: + cdvd.Reading = 0; + cdvd.Readed = 0; + cdvd.Ready = 0x4e; // should be 0x40 or something else? + cdvd.Status = 0; + cdvd.RErr = 0; + cdvd.nCommand = 0; + break; + } + cdvd.Action = cdvdAction_None; + + cdvd.PwOff |= 1< Scheduling block read interrupt at iopcycle=%8.8x.\n", + psxRegs.cycle + cdvd.ReadTime ); + + CDVDREAD_INT(cdvd.ReadTime); + return; + } + + if (cdvd.Reading == 1) { + if (cdvd.RErr == 0) { + cdr.pTransfer = CDVDgetBuffer(); + } else cdr.pTransfer = NULL; + if (cdr.pTransfer == NULL) { + cdvd.RetryCntP++; + Console::Error("CDVD READ ERROR, sector=%d", params cdvd.Sector); + if (cdvd.RetryCntP <= cdvd.RetryCnt) { + cdvd.RErr = CDVDreadTrack(cdvd.Sector, cdvd.ReadMode); + CDVDREAD_INT(cdvd.ReadTime); + return; + } + } + cdvd.Reading = 0; + } + + if (cdvdReadSector() == -1) { + assert( (int)cdvd.ReadTime > 0 ); + CDVDREAD_INT(cdvd.ReadTime); + return; + } + + cdvd.Sector++; + + if (--cdvd.nSectors <= 0) + { + cdvd.PwOff |= 1<= cdvd.ResultC) cdvd.sDataIn|= 0x40; + ret = cdvd.Result[cdvd.ResultP-1]; + } + } + CDR_LOG("cdvdRead18(SDataOut) %x (ResultC=%d, ResultP=%d)\n", ret, cdvd.ResultC, cdvd.ResultP); + return ret; +} + +u8 cdvdRead20(void) { + CDR_LOG("cdvdRead20(Key0) %x\n", cdvd.Key[0]); + + return cdvd.Key[0]; +} + +u8 cdvdRead21(void) { + CDR_LOG("cdvdRead21(Key1) %x\n", cdvd.Key[1]); + + return cdvd.Key[1]; +} + +u8 cdvdRead22(void) { + CDR_LOG("cdvdRead22(Key2) %x\n", cdvd.Key[2]); + + return cdvd.Key[2]; +} + +u8 cdvdRead23(void) { + CDR_LOG("cdvdRead23(Key3) %x\n", cdvd.Key[3]); + + return cdvd.Key[3]; +} + +u8 cdvdRead24(void) { + CDR_LOG("cdvdRead24(Key4) %x\n", cdvd.Key[4]); + + return cdvd.Key[4]; +} + +u8 cdvdRead28(void) { + CDR_LOG("cdvdRead28(Key5) %x\n", cdvd.Key[5]); + + return cdvd.Key[5]; +} + +u8 cdvdRead29(void) { + CDR_LOG("cdvdRead29(Key6) %x\n", cdvd.Key[6]); + + return cdvd.Key[6]; +} + +u8 cdvdRead2A(void) { + CDR_LOG("cdvdRead2A(Key7) %x\n", cdvd.Key[7]); + + return cdvd.Key[7]; +} + +u8 cdvdRead2B(void) { + CDR_LOG("cdvdRead2B(Key8) %x\n", cdvd.Key[8]); + + return cdvd.Key[8]; +} + +u8 cdvdRead2C(void) { + CDR_LOG("cdvdRead2C(Key9) %x\n", cdvd.Key[9]); + + return cdvd.Key[9]; +} + +u8 cdvdRead30(void) { + CDR_LOG("cdvdRead30(Key10) %x\n", cdvd.Key[10]); + + return cdvd.Key[10]; +} + +u8 cdvdRead31(void) { + CDR_LOG("cdvdRead31(Key11) %x\n", cdvd.Key[11]); + + return cdvd.Key[11]; +} + +u8 cdvdRead32(void) { + CDR_LOG("cdvdRead32(Key12) %x\n", cdvd.Key[12]); + + return cdvd.Key[12]; +} + +u8 cdvdRead33(void) { + CDR_LOG("cdvdRead33(Key13) %x\n", cdvd.Key[13]); + + return cdvd.Key[13]; +} + +u8 cdvdRead34(void) { + CDR_LOG("cdvdRead34(Key14) %x\n", cdvd.Key[14]); + + return cdvd.Key[14]; +} + +u8 cdvdRead38(void) { // valid parts of key data (first and last are valid) + CDR_LOG("cdvdRead38(KeysValid) %x\n", cdvd.Key[15]); + + return cdvd.Key[15]; +} + +u8 cdvdRead39(void) { // KEY-XOR + CDR_LOG("cdvdRead39(KeyXor) %x\n", cdvd.KeyXor); + + return cdvd.KeyXor; +} + +u8 cdvdRead3A(void) { // DEC_SET + CDR_LOG("cdvdRead3A(DecSet) %x\n", cdvd.decSet); + + SysPrintf("DecSet Read: %02X\n", cdvd.decSet); + return cdvd.decSet; +} + + +// Returns the number of IOP cycles until the event completes. +static uint cdvdStartSeek( uint newsector ) +{ + cdvd.SeekToSector = newsector; + + uint delta = abs(cdvd.SeekToSector - cdvd.Sector); + uint seektime; + + cdvd.Ready = 0; + cdvd.Reading = 0; + cdvd.Readed = 0; + cdvd.Status = 0; + + if( !cdvd.Spinning ) + { + CDR_LOG( "CdSpinUp > Simulating CdRom Spinup Time, and seek to sector %d\n", cdvd.SeekToSector ); + seektime = PSXCLK / 3; // 333ms delay + cdvd.Spinning = true; + } + else if( (Cdvd_Contigious_Seek >= 0) && (delta >= Cdvd_Contigious_Seek) ) + { + CDR_LOG( "CdSeek Begin > to sector %d, from %d - delta=%d\n", cdvd.SeekToSector, cdvd.Sector, delta ); + seektime = Cdvd_Avg_SeekCycles; + } + else + { + CDR_LOG( "CdSeek Begin > Contigious block without seek - delta=%d sectors\n", delta ); + + // seektime is the time it takes to read to the destination block: + seektime = delta * cdvd.ReadTime; + + if( delta == 0 ) + { + cdvd.Status = CDVD_STATUS_SEEK_COMPLETE; + cdvd.Readed = 1; + cdvd.RetryCntP = 0; + + // setting Readed to zero skips the seek logic, which means the next call to + // cdvdReadInterrupt will load a block. So make sure it's properly scheduled + // based on sector read speeds: + seektime = cdvd.ReadTime; + } + } + + return seektime; +} + +void cdvdWrite04(u8 rt) { // NCOMMAND + CDR_LOG("cdvdWrite04: NCMD %s (%x) (ParamP = %x)\n", nCmdName[rt], rt, cdvd.ParamP); + + cdvd.nCommand = rt; + cdvd.Status = CDVD_STATUS_NONE; + cdvd.PwOff = Irq_None; // good or bad? + + switch (rt) { + case 0x00: // CdSync + case 0x01: // CdNop_ + cdvdSetIrq(); + break; + + case 0x02: // CdStandby + + // Seek to sector zero. The cdvdStartSeek function will simulate + // spinup times if needed. + + DevCon::Notice( "CdStandby : %d", params rt ); + cdvd.Action = cdvdAction_Standby; + cdvd.ReadTime = cdvdBlockReadTime( MODE_DVDROM ); + CDVD_INT( cdvdStartSeek( 0 ) ); + break; + + case 0x03: // CdStop + DevCon::Notice( "CdStop : %d", params rt ); + cdvd.Action = cdvdAction_Stop; + CDVD_INT( PSXCLK / 6 ); // 166ms delay? + break; + + // from an emulation point of view there is not much need to do anything for this one + case 0x04: // CdPause + cdvdSetIrq(); + break; + + case 0x05: // CdSeek + cdvd.Action = cdvdAction_Seek; + cdvd.ReadTime = cdvdBlockReadTime( MODE_DVDROM ); + CDVD_INT( cdvdStartSeek( *(uint*)(cdvd.Param+0) ) ); + break; + + case 0x06: // CdRead + cdvd.SeekToSector = *(uint*)(cdvd.Param+0); + cdvd.nSectors = *(int*)(cdvd.Param+4); + cdvd.RetryCnt = (cdvd.Param[8] == 0) ? 0x100 : cdvd.Param[8]; + cdvd.SpindlCtrl = cdvd.Param[9]; + cdvd.Speed = 24; + switch (cdvd.Param[10]) { + case 2: cdvd.ReadMode = CDVD_MODE_2340; cdvd.BlockSize = 2340; break; + case 1: cdvd.ReadMode = CDVD_MODE_2328; cdvd.BlockSize = 2328; break; + case 0: default: cdvd.ReadMode = CDVD_MODE_2048; cdvd.BlockSize = 2048; break; + } + + CDR_LOG( "CdRead > startSector=%d, nSectors=%d, RetryCnt=%x, Speed=%x(%x), ReadMode=%x(%x) (1074=%x)\n", + cdvd.Sector, cdvd.nSectors, cdvd.RetryCnt, cdvd.Speed, cdvd.Param[9], cdvd.ReadMode, cdvd.Param[10], psxHu32(0x1074)); + + if (Config.cdvdPrint) SysPrintf("CdRead: Reading Sector %d(%d Blocks of Size %d) at Speed=%dx\n", cdvd.Sector, cdvd.nSectors,cdvd.BlockSize,cdvd.Speed); + + cdvd.ReadTime = cdvdBlockReadTime( MODE_CDROM ); + CDVDREAD_INT( cdvdStartSeek( cdvd.SeekToSector ) ); + + // Read-ahead by telling the plugin about the track now. + // This helps improve performance on actual from-cd emulation + // (ie, not using the hard drive) + cdvd.RErr = CDVDreadTrack( cdvd.SeekToSector, cdvd.ReadMode ); + + // Set the reading block flag. If a seek is pending then Readed will + // take priority in the handler anyway. If the read is contigeous then + // this'll skip the seek delay. + cdvd.Reading = 1; + break; + + case 0x07: // CdReadCDDA + case 0x0E: // CdReadXCDDA + cdvd.SeekToSector = *(int*)(cdvd.Param+0); + cdvd.nSectors = *(int*)(cdvd.Param+4); + if (cdvd.Param[8] == 0) cdvd.RetryCnt = 0x100; + else cdvd.RetryCnt = cdvd.Param[8]; + cdvd.SpindlCtrl = cdvd.Param[9]; + switch (cdvd.Param[9]) { + case 0x01: cdvd.Speed = 1; break; + case 0x02: cdvd.Speed = 2; break; + case 0x03: cdvd.Speed = 4; break; + case 0x04: cdvd.Speed = 12; break; + default: cdvd.Speed = 24; break; + } + switch (cdvd.Param[10]) { + case 1: cdvd.ReadMode = CDVD_MODE_2368; cdvd.BlockSize = 2368; break; + case 2: + case 0: cdvd.ReadMode = CDVD_MODE_2352; cdvd.BlockSize = 2352; break; + } + + CDR_LOG( "CdReadCDDA > startSector=%d, nSectors=%d, RetryCnt=%x, Speed=%xx(%x), ReadMode=%x(%x) (1074=%x)\n", + cdvd.Sector, cdvd.nSectors, cdvd.RetryCnt, cdvd.Speed, cdvd.Param[9], cdvd.ReadMode, cdvd.Param[10], psxHu32(0x1074)); + + if (Config.cdvdPrint) SysPrintf("CdAudioRead: Reading Sector %d(%d Blocks of Size %d) at Speed=%dx\n", cdvd.Sector, cdvd.nSectors,cdvd.BlockSize,cdvd.Speed); + + cdvd.ReadTime = cdvdBlockReadTime( MODE_CDROM ); + CDVDREAD_INT( cdvdStartSeek( cdvd.SeekToSector ) ); + + // Read-ahead by telling the plugin about the track now. + // This helps improve performance on actual from-cd emulation + // (ie, not using the hard drive) + cdvd.RErr = CDVDreadTrack( cdvd.SeekToSector, cdvd.ReadMode ); + + // Set the reading block flag. If a seek is pending then Readed will + // take priority in the handler anyway. If the read is contigeous then + // this'll skip the seek delay. + cdvd.Reading = 1; + break; + + case 0x08: // DvdRead + cdvd.SeekToSector = *(int*)(cdvd.Param+0); + cdvd.nSectors = *(int*)(cdvd.Param+4); + if (cdvd.Param[8] == 0) cdvd.RetryCnt = 0x100; + else cdvd.RetryCnt = cdvd.Param[8]; + cdvd.SpindlCtrl = cdvd.Param[9]; + cdvd.Speed = 4; + cdvd.ReadMode = CDVD_MODE_2048; + cdvd.BlockSize = 2064; // Why oh why was it 2064 + + CDR_LOG( "DvdRead > startSector=%d, nSectors=%d, RetryCnt=%x, Speed=%x(%x), ReadMode=%x(%x) (1074=%x)\n", + cdvd.Sector, cdvd.nSectors, cdvd.RetryCnt, cdvd.Speed, cdvd.Param[9], cdvd.ReadMode, cdvd.Param[10], psxHu32(0x1074)); + + if (Config.cdvdPrint) SysPrintf("DvdRead: Reading Sector %d(%d Blocks of Size %d) at Speed=%dx\n", cdvd.Sector, cdvd.nSectors,cdvd.BlockSize,cdvd.Speed); + + cdvd.ReadTime = cdvdBlockReadTime( MODE_DVDROM ); + CDVDREAD_INT( cdvdStartSeek( cdvd.SeekToSector ) ); + + // Read-ahead by telling the plugin about the track now. + // This helps improve performance on actual from-cd emulation + // (ie, not using the hard drive) + cdvd.RErr = CDVDreadTrack( cdvd.SeekToSector, cdvd.ReadMode ); + + // Set the reading block flag. If a seek is pending then Readed will + // take priority in the handler anyway. If the read is contigeous then + // this'll skip the seek delay. + cdvd.Reading = 1; + break; + + case 0x09: // CdGetToc & cdvdman_call19 + //Param[0] is 0 for CdGetToc and any value for cdvdman_call19 + //the code below handles only CdGetToc! + //if(cdvd.Param[0]==0x01) + //{ + DevCon::WriteLn("CDGetToc Param[0]=%d, Param[1]=%d", params cdvd.Param[0],cdvd.Param[1]); + //} + cdvdGetToc( PSXM( HW_DMA3_MADR ) ); + cdvdSetIrq( (1< remote key code + SetResultSize(5); + cdvd.Result[0] = 0x00; + cdvd.Result[1] = 0x14; + cdvd.Result[2] = 0x00; + cdvd.Result[3] = 0x00; + cdvd.Result[4] = 0x00; + break; + +// case 0x1F: // sceRemote2_7 (2:1) - cdvdman_call117 +// break; + + case 0x20: // sceRemote2_6 (0:3) // 00 01 00 + SetResultSize(3); + cdvd.Result[0] = 0x00; + cdvd.Result[1] = 0x01; + cdvd.Result[2] = 0x00; + break; + +// case 0x21: // sceCdWriteWakeUpTime (8:1) +// break; + + case 0x22: // sceCdReadWakeUpTime (0:10) + SetResultSize(10); + cdvd.Result[0] = 0; + cdvd.Result[1] = 0; + cdvd.Result[2] = 0; + cdvd.Result[3] = 0; + cdvd.Result[4] = 0; + cdvd.Result[5] = 0; + cdvd.Result[6] = 0; + cdvd.Result[7] = 0; + cdvd.Result[8] = 0; + cdvd.Result[9] = 0; + break; + + case 0x24: // sceCdRCBypassCtrl (1:1) - In V10 Bios + // FIXME: because PRId<0x23, the bit 0 of sio2 don't get updated 0xBF808284 + SetResultSize(1); + cdvd.Result[0] = 0; + break; + +// case 0x25: // cdvdman_call120 (1:1) - In V10 Bios +// break; + +// case 0x26: // cdvdman_call128 (0,3) - In V10 Bios +// break; + +// case 0x27: // cdvdman_call148 (0:13) - In V10 Bios +// break; + +// case 0x28: // cdvdman_call150 (1:1) - In V10 Bios +// break; + + case 0x29: //sceCdNoticeGameStart (1:1) + SetResultSize(1); + cdvd.Result[0] = 0; + break; + +// case 0x2C: //sceCdXBSPowerCtl (2:2) +// break; + +// case 0x2D: //sceCdXLEDCtl (2:2) +// break; + +// case 0x2E: //sceCdBuzzerCtl (0:1) +// break; + +// case 0x2F: //cdvdman_call167 (16:1) +// break; + +// case 0x30: //cdvdman_call169 (1:9) +// break; + + case 0x31: //sceCdSetMediumRemoval (1:1) + SetResultSize(1); + cdvd.Result[0] = 0; + break; + + case 0x32: //sceCdGetMediumRemoval (0:2) + SetResultSize(2); + cdvd.Result[0] = 0; + cdvd.Result[0] = 0; + break; + +// case 0x33: //sceCdXDVRPReset (1:1) +// break; + + case 0x36: //cdvdman_call189 [__sceCdReadRegionParams - made up name] (0:15) i think it is 16, not 15 + SetResultSize(15); + cdvdGetMechaVer(&cdvd.Result[1]); + cdvd.Result[0] = cdvdReadRegionParams(&cdvd.Result[3]);//size==8 + SysPrintf("REGION PARAMS = %s %s\n", mg_zones[cdvd.Result[1]], &cdvd.Result[3]); + cdvd.Result[1] = 1 << cdvd.Result[1]; //encryption zone; see offset 0x1C in encrypted headers + ////////////////////////////////////////// + cdvd.Result[2] = 0; //?? +// cdvd.Result[3] == ROMVER[4] == *0xBFC7FF04 +// cdvd.Result[4] == OSDVER[4] == CAP Jjpn, Aeng, Eeng, Heng, Reng, Csch, Kkor? +// cdvd.Result[5] == OSDVER[5] == small +// cdvd.Result[6] == OSDVER[6] == small +// cdvd.Result[7] == OSDVER[7] == small +// cdvd.Result[8] == VERSTR[0x22] == *0xBFC7FF52 +// cdvd.Result[9] == DVDID J U O E A R C M +// cdvd.Result[10]== 0; //?? + cdvd.Result[11] = 0; //?? + cdvd.Result[12] = 0; //?? + ////////////////////////////////////////// + cdvd.Result[13] = 0; //0xFF - 77001 + cdvd.Result[14] = 0; //?? + break; + + case 0x37: //called from EECONF [sceCdReadMAC - made up name] (0:9) + SetResultSize(9); + cdvd.Result[0] = cdvdReadMAC(&cdvd.Result[1]); + break; + + case 0x38: //used to fix the MAC back after accidentally trashed it :D [sceCdWriteMAC - made up name] (8:1) + SetResultSize(1); + cdvd.Result[0] = cdvdWriteMAC(&cdvd.Param[0]); + break; + + case 0x3E: //[__sceCdWriteRegionParams - made up name] (15:1) [Florin: hum, i was expecting 14:1] + SetResultSize(1); + cdvd.Result[0] = cdvdWriteRegionParams(&cdvd.Param[2]); + break; + + case 0x40: // CdOpenConfig (3:1) + cdvd.CReadWrite = cdvd.Param[0]; + cdvd.COffset = cdvd.Param[1]; + cdvd.CNumBlocks = cdvd.Param[2]; + cdvd.CBlockIndex= 0; + SetResultSize(1); + cdvd.Result[0] = 0; + break; + + case 0x41: // CdReadConfig (0:16) + SetResultSize(16); + cdvdReadConfig(&cdvd.Result[0]); + break; + + case 0x42: // CdWriteConfig (16:1) + SetResultSize(1); + cdvd.Result[0] = cdvdWriteConfig(&cdvd.Param[0]); + break; + + case 0x43: // CdCloseConfig (0:1) + cdvd.CReadWrite = 0; + cdvd.COffset = 0; + cdvd.CNumBlocks = 0; + cdvd.CBlockIndex= 0; + SetResultSize(1); + cdvd.Result[0] = 0; + break; + + case 0x80: // secrman: __mechacon_auth_0x80 + cdvd.mg_datatype = 0;//data + SetResultSize(1);//in:1 + cdvd.Result[0] = 0; + break; + + case 0x81: // secrman: __mechacon_auth_0x81 + cdvd.mg_datatype = 0;//data + SetResultSize(1);//in:1 + cdvd.Result[0] = 0; + break; + + case 0x82: // secrman: __mechacon_auth_0x82 + SetResultSize(1);//in:16 + cdvd.Result[0] = 0; + break; + + case 0x83: // secrman: __mechacon_auth_0x83 + SetResultSize(1);//in:8 + cdvd.Result[0] = 0; + break; + + case 0x84: // secrman: __mechacon_auth_0x84 + SetResultSize(1+8+4);//in:0 + cdvd.Result[0] = 0; + + cdvd.Result[1] = 0x21; + cdvd.Result[2] = 0xdc; + cdvd.Result[3] = 0x31; + cdvd.Result[4] = 0x96; + cdvd.Result[5] = 0xce; + cdvd.Result[6] = 0x72; + cdvd.Result[7] = 0xe0; + cdvd.Result[8] = 0xc8; + + cdvd.Result[9] = 0x69; + cdvd.Result[10] = 0xda; + cdvd.Result[11] = 0x34; + cdvd.Result[12] = 0x9b; + break; + + case 0x85: // secrman: __mechacon_auth_0x85 + SetResultSize(1+4+8);//in:0 + cdvd.Result[0] = 0; + + cdvd.Result[1] = 0xeb; + cdvd.Result[2] = 0x01; + cdvd.Result[3] = 0xc7; + cdvd.Result[4] = 0xa9; + + cdvd.Result[ 5] = 0x3f; + cdvd.Result[ 6] = 0x9c; + cdvd.Result[ 7] = 0x5b; + cdvd.Result[ 8] = 0x19; + cdvd.Result[ 9] = 0x31; + cdvd.Result[10] = 0xa0; + cdvd.Result[11] = 0xb3; + cdvd.Result[12] = 0xa3; + break; + + case 0x86: // secrman: __mechacon_auth_0x86 + SetResultSize(1);//in:16 + cdvd.Result[0] = 0; + break; + + case 0x87: // secrman: __mechacon_auth_0x87 + SetResultSize(1);//in:8 + cdvd.Result[0] = 0; + break; + + case 0x8D: // sceMgWriteData + SetResultSize(1);//in:length<=16 + if (cdvd.mg_size + cdvd.ParamC > cdvd.mg_maxsize) + cdvd.Result[0] = 0x80; + else{ + memcpy_fast(cdvd.mg_buffer + cdvd.mg_size, cdvd.Param, cdvd.ParamC); + cdvd.mg_size += cdvd.ParamC; + cdvd.Result[0] = 0; // 0 complete ; 1 busy ; 0x80 error + } + break; + + case 0x8E: // sceMgReadData + SetResultSize( std::min(16, cdvd.mg_size) ); + memcpy_fast(cdvd.Result, cdvd.mg_buffer, cdvd.ResultC); + cdvd.mg_size -= cdvd.ResultC; + memcpy_fast(cdvd.mg_buffer, cdvd.mg_buffer+cdvd.ResultC, cdvd.mg_size); + break; + + case 0x88: // secrman: __mechacon_auth_0x88 //for now it is the same; so, fall;) + case 0x8F: // secrman: __mechacon_auth_0x8F + SetResultSize(1);//in:0 + if (cdvd.mg_datatype == 1){// header data + u64* psrc, *pdst; + int bit_ofs, i; + + if (cdvd.mg_maxsize != cdvd.mg_size) goto fail_pol_cal; + if (cdvd.mg_size < 0x20) goto fail_pol_cal; + if (cdvd.mg_size != *(u16*)&cdvd.mg_buffer[0x14]) goto fail_pol_cal; + SysPrintf("[MG] ELF_size=0x%X Hdr_size=0x%X unk=0x%X flags=0x%X count=%d zones=", + *(u32*)&cdvd.mg_buffer[0x10], *(u16*)&cdvd.mg_buffer[0x14], *(u16*)&cdvd.mg_buffer[0x16], + *(u16*)&cdvd.mg_buffer[0x18], *(u16*)&cdvd.mg_buffer[0x1A]); + for (i=0; i<8; i++) + if (cdvd.mg_buffer[0x1C] & (1<> 0) & 0xFF; + cdvd.Result[2] = (cdvd.mg_size >> 8) & 0xFF; + break; + + case 0x92: // sceMgWriteDatainLength + cdvd.mg_size = 0; + cdvd.mg_datatype = 0;//data (encrypted) + cdvd.mg_maxsize = cdvd.Param[0] | (((int)cdvd.Param[1])<<8); + SetResultSize(1);//in:2 + cdvd.Result[0] = 0; // 0 complete ; 1 busy ; 0x80 error + break; + + case 0x93: // sceMgWriteDataoutLength + SetResultSize(1);//in:2 + if (((cdvd.Param[0] | (((int)cdvd.Param[1])<<8)) == cdvd.mg_size) && (cdvd.mg_datatype == 0)){ + cdvd.mg_maxsize = 0; // don't allow any write + cdvd.Result[0] = 0; // 0 complete ; 1 busy ; 0x80 error + }else + cdvd.Result[0] = 0x80; + break; + + case 0x94: // sceMgReadKbit - read first half of BIT key + SetResultSize(1+8);//in:0 + cdvd.Result[0] = 0; + + ((int*)(cdvd.Result+1))[0] = ((int*)cdvd.mg_kbit)[0]; + ((int*)(cdvd.Result+1))[1] = ((int*)cdvd.mg_kbit)[1];//memcpy(cdvd.Result+1, cdvd.mg_kbit, 8); + break; + + case 0x95: // sceMgReadKbit2 - read second half of BIT key + SetResultSize(1+8);//in:0 + cdvd.Result[0] = 0; + ((int*)(cdvd.Result+1))[0] = ((int*)(cdvd.mg_kbit+8))[0]; + ((int*)(cdvd.Result+1))[1] = ((int*)(cdvd.mg_kbit+8))[1];//memcpy(cdvd.Result+1, cdvd.mg_kbit+8, 8); + break; + + case 0x96: // sceMgReadKcon - read first half of content key + SetResultSize(1+8);//in:0 + cdvd.Result[0] = 0; + ((int*)(cdvd.Result+1))[0] = ((int*)cdvd.mg_kcon)[0]; + ((int*)(cdvd.Result+1))[1] = ((int*)cdvd.mg_kcon)[1];//memcpy(cdvd.Result+1, cdvd.mg_kcon, 8); + break; + + case 0x97: // sceMgReadKcon2 - read second half of content key + SetResultSize(1+8);//in:0 + cdvd.Result[0] = 0; + ((int*)(cdvd.Result+1))[0] = ((int*)(cdvd.mg_kcon+8))[0]; + ((int*)(cdvd.Result+1))[1] = ((int*)(cdvd.mg_kcon+8))[1];//memcpy(cdvd.Result+1, cdvd.mg_kcon+8, 8); + break; + + default: + // fake a 'correct' command + SysPrintf("SCMD Unknown %x\n", rt); + SetResultSize(1); //in:0 + cdvd.Result[0] = 0; // 0 complete ; 1 busy ; 0x80 error + break; + } + //SysPrintf("SCMD - %x\n", rt); + cdvd.ParamP = 0; cdvd.ParamC = 0; +} + +void cdvdWrite17(u8 rt) { // SDATAIN + CDR_LOG("cdvdWrite17(SDataIn) %x\n", rt); + + if (cdvd.ParamP < 32) { + cdvd.Param[cdvd.ParamP++] = rt; + cdvd.ParamC++; + } +} + +void cdvdWrite18(u8 rt) { // SDATAOUT + CDR_LOG("cdvdWrite18(SDataOut) %x\n", rt); + SysPrintf("*PCSX2* SDATAOUT\n"); +} + +void cdvdWrite3A(u8 rt) { // DEC-SET + CDR_LOG("cdvdWrite3A(DecSet) %x\n", rt); + cdvd.decSet = rt; + SysPrintf("DecSet Write: %02X\n", cdvd.decSet); +} \ No newline at end of file diff --git a/pcsx2/CDVD.h b/pcsx2/CDVD.h new file mode 100644 index 0000000000..aabbc52b69 --- /dev/null +++ b/pcsx2/CDVD.h @@ -0,0 +1,144 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __CDVD_H__ +#define __CDVD_H__ + +#include "PsxCommon.h" + +struct cdvdRTC { + u8 status; + u8 second; + u8 minute; + u8 hour; + u8 pad; + u8 day; + u8 month; + u8 year; +}; + +struct cdvdStruct { + u8 nCommand; + u8 Ready; + u8 Error; + u8 PwOff; + u8 Status; + u8 Type; + u8 sCommand; + u8 sDataIn; + u8 sDataOut; + u8 HowTo; + + u8 Param[32]; + u8 Result[32]; + + u8 ParamC; + u8 ParamP; + u8 ResultC; + u8 ResultP; + + u8 CBlockIndex; + u8 COffset; + u8 CReadWrite; + u8 CNumBlocks; + + int RTCcount; + cdvdRTC RTC; + + u32 Sector; + int nSectors; + int Readed; + int Reading; + int ReadMode; + int BlockSize; // Total bytes transfered at 1x speed + int Speed; + int RetryCnt; + int RetryCntP; + int RErr; + int SpindlCtrl; + + u8 Key[16]; + u8 KeyXor; + u8 decSet; + + u8 mg_buffer[65536]; + int mg_size; + int mg_maxsize; + int mg_datatype;//0-data(encrypted); 1-header + u8 mg_kbit[16];//last BIT key 'seen' + u8 mg_kcon[16];//last content key 'seen' + + u8 Action; // the currently scheduled emulated action + u32 SeekToSector; // Holds the destination sector during seek operations. + u32 ReadTime; // Avg. time to read one block of data (in Iop cycles) + bool Spinning; // indicates if the Cdvd is spinning or needs a spinup delay +}; + +void cdvdReset(); +void cdvdVsync(); +extern void cdvdActionInterrupt(); +extern void cdvdReadInterrupt(); +void cdvdNewDiskCB(); +u8 cdvdRead04(void); +u8 cdvdRead05(void); +u8 cdvdRead06(void); +u8 cdvdRead07(void); +u8 cdvdRead08(void); +u8 cdvdRead0A(void); +u8 cdvdRead0B(void); +u8 cdvdRead0C(void); +u8 cdvdRead0D(void); +u8 cdvdRead0E(void); +u8 cdvdRead0F(void); +u8 cdvdRead13(void); +u8 cdvdRead15(void); +u8 cdvdRead16(void); +u8 cdvdRead17(void); +u8 cdvdRead18(void); +u8 cdvdRead20(void); +u8 cdvdRead21(void); +u8 cdvdRead22(void); +u8 cdvdRead23(void); +u8 cdvdRead24(void); +u8 cdvdRead28(void); +u8 cdvdRead29(void); +u8 cdvdRead2A(void); +u8 cdvdRead2B(void); +u8 cdvdRead2C(void); +u8 cdvdRead30(void); +u8 cdvdRead31(void); +u8 cdvdRead32(void); +u8 cdvdRead33(void); +u8 cdvdRead34(void); +u8 cdvdRead38(void); +u8 cdvdRead39(void); +u8 cdvdRead3A(void); +void cdvdWrite04(u8 rt); +void cdvdWrite05(u8 rt); +void cdvdWrite06(u8 rt); +void cdvdWrite07(u8 rt); +void cdvdWrite08(u8 rt); +void cdvdWrite0A(u8 rt); +void cdvdWrite0F(u8 rt); +void cdvdWrite14(u8 rt); +void cdvdWrite16(u8 rt); +void cdvdWrite17(u8 rt); +void cdvdWrite18(u8 rt); +void cdvdWrite3A(u8 rt); + +#endif /* __CDVD_H__ */ diff --git a/pcsx2/CDVDiso.cpp b/pcsx2/CDVDiso.cpp new file mode 100644 index 0000000000..17bb7383b9 --- /dev/null +++ b/pcsx2/CDVDiso.cpp @@ -0,0 +1,818 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +/* + * Original code from libcdvd by Hiryu & Sjeep (C) 2002 + * Modified by Florin for PCSX2 emu + * Fixed CdRead by linuzappz + */ +#include "PrecompiledHeader.h" + +#include + +#include "CDVDiso.h" +#include "CDVDisodrv.h" + +struct dir_toc_data +{ + u32 start_LBA; + u32 num_sectors; + u32 num_entries; + u32 current_entry; + u32 current_sector; + u32 current_sector_offset; + u32 inc_dirs; + char extension_list[128+1]; +}; + +//static u8 cdVolDescriptor[2048]; +static dir_toc_data getDirTocData; +static cdVolDesc CDVolDesc; + +static const int JolietMaxPath = 1024; + +void _splitpath2(const char *constpath, char *dir, char *fname){ + // 255 char max path-length is an ISO9660 restriction + // we must change this for Joliet or relaxed iso restriction support + char pathcopy[JolietMaxPath+1]; + + char* slash; + + strncpy(pathcopy, constpath, 1024); + + slash = strrchr (pathcopy, '/'); + + // if the path doesn't contain a '/' then look for a '\' + if (!slash) + slash = strrchr (pathcopy, (int)'\\'); + + // if a slash was found + if (slash != NULL) + { + // null terminate the path + slash[0] = 0; + // and copy the path into 'dir' + strncpy(dir, pathcopy, 1024); + dir[255]=0; + + // copy the filename into 'fname' + strncpy(fname, slash+1, 128); + fname[128]=0; + } + else + { + dir[0] = 0; + + strncpy(fname, pathcopy, 128); + fname[128]=0; + } + +} + +// Used in findfile +//int tolower(int c); +int strcasecmp(const char *s1, const char *s2){ + while (*s1 != '\0' && tolower(*s1) == tolower(*s2)) + { + s1++; + s2++; + } + + return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2); +} + +// Copy a TOC Entry from the CD native format to our tidier format +void TocEntryCopy(TocEntry* tocEntry, const dirTocEntry* internalTocEntry){ + int i; + int filenamelen; + + tocEntry->fileSize = internalTocEntry->fileSize; + tocEntry->fileLBA = internalTocEntry->fileLBA; + tocEntry->fileProperties = internalTocEntry->fileProperties; + memcpy(tocEntry->date, internalTocEntry->dateStamp, 7); //TODO: Buffer read overrun, dateStamp is 6 bytes + + if (CDVolDesc.filesystemType == 2){ + // This is a Joliet Filesystem, so use Unicode to ISO string copy + + filenamelen = internalTocEntry->filenameLength/2; + + if (!(tocEntry->fileProperties & 0x02)){ + // strip the ;1 from the filename +// filenamelen -= 2;//(Florin) nah, do not strip ;1 + } + + for (i=0; i < filenamelen; i++) + tocEntry->filename[i] = internalTocEntry->filename[(i<<1)+1]; + + tocEntry->filename[filenamelen] = 0; + } + else{ + filenamelen = internalTocEntry->filenameLength; + + if (!(tocEntry->fileProperties & 0x02)){ + // strip the ;1 from the filename +// filenamelen -= 2;//(Florin) nah, do not strip ;1 + } + + // use normal string copy + strncpy(tocEntry->filename,internalTocEntry->filename,128); + tocEntry->filename[filenamelen] = 0; + } +} + +// Check if a TOC Entry matches our extension list +int TocEntryCompare(char* filename, char* extensions){ + static char ext_list[129]; + + char* token; + + char* ext_point; + + strncpy(ext_list,extensions,128); + ext_list[128]=0; + + token = strtok( ext_list, " ," ); + while( token != NULL ) + { + // if 'token' matches extension of 'filename' + // then return a match + ext_point = strrchr(filename,'.'); + + if (strnicmp(ext_point, token, strlen(token)) == 0) + return (TRUE); + + /* Get next token: */ + token = strtok( NULL, " ," ); + } + + // If not match found then return FALSE + return (FALSE); + +} + +#define CD_SECS 60 /* seconds per minute */ +#define CD_FRAMES 75 /* frames per second */ +#define CD_MSF_OFFSET 150 /* MSF numbering offset of first frame */ + +int CdRead(u32 lsn, u32 sectors, void *buf, CdRMode *mode){ + u32 i; + u8* buff; + int rmode; + + switch (mode->datapattern) { + case CdSecS2048: + rmode = CDVD_MODE_2048; break; + case CdSecS2328: + rmode = CDVD_MODE_2328; break; + case CdSecS2340: + rmode = CDVD_MODE_2340; break; + default: + return 0; + } + + for (i=0; idatapattern){ + case CdSecS2048: + memcpy_fast((void*)((uptr)buf+2048*i), buff, 2048);break;//only data + case CdSecS2328: + memcpy_fast((void*)((uptr)buf+2328*i), buff, 2328);break;//without sync & head & sub + case CdSecS2340: + memcpy_fast((void*)((uptr)buf+2340*i), buff, 2340);break;//without sync + } + } + return 1; +} + +int DvdRead(u32 lsn, u32 sectors, void *buf, CdRMode *mode){ + u32 i; + u8* buff; + + for (i=lsn; i<(lsn+sectors); i++){ + if (CDVDreadTrack(i, CDVD_MODE_2048)==-1) + return 0; + buff = CDVDgetBuffer(); + if (buff==NULL) return 0; + +// switch (mode->datapattern){ +// case CdSecS2064: + ((u32*)buf)[0] = i + 0x30000; + memcpy_fast((u8*)buf+12, buff, 2048); + buf = (char*)buf + 2064; break; +// default: +// return 0; +// } + } + + return 1; +} + +/************************************************************** +* The functions below are not exported for normal file-system * +* operations, but are used by the file-system operations, and * +* may also be exported for use via RPC * +**************************************************************/ + +int CDVD_GetVolumeDescriptor(void){ + // Read until we find the last valid Volume Descriptor + int volDescSector; + + cdVolDesc localVolDesc; + + DbgCon::WriteLn("CDVD_GetVolumeDescriptor called"); + + for (volDescSector = 16; volDescSector<20; volDescSector++) + { + CdRead(volDescSector,1,&localVolDesc,&cdReadMode); +// CdSync(0x00); + + // If this is still a volume Descriptor + if (strncmp((char*)localVolDesc.volID, "CD001", 5) == 0) + { + if ((localVolDesc.filesystemType == 1) || + (localVolDesc.filesystemType == 2)) + { + memcpy_fast(&CDVolDesc, &localVolDesc, sizeof(cdVolDesc)); + } + } + else + break; + } + + if (CDVolDesc.filesystemType == 1) + DbgCon::WriteLn( Color_Green, "CD FileSystem is ISO9660" ); + else if (CDVolDesc.filesystemType == 2) + DbgCon::WriteLn( Color_Green, "CD FileSystem is Joliet"); + else DbgCon::Notice("Could not detect CD FileSystem type"); + + // CdStop(); + + return TRUE; +} + +int CDVD_findfile(const char* fname, TocEntry* tocEntry){ + char filename[g_MaxPath+1]; + char pathname[JolietMaxPath+1]; + char toc[2048]; + char* dirname; + + TocEntry localTocEntry; // used for internal checking only + + int found_dir; + + int num_dir_sectors; + int current_sector; + + int dir_lba; + + dirTocEntry* tocEntryPointer; + + DbgCon::WriteLn("CDVD_findfile called"); + + //make sure we have good cdReadMode + cdReadMode.trycount = 0; + cdReadMode.spindlctrl = CdSpinStm; + cdReadMode.datapattern = CdSecS2048; + + _splitpath2(fname, pathname, filename); + + // Find the TOC for a specific directory + if (CDVD_GetVolumeDescriptor() != TRUE){ + RPC_LOG("Could not get CD Volume Descriptor\n"); + return -1; + } + + // Read the TOC of the root directory + if (CdRead(CDVolDesc.rootToc.tocLBA,1,toc,&cdReadMode) != TRUE){ + RPC_LOG("Couldn't Read from CD !\n"); + return -1; + } + //CdSync(0x00); + + // point the tocEntryPointer at the first real toc entry + tocEntryPointer = (dirTocEntry*)toc; + + num_dir_sectors = (tocEntryPointer->fileSize+2047) >> 11; //round up fix + current_sector = tocEntryPointer->fileLBA; + + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + + + localTocEntry.fileLBA = CDVolDesc.rootToc.tocLBA; + // while (there are more dir names in the path) + dirname = strtok( pathname, "\\/" ); + + while( dirname != NULL ) + { + found_dir = FALSE; +/* + while(tocEntryPointer->length > 0) + { + // while there are still more directory entries then search through + // for the one we want + + if (tocEntryPointer->fileProperties & 0x02) + { + // Copy the CD format TOC Entry to our format + TocEntryCopy(&localTocEntry, tocEntryPointer); + + // If this TOC Entry is a directory, + // then see if it has the right name + if (strcasecmp(dirname,localTocEntry.filename) == 0) + { + // if the name matches then we've found the directory + found_dir = TRUE; + break; + } + } + + // point to the next entry + (char*)tocEntryPointer += tocEntryPointer->length; + } +*/ + while(1) + { + if ((tocEntryPointer->length == 0) || (((char*)tocEntryPointer-toc)>=2048)) + { + num_dir_sectors--; + + if (num_dir_sectors > 0) + { + // If we've run out of entries, but arent on the last sector + // then load another sector + + current_sector++; + if (CdRead(current_sector,1,toc,&cdReadMode) != TRUE) + { + SysPrintf("Couldn't Read from CD !\n"); + return -1; + } +// CdSync(0x00); + + tocEntryPointer = (dirTocEntry*)toc; + } + else + { + // Couldnt find the directory, and got to end of directory + return -1; + } + } + + + if (tocEntryPointer->fileProperties & 0x02) + { + TocEntryCopy(&localTocEntry, tocEntryPointer); + + // If this TOC Entry is a directory, + // then see if it has the right name + if (strcmp(dirname,localTocEntry.filename) == 0) + { + // if the name matches then we've found the directory + found_dir = TRUE; + break; + } + } + + // point to the next entry + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + } + + // If we havent found the directory name we wanted then fail + if (found_dir != TRUE) + { + Console::Notice( "CDVD_findfile: could not find dir" ); + return -1; + } + + // Get next directory name + dirname = strtok( NULL, "\\/" ); + + // Read the TOC of the found subdirectory + if (CdRead(localTocEntry.fileLBA,1,toc,&cdReadMode) != TRUE) + { + Console::Error("Couldn't Read from CD !"); + return -1; + } +// CdSync(0x00); + + num_dir_sectors = (localTocEntry.fileSize+2047) >> 11; //round up fix + current_sector = localTocEntry.fileLBA; + + // and point the tocEntryPointer at the first real toc entry + tocEntryPointer = (dirTocEntry*)toc; + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + } + + RPC_LOG("[RPC:cdvd] findfile: found dir, now looking for file\n"); + + tocEntryPointer = (dirTocEntry*)toc; + + num_dir_sectors = (tocEntryPointer->fileSize+2047) >> 11; //round up fix + dir_lba = tocEntryPointer->fileLBA; + + tocEntryPointer = (dirTocEntry*)toc; + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + + while (num_dir_sectors > 0) + { + while(tocEntryPointer->length != 0) + { + // Copy the CD format TOC Entry to our format + TocEntryCopy(&localTocEntry, tocEntryPointer); + + if ((strnicmp(localTocEntry.filename, filename, strlen(filename)) == 0) || + ((filename[strlen(filename)-2] == ';') && + (localTocEntry.filename[strlen(localTocEntry.filename)-2] == ';') && + (strnicmp(localTocEntry.filename, filename, strlen(filename)-2) == 0))) + { + // if the filename matches then copy the toc Entry + tocEntry->fileLBA = localTocEntry.fileLBA; + tocEntry->fileProperties = localTocEntry.fileProperties; + tocEntry->fileSize = localTocEntry.fileSize; + + strcpy(tocEntry->filename, localTocEntry.filename); + memcpy(tocEntry->date, localTocEntry.date, 7); + + DbgCon::WriteLn("CDVD_findfile: found file"); + + return TRUE; + } + + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + } + + num_dir_sectors--; + + if (num_dir_sectors > 0) + { + dir_lba++; + + if (CdRead(dir_lba,1,toc,&cdReadMode) != TRUE){ + Console::Error("Couldn't Read from CD !"); + return -1; + } +// CdSync(0x00); + + tocEntryPointer = (dirTocEntry*)toc; + } + } + + DbgCon::Notice("CDVD_findfile: could not find file"); + + return FALSE; +} + +// This is the RPC-ready function which takes the request to start the tocEntry retrieval +int CDVD_GetDir_RPC_request(char* pathname, char* extensions, unsigned int inc_dirs){ +// int dir_depth = 1; + char toc[2048]; + char* dirname; + int found_dir; + int num_dir_sectors; + unsigned int toc_entry_num; + dirTocEntry* tocEntryPointer; + TocEntry localTocEntry; + int current_sector; + + // store the extension list statically for the retrieve function + strncpy(getDirTocData.extension_list, extensions, 128); + getDirTocData.extension_list[128]=0; + + getDirTocData.inc_dirs = inc_dirs; + + // Find the TOC for a specific directory + if (CDVD_GetVolumeDescriptor() != TRUE){ + RPC_LOG("[RPC:cdvd] Could not get CD Volume Descriptor\n"); + return -1; + } + + RPC_LOG("[RPC:cdvd] Getting Directory Listing for: \"%s\"\n", pathname); + + // Read the TOC of the root directory + if (CdRead(CDVolDesc.rootToc.tocLBA,1,toc,&cdReadMode) != TRUE){ + RPC_LOG("[RPC: ] Couldn't Read from CD !\n"); + return -1; + } + //CdSync(0x00); + + // point the tocEntryPointer at the first real toc entry + tocEntryPointer = (dirTocEntry*)toc; + + num_dir_sectors = (tocEntryPointer->fileSize+2047) >> 11; + current_sector = tocEntryPointer->fileLBA; + + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + + // use strtok to get the next dir name + + // if there isnt one, then assume we want the LBA + // for the current one, and exit the while loop + + // if there is another dir name then increment dir_depth + // and look through dir table entries until we find the right name + // if we dont find the right name + // before finding an entry at a higher level (lower num), then return nothing + + localTocEntry.fileLBA = CDVolDesc.rootToc.tocLBA; + + // while (there are more dir names in the path) + dirname = strtok( pathname, "\\/" ); + while( dirname != NULL ){ + found_dir = FALSE; + + while(1){ + if ((tocEntryPointer->length == 0) || (((char*)tocEntryPointer-toc)>=2048)) { + num_dir_sectors--; + + if (num_dir_sectors > 0){ + // If we've run out of entries, but arent on the last sector + // then load another sector + + current_sector++; + if (CdRead(current_sector,1,toc,&cdReadMode) != TRUE){ + RPC_LOG("[RPC: ] Couldn't Read from CD !\n"); + + return -1; + } + //CdSync(0x00); + + tocEntryPointer = (dirTocEntry*)toc; + } + else{ + // Couldnt find the directory, and got to end of directory + return -1; + } + } + + if (tocEntryPointer->fileProperties & 0x02){ + TocEntryCopy(&localTocEntry, tocEntryPointer); + + // If this TOC Entry is a directory, + // then see if it has the right name + if (strcmp(dirname,localTocEntry.filename) == 0){ + // if the name matches then we've found the directory + found_dir = TRUE; + RPC_LOG("[RPC: ] Found directory %s in subdir at sector %d\n",dirname,current_sector); + RPC_LOG("[RPC: ] LBA of found subdirectory = %d\n",localTocEntry.fileLBA); + + break; + } + } + + // point to the next entry + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + } + + // If we havent found the directory name we wanted then fail + if (found_dir != TRUE) + return -1; + + // Get next directory name + dirname = strtok( NULL, "\\/" ); + + // Read the TOC of the found subdirectory + if (CdRead(localTocEntry.fileLBA,1,toc,&cdReadMode) != TRUE){ + RPC_LOG("[RPC: ] Couldn't Read from CD !\n"); + return -1; + } + //CdSync(0x00); + + num_dir_sectors = (localTocEntry.fileSize+2047) >> 11; + current_sector = localTocEntry.fileLBA; + + // and point the tocEntryPointer at the first real toc entry + tocEntryPointer = (dirTocEntry*)toc; + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + } + + // We know how much data we need to read in from the DirTocHeader + // but we need to read in at least 1 sector before we can get this value + + // Now we need to COUNT the number of entries (dont do anything with info at this point) + // so set the tocEntryPointer to point to the first actual file entry + + // This is a bit of a waste of reads since we're not actually copying the data out yet, + // but we dont know how big this TOC might be, so we cant allocate a specific size + + tocEntryPointer = (dirTocEntry*)toc; + + // Need to STORE the start LBA and number of Sectors, for use by the retrieve func. + getDirTocData.start_LBA = localTocEntry.fileLBA; + getDirTocData.num_sectors = (tocEntryPointer->fileSize+2047) >> 11; + getDirTocData.num_entries = 0; + getDirTocData.current_entry = 0; + getDirTocData.current_sector = getDirTocData.start_LBA; + getDirTocData.current_sector_offset = 0; + + num_dir_sectors = getDirTocData.num_sectors; + + tocEntryPointer = (dirTocEntry*)toc; + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + + toc_entry_num=0; + + while(1){ + if ((tocEntryPointer->length == 0) || (((char*)tocEntryPointer-toc)>=2048)){ + // decrease the number of dirs remaining + num_dir_sectors--; + + if (num_dir_sectors > 0){ + // If we've run out of entries, but arent on the last sector + // then load another sector + getDirTocData.current_sector++; + + if (CdRead(getDirTocData.current_sector,1,toc,&cdReadMode) != TRUE){ + RPC_LOG("[RPC: ] Couldn't Read from CD !\n"); + return -1; + } + //CdSync(0x00); + + tocEntryPointer = (dirTocEntry*)toc; + +// continue; + } + else{ + getDirTocData.num_entries = toc_entry_num; + getDirTocData.current_sector = getDirTocData.start_LBA; + return (toc_entry_num); + } + } + + // We've found a file/dir in this directory + // now check if it matches our extension list (if there is one) + TocEntryCopy(&localTocEntry, tocEntryPointer); + + if (localTocEntry.fileProperties & 0x02){ + // If this is a subdir, then check if we want to include subdirs + if (getDirTocData.inc_dirs){ + toc_entry_num++; + } + } + else{ + if (strlen(getDirTocData.extension_list) > 0){ + if (TocEntryCompare(localTocEntry.filename, getDirTocData.extension_list) == TRUE){ + // increment the number of matching entries counter + toc_entry_num++; + } + } + else{ + toc_entry_num++; + } + } + + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + } + + + // THIS SHOULD BE UNREACHABLE - + // since we are trying to count ALL matching entries, rather than upto a limit + + + // STORE total number of TOC entries + getDirTocData.num_entries = toc_entry_num; + getDirTocData.current_sector = getDirTocData.start_LBA; + + + // we've reached the toc entry limit, so return how many we've done + return (toc_entry_num); + +} + +// This function can be called repeatedly after CDVD_GetDir_RPC_request to get the actual entries +// buffer (tocEntry) must be 18KB in size, and this will be filled with a maximum of 128 entries in one go +int CDVD_GetDir_RPC_get_entries(TocEntry tocEntry[], int req_entries){ + char toc[2048]; + int toc_entry_num; + + dirTocEntry* tocEntryPointer; + + if (CdRead(getDirTocData.current_sector,1,toc,&cdReadMode) != TRUE){ + RPC_LOG("[RPC:cdvd] Couldn't Read from CD !\n"); + return -1; + } + //CdSync(0x00); + + if (getDirTocData.current_entry == 0){ + // if this is the first read then make sure we point to the first real entry + tocEntryPointer = (dirTocEntry*)toc; + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + tocEntryPointer = (dirTocEntry*)((char*)tocEntryPointer + tocEntryPointer->length); + + getDirTocData.current_sector_offset = (char*)tocEntryPointer - toc; + } + else{ + tocEntryPointer = (dirTocEntry*)(toc + getDirTocData.current_sector_offset); + } + + if (req_entries > 128) + req_entries = 128; + + for (toc_entry_num=0; toc_entry_num < req_entries;){ + if ((tocEntryPointer->length == 0) || (getDirTocData.current_sector_offset >= 2048)){ + // decrease the number of dirs remaining + getDirTocData.num_sectors--; + + if (getDirTocData.num_sectors > 0){ + // If we've run out of entries, but arent on the last sector + // then load another sector + getDirTocData.current_sector++; + + if (CdRead(getDirTocData.current_sector,1,toc,&cdReadMode) != TRUE){ + RPC_LOG("[RPC:cdvd] Couldn't Read from CD !\n"); + return -1; + } + //CdSync(0x00); + + getDirTocData.current_sector_offset = 0; + tocEntryPointer = (dirTocEntry*)(toc + getDirTocData.current_sector_offset); + +// continue; + } + else{ + return (toc_entry_num); + } + } + + // This must be incremented even if the filename doesnt match extension list + getDirTocData.current_entry++; + + // We've found a file in this directory + // now check if it matches our extension list (if there is one) + + // Copy the entry regardless, as it makes the comparison easier + // if it doesn't match then it will just be overwritten + TocEntryCopy(&tocEntry[toc_entry_num], tocEntryPointer); + + if (tocEntry[toc_entry_num].fileProperties & 0x02){ + // If this is a subdir, then check if we want to include subdirs + if (getDirTocData.inc_dirs) { + toc_entry_num++; + } + + getDirTocData.current_sector_offset += tocEntryPointer->length; + tocEntryPointer = (dirTocEntry*)(toc + getDirTocData.current_sector_offset); + } + else{ + if (strlen(getDirTocData.extension_list) > 0){ + if (TocEntryCompare(tocEntry[toc_entry_num].filename, getDirTocData.extension_list) == TRUE){ + // increment the number of matching entries counter + toc_entry_num++; + } + + getDirTocData.current_sector_offset += tocEntryPointer->length; + tocEntryPointer = (dirTocEntry*)(toc + getDirTocData.current_sector_offset); + + } + else{ + toc_entry_num++; + getDirTocData.current_sector_offset += tocEntryPointer->length; + tocEntryPointer = (dirTocEntry*)(toc + getDirTocData.current_sector_offset); + } + } +/* + if (strlen(getDirTocData.extension_list) > 0) + { + if (TocEntryCompare(tocEntry[toc_entry_num].filename, getDirTocData.extension_list) == TRUE) + { + + // increment this here, rather than in the main for loop + // since this should count the number of matching entries + toc_entry_num++; + } + + getDirTocData.current_sector_offset += tocEntryPointer->length; + (char*)tocEntryPointer = toc + getDirTocData.current_sector_offset; + } + else + { + toc_entry_num++; + getDirTocData.current_sector_offset += tocEntryPointer->length; + (char*)tocEntryPointer = toc + getDirTocData.current_sector_offset; + } +*/ + } + return (toc_entry_num); +} diff --git a/pcsx2/CDVDiso.h b/pcsx2/CDVDiso.h new file mode 100644 index 0000000000..e68d85eb82 --- /dev/null +++ b/pcsx2/CDVDiso.h @@ -0,0 +1,149 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +/* + * Original code from libcdvd by Hiryu & Sjeep (C) 2002 + * Modified by Florin for PCSX2 emu + */ + +#ifndef __CDVDISO_H__ +#define __CDVDISO_H__ + +#include "CDVDlib.h" + +int CDVD_findfile(const char* fname, TocEntry* tocEntry); +int CDVD_GetDir_RPC_request(char* pathname, char* extensions, unsigned int inc_dirs); +int CDVD_GetDir_RPC_get_entries(TocEntry tocEntry[], int req_entries); + +#if defined(_MSC_VER) +#pragma pack(1) +#pragma warning(disable:4996) //ignore the stricmp deprecated warning +#endif + +struct rootDirTocHeader +{ + u16 length; //+00 + u32 tocLBA; //+02 + u32 tocLBA_bigend; //+06 + u32 tocSize; //+0A + u32 tocSize_bigend; //+0E + u8 dateStamp[8]; //+12 + u8 reserved[6]; //+1A + u8 reserved2; //+20 + u8 reserved3; //+21 +#if defined(_MSC_VER) +}; //+22 +#else +} __attribute__((packed)); +#endif + +struct asciiDate +{ + char year[4]; + char month[2]; + char day[2]; + char hours[2]; + char minutes[2]; + char seconds[2]; + char hundreths[2]; + char terminator[1]; +#if defined(_MSC_VER) +}; +#else +} __attribute__((packed)); +#endif + +struct cdVolDesc +{ + u8 filesystemType; // 0x01 = ISO9660, 0x02 = Joliet, 0xFF = NULL + u8 volID[5]; // "CD001" + u8 reserved2; + u8 reserved3; + u8 sysIdName[32]; + u8 volName[32]; // The ISO9660 Volume Name + u8 reserved5[8]; + u32 volSize; // Volume Size + u32 volSizeBig; // Volume Size Big-Endian + u8 reserved6[32]; + u32 unknown1; + u32 unknown1_bigend; + u16 volDescSize; //+80 + u16 volDescSize_bigend; //+82 + u32 unknown3; //+84 + u32 unknown3_bigend; //+88 + u32 priDirTableLBA; // LBA of Primary Dir Table //+8C + u32 reserved7; //+90 + u32 secDirTableLBA; // LBA of Secondary Dir Table //+94 + u32 reserved8; //+98 + struct rootDirTocHeader rootToc; + s8 volSetName[128]; + s8 publisherName[128]; + s8 preparerName[128]; + s8 applicationName[128]; + s8 copyrightFileName[37]; + s8 abstractFileName[37]; + s8 bibliographyFileName[37]; + struct asciiDate creationDate; + struct asciiDate modificationDate; + struct asciiDate effectiveDate; + struct asciiDate expirationDate; + u8 reserved10; + u8 reserved11[1166]; +#if defined(_MSC_VER) +}; +#else +} __attribute__((packed)); +#endif + +struct dirTableEntry +{ + u8 dirNameLength; + u8 reserved; + u32 dirTOCLBA; + u16 dirDepth; + u8 dirName[32]; +#if defined(_MSC_VER) +}; +#else +} __attribute__((packed)); +#endif + +struct dirTocEntry +{ + short length; + u32 fileLBA; + u32 fileLBA_bigend; + u32 fileSize; + u32 fileSize_bigend; + u8 dateStamp[6]; + u8 reserved1; + u8 fileProperties; + u8 reserved2[6]; + u8 filenameLength; + char filename[128]; +#if defined(_MSC_VER) +}; +#else +} __attribute__((packed)); +#endif // This is the internal format on the CD +// TocEntry structure contains only the important stuff needed for export + +#if defined(_MSC_VER) +#pragma pack() +#endif + +#endif//__CDVDISO_H__ diff --git a/pcsx2/CDVDisodrv.cpp b/pcsx2/CDVDisodrv.cpp new file mode 100644 index 0000000000..4b33baaec0 --- /dev/null +++ b/pcsx2/CDVDisodrv.cpp @@ -0,0 +1,254 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +/* + * Original code from libcdvd by Hiryu & Sjeep (C) 2002 + * Modified by Florin for PCSX2 emu + */ + +#include "PrecompiledHeader.h" + +#include "CDVDiso.h" +#include "CDVDisodrv.h" + +CdRMode cdReadMode; + +struct fdtable{ +//int fd; + int fileSize; + int LBA; + int filePos; +}; + +static struct fdtable fd_table[16]; +static int fd_used[16]; +static int files_open=0; +static int inited=FALSE; + +/************************************************************* +* The functions below are the normal file-system operations, * +* used to provide a standard filesystem interface * +*************************************************************/ + +////////////////////////////////////////////////////////////////////// +// CDVDFS_init +// called by 80000592 sceCdInit() +////////////////////////////////////////////////////////////////////// +void CDVDFS_init(){ + + if (inited) return;//might change in the future as a param; forceInit/Reset + + RPC_LOG("[CDVDisodrv:init] CDVD Filesystem v1.00\n"); + RPC_LOG("[CDVDisodrv ] \tby A.Lee (aka Hiryu) & Nicholas Van Veen (aka Sjeep)\n"); + RPC_LOG("[CDVDisodrv ] Initializing '%s' file driver.\n", "cdfs"); + + //CdInit(0); already called by plugin loading system ;) + + cdReadMode.trycount = 0; + cdReadMode.spindlctrl = CdSpinStm; + cdReadMode.datapattern = CdSecS2048; //isofs driver only needs + //2KB sectors + + memzero_obj( fd_table ); + memzero_obj( fd_used ); + + inited = TRUE; + + return; +} + +////////////////////////////////////////////////////////////////////// +// CDVDFS_open +// called by 80000001 fileio_open for devices: "cdrom:", "cdrom0:" +////////////////////////////////////////////////////////////////////// +int CDVDFS_open(const char *name, int mode){ + int j; + static struct TocEntry tocEntry; + + // check if the file exists + if (CDVD_findfile(name, &tocEntry) != TRUE) + return -1; + + if(mode != 1) return -2; //SCE_RDONLY + + // set up a new file descriptor + for(j=0; j < 16; j++) if(fd_used[j] == 0) break; + if(j >= 16) return -3; + + fd_used[j] = 1; + files_open++; + + RPC_LOG("[CDVDisodrv:open] internal fd=%d\n", j); + + fd_table[j].fileSize = tocEntry.fileSize; + fd_table[j].LBA = tocEntry.fileLBA; + fd_table[j].filePos = 0; + + RPC_LOG("[CDVDisodrv ] tocEntry.fileSize = %d\n",tocEntry.fileSize); + return j; +} + +////////////////////////////////////////////////////////////////////// +// CDVDFS_lseek +// called by 80000001 fileio_lseek for devices: "cdrom:", "cdrom0:" +////////////////////////////////////////////////////////////////////// +int CDVDFS_lseek(int fd, int offset, int whence){ + + if ((fd >= 16) || (fd_used[fd]==0)){ + RPC_LOG("[CDVDisodrv:lseek] ERROR: File does not appear to be open!\n"); + return -1; + } + + switch(whence){ + case SEEK_SET: + fd_table[fd].filePos = offset; + break; + + case SEEK_CUR: + fd_table[fd].filePos += offset; + break; + + case SEEK_END: + fd_table[fd].filePos = fd_table[fd].fileSize + offset; + break; + + default: + return -1; + } + + if (fd_table[fd].filePos < 0) + fd_table[fd].filePos = 0; + + if (fd_table[fd].filePos > fd_table[fd].fileSize) + fd_table[fd].filePos = fd_table[fd].fileSize; + + return fd_table[fd].filePos; +} + +////////////////////////////////////////////////////////////////////// +// CDVDFS_read +// called by 80000001 fileio_read for devices: "cdrom:", "cdrom0:", "cdfs:" +////////////////////////////////////////////////////////////////////// +int CDVDFS_read( int fd, char *buffer, int size ){ +// int start_sector; + int off_sector; +// int num_sectors; + + //static char local_buffer[2024*2048]; //4MB + static char lb[2048]; //2KB + //Start, Aligned, End + int ssector, asector, esector; + int ssize=0, asize, esize; + + if ((fd >= 16) || (fd_used[fd]==0)){ + RPC_LOG("[CDVDisodrv:read] ERROR: File does not appear to be open!\n"); + return -1; + } + + // A few sanity checks + if (fd_table[fd].filePos > fd_table[fd].fileSize){ + // We cant start reading from past the beginning of the file + return 0; // File exists but we couldnt read anything from it + } + + if ((fd_table[fd].filePos + size) > fd_table[fd].fileSize) + size = fd_table[fd].fileSize - fd_table[fd].filePos; + + // Now work out where we want to start reading from + asector = ssector = fd_table[fd].LBA + (fd_table[fd].filePos >> 11); + off_sector = (fd_table[fd].filePos & 0x7FF); + if (off_sector){ + ssize = std::min(2048 - off_sector, size); + size -= ssize; + asector++; + } + asize = size & 0xFFFFF800; + esize = size & 0x000007FF; + esector=asector + (asize >> 11); + size += ssize; + + RPC_LOG("[CDVDisodrv:read] read sectors 0x%08X to 0x%08X\n", ssector, esector-(esize==0)); + + if (ssize){ + if (CdRead(ssector, 1, lb, &cdReadMode) != TRUE){ + RPC_LOG("[CDVDisodrv: ] Couldn't Read from file for some reason\n"); + return 0; + } + memcpy_fast(buffer, lb + off_sector, ssize); + } + if (asize) if (CdRead(asector, asize >> 11, buffer+ssize, &cdReadMode) != TRUE){ + RPC_LOG("[CDVDisodrv: ] Couldn't Read from file for some reason\n"); + return 0; + } + if (esize){ + if (CdRead(esector, 1, lb, &cdReadMode) != TRUE){ + RPC_LOG("[CDVDisodrv: ] Couldn't Read from file for some reason\n"); + return 0; + } + memcpy_fast(buffer+ssize+asize, lb, esize); + } +/*********************** + // Now work out where we want to start reading from + start_sector = fd_table[fd].LBA + (fd_table[fd].filePos >> 11); + off_sector = (fd_table[fd].filePos & 0x7FF); + num_sectors = ((off_sector + size) >> 11) + 1; + + RPC_LOG("[CDVDisodrv:read] read sectors 0x%08X to 0x%08X\n",start_sector,start_sector+num_sectors); + + // Read the data (we only ever get 16KB max request at once) + if (CdRead(start_sector, num_sectors, local_buffer, &cdReadMode) != TRUE){ + + //RPC_LOG("sector = %d, start sector = %d\n",sector,start_sector); + RPC_LOG("[CDVDisodrv: ] Couldn't Read from file for some reason\n"); + return 0; + } + //CdSync(0); hm, a wait function maybe... + + memcpy_fast(buffer,local_buffer+off_sector,size); +**************************/ + fd_table[fd].filePos += size; + + return (size); +} + +////////////////////////////////////////////////////////////////////// +// CDVDFS_write +// called by 80000001 fileio_write for devices: "cdrom:", "cdrom0:" +// hehe, this ain't a CD writing option :D +////////////////////////////////////////////////////////////////////// +int CDVDFS_write( int fd, char * buffer, int size ){ + if(size == 0) return 0; + else return -1; +} + +////////////////////////////////////////////////////////////////////// +// CDVDFS_close +// called by 80000001 fileio_close for devices: "cdrom:", "cdrom0:" +////////////////////////////////////////////////////////////////////// +int CDVDFS_close( int fd){ + + if ((fd >= 16) || (fd_used[fd]==0)){ + RPC_LOG("[CDVDisodrv:close] ERROR: File does not appear to be open!\n"); + return -1; + } + RPC_LOG("[CDVDisodrv:close] internal fd %d\n", fd); + fd_used[fd] = 0; + files_open--; + + return 0; +} + diff --git a/pcsx2/CDVDisodrv.h b/pcsx2/CDVDisodrv.h new file mode 100644 index 0000000000..65ed7387bc --- /dev/null +++ b/pcsx2/CDVDisodrv.h @@ -0,0 +1,38 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +/* + * Original code from libcdvd by Hiryu & Sjeep (C) 2002 + * Modified by Florin for PCSX2 emu + */ + +#ifndef __CDVDISODRV_H__ +#define __CDVDISODRV_H__ + +#include "CDVDlib.h" + +extern CdRMode cdReadMode; + +/* Filing-system exported functions */ +void CDVDFS_init(); +int CDVDFS_open(const char *name, int mode); +int CDVDFS_lseek(int fd, int offset, int whence); +int CDVDFS_read( int fd, char * buffer, int size ); +int CDVDFS_write( int fd, char * buffer, int size ); +int CDVDFS_close( int fd); + +#endif//__CDVDISODRV_H__ diff --git a/pcsx2/CDVDlib.h b/pcsx2/CDVDlib.h new file mode 100644 index 0000000000..72aa44d9c9 --- /dev/null +++ b/pcsx2/CDVDlib.h @@ -0,0 +1,171 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +/* + * Original code from libcdvd by Hiryu & Sjeep (C) 2002 + * Linux kernel headers + * Modified by Florin for PCSX2 emu + */ + +#ifndef _CDVDLIB_H +#define _CDVDLIB_H + +#include "Common.h" + +// Macros for READ Data pattan +#define CdSecS2048 0 // sector size 2048 +#define CdSecS2328 1 // sector size 2328 +#define CdSecS2340 2 // sector size 2340 + +//#define CD_FRAMESIZE_RAW1 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) /*2340*/ +//#define CD_FRAMESIZE_RAW0 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) /*2336*/ +//#define CD_HEAD_SIZE 4 /* header (address) bytes per raw data frame */ +//#define CD_SUBHEAD_SIZE 8 /* subheader bytes per raw XA data frame */ +//#define CD_XA_HEAD (CD_HEAD_SIZE+CD_SUBHEAD_SIZE) /* "before data" part of raw XA frame */ + +/* + * A CD-ROM physical sector size is 2048, 2052, 2056, 2324, 2332, 2336, + * 2340, or 2352 bytes long. + * Sector types of the standard CD-ROM data formats: + * + * format sector type user data size (bytes) + * ----------------------------------------------------------------------------- + * 1 (Red Book) CD-DA 2352 (CD_FRAMESIZE_RAW) + * 2 (Yellow Book) Mode1 Form1 2048 (CD_FRAMESIZE) + * 3 (Yellow Book) Mode1 Form2 2336 (CD_FRAMESIZE_RAW0) + * 4 (Green Book) Mode2 Form1 2048 (CD_FRAMESIZE) + * 5 (Green Book) Mode2 Form2 2328 (2324+4 spare bytes) + * + * + * The layout of the standard CD-ROM data formats: + * ----------------------------------------------------------------------------- + * - audio (red): | audio_sample_bytes | + * | 2352 | + * + * - data (yellow, mode1): | sync - head - data - EDC - zero - ECC | + * | 12 - 4 - 2048 - 4 - 8 - 276 | + * + * - data (yellow, mode2): | sync - head - data | + * | 12 - 4 - 2336 | + * + * - XA data (green, mode2 form1): | sync - head - sub - data - EDC - ECC | + * | 12 - 4 - 8 - 2048 - 4 - 276 | + * + * - XA data (green, mode2 form2): | sync - head - sub - data - Spare | + * | 12 - 4 - 8 - 2324 - 4 | + * + */ + +// Macros for Spindle control +#define CdSpinMax 0 +#define CdSpinNom 1 // Starts reading data at maximum rotational velocity and if a read error occurs, the rotational velocity is reduced. +#define CdSpinStm 0 // Recommended stream rotation speed. + +// Macros for TrayReq +#define CdTrayOpen 0 +#define CdTrayClose 1 +#define CdTrayCheck 2 + +/* + * Macros for sceCdGetDiskType() //comments translated from japanese;) + */ + +#define SCECdIllgalMedia 0xff // ILIMEDIA (Illegal Media) A non-PS / non-PS2 Disc. +#define SCECdDVDV 0xfe // DVDV (DVD Video) A non-PS / non-PS2 Disc, but a DVD Video Disc +#define SCECdCDDA 0xfd // CDDA (CD DA) A non-PS / non-PS2 Disc that include a DA track +#define SCECdPS2DVD 0x14 // PS2DVD PS2 consumer DVD. +#define SCECdPS2CDDA 0x13 // PS2CDDA PS2 consumer CD that includes a DA track +#define SCECdPS2CD 0x12 // PS2CD PS2 consumer CD that does not include a DA track +#define SCECdPSCDDA 0x11 // PSCDDA PS CD that includes a DA track +#define SCECdPSCD 0x10 // PSCD PS CD that does not include a DA track +#define SCECdDETCT 0x01 // DETCT (Detecting) Disc distinction action +#define SCECdNODISC 0x00 // NODISC (No disc) No disc entered + + +/* + * Media mode + */ +#define SCECdCD 1 +#define SCECdDVD 2 + +typedef struct { + u8 stat; // 0: normal. Any other: error + u8 second; // second (BCD value) + u8 minute; // minute (BCD value) + u8 hour; // hour (BCD value) + u8 week; // week (BCD value) + u8 day; // day (BCD value) + u8 month; // month (BCD value) + u8 year; // year (BCD value) +} CdCLOCK; + +typedef struct { + u32 lsn; // Logical sector number of file + u32 size; // File size (in bytes) + char name[16]; // Filename + u8 date[8]; // 1th: Seconds + // 2th: Minutes + // 3th: Hours + // 4th: Date + // 5th: Month + // 6th 7th: Year (4 digits) +} CdlFILE; + +typedef struct { + u8 minute; // Minutes + u8 second; // Seconds + u8 sector; // Sector + u8 track; // Track number +} CdlLOCCD; + +typedef struct { + u8 trycount; // Read try count (No. of error retries + 1) (0 - 255) + u8 spindlctrl; // SCECdSpinStm: Recommended stream rotation speed. + // SCECdSpinNom: Starts reading data at maximum rotational velocity and if a read error occurs, the rotational velocity is reduced. + u8 datapattern; // SCECdSecS2048: Data size 2048 bytes + // SCECdSecS2328: 2328 bytes + // SCECdSecS2340: 2340 bytes + u8 pad; // Padding data produced by alignment. +} CdRMode; + +#if defined(_MSC_VER) +#pragma pack(1) +#endif + +struct TocEntry +{ + u32 fileLBA; + u32 fileSize; + u8 fileProperties; + u8 padding1[3]; + char filename[128+1]; + u8 date[7]; +#if defined(_MSC_VER) +}; +#else +} __attribute__((packed)); +#endif + +#if defined(_MSC_VER) +#pragma pack() +#endif + +int CDVD_findfile(const char* fname, struct TocEntry* tocEntry); +int CdRead(u32 lsn, u32 sectors, void *buf, CdRMode *mode); +int DvdRead(u32 lsn, u32 sectors, void *buf, CdRMode *mode); + +#endif // _CDVDLIB_H diff --git a/pcsx2/COP0.cpp b/pcsx2/COP0.cpp new file mode 100644 index 0000000000..41a4ab03be --- /dev/null +++ b/pcsx2/COP0.cpp @@ -0,0 +1,357 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900.h" +#include "R5900OpcodeTables.h" + +u32 s_iLastCOP0Cycle = 0; +u32 s_iLastPERFCycle[2] = { 0, 0 }; + +void UpdateCP0Status() { + u32 value = cpuRegs.CP0.n.Status.val; + + if (value & 0x06 || + (value & 0x18) == 0) { // Kernel Mode (KSU = 0 | EXL = 1 | ERL = 1)*/ + memSetKernelMode(); // Kernel memory always + } else { // User Mode + memSetUserMode(); + } + cpuTestHwInts(); +} + +void WriteCP0Status(u32 value) { + cpuRegs.CP0.n.Status.val = value; + UpdateCP0Status(); +} + +void MapTLB(int i) +{ + u32 mask, addr; + u32 saddr, eaddr; + + DevCon::WriteLn("MAP TLB %d: %08x-> [%08x %08x] S=%d G=%d ASID=%d Mask= %03X", params + i,tlb[i].VPN2,tlb[i].PFN0,tlb[i].PFN1,tlb[i].S,tlb[i].G,tlb[i].ASID,tlb[i].Mask); + + if (tlb[i].S) + { + DevCon::WriteLn("OMG SPRAM MAPPING %08X %08X\n",params tlb[i].VPN2,tlb[i].Mask); + vtlb_VMapBuffer(tlb[i].VPN2,psS,0x4000); + } + + if (tlb[i].VPN2 == 0x70000000) return; //uh uhh right ... + + if (tlb[i].EntryLo0 & 0x2) { + mask = ((~tlb[i].Mask) << 1) & 0xfffff; + saddr = tlb[i].VPN2 >> 12; + eaddr = saddr + tlb[i].Mask + 1; + + for (addr=saddr; addr> 12) & mask)) { //match + memSetPageAddr(addr << 12, tlb[i].PFN0 + ((addr - saddr) << 12)); + Cpu->Clear(addr << 12, 1); + } + } + } + + if (tlb[i].EntryLo1 & 0x2) { + mask = ((~tlb[i].Mask) << 1) & 0xfffff; + saddr = (tlb[i].VPN2 >> 12) + tlb[i].Mask + 1; + eaddr = saddr + tlb[i].Mask + 1; + + for (addr=saddr; addr> 12) & mask)) { //match + memSetPageAddr(addr << 12, tlb[i].PFN1 + ((addr - saddr) << 12)); + Cpu->Clear(addr << 12, 1); + } + } + } +} + +void UnmapTLB(int i) +{ + //SysPrintf("Clear TLB %d: %08x-> [%08x %08x] S=%d G=%d ASID=%d Mask= %03X\n",i,tlb[i].VPN2,tlb[i].PFN0,tlb[i].PFN1,tlb[i].S,tlb[i].G,tlb[i].ASID,tlb[i].Mask); + u32 mask, addr; + u32 saddr, eaddr; + + if (tlb[i].S) + { + vtlb_VMapUnmap(tlb[i].VPN2,0x4000); + return; + } + + if (tlb[i].EntryLo0 & 0x2) + { + mask = ((~tlb[i].Mask) << 1) & 0xfffff; + saddr = tlb[i].VPN2 >> 12; + eaddr = saddr + tlb[i].Mask + 1; + // SysPrintf("Clear TLB: %08x ~ %08x\n",saddr,eaddr-1); + for (addr=saddr; addr> 12) & mask)) { //match + memClearPageAddr(addr << 12); + Cpu->Clear(addr << 12, 1); + } + } + } + + if (tlb[i].EntryLo1 & 0x2) { + mask = ((~tlb[i].Mask) << 1) & 0xfffff; + saddr = (tlb[i].VPN2 >> 12) + tlb[i].Mask + 1; + eaddr = saddr + tlb[i].Mask + 1; + // SysPrintf("Clear TLB: %08x ~ %08x\n",saddr,eaddr-1); + for (addr=saddr; addr> 12) & mask)) { //match + memClearPageAddr(addr << 12); + Cpu->Clear(addr << 12, 1); + } + } + } +} + +void WriteTLB(int i) +{ + tlb[i].PageMask = cpuRegs.CP0.n.PageMask; + tlb[i].EntryHi = cpuRegs.CP0.n.EntryHi; + tlb[i].EntryLo0 = cpuRegs.CP0.n.EntryLo0; + tlb[i].EntryLo1 = cpuRegs.CP0.n.EntryLo1; + + tlb[i].Mask = (cpuRegs.CP0.n.PageMask >> 13) & 0xfff; + tlb[i].nMask = (~tlb[i].Mask) & 0xfff; + tlb[i].VPN2 = ((cpuRegs.CP0.n.EntryHi >> 13) & (~tlb[i].Mask)) << 13; + tlb[i].ASID = cpuRegs.CP0.n.EntryHi & 0xfff; + tlb[i].G = cpuRegs.CP0.n.EntryLo0 & cpuRegs.CP0.n.EntryLo1 & 0x1; + tlb[i].PFN0 = (((cpuRegs.CP0.n.EntryLo0 >> 6) & 0xFFFFF) & (~tlb[i].Mask)) << 12; + tlb[i].PFN1 = (((cpuRegs.CP0.n.EntryLo1 >> 6) & 0xFFFFF) & (~tlb[i].Mask)) << 12; + tlb[i].S = cpuRegs.CP0.n.EntryLo0&0x80000000; + + MapTLB(i); +} + +namespace R5900 { +namespace Interpreter { +namespace OpcodeImpl { +namespace COP0 { + +void MFC0() { + if (!_Rt_) return; + if (_Rd_ != 9) { COP0_LOG("%s\n", disR5900Current.getCString() ); } + + //if(bExecBIOS == FALSE && _Rd_ == 25) SysPrintf("MFC0 _Rd_ %x = %x\n", _Rd_, cpuRegs.CP0.r[_Rd_]); + switch (_Rd_) { + + case 12: cpuRegs.GPR.r[_Rt_].UD[0] = (s64)(cpuRegs.CP0.r[_Rd_] & 0xf0c79c1f); break; + case 25: + switch(_Imm_ & 0x3F){ + case 0: cpuRegs.GPR.r[_Rt_].UD[0] = (s64)cpuRegs.PERF.n.pccr; break; + case 1: + if((cpuRegs.PERF.n.pccr & 0x800003E0) == 0x80000020) { + cpuRegs.PERF.n.pcr0 += cpuRegs.cycle-s_iLastPERFCycle[0]; + s_iLastPERFCycle[0] = cpuRegs.cycle; + } + + cpuRegs.GPR.r[_Rt_].UD[0] = (s64)cpuRegs.PERF.n.pcr0; + break; + case 3: + if((cpuRegs.PERF.n.pccr & 0x800F8000) == 0x80008000) { + cpuRegs.PERF.n.pcr1 += cpuRegs.cycle-s_iLastPERFCycle[1]; + s_iLastPERFCycle[1] = cpuRegs.cycle; + } + cpuRegs.GPR.r[_Rt_].UD[0] = (s64)cpuRegs.PERF.n.pcr1; + break; + } + /*SysPrintf("MFC0 PCCR = %x PCR0 = %x PCR1 = %x IMM= %x\n", + cpuRegs.PERF.n.pccr, cpuRegs.PERF.n.pcr0, cpuRegs.PERF.n.pcr1, _Imm_ & 0x3F);*/ + break; + case 24: + SysPrintf("MFC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF); + break; + case 9: + // update + cpuRegs.CP0.n.Count += cpuRegs.cycle-s_iLastCOP0Cycle; + s_iLastCOP0Cycle = cpuRegs.cycle; + default: cpuRegs.GPR.r[_Rt_].UD[0] = (s64)cpuRegs.CP0.r[_Rd_]; + } +} + +void MTC0() { + COP0_LOG("%s\n", disR5900Current.getCString()); + //if(bExecBIOS == FALSE && _Rd_ == 25) SysPrintf("MTC0 _Rd_ %x = %x\n", _Rd_, cpuRegs.CP0.r[_Rd_]); + switch (_Rd_) { + case 25: + /*if(bExecBIOS == FALSE && _Rd_ == 25) SysPrintf("MTC0 PCCR = %x PCR0 = %x PCR1 = %x IMM= %x\n", + cpuRegs.PERF.n.pccr, cpuRegs.PERF.n.pcr0, cpuRegs.PERF.n.pcr1, _Imm_ & 0x3F);*/ + switch(_Imm_ & 0x3F){ + case 0: + if((cpuRegs.PERF.n.pccr & 0x800003E0) == 0x80000020) + cpuRegs.PERF.n.pcr0 += cpuRegs.cycle-s_iLastPERFCycle[0]; + if((cpuRegs.PERF.n.pccr & 0x800F8000) == 0x80008000) + cpuRegs.PERF.n.pcr1 += cpuRegs.cycle-s_iLastPERFCycle[1]; + cpuRegs.PERF.n.pccr = cpuRegs.GPR.r[_Rt_].UL[0]; + s_iLastPERFCycle[0] = cpuRegs.cycle; + s_iLastPERFCycle[1] = cpuRegs.cycle; + break; + case 1: cpuRegs.PERF.n.pcr0 = cpuRegs.GPR.r[_Rt_].UL[0]; s_iLastPERFCycle[0] = cpuRegs.cycle; break; + case 3: cpuRegs.PERF.n.pcr1 = cpuRegs.GPR.r[_Rt_].UL[0]; s_iLastPERFCycle[1] = cpuRegs.cycle; break; + } + break; + case 24: + SysPrintf("MTC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF); + break; + case 12: WriteCP0Status(cpuRegs.GPR.r[_Rt_].UL[0]); break; + case 9: s_iLastCOP0Cycle = cpuRegs.cycle; cpuRegs.CP0.r[9] = cpuRegs.GPR.r[_Rt_].UL[0]; break; + default: cpuRegs.CP0.r[_Rd_] = cpuRegs.GPR.r[_Rt_].UL[0]; break; + } +} + +int CPCOND0() { + return (((psHu16(DMAC_STAT) & psHu16(DMAC_PCR)) & 0x3ff) == (psHu16(DMAC_PCR) & 0x3ff)); +} + +//#define CPCOND0 1 + +#define BC0(cond) \ + if (CPCOND0() cond) { \ + intDoBranch(_BranchTarget_); \ + } + +void BC0F() { + BC0(== 0); + COP0_LOG( "COP0 > BC0F\n" ); +} + +void BC0T() { + BC0(== 1); + COP0_LOG( "COP0 > BC0T\n" ); +} + +#define BC0L(cond) \ + if (CPCOND0() cond) { \ + intDoBranch(_BranchTarget_); \ + } else cpuRegs.pc+= 4; + +void BC0FL() { + BC0L(== 0); + COP0_LOG( "COP0 > BC0FL\n" ); +} + +void BC0TL() { + BC0L(== 1); + COP0_LOG( "COP0 > BCOTL\n" ); +} + +void TLBR() { +/* CPU_LOG("COP0_TLBR %d:%x,%x,%x,%x\n", + cpuRegs.CP0.n.Random, cpuRegs.CP0.n.PageMask, cpuRegs.CP0.n.EntryHi, + cpuRegs.CP0.n.EntryLo0, cpuRegs.CP0.n.EntryLo1);*/ + + int i = cpuRegs.CP0.n.Index&0x1f; + + COP0_LOG("COP0 > TLBR\n"); + cpuRegs.CP0.n.PageMask = tlb[i].PageMask; + cpuRegs.CP0.n.EntryHi = tlb[i].EntryHi&~(tlb[i].PageMask|0x1f00); + cpuRegs.CP0.n.EntryLo0 = (tlb[i].EntryLo0&~1)|((tlb[i].EntryHi>>12)&1); + cpuRegs.CP0.n.EntryLo1 =(tlb[i].EntryLo1&~1)|((tlb[i].EntryHi>>12)&1); +} + +void TLBWI() { + int j = cpuRegs.CP0.n.Index & 0x3f; + + if (j > 48) return; + +/* CPU_LOG("COP0_TLBWI %d:%x,%x,%x,%x\n", + cpuRegs.CP0.n.Index, cpuRegs.CP0.n.PageMask, cpuRegs.CP0.n.EntryHi, + cpuRegs.CP0.n.EntryLo0, cpuRegs.CP0.n.EntryLo1);*/ + + UnmapTLB(j); + WriteTLB(j); +} + +void TLBWR() { + int j = cpuRegs.CP0.n.Random & 0x3f; + + if (j > 48) return; + +/* CPU_LOG("COP0_TLBWR %d:%x,%x,%x,%x\n", + cpuRegs.CP0.n.Random, cpuRegs.CP0.n.PageMask, cpuRegs.CP0.n.EntryHi, + cpuRegs.CP0.n.EntryLo0, cpuRegs.CP0.n.EntryLo1);*/ + +// if( !bExecBIOS ) +// __Log("TLBWR %d\n", j); + + UnmapTLB(j); + WriteTLB(j); +} + +void TLBP() { + int i; + + + union { + struct { + u32 VPN2:19; + u32 VPN2X:2; + u32 G:3; + u32 ASID:8; + } s; + u32 u; + } EntryHi32; + + EntryHi32.u=cpuRegs.CP0.n.EntryHi; + + cpuRegs.CP0.n.Index=0xFFFFFFFF; + for(i=0;i<48;i++){ + if(tlb[i].VPN2==((~tlb[i].Mask)&(EntryHi32.s.VPN2)) + &&((tlb[i].G&1)||((tlb[i].ASID & 0xff) == EntryHi32.s.ASID))) { + cpuRegs.CP0.n.Index=i; + break; + } + } + if(cpuRegs.CP0.n.Index == 0xFFFFFFFF) cpuRegs.CP0.n.Index = 0x80000000; +} + +void ERET() { + if (cpuRegs.CP0.n.Status.b.ERL) { + cpuRegs.pc = cpuRegs.CP0.n.ErrorEPC; + cpuRegs.CP0.n.Status.b.ERL = 0; + } else { + cpuRegs.pc = cpuRegs.CP0.n.EPC; + cpuRegs.CP0.n.Status.b.EXL = 0; + } + UpdateCP0Status(); + intSetBranch(); +} + +void DI() { + if (cpuRegs.CP0.n.Status.b._EDI || cpuRegs.CP0.n.Status.b.EXL || + cpuRegs.CP0.n.Status.b.ERL || (cpuRegs.CP0.n.Status.b.KSU == 0)) { + cpuRegs.CP0.n.Status.b.EIE = 0; + //UpdateCP0Status(); // ints are disabled so checking for them is kinda silly... + } +} + +void EI() { + if (cpuRegs.CP0.n.Status.b._EDI || cpuRegs.CP0.n.Status.b.EXL || + cpuRegs.CP0.n.Status.b.ERL || (cpuRegs.CP0.n.Status.b.KSU == 0)) { + cpuRegs.CP0.n.Status.b.EIE = 1; + UpdateCP0Status(); + } +} + +} } } } // end namespace R5900::Interpreter::OpcodeImpl \ No newline at end of file diff --git a/pcsx2/COP0.h b/pcsx2/COP0.h new file mode 100644 index 0000000000..1945532fa4 --- /dev/null +++ b/pcsx2/COP0.h @@ -0,0 +1,28 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __COP0_H__ +#define __COP0_H__ + +void WriteCP0Status(u32 value); +void UpdateCP0Status(); +void WriteTLB(int i); +void UnmapTLB(int i); +void MapTLB(int i); + +#endif /* __COP0_H__ */ diff --git a/pcsx2/COP2.cpp b/pcsx2/COP2.cpp new file mode 100644 index 0000000000..d52f32cdea --- /dev/null +++ b/pcsx2/COP2.cpp @@ -0,0 +1,89 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "DebugTools/Debug.h" +#include "R5900.h" +#include "R5900OpcodeTables.h" +#include "VUops.h" +#include "VUmicro.h" + +//namespace R5900 { +//namespace Interpreter { +//namespace OpcodeImpl{ + +using namespace R5900; +using namespace R5900::Interpreter; + +#define CP2COND (((VU0.VI[REG_VPU_STAT].US[0] >> 8) & 1)) + +void VCALLMS() { + vu0Finish(); + vu0ExecMicro(((cpuRegs.code >> 6) & 0x7FFF) * 8); +} + +void VCALLMSR() { + vu0Finish(); + vu0ExecMicro(VU0.VI[REG_CMSAR0].US[0] * 8); +} + +void BC2F() +{ + if (CP2COND == 0) + { + SysPrintf("VU0 Macro Branch \n"); + intDoBranch(_BranchTarget_); + } +} +void BC2T() +{ + if (CP2COND == 1) + { + SysPrintf("VU0 Macro Branch \n"); + intDoBranch(_BranchTarget_); + } +} + +void BC2FL() +{ + if (CP2COND == 0) + { + SysPrintf("VU0 Macro Branch \n"); + intDoBranch(_BranchTarget_); + } + else + { + cpuRegs.pc+= 4; + } +} +void BC2TL() +{ + if (CP2COND == 1) + { + SysPrintf("VU0 Macro Branch \n"); + intDoBranch(_BranchTarget_); + } + else + { + cpuRegs.pc+= 4; + } +} + +//}}} diff --git a/pcsx2/Cache.cpp b/pcsx2/Cache.cpp new file mode 100644 index 0000000000..2ded259fed --- /dev/null +++ b/pcsx2/Cache.cpp @@ -0,0 +1,400 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "Cache.h" + +_cacheS pCache[64]; + +namespace R5900{ +namespace Interpreter +{ + +#ifdef PCSX2_CACHE_EMU_MEM +int getFreeCache(u32 mem, int mode, int * way) { + u8 * out; + u32 paddr; + u32 taddr[2]; + u8 * t; + int number; + int i = (mem >> 6) & 0x3F; + + paddr = memLUTR[mem >> 12]; + taddr[0] = memLUTW[pCache[i].tag[0]>>12]; + taddr[1] = memLUTW[pCache[i].tag[1]>>12]; + + if (taddr[0] == paddr && (pCache[i].tag[0] & 0x20)) + { + *way = 0; + return i; + }else if(taddr[1] == paddr && (pCache[i].tag[1] & 0x20)) + { + *way = 1; + return i; + } + + number = ((pCache[i].tag[0]>>4) & 1) ^ ((pCache[i].tag[1]>>4) & 1); + + if(pCache[i].tag[number] & 0x60) // Valid Dirty + { + t = (u8*)(taddr[number]); + out = (u8*)(t + (mem & 0xFC0)); + ((u64*)out)[0] = ((u64*)pCache[i].data[number][0].b8._8)[0]; + ((u64*)out)[1] = ((u64*)pCache[i].data[number][0].b8._8)[1]; + ((u64*)out)[2] = ((u64*)pCache[i].data[number][1].b8._8)[0]; + ((u64*)out)[3] = ((u64*)pCache[i].data[number][1].b8._8)[1]; + ((u64*)out)[4] = ((u64*)pCache[i].data[number][2].b8._8)[0]; + ((u64*)out)[5] = ((u64*)pCache[i].data[number][2].b8._8)[1]; + ((u64*)out)[6] = ((u64*)pCache[i].data[number][3].b8._8)[0]; + ((u64*)out)[7] = ((u64*)pCache[i].data[number][3].b8._8)[1]; + } + + + + if(mode == 1) + { + pCache[i].tag[number] |= 0x40; // Set Dirty Bit if mode == write + } + + pCache[i].tag[number] &= ~(0xFFFFF000); + pCache[i].tag[number] |= ((mem>>12) & 0xFFFFF) << 12; + + + t = (u8 *)paddr; + out= (u8*)(t + (mem & 0xFC0)); + ((u64*)pCache[i].data[number][0].b8._8)[0] = ((u64*)out)[0]; + ((u64*)pCache[i].data[number][0].b8._8)[1] = ((u64*)out)[1]; + ((u64*)pCache[i].data[number][1].b8._8)[0] = ((u64*)out)[2]; + ((u64*)pCache[i].data[number][1].b8._8)[1] = ((u64*)out)[3]; + ((u64*)pCache[i].data[number][2].b8._8)[0] = ((u64*)out)[4]; + ((u64*)pCache[i].data[number][2].b8._8)[1] = ((u64*)out)[5]; + ((u64*)pCache[i].data[number][3].b8._8)[0] = ((u64*)out)[6]; + ((u64*)pCache[i].data[number][3].b8._8)[1] = ((u64*)out)[7]; + + if(pCache[i].tag[number] & 0x10) pCache[i].tag[number] &= ~(0x10); + else pCache[i].tag[number] |= 0x10; + + pCache[i].tag[number] |= 0x20; + *way = number; + return i; +} + +void writeCache8(u32 mem, u8 value) { + int i, number; + + i = getFreeCache(mem,1,&number); +// CACHE_LOG("writeCache8 %8.8x adding to %d, way %d, value %x\n", mem, i,number,value); + + pCache[i].data[number][(mem>>4) & 0x3].b8._8[(mem&0xf)] = value; +} + +void writeCache16(u32 mem, u16 value) { + int i, number; + + i = getFreeCache(mem,1,&number); +// CACHE_LOG("writeCache16 %8.8x adding to %d, way %d, value %x\n", mem, i,number,value); + + *(u16*)(&pCache[i].data[number][(mem>>4) & 0x3].b8._8[(mem&0xf)]) = value; +} + +void writeCache32(u32 mem, u32 value) { + int i, number; + + i = getFreeCache(mem,1,&number); +// CACHE_LOG("writeCache32 %8.8x adding to %d, way %d, value %x\n", mem, i,number,value); + *(u32*)(&pCache[i].data[number][(mem>>4) & 0x3].b8._8[(mem&0xf)]) = value; +} + +void writeCache64(u32 mem, u64 value) { + int i, number; + + i = getFreeCache(mem,1,&number); +// CACHE_LOG("writeCache64 %8.8x adding to %d, way %d, value %x\n", mem, i,number,value); + *(u64*)(&pCache[i].data[number][(mem>>4) & 0x3].b8._8[(mem&0xf)]) = value; +} + +void writeCache128(u32 mem, u64 *value) { + int i, number; + + i = getFreeCache(mem,1,&number); +// CACHE_LOG("writeCache128 %8.8x adding to %d\n", mem, i); + ((u64*)pCache[i].data[number][(mem>>4) & 0x3].b8._8)[0] = value[0]; + ((u64*)pCache[i].data[number][(mem>>4) & 0x3].b8._8)[1] = value[1]; +} + +u8 *readCache(u32 mem) { + int i, number; + + i = getFreeCache(mem,0,&number); +// CACHE_LOG("readCache %8.8x from %d, way %d\n", mem, i,number); + + return pCache[i].data[number][(mem>>4) & 0x3].b8._8; +} + +namespace OpcodeImpl +{ + +extern int Dcache; +void CACHE() { + u32 addr; + //if(Dcache == 0) return; + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + switch (_Rt_) { + case 0x1a: + { + int index = (addr >> 6) & 0x3F; + u32 paddr[2]; + int way; + u32 taddr = memLUTR[addr >> 12]; + paddr[0] = memLUTW[pCache[index].tag[0] >> 12]; + paddr[1] = memLUTW[pCache[index].tag[1] >> 12]; + + if(paddr[0] == taddr && (pCache[index].tag[0] & 0x20)) + { + way = 0; + } + else if(paddr[1] == taddr && (pCache[index].tag[1] & 0x20)) + { + way = 1; + } + else + { + return; + } + + CACHE_LOG("CACHE DHIN addr %x, index %d, way %d, Flags %x\n",addr,index,way,pCache[index].tag[way] & 0x78); + + pCache[index].tag[way] &= ~(0x6F); + ((u64*)pCache[index].data[way][0].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][0].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][1].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][1].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][2].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][2].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][3].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][3].b8._8)[1] = 0; + break; + } + case 0x18: + { + u8 * out; + int index = (addr >> 6) & 0x3F; + u32 paddr[2]; + int way; + u32 taddr = memLUTW[addr >> 12]; + paddr[0] = memLUTW[pCache[index].tag[0] >> 12]; + paddr[1] = memLUTW[pCache[index].tag[1] >> 12]; + + if(paddr[0] == taddr && (pCache[index].tag[0] & 0x20)) + { + way = 0; + } + else if(paddr[1] == taddr && (pCache[index].tag[1] & 0x20)) + { + way = 1; + } + else + { + return; + } + + CACHE_LOG("CACHE DHWBIN addr %x, index %d, way %d, Flags %x\n",addr,index,way,pCache[index].tag[way] & 0x78); + + if(pCache[index].tag[way] & 0x60) // Valid Dirty + { + char * t = (char *)(taddr);//paddr[way]); + out = (u8*)(t + (addr & 0xFC0)); + ((u64*)out)[0] = ((u64*)pCache[index].data[way][0].b8._8)[0]; + ((u64*)out)[1] = ((u64*)pCache[index].data[way][0].b8._8)[1]; + ((u64*)out)[2] = ((u64*)pCache[index].data[way][1].b8._8)[0]; + ((u64*)out)[3] = ((u64*)pCache[index].data[way][1].b8._8)[1]; + ((u64*)out)[4] = ((u64*)pCache[index].data[way][2].b8._8)[0]; + ((u64*)out)[5] = ((u64*)pCache[index].data[way][2].b8._8)[1]; + ((u64*)out)[6] = ((u64*)pCache[index].data[way][3].b8._8)[0]; + ((u64*)out)[7] = ((u64*)pCache[index].data[way][3].b8._8)[1]; + } + + pCache[index].tag[way] &= ~(0x6F); + ((u64*)pCache[index].data[way][0].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][0].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][1].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][1].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][2].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][2].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][3].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][3].b8._8)[1] = 0; + + break; + } + case 0x1c: + { + u8 * out; + int index = (addr >> 6) & 0x3F; + u32 paddr[2]; + int way; + u32 taddr = memLUTW[addr >> 12]; + paddr[0] = memLUTW[pCache[index].tag[0] >> 12]; + paddr[1] = memLUTW[pCache[index].tag[1] >> 12]; + + if(paddr[0] == taddr && (pCache[index].tag[0] & 0x20)) + { + way = 0; + } + else if(paddr[1] == taddr && (pCache[index].tag[1] & 0x20)) + { + way = 1; + } + else + { + return; + } + CACHE_LOG("CACHE DHWOIN addr %x, index %d, way %d, Flags %x\n",addr,index,way,pCache[index].tag[way] & 0x78); + + if(pCache[index].tag[way] & 0x60) // Valid Dirty + { + char * t = (char *)(taddr); + out = (u8*)(t + (addr & 0xFC0)); + ((u64*)out)[0] = ((u64*)pCache[index].data[way][0].b8._8)[0]; + ((u64*)out)[1] = ((u64*)pCache[index].data[way][0].b8._8)[1]; + ((u64*)out)[2] = ((u64*)pCache[index].data[way][1].b8._8)[0]; + ((u64*)out)[3] = ((u64*)pCache[index].data[way][1].b8._8)[1]; + ((u64*)out)[4] = ((u64*)pCache[index].data[way][2].b8._8)[0]; + ((u64*)out)[5] = ((u64*)pCache[index].data[way][2].b8._8)[1]; + ((u64*)out)[6] = ((u64*)pCache[index].data[way][3].b8._8)[0]; + ((u64*)out)[7] = ((u64*)pCache[index].data[way][3].b8._8)[1]; + } + + pCache[index].tag[way] &= ~(0x40); + break; + } + case 0x16: + { + int index = (addr >> 6) & 0x3F; + int way = addr & 0x1; + + CACHE_LOG("CACHE DXIN addr %x, index %d, way %d, flag %x\n",addr,index,way,pCache[index].tag[way] & 0x78); + + pCache[index].tag[way] &= ~(0x6F); + + ((u64*)pCache[index].data[way][0].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][0].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][1].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][1].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][2].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][2].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][3].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][3].b8._8)[1] = 0; + break; + } + case 0x11: + { + int index = (addr >> 6) & 0x3F; + int way = addr & 0x1; + u8 * out = pCache[index].data[way][(addr>>4) & 0x3].b8._8; + cpuRegs.CP0.r[28] = *(u32 *)(out+(addr&0xf)); + + CACHE_LOG("CACHE DXLDT addr %x, index %d, way %d, DATA %x\n",addr,index,way,cpuRegs.CP0.r[28]); + + break; + } + case 0x10: + { + int index = (addr >> 6) & 0x3F; + int way = addr & 0x1; + + cpuRegs.CP0.r[28] = 0; + cpuRegs.CP0.r[28] = pCache[index].tag[way]; + + CACHE_LOG("CACHE DXLTG addr %x, index %d, way %d, DATA %x\n",addr,index,way,cpuRegs.CP0.r[28]); + + break; + } + case 0x13: + { + int index = (addr >> 6) & 0x3F; + int way = addr & 0x1; + //u8 * out = pCache[index].data[way][(addr>>4) & 0x3].b8._8; + *(u32*)(&pCache[index].data[way][(addr>>4) & 0x3].b8._8[(addr&0xf)]) = cpuRegs.CP0.r[28]; + + CACHE_LOG("CACHE DXSDT addr %x, index %d, way %d, DATA %x\n",addr,index,way,cpuRegs.CP0.r[28]); + + break; + } + case 0x12: + { + int index = (addr >> 6) & 0x3F; + int way = addr & 0x1; + pCache[index].tag[way] = cpuRegs.CP0.r[28]; + + CACHE_LOG("CACHE DXSTG addr %x, index %d, way %d, DATA %x\n",addr,index,way,cpuRegs.CP0.r[28] & 0x6F); + + break; + } + case 0x14: + { + + u8 * out; + int index = (addr >> 6) & 0x3F; + int way = addr & 0x1; + + + CACHE_LOG("CACHE DXWBIN addr %x, index %d, way %d, Flags %x\n",addr,index,way,pCache[index].tag[way] & 0x78); + + if(pCache[index].tag[way] & 0x60) // Dirty + { + u32 paddr = memLUTW[pCache[index].tag[way] >> 12]; + char * t = (char *)(paddr); + out = (u8*)(t + (addr & 0xFC0)); + ((u64*)out)[0] = ((u64*)pCache[index].data[way][0].b8._8)[0]; + ((u64*)out)[1] = ((u64*)pCache[index].data[way][0].b8._8)[1]; + ((u64*)out)[2] = ((u64*)pCache[index].data[way][1].b8._8)[0]; + ((u64*)out)[3] = ((u64*)pCache[index].data[way][1].b8._8)[1]; + ((u64*)out)[4] = ((u64*)pCache[index].data[way][2].b8._8)[0]; + ((u64*)out)[5] = ((u64*)pCache[index].data[way][2].b8._8)[1]; + ((u64*)out)[6] = ((u64*)pCache[index].data[way][3].b8._8)[0]; + ((u64*)out)[7] = ((u64*)pCache[index].data[way][3].b8._8)[1]; + } + + pCache[index].tag[way] &= ~(0x6F); + ((u64*)pCache[index].data[way][0].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][0].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][1].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][1].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][2].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][2].b8._8)[1] = 0; + ((u64*)pCache[index].data[way][3].b8._8)[0] = 0; + ((u64*)pCache[index].data[way][3].b8._8)[1] = 0; + break; + } + } +} +} // end namespace OpcodeImpl +#else + +namespace OpcodeImpl +{ + +void CACHE() { +} +} + +#endif + +}} \ No newline at end of file diff --git a/pcsx2/Cache.h b/pcsx2/Cache.h new file mode 100644 index 0000000000..1a5ebf80f6 --- /dev/null +++ b/pcsx2/Cache.h @@ -0,0 +1,48 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __CACHE_H__ +#define __CACHE_H__ + +#include "Common.h" + +struct _u8bit_128 { + u8 _8[16]; + +}; + +struct u8bit_128 { + _u8bit_128 b8; + +}; + +struct _cacheS { + u32 tag[2]; + u8bit_128 data[2][4]; +}; + +extern _cacheS pCache[64]; + +void writeCache8(u32 mem, u8 value); +void writeCache16(u32 mem, u16 value); +void writeCache32(u32 mem, u32 value); +void writeCache64(u32 mem, u64 value); +void writeCache128(u32 mem, u64 *value); +u8 *readCache(u32 mem); + +#endif /* __CACHE_H__ */ diff --git a/pcsx2/CdRom.cpp b/pcsx2/CdRom.cpp new file mode 100644 index 0000000000..44465d7226 --- /dev/null +++ b/pcsx2/CdRom.cpp @@ -0,0 +1,959 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" + +//THIS ALL IS FOR THE CDROM REGISTERS HANDLING + +#define CdlSync 0 +#define CdlNop 1 +#define CdlSetloc 2 +#define CdlPlay 3 +#define CdlForward 4 +#define CdlBackward 5 +#define CdlReadN 6 +#define CdlStandby 7 +#define CdlStop 8 +#define CdlPause 9 +#define CdlInit 10 +#define CdlMute 11 +#define CdlDemute 12 +#define CdlSetfilter 13 +#define CdlSetmode 14 +#define CdlGetmode 15 +#define CdlGetlocL 16 +#define CdlGetlocP 17 +#define Cdl18 18 +#define CdlGetTN 19 +#define CdlGetTD 20 +#define CdlSeekL 21 +#define CdlSeekP 22 +#define CdlTest 25 +#define CdlID 26 +#define CdlReadS 27 +#define CdlReset 28 +#define CdlReadToc 30 + +#define AUTOPAUSE 249 +#define READ_ACK 250 +#define READ 251 +#define REPPLAY_ACK 252 +#define REPPLAY 253 +#define ASYNC 254 +/* don't set 255, it's reserved */ + +const char *CmdName[0x100]= { + "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay", + "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby", + "CdlStop", "CdlPause", "CdlInit", "CdlMute", + "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetmode", + "CdlGetlocL", "CdlGetlocP", "Cdl18", "CdlGetTN", + "CdlGetTD", "CdlSeekL", "CdlSeekP", NULL, + NULL, "CdlTest", "CdlID", "CdlReadS", + "CdlReset", NULL, "CDlReadToc", NULL +}; + +cdrStruct cdr; +long LoadCdBios; +int cdOpenCase; + +u8 Test04[] = { 0 }; +u8 Test05[] = { 0 }; +u8 Test20[] = { 0x98, 0x06, 0x10, 0xC3 }; +u8 Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F }; +u8 Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 }; + +// 1x = 75 sectors per second +// PSXCLK = 1 sec in the ps +// so (PSXCLK / 75) / BIAS = cdr read time (linuzappz) +//#define cdReadTime ((PSXCLK / 75) / BIAS) +unsigned long cdReadTime;// = ((PSXCLK / 75) / BIAS); + +#define btoi(b) ((b)/16*10 + (b)%16) /* BCD to u_char */ +#define itob(i) ((i)/10*16 + (i)%10) /* u_char to BCD */ + +#define CDR_INT(eCycle) PSX_INT(IopEvt_Cdrom, eCycle) +#define CDREAD_INT(eCycle) PSX_INT(IopEvt_CdromRead, eCycle) + +static __forceinline void StartReading(unsigned long type) { + cdr.Reading = type; + cdr.FirstSector = 1; + cdr.Readed = 0xff; + AddIrqQueue(READ_ACK, 0x800); +} + +static __forceinline void StopReading() { + if (cdr.Reading) { + cdr.Reading = 0; + psxRegs.interrupt &= ~(1<(cdr.Transfer); + cdr.Stat = DiskError; + cdr.Result[0]|= 0x01; + ReadTrack(); + CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); + return; + } + memcpy_fast(cdr.Transfer, buf+12, 2340); + cdr.Stat = DataReady; + + CDR_LOG(" %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]); + + cdr.SetSector[2]++; + + if (cdr.SetSector[2] == 75) { + cdr.SetSector[2] = 0; + cdr.SetSector[1]++; + if (cdr.SetSector[1] == 60) { + cdr.SetSector[1] = 0; + cdr.SetSector[0]++; + } + } + + cdr.Readed = 0; + + if ((cdr.Transfer[4+2] & 0x80) && (cdr.Mode & 0x2)) { // EOF + CDR_LOG("AutoPausing Read\n"); + AddIrqQueue(CdlPause, 0x800); + } + else { + ReadTrack(); + CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); + } + + psxHu32(0x1070)|= 0x4; + return; +} + +/* +cdrRead0: + bit 0 - 0 REG1 command send / 1 REG1 data read + bit 1 - 0 data transfer finish / 1 data transfer ready/in progress + bit 2 - unknown + bit 3 - unknown + bit 4 - unknown + bit 5 - 1 result ready + bit 6 - 1 dma ready + bit 7 - 1 command being processed +*/ + +u8 cdrRead0(void) { + if (cdr.ResultReady) + cdr.Ctrl |= 0x20; + else + cdr.Ctrl &= ~0x20; + + if (cdr.OCUP) + cdr.Ctrl |= 0x40; + else + cdr.Ctrl &= ~0x40; + + // what means the 0x10 and the 0x08 bits? i only saw it used by the bios + cdr.Ctrl|=0x18; + + CDR_LOG("CD0 Read: %x\n", cdr.Ctrl); + return psxHu8(0x1800) = cdr.Ctrl; +} + +/* +cdrWrite0: + 0 - to send a command / 1 - to get the result +*/ + +void cdrWrite0(u8 rt) { + CDR_LOG("CD0 write: %x\n", rt); + + cdr.Ctrl = rt | (cdr.Ctrl & ~0x3); + + if (rt == 0) { + cdr.ParamP = 0; + cdr.ParamC = 0; + cdr.ResultReady = 0; + } +} + +u8 cdrRead1(void) { + if (cdr.ResultReady && cdr.Ctrl & 0x1) { + psxHu8(0x1801) = cdr.Result[cdr.ResultP++]; + if (cdr.ResultP == cdr.ResultC) cdr.ResultReady = 0; + } + else + psxHu8(0x1801) = 0; + + CDR_LOG("CD1 Read: %x\n", psxHu8(0x1801)); + return psxHu8(0x1801); +} + +void cdrWrite1(u8 rt) { + int i; + + CDR_LOG("CD1 write: %x (%s)\n", rt, CmdName[rt]); + cdr.Cmd = rt; + cdr.OCUP = 0; + +#ifdef CDRCMD_DEBUG + SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]); + if (cdr.ParamC) { + SysPrintf(" Param[%d] = {", cdr.ParamC); + for (i=0;i 1) cdr.CurTrack--; + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlReadN: + cdr.Irq = 0; + StopReading(); + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + StartReading(1); + break; + + case CdlStandby: + StopCdda(); + StopReading(); + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlStop: + StopCdda(); + StopReading(); + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlPause: + StopCdda(); + StopReading(); + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x40000); + break; + + case CdlReset: + case CdlInit: + StopCdda(); + StopReading(); + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlMute: + cdr.Muted = 0; + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlDemute: + cdr.Muted = 1; + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlSetfilter: + cdr.File = cdr.Param[0]; + cdr.Channel = cdr.Param[1]; + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlSetmode: + CDR_LOG("Setmode %x\n", cdr.Param[0]); + + cdr.Mode = cdr.Param[0]; + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlGetmode: + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlGetlocL: + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlGetlocP: + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlGetTN: + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlGetTD: + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlSeekL: + ((unsigned long *)cdr.SetSectorSeek)[0] = ((unsigned long *)cdr.SetSector)[0]; + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlSeekP: + ((unsigned long *)cdr.SetSectorSeek)[0] = ((unsigned long *)cdr.SetSector)[0]; + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlTest: + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlID: + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + case CdlReadS: + cdr.Irq = 0; + StopReading(); + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + StartReading(2); + break; + + case CdlReadToc: + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + + default: + CDR_LOG("Unknown Cmd: %x\n", cdr.Cmd); + return; + } + if (cdr.Stat != NoIntr) + iopIntcIrq( 2 ); +} + +u8 cdrRead2(void) { + u8 ret; + + if (cdr.Readed == 0) { + ret = 0; + } else { + ret = *cdr.pTransfer++; + } + + CDR_LOG("CD2 Read: %x\n", ret); + return ret; +} + +void cdrWrite2(u8 rt) { + CDR_LOG("CD2 write: %x\n", rt); + + if (cdr.Ctrl & 0x1) { + switch (rt) { + case 0x07: + cdr.ParamP = 0; + cdr.ParamC = 0; + cdr.ResultReady = 0; + cdr.Ctrl = 0; + break; + + default: + cdr.Reg2 = rt; + break; + } + } + else + { + if (!(cdr.Ctrl & 0x1) && cdr.ParamP < 8) { + cdr.Param[cdr.ParamP++] = rt; + cdr.ParamC++; + } + } +} + +u8 cdrRead3(void) { + if (cdr.Stat) { + if (cdr.Ctrl & 0x1) + psxHu8(0x1803) = cdr.Stat | 0xE0; + else + psxHu8(0x1803) = 0xff; + } else psxHu8(0x1803) = 0; + + CDR_LOG("CD3 Read: %x\n", psxHu8(0x1803)); + return psxHu8(0x1803); +} + +void cdrWrite3(u8 rt) { + CDR_LOG("CD3 write: %x\n", rt); + + if (rt == 0x07 && cdr.Ctrl & 0x1) { + cdr.Stat = 0; + + if (cdr.Irq == 0xff) { cdr.Irq = 0; return; } + if (cdr.Irq) { + CDR_INT(cdr.eCycle); + } + return; + } + + if (rt == 0x80 && !(cdr.Ctrl & 0x1) && cdr.Readed == 0) { + cdr.Readed = 1; + cdr.pTransfer = cdr.Transfer; + + switch (cdr.Mode&0x30) { + case 0x10: + case 0x00: cdr.pTransfer+=12; break; + default: break; + } + } +} + +void psxDma3(u32 madr, u32 bcr, u32 chcr) { + u32 cdsize; + + CDR_LOG("*** DMA 3 *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); + + switch (chcr) { + case 0x11000000: + case 0x11400100: + if (cdr.Readed == 0) { + CDR_LOG("*** DMA 3 *** NOT READY\n"); + return; + } + + cdsize = (bcr & 0xffff) * 4; + memcpy_fast((u8*)PSXM(madr), cdr.pTransfer, cdsize); + psxCpu->Clear(madr, cdsize/4); + cdr.pTransfer+=cdsize; + + break; + case 0x41000200: + //SysPrintf("unhandled cdrom dma3: madr: %x, bcr: %x, chcr %x\n", madr, bcr, chcr); + return; + + default: + CDR_LOG("Unknown cddma %lx\n", chcr); + break; + } + HW_DMA3_CHCR &= ~0x01000000; + psxDmaInterrupt(3); +} + +void cdrReset() { + memzero_obj(cdr); + cdr.CurTrack=1; + cdr.File=1; cdr.Channel=1; + cdReadTime = (PSXCLK / 1757) * BIAS; +} + +void SaveState::cdrFreeze() { + Freeze(cdr); + + // Alrighty! This code used to, for some reason, recalculate the pTransfer value + // even though it's being saved as part of the cdr struct. Probably a backwards + // compat fix with an earlier save version. + + int tmp; // = (int)(cdr.pTransfer - cdr.Transfer); + Freeze(tmp); + //if (Mode == 0) cdr.pTransfer = cdr.Transfer + tmp; +} + diff --git a/pcsx2/CdRom.h b/pcsx2/CdRom.h new file mode 100644 index 0000000000..d8bd02873f --- /dev/null +++ b/pcsx2/CdRom.h @@ -0,0 +1,93 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __CDROM_H__ +#define __CDROM_H__ + +#include "PsxCommon.h" +#include "Decode_XA.h" +#include "PS2Edefs.h" + +struct cdrStruct +{ + u8 OCUP; + u8 Reg1Mode; + u8 Reg2; + u8 CmdProcess; + u8 Ctrl; + u8 Stat; + + u8 StatP; + + u8 Transfer[2352]; + u8 *pTransfer; + + u8 Prev[4]; + u8 Param[8]; + u8 Result[8]; + + u8 ParamC; + u8 ParamP; + u8 ResultC; + u8 ResultP; + u8 ResultReady; + u8 Cmd; + u8 Readed; + unsigned long Reading; + + cdvdTN ResultTN; + u8 ResultTD[4]; + u8 SetSector[4]; + u8 SetSectorSeek[4]; + u8 Track; + int Play; + int CurTrack; + int Mode, File, Channel, Muted; + int Reset; + int RErr; + int FirstSector; + + xa_decode_t Xa; + + int Init; + + u8 Irq; + unsigned long eCycle; + + char Unused[4087]; +}; + +extern cdrStruct cdr; + +s32 MSFtoLSN(u8 *Time); +void LSNtoMSF(u8 *Time, s32 lsn); +void AddIrqQueue(u8 irq, unsigned long ecycle); + +void cdrReset(); +void cdrInterrupt(); +void cdrReadInterrupt(); +u8 cdrRead0(void); +u8 cdrRead1(void); +u8 cdrRead2(void); +u8 cdrRead3(void); +void cdrWrite0(u8 rt); +void cdrWrite1(u8 rt); +void cdrWrite2(u8 rt); +void cdrWrite3(u8 rt); + +#endif /* __CDROM_H__ */ diff --git a/pcsx2/Common.h b/pcsx2/Common.h new file mode 100644 index 0000000000..90aa7b6246 --- /dev/null +++ b/pcsx2/Common.h @@ -0,0 +1,79 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include "PS2Etypes.h" + +struct TESTRUNARGS +{ + u8 enabled; + u8 jpgcapture; + + int frame; // if < 0, frame is unlimited (run until crash). + int numimages; + int curimage; + u32 autopad; // mask for auto buttons + bool efile; + int snapdone; + + const char* ptitle; + const char* pimagename; + const char* plogname; + const char* pgsdll, *pcdvddll, *pspudll; + +}; + +extern TESTRUNARGS g_TestRun; + +#define BIAS 2 // Bus is half of the actual ps2 speed +//#define PS2CLK 36864000 /* 294.912 mhz */ +//#define PSXCLK 9216000 /* 36.864 Mhz */ +//#define PSXCLK 186864000 /* 36.864 Mhz */ +#define PS2CLK 294912000 //hz /* 294.912 mhz */ + + +/* Config.PsxType == 1: PAL: + VBlank interlaced 50.00 Hz + VBlank non-interlaced 49.76 Hz + HBlank 15.625 KHz + Config.PsxType == 0: NSTC + VBlank interlaced 59.94 Hz + VBlank non-interlaced 59.82 Hz + HBlank 15.73426573 KHz */ + +//Misc Clocks +#define PSXPIXEL ((int)(PSXCLK / 13500000)) +#define PSXSOUNDCLK ((int)(48000)) + +#include "Plugins.h" +#include "Misc.h" +#include "SaveState.h" +#include "DebugTools/Debug.h" +#include "R5900.h" +#include "Memory.h" +#include "Elfheader.h" +#include "Hw.h" +// Moving this before one of the other includes causes compilation issues. +//#include "Misc.h" +#include "Patch.h" + +#define PCSX2_VERSION "Playground (beta)" + +#endif /* __COMMON_H__ */ diff --git a/pcsx2/Console.cpp b/pcsx2/Console.cpp new file mode 100644 index 0000000000..a7f97121bb --- /dev/null +++ b/pcsx2/Console.cpp @@ -0,0 +1,179 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "System.h" +#include "DebugTools/Debug.h" + +using namespace std; + +const _VARG_PARAM va_arg_dummy = { 0 }; + +// Methods of the Console namespace not defined here are to be found in the platform +// dependent implementations in WinConsole.cpp and LnxConsole.cpp. + +namespace Console +{ + __forceinline void __fastcall _WriteLn( Colors color, const char* fmt, va_list args ) + { + SetColor( color ); + WriteLn( vfmt_string( fmt, args ).c_str() ); + ClearColor(); + } + + bool Write( const char* fmt, VARG_PARAM dummy, ... ) + { + varg_assert(); + + va_list list; + va_start(list,dummy); + WriteLn( vfmt_string( fmt, list ).c_str() ); + va_end(list); + + return false; + } + + bool Write( Colors color, const char* fmt ) + { + SetColor( color ); + Write( fmt ); + ClearColor(); + return false; + } + + bool Write( Colors color, const char* fmt, VARG_PARAM dummy, ... ) + { + varg_assert(); + + va_list list; + va_start(list,dummy); + Write( vfmt_string( fmt, list ).c_str() ); + va_end(list); + + return false; + } + + bool WriteLn( const char* fmt, VARG_PARAM dummy, ... ) + { + varg_assert(); + + va_list list; + va_start(list,dummy); + WriteLn( vfmt_string( fmt, list).c_str() ); + va_end(list); + + return false; + } + + // Writes an unformatted string of text to the console (fast!) + // A newline is automatically appended. + __forceinline bool __fastcall WriteLn( const char* fmt ) + { + Write( fmt ); + Newline(); + return false; + } + + // Writes an unformatted string of text to the console (fast!) + // A newline is automatically appended. + __forceinline bool __fastcall WriteLn( Colors color, const char* fmt ) + { + Write( color, fmt ); + Newline(); + return false; + } + + // Writes a line of colored text to the console, with automatic newline appendage. + bool WriteLn( Colors color, const char* fmt, VARG_PARAM dummy, ... ) + { + varg_assert(); + + va_list list; + va_start(list,dummy); + _WriteLn( Color_White, fmt, list ); + va_end(list); + return false; + } + + // Displays a message in the console with red emphasis. + // Newline is automatically appended. + bool Error( const char* fmt, VARG_PARAM dummy, ... ) + { + varg_assert(); + + va_list list; + va_start(list,dummy); + _WriteLn( Color_Red, fmt, list ); + va_end(list); + return false; + } + + // Displays a message in the console with yellow emphasis. + // Newline is automatically appended. + bool Notice( const char* fmt, VARG_PARAM dummy, ... ) + { + varg_assert(); + + va_list list; + + va_start(list,dummy); + _WriteLn( Color_Yellow, fmt, list ); + va_end(list); + return false; + } + + // Displays a message in the console with green emphasis. + // Newline is automatically appended. + bool Status( const char* fmt, VARG_PARAM dummy, ... ) + { + varg_assert(); + + va_list list; + va_start(list,dummy); + _WriteLn( Color_Green, fmt, list ); + va_end(list); + return false; + } + + // Displays a message in the console with red emphasis. + // Newline is automatically appended. + bool Error( const char* fmt ) + { + WriteLn( Color_Red, fmt ); + return false; + } + + // Displays a message in the console with yellow emphasis. + // Newline is automatically appended. + bool Notice( const char* fmt ) + { + WriteLn( Color_Yellow, fmt ); + return false; + } + + // Displays a message in the console with green emphasis. + // Newline is automatically appended. + bool Status( const char* fmt ) + { + WriteLn( Color_Green, fmt ); + return false; + } + +} + diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp new file mode 100644 index 0000000000..6900a6f214 --- /dev/null +++ b/pcsx2/Counters.cpp @@ -0,0 +1,863 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include +#include +#include "Common.h" +#include "Counters.h" + +#include "R3000A.h" +#include "IopCounters.h" + +#include "GS.h" +#include "VUmicro.h" + +using namespace Threading; + +extern u8 psxhblankgate; +u32 g_vu1SkipCount; // number of frames to disable/skip VU1 + +static const uint EECNT_FUTURE_TARGET = 0x10000000; + +u64 profile_starttick = 0; +u64 profile_totalticks = 0; + +int gates = 0; + +// Counter 4 takes care of scanlines - hSync/hBlanks +// Counter 5 takes care of vSync/vBlanks +Counter counters[6]; + +u32 nextsCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate() +s32 nextCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate() + +// VUSkip Locals and Globals + +void rcntReset(int index) { + counters[index].count = 0; + counters[index].sCycleT = cpuRegs.cycle; +} + +// Updates the state of the nextCounter value (if needed) to serve +// any pending events for the given counter. +// Call this method after any modifications to the state of a counter. +static __forceinline void _rcntSet( int cntidx ) +{ + s32 c; + jASSUME( cntidx <= 4 ); // rcntSet isn't valid for h/vsync counters. + + const Counter& counter = counters[cntidx]; + + // Stopped or special hsync gate? + if (!counter.mode.IsCounting || (counter.mode.ClockSource == 0x3) ) return; + + // check for special cases where the overflow or target has just passed + // (we probably missed it because we're doing/checking other things) + if( counter.count > 0x10000 || counter.count > counter.target ) + { + nextCounter = 4; + return; + } + + // nextCounter is relative to the cpuRegs.cycle when rcntUpdate() was last called. + // However, the current _rcntSet could be called at any cycle count, so we need to take + // that into account. Adding the difference from that cycle count to the current one + // will do the trick! + + c = ((0x10000 - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT); + c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate(); + if (c < nextCounter) nextCounter = c; + + // Ignore target diff if target is currently disabled. + // (the overflow is all we care about since it goes first, and then the + // target will be turned on afterward). + + if( counter.target & EECNT_FUTURE_TARGET ) return; + c = ((counter.target - counter.count) * counter.rate) - (cpuRegs.cycle - counter.sCycleT); + c += cpuRegs.cycle - nextsCounter; // adjust for time passed since last rcntUpdate(); + if (c < nextCounter) nextCounter = c; +} + + +static __forceinline void cpuRcntSet() +{ + int i; + + nextsCounter = cpuRegs.cycle; + nextCounter = (counters[5].sCycle + counters[5].CycleT) - cpuRegs.cycle; + + for (i = 0; i < 4; i++) + _rcntSet( i ); + + // sanity check! + if( nextCounter < 0 ) nextCounter = 0; +} + +void rcntInit() { + int i; + + memzero_obj(counters); + + for (i=0; i<4; i++) { + counters[i].rate = 2; + counters[i].target = 0xffff; + } + counters[0].interrupt = 9; + counters[1].interrupt = 10; + counters[2].interrupt = 11; + counters[3].interrupt = 12; + + counters[4].modeval = MODE_HRENDER; + counters[4].sCycle = cpuRegs.cycle; + counters[5].modeval = MODE_VRENDER; + counters[5].sCycle = cpuRegs.cycle; + + UpdateVSyncRate(); + + for (i=0; i<4; i++) rcntReset(i); + cpuRcntSet(); +} + +// debug code, used for stats +int g_nCounters[4]; +static int iFrame = 0; + +#ifndef _WIN32 +#include +#endif + +static s64 m_iTicks=0; +static u64 m_iStart=0; + +struct vSyncTimingInfo +{ + u32 Framerate; // frames per second * 100 (so 2500 for PAL and 2997 for NTSC) + u32 Render; // time from vblank end to vblank start (cycles) + u32 Blank; // time from vblank start to vblank end (cycles) + + u32 hSyncError; // rounding error after the duration of a rendered frame (cycles) + u32 hRender; // time from hblank end to hblank start (cycles) + u32 hBlank; // time from hblank start to hblank end (cycles) + u32 hScanlinesPerFrame; // number of scanlines per frame (525/625 for NTSC/PAL) +}; + + +static vSyncTimingInfo vSyncInfo; + + +static __forceinline void vSyncInfoCalc( vSyncTimingInfo* info, u32 framesPerSecond, u32 scansPerFrame ) +{ + // Important: Cannot use floats or doubles here. The emulator changes rounding modes + // depending on user-set speedhack options, and it can break float/double code + // (as in returning infinities and junk) + + // NOTE: mgs3 likes a /4 vsync, but many games prefer /2. This seems to indicate a + // problem in the counters vsync gates somewhere. + + u64 Frame = ((u64)PS2CLK * 1000000ULL) / framesPerSecond; + u64 HalfFrame = Frame / 2; + u64 Blank = HalfFrame / 2; // two blanks and renders per frame + u64 Render = HalfFrame - Blank; // so use the half-frame value for these... + + // Important! The hRender/hBlank timers should be 50/50 for best results. + // In theory a 70%/30% ratio would be more correct but in practice it runs + // like crap and totally screws audio synchronization and other things. + + u64 Scanline = Frame / scansPerFrame; + u64 hBlank = Scanline / 2; + u64 hRender = Scanline - hBlank; + + info->Framerate = framesPerSecond; + info->Render = (u32)(Render/10000); + info->Blank = (u32)(Blank/10000); + + info->hRender = (u32)(hRender/10000); + info->hBlank = (u32)(hBlank/10000); + info->hScanlinesPerFrame = scansPerFrame; + + // Apply rounding: + if( ( Render - info->Render ) >= 5000 ) info->Render++; + else if( ( Blank - info->Blank ) >= 5000 ) info->Blank++; + + if( ( hRender - info->hRender ) >= 5000 ) info->hRender++; + else if( ( hBlank - info->hBlank ) >= 5000 ) info->hBlank++; + + // Calculate accumulative hSync rounding error per half-frame: + { + u32 hSyncCycles = ((info->hRender + info->hBlank) * scansPerFrame) / 2; + u32 vSyncCycles = (info->Render + info->Blank); + info->hSyncError = vSyncCycles - hSyncCycles; + } + + // Note: In NTSC modes there is some small rounding error in the vsync too, + // however it would take thousands of frames for it to amount to anything and + // is thus not worth the effort at this time. +} + + +u32 UpdateVSyncRate() +{ + const char *limiterMsg = "Framelimiter rate updated (UpdateVSyncRate): %d.%d fps"; + + // fixme - According to some docs, progressive-scan modes actually refresh slower than + // interlaced modes. But I can't fathom how, since the refresh rate is a function of + // the television and all the docs I found on TVs made no indication that they ever + // run anything except their native refresh rate. + + //#define VBLANK_NTSC ((Config.PsxType & 2) ? 59.94 : 59.82) //59.94 is more precise + //#define VBLANK_PAL ((Config.PsxType & 2) ? 50.00 : 49.76) + + if(Config.PsxType & 1) + { + if( vSyncInfo.Framerate != FRAMERATE_PAL ) + vSyncInfoCalc( &vSyncInfo, FRAMERATE_PAL, SCANLINES_TOTAL_PAL ); + } + else + { + if( vSyncInfo.Framerate != FRAMERATE_NTSC ) + vSyncInfoCalc( &vSyncInfo, FRAMERATE_NTSC, SCANLINES_TOTAL_NTSC ); + } + + counters[4].CycleT = vSyncInfo.hRender; // Amount of cycles before the counter will be updated + counters[5].CycleT = vSyncInfo.Render; // Amount of cycles before the counter will be updated + + if (Config.CustomFps > 0) + { + s64 ticks = GetTickFrequency() / Config.CustomFps; + if( m_iTicks != ticks ) + { + m_iTicks = ticks; + gsOnModeChanged( vSyncInfo.Framerate, m_iTicks ); + Console::Status( limiterMsg, params Config.CustomFps, 0 ); + } + } + else + { + s64 ticks = (GetTickFrequency() * 50) / vSyncInfo.Framerate; + if( m_iTicks != ticks ) + { + m_iTicks = ticks; + gsOnModeChanged( vSyncInfo.Framerate, m_iTicks ); + Console::Status( limiterMsg, params vSyncInfo.Framerate/50, (vSyncInfo.Framerate*2)%100 ); + } + } + + m_iStart = GetCPUTicks(); + cpuRcntSet(); + + // Initialize VU Skip Stuff... + g_vu1SkipCount = 0; + + return (u32)m_iTicks; +} + +extern u32 vu0time; + + +void vSyncDebugStuff() { + +#ifdef PCSX2_DEVBUILD + if( g_TestRun.enabled && g_TestRun.frame > 0 ) { + if( iFrame > g_TestRun.frame ) { + // take a snapshot + if( g_TestRun.pimagename != NULL && GSmakeSnapshot2 != NULL ) { + if( g_TestRun.snapdone ) { + g_TestRun.curimage++; + g_TestRun.snapdone = 0; + g_TestRun.frame += 20; + if( g_TestRun.curimage >= g_TestRun.numimages ) { + // exit + SysClose(); + exit(0); + } + } + else { + // query for the image + GSmakeSnapshot2(g_TestRun.pimagename, &g_TestRun.snapdone, g_TestRun.jpgcapture); + } + } + else { + // exit + SysClose(); + exit(0); + } + } + } + + GSVSYNC(); + + if( g_SaveGSStream == 1 ) { + freezeData fP; + + g_SaveGSStream = 2; + g_fGSSave->gsFreeze(); + + if (GSfreeze(FREEZE_SIZE, &fP) == -1) { + safe_delete( g_fGSSave ); + g_SaveGSStream = 0; + } + else { + fP.data = (s8*)malloc(fP.size); + if (fP.data == NULL) { + safe_delete( g_fGSSave ); + g_SaveGSStream = 0; + } + else { + if (GSfreeze(FREEZE_SAVE, &fP) == -1) { + safe_delete( g_fGSSave ); + g_SaveGSStream = 0; + } + else { + g_fGSSave->Freeze( fP.size ); + if (fP.size) { + g_fGSSave->FreezeMem( fP.data, fP.size ); + free(fP.data); + } + } + } + } + } + else if( g_SaveGSStream == 2 ) { + + if( --g_nLeftGSFrames <= 0 ) { + safe_delete( g_fGSSave ); + g_SaveGSStream = 0; + Console::WriteLn("Done saving GS stream"); + } + } +#endif +} + +void frameLimitReset() +{ + m_iStart = GetCPUTicks(); +} + +// Framelimiter - Measures the delta time between calls and stalls until a +// certain amount of time passes if such time hasn't passed yet. +// See the GS FrameSkip function for details on why this is here and not in the GS. +static __forceinline void frameLimit() +{ + s64 sDeltaTime; + u64 uExpectedEnd; + u64 iEnd; + + if( CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_NORMAL ) return; + if( Config.CustomFps >= 999 ) return; // means the user would rather just have framelimiting turned off... + + uExpectedEnd = m_iStart + m_iTicks; + iEnd = GetCPUTicks(); + + sDeltaTime = iEnd - uExpectedEnd; + + // If the framerate drops too low, reset the expected value. This avoids + // excessive amounts of "fast forward" syndrome which would occur if we + // tried to catch up too much. + + if( sDeltaTime > m_iTicks*8 ) + { + m_iStart = iEnd - m_iTicks; + + // Let the GS Skipper know we lost time. + // Keeps the GS skipper from trying to catch up to a framerate + // that the limiter already gave up on. + + gsSyncLimiterLostTime( (s32)(m_iStart - uExpectedEnd) ); + return; + } + + // use the expected frame completion time as our starting point. + // improves smoothness by making the framelimiter more adaptive to the + // imperfect TIMESLICE() wait, and allows it to speed up a wee bit after + // slow frames to "catch up." + + m_iStart = uExpectedEnd; + + while( sDeltaTime < 0 ) + { + Timeslice(); + iEnd = GetCPUTicks(); + sDeltaTime = iEnd - uExpectedEnd; + } +} + +static __forceinline void VSyncStart(u32 sCycle) +{ + EECNT_LOG( "///////// EE COUNTER VSYNC START \\\\\\\\\\ (frame: %d)\n", iFrame ); + vSyncDebugStuff(); // EE Profiling and Debug code + + if ((CSRw & 0x8)) GSCSRr|= 0x8; + if (!(GSIMR&0x800)) gsIrq(); + + hwIntcIrq(2); + psxVBlankStart(); + + if (gates) rcntStartGate(0x8, sCycle); // Counters Start Gate code + if (Config.Patch) applypatch(1); // Apply patches (ToDo: clean up patch code) + + // INTC - VB Blank Start Hack -- + // Hack fix! This corrects a freezeup in Granda 2 where it decides to spin + // on the INTC_STAT register after the exception handler has already cleared + // it. But be warned! Set the value to larger than 4 and it breaks Dark + // Cloud and other games. -_- + + // How it works: Normally the INTC raises exceptions immediately at the end of the + // current branch test. But in the case of Grandia 2, the game's code is spinning + // on the INTC status, and the exception handler (for some reason?) clears the INTC + // before returning *and* returns to a location other than EPC. So the game never + // gets to the point where it sees the INTC Irq set true. + + // (I haven't investigated why Dark Cloud freezes on larger values) + // (all testing done using the recompiler -- dunno how the ints respond yet) + + cpuRegs.eCycle[30] = 2; +} + +static __forceinline void VSyncEnd(u32 sCycle) +{ + EECNT_LOG( "///////// EE COUNTER VSYNC END \\\\\\\\\\ (frame: %d)\n", iFrame ); + + iFrame++; + + if( g_vu1SkipCount > 0 ) + { + gsPostVsyncEnd( false ); + AtomicDecrement( g_vu1SkipCount ); + vu1MicroEnableSkip(); + } + else + { + gsPostVsyncEnd( true ); + vu1MicroDisableSkip(); + } + + hwIntcIrq(3); // HW Irq + psxVBlankEnd(); // psxCounters vBlank End + if (gates) rcntEndGate(0x8, sCycle); // Counters End Gate Code + frameLimit(); // limit FPS + + // This doesn't seem to be needed here. Games only seem to break with regard to the + // vsyncstart irq. + //cpuRegs.eCycle[30] = 2; +} + +//#define VSYNC_DEBUG // Uncomment this to enable some vSync Timer debugging features. +#ifdef VSYNC_DEBUG +static u32 hsc=0; +static int vblankinc = 0; +#endif + +__forceinline void rcntUpdate_hScanline() +{ + if( !cpuTestCycle( counters[4].sCycle, counters[4].CycleT ) ) return; + + //iopBranchAction = 1; + if (counters[4].modeval & MODE_HBLANK) { //HBLANK Start + rcntStartGate(0, counters[4].sCycle); + psxCheckStartGate16(0); + + // Setup the hRender's start and end cycle information: + counters[4].sCycle += vSyncInfo.hBlank; // start (absolute cycle value) + counters[4].CycleT = vSyncInfo.hRender; // endpoint (delta from start value) + counters[4].modeval = MODE_HRENDER; + } + else { //HBLANK END / HRENDER Begin + if (CSRw & 0x4) GSCSRr |= 4; // signal + if (!(GSIMR&0x400)) gsIrq(); + if (gates) rcntEndGate(0, counters[4].sCycle); + if (psxhblankgate) psxCheckEndGate16(0); + + // set up the hblank's start and end cycle information: + counters[4].sCycle += vSyncInfo.hRender; // start (absolute cycle value) + counters[4].CycleT = vSyncInfo.hBlank; // endpoint (delta from start value) + counters[4].modeval = MODE_HBLANK; + +# ifdef VSYNC_DEBUG + hsc++; +# endif + } +} + +__forceinline bool rcntUpdate_vSync() +{ + s32 diff = (cpuRegs.cycle - counters[5].sCycle); + if( diff < counters[5].CycleT ) return false; + + //iopBranchAction = 1; + if (counters[5].modeval == MODE_VSYNC) + { + VSyncEnd(counters[5].sCycle); + + counters[5].sCycle += vSyncInfo.Blank; + counters[5].CycleT = vSyncInfo.Render; + counters[5].modeval = MODE_VRENDER; + + return true; +// SysUpdate(); // check for and handle keyevents + } + else // VSYNC end / VRENDER begin + { + VSyncStart(counters[5].sCycle); + + counters[5].sCycle += vSyncInfo.Render; + counters[5].CycleT = vSyncInfo.Blank; + counters[5].modeval = MODE_VSYNC; + + // Accumulate hsync rounding errors: + counters[4].sCycle += vSyncInfo.hSyncError; + +# ifdef VSYNC_DEBUG + vblankinc++; + if( vblankinc > 1 ) + { + if( hsc != vSyncInfo.hScanlinesPerFrame ) + SysPrintf( " ** vSync > Abnormal Scanline Count: %d\n", hsc ); + hsc = 0; + vblankinc = 0; + } +# endif + } + return false; +} + +static __forceinline void __fastcall _cpuTestTarget( int i ) +{ + if (counters[i].count < counters[i].target) return; + + if(counters[i].mode.TargetInterrupt) { + + EECNT_LOG("EE Counter[%d] TARGET reached - mode=%x, count=%x, target=%x\n", i, counters[i].mode, counters[i].count, counters[i].target); + counters[i].mode.TargetReached = 1; + hwIntcIrq(counters[i].interrupt); + + // The PS2 only resets if the interrupt is enabled - Tested on PS2 + if (counters[i].mode.ZeroReturn) + counters[i].count -= counters[i].target; // Reset on target + else + counters[i].target |= EECNT_FUTURE_TARGET; + } + else counters[i].target |= EECNT_FUTURE_TARGET; +} + +static __forceinline void _cpuTestOverflow( int i ) +{ + if (counters[i].count <= 0xffff) return; + + if (counters[i].mode.OverflowInterrupt) { + EECNT_LOG("EE Counter[%d] OVERFLOW - mode=%x, count=%x\n", i, counters[i].mode, counters[i].count); + counters[i].mode.OverflowReached = 1; + hwIntcIrq(counters[i].interrupt); + } + + // wrap counter back around zero, and enable the future target: + counters[i].count -= 0x10000; + counters[i].target &= 0xffff; +} + + +// forceinline note: this method is called from two locations, but one +// of them is the interpreter, which doesn't count. ;) So might as +// well forceinline it! +__forceinline bool rcntUpdate() +{ + bool retval = rcntUpdate_vSync(); + + // Update counters so that we can perform overflow and target tests. + + for (int i=0; i<=3; i++) { + + // We want to count gated counters (except the hblank which exclude below, and are + // counted by the hblank timer instead) + + //if ( gates & (1< 0 ) + { + counters[index].count += change / counters[index].rate; + change -= (change / counters[index].rate) * counters[index].rate; + counters[index].sCycleT = cpuRegs.cycle - change; + } + } + } + else counters[index].sCycleT = cpuRegs.cycle; + + counters[index].modeval &= ~(value & 0xc00); //Clear status flags, the ps2 only clears what is given in the value + counters[index].modeval = (counters[index].modeval & 0xc00) | (value & 0x3ff); + EECNT_LOG("EE Counter[%d] writeMode = %x passed value=%x\n", index, counters[index].modeval, value ); + + switch (counters[index].mode.ClockSource) { //Clock rate divisers *2, they use BUSCLK speed not PS2CLK + case 0: counters[index].rate = 2; break; + case 1: counters[index].rate = 32; break; + case 2: counters[index].rate = 512; break; + case 3: counters[index].rate = vSyncInfo.hBlank+vSyncInfo.hRender; break; + } + + _rcntSetGate( index ); + _rcntSet( index ); +} + +void __fastcall rcntWcount(int index, u32 value) +{ + EECNT_LOG("EE Counter[%d] writeCount = %x, oldcount=%x, target=%x\n", index, value, counters[index].count, counters[index].target ); + + counters[index].count = value & 0xffff; + + // reset the target, and make sure we don't get a premature target. + counters[index].target &= 0xffff; + if( counters[index].count > counters[index].target ) + counters[index].target |= EECNT_FUTURE_TARGET; + + // re-calculate the start cycle of the counter based on elapsed time since the last counter update: + if(counters[index].mode.IsCounting) { + if(counters[index].mode.ClockSource != 0x3) { + s32 change = cpuRegs.cycle - counters[index].sCycleT; + if( change > 0 ) { + change -= (change / counters[index].rate) * counters[index].rate; + counters[index].sCycleT = cpuRegs.cycle - change; + } + } + } + else counters[index].sCycleT = cpuRegs.cycle; + + _rcntSet( index ); +} + +void __fastcall rcntWtarget(int index, u32 value) +{ + EECNT_LOG("EE Counter[%d] writeTarget = %x\n", index, value); + + counters[index].target = value & 0xffff; + + // guard against premature (instant) targeting. + // If the target is behind the current count, set it up so that the counter must + // overflow first before the target fires: + + if( counters[index].target <= rcntCycle(index) ) + counters[index].target |= EECNT_FUTURE_TARGET; + + _rcntSet( index ); +} + +void __fastcall rcntWhold(int index, u32 value) +{ + EECNT_LOG("EE Counter[%d] Hold Write = %x\n", index, value); + counters[index].hold = value; +} + +u32 __fastcall rcntRcount(int index) +{ + u32 ret; + + // only count if the counter is turned on (0x80) and is not an hsync gate (!0x03) + if (counters[index].mode.IsCounting && (counters[index].mode.ClockSource != 0x3)) + ret = counters[index].count + ((cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate); + else + ret = counters[index].count; + + EECNT_LOG("EE Counter[%d] readCount32 = %x\n", index, ret); + return ret; +} + +u32 __fastcall rcntCycle(int index) +{ + if (counters[index].mode.IsCounting && (counters[index].mode.ClockSource != 0x3)) + return counters[index].count + ((cpuRegs.cycle - counters[index].sCycleT) / counters[index].rate); + else + return counters[index].count; +} + +void SaveState::rcntFreeze() +{ + Freeze(counters); + Freeze(nextCounter); + Freeze(nextsCounter); + + // New in version 11 -- save the PAL/NTSC info! + if( GetVersion() > 0x10 ) + { + Freeze( Config.PsxType ); + } + + if( IsLoading() ) + { + UpdateVSyncRate(); + + // make sure the gate flags are set based on the counter modes... + for( int i=0; i<4; i++ ) + _rcntSetGate( i ); + + iopBranchAction = 1; // probably not needed but won't hurt anything either. + } +} diff --git a/pcsx2/Counters.h b/pcsx2/Counters.h new file mode 100644 index 0000000000..c058fd6587 --- /dev/null +++ b/pcsx2/Counters.h @@ -0,0 +1,148 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __COUNTERS_H__ +#define __COUNTERS_H__ + +struct EECNT_MODE +{ + // 0 - BUSCLK + // 1 - 1/16th of BUSCLK + // 2 - 1/256th of BUSCLK + // 3 - External Clock (hblank!) + u32 ClockSource:2; + + // Enables the counter gate (turns counter on/off as according to the + // h/v blank type set in GateType). + u32 EnableGate:1; + + // 0 - hblank! 1 - vblank! + // Note: the hblank source type is disabled when ClockSel = 3 + u32 GateSource:1; + + // 0 - count when the gate signal is low + // 1 - reset and start counting at the signal's rising edge (h/v blank end) + // 2 - reset and start counting at the signal's falling edge (h/v blank start) + // 3 - reset and start counting at both signal edges + u32 GateMode:2; + + // Counter cleared to zero when target reached. + // The PS2 only resets if the TargetInterrupt is enabled - Tested on PS2 + u32 ZeroReturn:1; + + // General count enable/status. If 0, no counting happens. + // This flag is set/unset by the gates. + u32 IsCounting:1; + + // Enables target interrupts. + u32 TargetInterrupt:1; + + // Enables overflow interrupts. + u32 OverflowInterrupt:1; + + // Set to true by the counter when the target is reached. + // Flag is set only when TargetInterrupt is enabled. + u32 TargetReached:1; + + // Set to true by the counter when the target has overflowed. + // Flag is set only when OverflowInterrupt is enabled. + u32 OverflowReached:1; +}; + +// fixme: Cycle and sCycleT members are unused. +// But they can't be removed without making a new savestate version. +struct Counter { + u32 count; + union + { + u32 modeval; // the mode as a 32 bit int (for optimized combination masks) + EECNT_MODE mode; + }; + u32 target, hold; + u32 rate, interrupt; + u32 Cycle; + u32 sCycle; // start cycle of timer + s32 CycleT; + u32 sCycleT; // delta values should be signed. +}; + +//------------------------------------------------------------------ +// SPEED HACKS!!! (1 is normal) (They have inverse affects, only set 1 at a time) +//------------------------------------------------------------------ +#define HBLANK_COUNTER_SPEED 1 //Set to '3' to double the speed of games like KHII +//#define HBLANK_TIMER_SLOWDOWN 1 //Set to '2' to increase the speed of games like God of War (FPS will be less, but game will be faster) + +//------------------------------------------------------------------ +// NTSC Timing Information!!! (some scanline info is guessed) +//------------------------------------------------------------------ +#define FRAMERATE_NTSC 2997// frames per second * 100 (29.97) + +#define SCANLINES_TOTAL_NTSC 525 // total number of scanlines +#define SCANLINES_VSYNC_NTSC 3 // scanlines that are used for syncing every half-frame +#define SCANLINES_VRENDER_NTSC 240 // scanlines in a half-frame (because of interlacing) +#define SCANLINES_VBLANK1_NTSC 19 // scanlines used for vblank1 (even interlace) +#define SCANLINES_VBLANK2_NTSC 20 // scanlines used for vblank2 (odd interlace) + +#define HSYNC_ERROR_NTSC ((s32)VSYNC_NTSC - (s32)(((HRENDER_TIME_NTSC+HBLANK_TIME_NTSC) * SCANLINES_TOTAL_NTSC)/2) ) + +//------------------------------------------------------------------ +// PAL Timing Information!!! (some scanline info is guessed) +//------------------------------------------------------------------ +#define FRAMERATE_PAL 2500// frames per second * 100 (25) + +#define SCANLINES_TOTAL_PAL 625 // total number of scanlines per frame +#define SCANLINES_VSYNC_PAL 5 // scanlines that are used for syncing every half-frame +#define SCANLINES_VRENDER_PAL 288 // scanlines in a half-frame (because of interlacing) +#define SCANLINES_VBLANK1_PAL 19 // scanlines used for vblank1 (even interlace) +#define SCANLINES_VBLANK2_PAL 20 // scanlines used for vblank2 (odd interlace) + +//------------------------------------------------------------------ +// vSync and hBlank Timing Modes +//------------------------------------------------------------------ +#define MODE_VRENDER 0x0 //Set during the Render/Frame Scanlines +#define MODE_VBLANK 0x1 //Set during the Blanking Scanlines +#define MODE_VSYNC 0x3 //Set during the Syncing Scanlines +#define MODE_VBLANK1 0x0 //Set during the Blanking Scanlines (half-frame 1) +#define MODE_VBLANK2 0x1 //Set during the Blanking Scanlines (half-frame 2) + +#define MODE_HRENDER 0x0 //Set for ~5/6 of 1 Scanline +#define MODE_HBLANK 0x1 //Set for the remaining ~1/6 of 1 Scanline + + +extern Counter counters[6]; +extern s32 nextCounter; // delta until the next counter event (must be signed) +extern u32 nextsCounter; + +extern void rcntUpdate_hScanline(); +extern bool rcntUpdate_vSync(); +extern bool rcntUpdate(); + +extern void rcntInit(); +extern void __fastcall rcntStartGate(unsigned int mode, u32 sCycle); +extern void __fastcall rcntEndGate(unsigned int mode, u32 sCycle); +extern void __fastcall rcntWcount(int index, u32 value); +extern void __fastcall rcntWmode(int index, u32 value); +extern void __fastcall rcntWtarget(int index, u32 value); +extern void __fastcall rcntWhold(int index, u32 value); +extern u32 __fastcall rcntRcount(int index); +extern u32 __fastcall rcntCycle(int index); + +u32 UpdateVSyncRate(); +void frameLimitReset(); + +#endif /* __COUNTERS_H__ */ diff --git a/pcsx2/DebugTools/Debug.h b/pcsx2/DebugTools/Debug.h new file mode 100644 index 0000000000..5e0c0d03e7 --- /dev/null +++ b/pcsx2/DebugTools/Debug.h @@ -0,0 +1,197 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#include "Misc.h" + +extern FILE *emuLog; + +extern char* disVU0MicroUF(u32 code, u32 pc); +extern char* disVU0MicroLF(u32 code, u32 pc); +extern char* disVU1MicroUF(u32 code, u32 pc); +extern char* disVU1MicroLF(u32 code, u32 pc); + +extern const char * const CP2VFnames[]; +extern const char * const disRNameCP2f[]; +extern const char * const disRNameCP2i[]; + +namespace R5900 +{ + // [TODO] : These function names can be de-obfuscated with the help of a little namespace love. + + void disR5900F( std::string& output, u32 code ); + void disR5900Fasm( std::string& output, u32 code, u32 pc); + void disR5900AddSym(u32 addr, const char *name); + const char* disR5900GetSym(u32 addr); + const char* disR5900GetUpperSym(u32 addr); + void disR5900FreeSyms(); + void dFindSym( std::string& output, u32 addr ); + + extern const char * const disRNameGPR[]; + + // A helper class for getting a quick and efficient string representation of the + // R5900's current instruction. This class is *not* thread safe! + class DisR5900CurrentState + { + protected: + std::string result; + + public: + const std::string& getString(); + const char* getCString(); + }; + + extern DisR5900CurrentState disR5900Current; +} + +namespace R3000A +{ + extern void (*IOP_DEBUG_BSC[64])(char *buf); + + extern const char * const disRNameGPR[]; + extern char* disR3000Fasm(u32 code, u32 pc); + extern char* disR3000AF(u32 code, u32 pc); +} + +#ifdef PCSX2_DEVBUILD + +extern u32 varLog; + +void SourceLog( u16 protocol, u8 source, u32 cpuPc, u32 cpuCycle, const char *fmt, ...); +void __Log( const char* fmt, ... ); + +extern void SrcLog_CPU( const char* fmt, ... ); +extern void SrcLog_COP0( const char* fmt, ... ); +extern void SrcLog_FPU( const char* fmt, ... ); +extern void SrcLog_MMI( const char* fmt, ... ); + +extern void SrcLog_MEM( const char* fmt, ... ); +extern void SrcLog_HW( const char* fmt, ... ); +extern void SrcLog_DMA( const char* fmt, ... ); +extern void SrcLog_BIOS( const char* fmt, ... ); +extern void SrcLog_ELF( const char* fmt, ... ); +extern void SrcLog_VU0( const char* fmt, ... ); + +extern void SrcLog_VIF( const char* fmt, ... ); +extern void SrcLog_SPR( const char* fmt, ... ); +extern void SrcLog_GIF( const char* fmt, ... ); +extern void SrcLog_SIF( const char* fmt, ... ); +extern void SrcLog_IPU( const char* fmt, ... ); +extern void SrcLog_VUM( const char* fmt, ... ); +extern void SrcLog_RPC( const char* fmt, ... ); +extern void SrcLog_EECNT( const char* fmt, ... ); + +extern void SrcLog_PSXCPU( const char* fmt, ... ); +extern void SrcLog_PSXMEM( const char* fmt, ... ); +extern void SrcLog_PSXHW( const char* fmt, ... ); +extern void SrcLog_PSXBIOS( const char* fmt, ... ); +extern void SrcLog_PSXDMA( const char* fmt, ... ); +extern void SrcLog_PSXCNT( const char* fmt, ... ); + +extern void SrcLog_MEMCARDS( const char* fmt, ... ); +extern void SrcLog_PAD( const char* fmt, ... ); +extern void SrcLog_GTE( const char* fmt, ... ); +extern void SrcLog_CDR( const char* fmt, ... ); +extern void SrcLog_GPU( const char* fmt, ... ); + +#define CPU_LOG if (varLog & 0x00000001) SrcLog_CPU +#define MEM_LOG if (varLog & 0x00000002) SrcLog_MEM +#define HW_LOG if (varLog & 0x00000004) SrcLog_HW +#define DMA_LOG if (varLog & 0x00000008) SrcLog_DMA +#define BIOS_LOG if (varLog & 0x00000010) SrcLog_BIOS +#define ELF_LOG if (varLog & 0x00000020) SrcLog_ELF +#define FPU_LOG if (varLog & 0x00000040) SrcLog_FPU +#define MMI_LOG if (varLog & 0x00000080) SrcLog_MMI +#define VU0_LOG if (varLog & 0x00000100) SrcLog_VU0 +#define COP0_LOG if (varLog & 0x00000200) SrcLog_COP0 +#define VIF_LOG if (varLog & 0x00000400) SrcLog_VIF +#define SPR_LOG if (varLog & 0x00000800) SrcLog_SPR +#define GIF_LOG if (varLog & 0x00001000) SrcLog_GIF +#define SIF_LOG if (varLog & 0x00002000) SrcLog_SIF +#define IPU_LOG if (varLog & 0x00004000) SrcLog_IPU +#define VUM_LOG if (varLog & 0x00008000) SrcLog_VUM +#define RPC_LOG if (varLog & 0x00010000) SrcLog_RPC +#define EECNT_LOG if (varLog & 0x40000000) SrcLog_EECNT + +#define PSXCPU_LOG if (varLog & 0x00100000) SrcLog_PSXCPU +#define PSXMEM_LOG if (varLog & 0x00200000) SrcLog_PSXMEM +#define PSXHW_LOG if (varLog & 0x00400000) SrcLog_PSXHW +#define PSXBIOS_LOG if (varLog & 0x00800000) SrcLog_PSXBIOS +#define PSXDMA_LOG if (varLog & 0x01000000) SrcLog_PSXDMA +#define PSXCNT_LOG if (varLog & 0x20000000) SrcLog_PSXCNT + +//memcard has the same number as PAD_LOG for now +#define MEMCARDS_LOG if (varLog & 0x02000000) SrcLog_MEMCARDS +#define PAD_LOG if (varLog & 0x02000000) SrcLog_PAD +#define GTE_LOG if (varLog & 0x04000000) SrcLog_GTE +#define CDR_LOG if (varLog & 0x08000000) SrcLog_CDR +#define GPU_LOG if (varLog & 0x10000000) SrcLog_GPU + +// fixme - currently we don't log cache +#define CACHE_LOG 0&& + +#else // PCSX2_DEVBUILD + +#define varLog 0 + +#define CPU_LOG 0&& +#define MEM_LOG 0&& +#define HW_LOG 0&& +#define DMA_LOG 0&& +#define BIOS_LOG 0&& +#define ELF_LOG 0&& +#define FPU_LOG 0&& +#define MMI_LOG 0&& +#define VU0_LOG 0&& +#define COP0_LOG 0&& +#define VIF_LOG 0&& +#define SPR_LOG 0&& +#define GIF_LOG 0&& +#define SIF_LOG 0&& +#define IPU_LOG 0&& +#define VUM_LOG 0&& +#define RPC_LOG 0&& + +#define PSXCPU_LOG 0&& +#define PSXMEM_LOG 0&& +#define PSXHW_LOG 0&& +#define PSXBIOS_LOG 0&& +#define PSXDMA_LOG 0&& + +#define PAD_LOG 0&& +#define GTE_LOG 0&& +#define CDR_LOG 0&& +#define GPU_LOG 0&& +#define PSXCNT_LOG 0&& +#define EECNT_LOG 0&& + +#define EMU_LOG 0&& +#define CACHE_LOG 0&& +#define MEMCARDS_LOG 0&& +#endif + +#ifdef VIFUNPACKDEBUG +#define VIFUNPACK_LOG VIF_LOG +#else +#define VIFUNPACK_LOG 0&& +#endif + +#endif /* __DEBUG_H__ */ diff --git a/pcsx2/DebugTools/DisASM.h b/pcsx2/DebugTools/DisASM.h new file mode 100644 index 0000000000..1147df12a7 --- /dev/null +++ b/pcsx2/DebugTools/DisASM.h @@ -0,0 +1,52 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 +#include + +//DECODE PROCUDURES + +//cop0 +#define DECODE_FS (DECODE_RD) +#define DECODE_FT (DECODE_RT) +#define DECODE_FD (DECODE_SA) +///******** + +#define DECODE_FUNCTION ((cpuRegs.code) & 0x3F) +#define DECODE_RD ((cpuRegs.code >> 11) & 0x1F) // The rd part of the instruction register +#define DECODE_RT ((cpuRegs.code >> 16) & 0x1F) // The rt part of the instruction register +#define DECODE_RS ((cpuRegs.code >> 21) & 0x1F) // The rs part of the instruction register +#define DECODE_SA ((cpuRegs.code >> 6) & 0x1F) // The sa part of the instruction register +#define DECODE_IMMED ( cpuRegs.code & 0xFFFF) // The immediate part of the instruction register +#define DECODE_OFFSET ((((short)DECODE_IMMED * 4) + opcode_addr + 4)) +#define DECODE_JUMP (opcode_addr & 0xf0000000)|((cpuRegs.code&0x3ffffff)<<2) +#define DECODE_SYSCALL ((opcode_addr & 0x03FFFFFF) >> 6) +#define DECODE_BREAK (DECODE_SYSCALL) +#define DECODE_C0BC ((cpuRegs.code >> 16) & 0x03) +#define DECODE_C1BC ((cpuRegs.code >> 16) & 0x03) +#define DECODE_C2BC ((cpuRegs.code >> 16) & 0x03) + +//IOP + +#define DECODE_RD_IOP ((psxRegs.code >> 11) & 0x1F) +#define DECODE_RT_IOP ((psxRegs.code >> 16) & 0x1F) +#define DECODE_RS_IOP ((psxRegs.code >> 21) & 0x1F) +#define DECODE_IMMED_IOP ( psxRegs.code & 0xFFFF) +#define DECODE_SA_IOP ((psxRegs.code >> 6) & 0x1F) +#define DECODE_FS_IOP (DECODE_RD_IOP) + diff --git a/pcsx2/DebugTools/DisR3000A.cpp b/pcsx2/DebugTools/DisR3000A.cpp new file mode 100644 index 0000000000..3bd08d9de2 --- /dev/null +++ b/pcsx2/DebugTools/DisR3000A.cpp @@ -0,0 +1,322 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "R3000A.h" +#include "Debug.h" + +namespace R3000A +{ + static char ostr[1024]; + +// Names of registers + const char * const disRNameGPR[] = { + "r0", "at", "v0", "v1", "a0", "a1","a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5","t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5","s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp","fp", "ra"}; + + const char * const disRNameCP0[] = { + "Index" , "Random" , "EntryLo0", "EntryLo1", "Context" , "PageMask" , "Wired" , "*Check me*", + "BadVAddr" , "Count" , "EntryHi" , "Compare" , "Status" , "Cause" , "ExceptPC" , "PRevID" , + "Config" , "LLAddr" , "WatchLo" , "WatchHi" , "XContext", "*RES*" , "*RES*" , "*RES*" , + "*RES*" , "*RES* " , "PErr" , "CacheErr", "TagLo" , "TagHi" , "ErrorEPC" , "*RES*" }; + +// Type definition of our functions + +typedef char* (*TdisR3000AF)(u32 code, u32 pc); + +// These macros are used to assemble the disassembler functions +#define MakeDisFg(fn, b) char* fn(u32 code, u32 pc) { b; return ostr; } +#define MakeDisF(fn, b) \ + static char* fn(u32 code, u32 pc) { \ + sprintf (ostr, "%8.8lx %8.8lx:", pc, code); \ + b; /*ostr[(strlen(ostr) - 1)] = 0;*/ return ostr; \ + } + + +#undef _Funct_ +#undef _Rd_ +#undef _Rt_ +#undef _Rs_ +#undef _Sa_ +#undef _Im_ +#undef _Target_ + +#define _Funct_ ((code ) & 0x3F) // The funct part of the instruction register +#define _Rd_ ((code >> 11) & 0x1F) // The rd part of the instruction register +#define _Rt_ ((code >> 16) & 0x1F) // The rt part of the instruction register +#define _Rs_ ((code >> 21) & 0x1F) // The rs part of the instruction register +#define _Sa_ ((code >> 6) & 0x1F) // The sa part of the instruction register +#define _Im_ ( code & 0xFFFF) // The immediate part of the instruction register + +#define _Target_ ((pc & 0xf0000000) + ((code & 0x03ffffff) * 4)) +#define _Branch_ (pc + 4 + ((short)_Im_ * 4)) +#define _OfB_ _Im_, _nRs_ + +#define dName(i) sprintf(ostr, "%s %-7s,", ostr, i) +#define dGPR(i) sprintf(ostr, "%s %8.8lx (%s),", ostr, psxRegs.GPR.r[i], disRNameGPR[i]) +#define dCP0(i) sprintf(ostr, "%s %8.8lx (%s),", ostr, psxRegs.CP0.r[i], disRNameCP0[i]) +#define dHI() sprintf(ostr, "%s %8.8lx (%s),", ostr, psxRegs.GPR.n.hi, "hi") +#define dLO() sprintf(ostr, "%s %8.8lx (%s),", ostr, psxRegs.GPR.n.lo, "lo") +#define dImm() sprintf(ostr, "%s %4.4lx (%ld),", ostr, _Im_, _Im_) +#define dTarget() sprintf(ostr, "%s %8.8lx,", ostr, _Target_) +#define dSa() sprintf(ostr, "%s %2.2lx (%ld),", ostr, _Sa_, _Sa_) +#define dOfB() sprintf(ostr, "%s %4.4lx (%8.8lx (%s)),", ostr, _Im_, psxRegs.GPR.r[_Rs_], disRNameGPR[_Rs_]) +#define dOffset() sprintf(ostr, "%s %8.8lx,", ostr, _Branch_) +#define dCode() sprintf(ostr, "%s %8.8lx,", ostr, (code >> 6) & 0xffffff) + +/********************************************************* +* Arithmetic with immediate operand * +* Format: OP rt, rs, immediate * +*********************************************************/ +MakeDisF(disADDI, dName("ADDI"); dGPR(_Rt_); dGPR(_Rs_); dImm();) +MakeDisF(disADDIU, dName("ADDIU"); dGPR(_Rt_); dGPR(_Rs_); dImm();) +MakeDisF(disANDI, dName("ANDI"); dGPR(_Rt_); dGPR(_Rs_); dImm();) +MakeDisF(disORI, dName("ORI"); dGPR(_Rt_); dGPR(_Rs_); dImm();) +MakeDisF(disSLTI, dName("SLTI"); dGPR(_Rt_); dGPR(_Rs_); dImm();) +MakeDisF(disSLTIU, dName("SLTIU"); dGPR(_Rt_); dGPR(_Rs_); dImm();) +MakeDisF(disXORI, dName("XORI"); dGPR(_Rt_); dGPR(_Rs_); dImm();) + +/********************************************************* +* Register arithmetic * +* Format: OP rd, rs, rt * +*********************************************************/ +MakeDisF(disADD, dName("ADD"); dGPR(_Rd_); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disADDU, dName("ADDU"); dGPR(_Rd_); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disAND, dName("AND"); dGPR(_Rd_); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disNOR, dName("NOR"); dGPR(_Rd_); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disOR, dName("OR"); dGPR(_Rd_); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disSLT, dName("SLT"); dGPR(_Rd_); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disSLTU, dName("SLTU"); dGPR(_Rd_); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disSUB, dName("SUB"); dGPR(_Rd_); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disSUBU, dName("SUBU"); dGPR(_Rd_); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disXOR, dName("XOR"); dGPR(_Rd_); dGPR(_Rs_); dGPR(_Rt_);) + +/********************************************************* +* Register arithmetic & Register trap logic * +* Format: OP rs, rt * +*********************************************************/ +MakeDisF(disDIV, dName("DIV"); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disDIVU, dName("DIVU"); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disMULT, dName("MULT"); dGPR(_Rs_); dGPR(_Rt_);) +MakeDisF(disMULTU, dName("MULTU"); dGPR(_Rs_); dGPR(_Rt_);) + +/********************************************************* +* Register branch logic * +* Format: OP rs, offset * +*********************************************************/ +MakeDisF(disBGEZ, dName("BGEZ"); dGPR(_Rs_); dOffset();) +MakeDisF(disBGEZAL, dName("BGEZAL"); dGPR(_Rs_); dOffset();) +MakeDisF(disBGTZ, dName("BGTZ"); dGPR(_Rs_); dOffset();) +MakeDisF(disBLEZ, dName("BLEZ"); dGPR(_Rs_); dOffset();) +MakeDisF(disBLTZ, dName("BLTZ"); dGPR(_Rs_); dOffset();) +MakeDisF(disBLTZAL, dName("BLTZAL"); dGPR(_Rs_); dOffset();) + +/********************************************************* +* Shift arithmetic with constant shift * +* Format: OP rd, rt, sa * +*********************************************************/ +MakeDisF(disSLL, if (code) { dName("SLL"); dGPR(_Rd_); dGPR(_Rt_); dSa(); } else { dName("NOP"); }) +MakeDisF(disSRA, dName("SRA"); dGPR(_Rd_); dGPR(_Rt_); dSa();) +MakeDisF(disSRL, dName("SRL"); dGPR(_Rd_); dGPR(_Rt_); dSa();) + +/********************************************************* +* Shift arithmetic with variant register shift * +* Format: OP rd, rt, rs * +*********************************************************/ +MakeDisF(disSLLV, dName("SLLV"); dGPR(_Rd_); dGPR(_Rt_); dGPR(_Rs_);) +MakeDisF(disSRAV, dName("SRAV"); dGPR(_Rd_); dGPR(_Rt_); dGPR(_Rs_);) +MakeDisF(disSRLV, dName("SRLV"); dGPR(_Rd_); dGPR(_Rt_); dGPR(_Rs_);) + +/********************************************************* +* Load higher 16 bits of the first word in GPR with imm * +* Format: OP rt, immediate * +*********************************************************/ +MakeDisF(disLUI, dName("LUI"); dGPR(_Rt_); dImm();) + +/********************************************************* +* Move from HI/LO to GPR * +* Format: OP rd * +*********************************************************/ +MakeDisF(disMFHI, dName("MFHI"); dGPR(_Rd_); dHI();) +MakeDisF(disMFLO, dName("MFLO"); dGPR(_Rd_); dLO();) + +/********************************************************* +* Move from GPR to HI/LO * +* Format: OP rd * +*********************************************************/ +MakeDisF(disMTHI, dName("MTHI"); dHI(); dGPR(_Rs_);) +MakeDisF(disMTLO, dName("MTLO"); dLO(); dGPR(_Rs_);) + +/********************************************************* +* Special purpose instructions * +* Format: OP * +*********************************************************/ +MakeDisF(disBREAK, dName("BREAK")) +MakeDisF(disRFE, dName("RFE")) +MakeDisF(disSYSCALL, dName("SYSCALL")) + + + +MakeDisF(disRTPS, dName("RTPS")) +MakeDisF(disOP , dName("OP")) +MakeDisF(disNCLIP, dName("NCLIP")) +MakeDisF(disDPCS, dName("DPCS")) +MakeDisF(disINTPL, dName("INTPL")) +MakeDisF(disMVMVA, dName("MVMVA")) +MakeDisF(disNCDS , dName("NCDS")) +MakeDisF(disCDP , dName("CDP")) +MakeDisF(disNCDT , dName("NCDT")) +MakeDisF(disNCCS , dName("NCCS")) +MakeDisF(disCC , dName("CC")) +MakeDisF(disNCS , dName("NCS")) +MakeDisF(disNCT , dName("NCT")) +MakeDisF(disSQR , dName("SQR")) +MakeDisF(disDCPL , dName("DCPL")) +MakeDisF(disDPCT , dName("DPCT")) +MakeDisF(disAVSZ3, dName("AVSZ3")) +MakeDisF(disAVSZ4, dName("AVSZ4")) +MakeDisF(disRTPT , dName("RTPT")) +MakeDisF(disGPF , dName("GPF")) +MakeDisF(disGPL , dName("GPL")) +MakeDisF(disNCCT , dName("NCCT")) + +MakeDisF(disMFC2, dName("MFC2"); dGPR(_Rt_);) +MakeDisF(disCFC2, dName("CFC2"); dGPR(_Rt_);) +MakeDisF(disMTC2, dName("MTC2")) +MakeDisF(disCTC2, dName("CTC2")) + +/********************************************************* +* Register branch logic * +* Format: OP rs, rt, offset * +*********************************************************/ +MakeDisF(disBEQ, dName("BEQ"); dGPR(_Rs_); dGPR(_Rt_); dOffset();) +MakeDisF(disBNE, dName("BNE"); dGPR(_Rs_); dGPR(_Rt_); dOffset();) + +/********************************************************* +* Jump to target * +* Format: OP target * +*********************************************************/ +MakeDisF(disJ, dName("J"); dTarget();) +MakeDisF(disJAL, dName("JAL"); dTarget(); dGPR(31);) + +/********************************************************* +* Register jump * +* Format: OP rs, rd * +*********************************************************/ +MakeDisF(disJR, dName("JR"); dGPR(_Rs_);) +MakeDisF(disJALR, dName("JALR"); dGPR(_Rs_); dGPR(_Rd_)) + +/********************************************************* +* Load and store for GPR * +* Format: OP rt, offset(base) * +*********************************************************/ +MakeDisF(disLB, dName("LB"); dGPR(_Rt_); dOfB();) +MakeDisF(disLBU, dName("LBU"); dGPR(_Rt_); dOfB();) +MakeDisF(disLH, dName("LH"); dGPR(_Rt_); dOfB();) +MakeDisF(disLHU, dName("LHU"); dGPR(_Rt_); dOfB();) +MakeDisF(disLW, dName("LW"); dGPR(_Rt_); dOfB();) +MakeDisF(disLWL, dName("LWL"); dGPR(_Rt_); dOfB();) +MakeDisF(disLWR, dName("LWR"); dGPR(_Rt_); dOfB();) +MakeDisF(disLWC2, dName("LWC2"); dGPR(_Rt_); dOfB();) +MakeDisF(disSB, dName("SB"); dGPR(_Rt_); dOfB();) +MakeDisF(disSH, dName("SH"); dGPR(_Rt_); dOfB();) +MakeDisF(disSW, dName("SW"); dGPR(_Rt_); dOfB();) +MakeDisF(disSWL, dName("SWL"); dGPR(_Rt_); dOfB();) +MakeDisF(disSWR, dName("SWR"); dGPR(_Rt_); dOfB();) +MakeDisF(disSWC2, dName("SWC2"); dGPR(_Rt_); dOfB();) + +/********************************************************* +* Moves between GPR and COPx * +* Format: OP rt, fs * +*********************************************************/ +MakeDisF(disMFC0, dName("MFC0"); dGPR(_Rt_); dCP0(_Rd_);) +MakeDisF(disMTC0, dName("MTC0"); dCP0(_Rd_); dGPR(_Rt_);) +MakeDisF(disCFC0, dName("CFC0"); dGPR(_Rt_); dCP0(_Rd_);) +MakeDisF(disCTC0, dName("CTC0"); dCP0(_Rd_); dGPR(_Rt_);) + +/********************************************************* +* Unknow instruction (would generate an exception) * +* Format: ? * +*********************************************************/ +MakeDisF(disNULL, dName("*** Bad OP ***");) + + +TdisR3000AF disR3000A_SPECIAL[] = { // Subset of disSPECIAL + disSLL , disNULL , disSRL , disSRA , disSLLV , disNULL , disSRLV , disSRAV , + disJR , disJALR , disNULL, disNULL, disSYSCALL, disBREAK , disNULL , disNULL , + disMFHI, disMTHI , disMFLO, disMTLO, disNULL , disNULL , disNULL , disNULL , + disMULT, disMULTU, disDIV , disDIVU, disNULL , disNULL , disNULL , disNULL , + disADD , disADDU , disSUB , disSUBU, disAND , disOR , disXOR , disNOR , + disNULL, disNULL , disSLT , disSLTU, disNULL , disNULL , disNULL , disNULL , + disNULL, disNULL , disNULL, disNULL, disNULL , disNULL , disNULL , disNULL , + disNULL, disNULL , disNULL, disNULL, disNULL , disNULL , disNULL , disNULL}; + +MakeDisF(disSPECIAL, disR3000A_SPECIAL[_Funct_](code, pc)) + +TdisR3000AF disR3000A_BCOND[] = { // Subset of disBCOND + disBLTZ , disBGEZ , disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disBLTZAL, disBGEZAL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, disNULL, disNULL}; + +MakeDisF(disBCOND, disR3000A_BCOND[_Rt_](code, pc)) + +TdisR3000AF disR3000A_COP0[] = { // Subset of disCOP0 + disMFC0, disNULL, disCFC0, disNULL, disMTC0, disNULL, disCTC0, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disRFE , disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL}; + +MakeDisF(disCOP0, disR3000A_COP0[_Rs_](code, pc)) + +TdisR3000AF disR3000A_BASIC[] = { // Subset of disBASIC (based on rs) + disMFC2, disNULL, disCFC2, disNULL, disMTC2, disNULL, disCTC2, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL}; + +MakeDisF(disBASIC, disR3000A_BASIC[_Rs_](code, pc)) + +TdisR3000AF disR3000A_COP2[] = { // Subset of disR3000F_COP2 (based on funct) + disBASIC, disRTPS , disNULL , disNULL , disNULL, disNULL , disNCLIP, disNULL, + disNULL , disNULL , disNULL , disNULL , disOP , disNULL , disNULL , disNULL, + disDPCS , disINTPL, disMVMVA, disNCDS , disCDP , disNULL , disNCDT , disNULL, + disNULL , disNULL , disNULL , disNCCS , disCC , disNULL , disNCS , disNULL, + disNCT , disNULL , disNULL , disNULL , disNULL, disNULL , disNULL , disNULL, + disSQR , disDCPL , disDPCT , disNULL , disNULL, disAVSZ3, disAVSZ4, disNULL, + disRTPT , disNULL , disNULL , disNULL , disNULL, disNULL , disNULL , disNULL, + disNULL , disNULL , disNULL , disNULL , disNULL, disGPF , disGPL , disNCCT }; + +MakeDisF(disCOP2, disR3000A_COP2[_Funct_](code, pc)) + +TdisR3000AF disR3000A[] = { + disSPECIAL , disBCOND , disJ , disJAL , disBEQ , disBNE , disBLEZ , disBGTZ , + disADDI , disADDIU , disSLTI , disSLTIU, disANDI, disORI , disXORI , disLUI , + disCOP0 , disNULL , disCOP2 , disNULL , disNULL, disNULL, disNULL , disNULL , + disNULL , disNULL , disNULL , disNULL , disNULL, disNULL, disNULL , disNULL , + disLB , disLH , disLWL , disLW , disLBU , disLHU , disLWR , disNULL , + disSB , disSH , disSWL , disSW , disNULL, disNULL, disSWR , disNULL , + disNULL , disNULL , disLWC2 , disNULL , disNULL, disNULL, disNULL , disNULL , + disNULL , disNULL , disSWC2 , disNULL , disNULL, disNULL, disNULL , disNULL }; + +MakeDisFg(disR3000AF, disR3000A[code >> 26](code, pc)) + +} \ No newline at end of file diff --git a/pcsx2/DebugTools/DisR3000asm.cpp b/pcsx2/DebugTools/DisR3000asm.cpp new file mode 100644 index 0000000000..b92db4532b --- /dev/null +++ b/pcsx2/DebugTools/DisR3000asm.cpp @@ -0,0 +1,363 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Debug.h" +#include "R3000A.h" +#include "DisASM.h" + +namespace R3000A { + +unsigned long IOP_opcode_addr; + +const char *GPR_IOP_REG[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" +}; +const char *COP0_IOP_REG[32] ={ + "Index","Random","EntryLo0","EntryLo1","Context","PageMask", + "Wired","C0r7","BadVaddr","Count","EntryHi","Compare","Status", + "Cause","EPC","PRId","Config","C0r17","C0r18","C0r19","C0r20", + "C0r21","C0r22","C0r23","Debug","Perf","C0r26","C0r27","TagLo", + "TagHi","ErrorPC","C0r31" +}; +void IOPD_SPECIAL(char *buf); +void IOPD_REGIMM(char *buf); +void IOPD_J(char *buf); +void IOPD_JAL(char *buf); +void IOPD_BEQ(char *buf); +void IOPD_BNE(char *buf); +void IOPD_BLEZ(char *buf); +void IOPD_BGTZ(char *buf); +void IOPD_ADDI(char *buf); +void IOPD_ADDIU(char *buf); +void IOPD_SLTI(char *buf); +void IOPD_SLTIU(char *buf); +void IOPD_ANDI(char *buf); +void IOPD_ORI(char *buf); +void IOPD_XORI(char *buf); +void IOPD_LUI(char *buf); +void IOPD_COP0(char *buf); +void IOPD_COP2(char *buf); +void IOPD_LB(char *buf); +void IOPD_LH(char *buf); +void IOPD_LWL(char *buf); +void IOPD_LW(char *buf); +void IOPD_LBU(char *buf); +void IOPD_LHU(char *buf); +void IOPD_LWR(char *buf); +void IOPD_SB(char *buf); +void IOPD_SH(char *buf); +void IOPD_SWL(char *buf); +void IOPD_SW(char *buf); +void IOPD_SWR(char *buf); +void IOPD_LWC2(char *buf); +void IOPD_SWC2(char *buf); + +void IOPD_SLL(char *buf); +void IOPD_SRL(char *buf); +void IOPD_SRA(char *buf); +void IOPD_SLLV(char *buf); +void IOPD_SRLV(char *buf); +void IOPD_SRAV(char *buf); +void IOPD_JR(char *buf); +void IOPD_JALR(char *buf); +void IOPD_SYSCALL(char *buf); +void IOPD_BREAK(char *buf); +void IOPD_MFHI(char *buf); +void IOPD_MTHI(char *buf); +void IOPD_MFLO(char *buf); +void IOPD_MTLO(char *buf); +void IOPD_MULT(char *buf); +void IOPD_MULTU(char *buf); +void IOPD_DIV(char *buf); +void IOPD_DIVU(char *buf); +void IOPD_ADD(char *buf); +void IOPD_ADDU(char *buf); +void IOPD_SUB(char *buf); +void IOPD_SUBU(char *buf); +void IOPD_AND(char *buf); +void IOPD_OR(char *buf); +void IOPD_XOR(char *buf); +void IOPD_NOR(char *buf); +void IOPD_SLT(char *buf); +void IOPD_SLTU(char *buf); + + +void IOPD_BLTZ(char *buf); +void IOPD_BGEZ(char *buf); +void IOPD_BLTZAL(char *buf); +void IOPD_BGEZAL(char *buf); + + + +void IOPD_MFC0(char *buf); +void IOPD_CFC0(char *buf); +void IOPD_MTC0(char *buf); +void IOPD_CTC0(char *buf); +void IOPD_RFE(char *buf); + + + +void IOPD_BASIC(char *buf); +void IOPD_RTPS(char *buf); +void IOPD_NCLIP(char *buf); +void IOPD_OP(char *buf); +void IOPD_DPCS(char *buf); +void IOPD_INTPL(char *buf); +void IOPD_MVMVA(char *buf); +void IOPD_NCDS(char *buf); +void IOPD_CDP(char *buf); +void IOPD_NCDT(char *buf); +void IOPD_NCCS(char *buf); +void IOPD_CC(char *buf); +void IOPD_NCS(char *buf); +void IOPD_NCT(char *buf); +void IOPD_SQR(char *buf); +void IOPD_DCPL(char *buf); +void IOPD_DPCT(char *buf); +void IOPD_AVSZ3(char *buf); +void IOPD_AVSZ4(char *buf); +void IOPD_RTPT(char *buf); +void IOPD_GPF(char *buf); +void IOPD_GPL(char *buf); +void IOPD_NCCT(char *buf); + + + +void IOPD_MFC2(char *buf); +void IOPD_CFC2(char *buf); +void IOPD_MTC2(char *buf); +void IOPD_CTC2(char *buf); +void IOPD_NULL(char *buf); + + + + void (*IOP_DEBUG_BSC[64])(char *buf) = { + IOPD_SPECIAL, IOPD_REGIMM, IOPD_J , IOPD_JAL , IOPD_BEQ , IOPD_BNE , IOPD_BLEZ, IOPD_BGTZ, + IOPD_ADDI , IOPD_ADDIU , IOPD_SLTI, IOPD_SLTIU, IOPD_ANDI, IOPD_ORI , IOPD_XORI, IOPD_LUI , + IOPD_COP0 , IOPD_NULL , IOPD_COP2, IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, + IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, + IOPD_LB , IOPD_LH , IOPD_LWL , IOPD_LW , IOPD_LBU , IOPD_LHU , IOPD_LWR , IOPD_NULL, + IOPD_SB , IOPD_SH , IOPD_SWL , IOPD_SW , IOPD_NULL, IOPD_NULL, IOPD_SWR , IOPD_NULL, + IOPD_NULL , IOPD_NULL , IOPD_LWC2, IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, + IOPD_NULL , IOPD_NULL , IOPD_SWC2, IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL +}; + + +void (*IOP_DEBUG_SPC[64])(char *buf) = { + IOPD_SLL , IOPD_NULL , IOPD_SRL , IOPD_SRA , IOPD_SLLV , IOPD_NULL , IOPD_SRLV, IOPD_SRAV, + IOPD_JR , IOPD_JALR , IOPD_NULL, IOPD_NULL, IOPD_SYSCALL, IOPD_BREAK, IOPD_NULL, IOPD_NULL, + IOPD_MFHI, IOPD_MTHI , IOPD_MFLO, IOPD_MTLO, IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL, + IOPD_MULT, IOPD_MULTU, IOPD_DIV , IOPD_DIVU, IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL, + IOPD_ADD , IOPD_ADDU , IOPD_SUB , IOPD_SUBU, IOPD_AND , IOPD_OR , IOPD_XOR , IOPD_NOR , + IOPD_NULL, IOPD_NULL , IOPD_SLT , IOPD_SLTU, IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL, + IOPD_NULL, IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL, + IOPD_NULL, IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL +}; + +void (*IOP_DEBUG_REG[32])(char *buf) = { + IOPD_BLTZ , IOPD_BGEZ , IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, + IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, + IOPD_BLTZAL, IOPD_BGEZAL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, + IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL +}; + +void (*IOP_DEBUG_CP0[32])(char *buf) = { + IOPD_MFC0, IOPD_NULL, IOPD_CFC0, IOPD_NULL, IOPD_MTC0, IOPD_NULL, IOPD_CTC0, IOPD_NULL, + IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, + IOPD_RFE , IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, + IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL +}; + +void (*IOP_DEBUG_CP2[64])(char *buf) = { + IOPD_BASIC, IOPD_RTPS , IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL , IOPD_NCLIP, IOPD_NULL, + IOPD_NULL , IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_OP , IOPD_NULL , IOPD_NULL , IOPD_NULL, + IOPD_DPCS , IOPD_INTPL, IOPD_MVMVA, IOPD_NCDS, IOPD_CDP , IOPD_NULL , IOPD_NCDT , IOPD_NULL, + IOPD_NULL , IOPD_NULL , IOPD_NULL , IOPD_NCCS, IOPD_CC , IOPD_NULL , IOPD_NCS , IOPD_NULL, + IOPD_NCT , IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL , IOPD_NULL , IOPD_NULL, + IOPD_SQR , IOPD_DCPL , IOPD_DPCT , IOPD_NULL, IOPD_NULL, IOPD_AVSZ3, IOPD_AVSZ4, IOPD_NULL, + IOPD_RTPT , IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_NULL , IOPD_NULL , IOPD_NULL, + IOPD_NULL , IOPD_NULL , IOPD_NULL , IOPD_NULL, IOPD_NULL, IOPD_GPF , IOPD_GPL , IOPD_NCCT +}; + +void (*IOP_DEBUG_CP2BSC[32])(char *buf) = { + IOPD_MFC2, IOPD_NULL, IOPD_CFC2, IOPD_NULL, IOPD_MTC2, IOPD_NULL, IOPD_CTC2, IOPD_NULL, + IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, + IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, + IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL, IOPD_NULL +}; + +static char dbuf2[1024]; +static char obuf2[1024]; + +char *disR3000Fasm(u32 code, u32 pc) { + u32 scode = psxRegs.code; + IOP_opcode_addr = pc; + psxRegs.code = code; + IOP_DEBUG_BSC[(code) >> 26](dbuf2); + + sprintf(obuf2, "%08lX:\t%s", pc, dbuf2); + + psxRegs.code = scode; + return obuf2; +} +char *IOP_jump_decode(void) +{ + static char buf[256]; + unsigned long addr; + addr = (IOP_opcode_addr & 0xf0000000)|((psxRegs.code&0x3ffffff)<<2); + sprintf(buf, "0x%08lX", addr); + return buf; +} +char *IOP_offset_decode(void) +{ + static char buf[256]; + unsigned long addr; + addr = ((((short)( psxRegs.code & 0xFFFF) * 4) + IOP_opcode_addr + 4)); + sprintf(buf, "0x%08lX", addr); + return buf; +} +//basic table +void IOPD_SPECIAL(char *buf){IOP_DEBUG_SPC[((psxRegs.code) & 0x3F)](buf);} +void IOPD_REGIMM(char *buf){IOP_DEBUG_REG[DECODE_RT_IOP](buf);} +void IOPD_J(char *buf) { sprintf(buf, "j\t%s", IOP_jump_decode());} +void IOPD_JAL(char *buf){sprintf(buf, "jal\t%s", IOP_jump_decode());} +void IOPD_BEQ(char *buf){sprintf(buf, "beq\t%s, %s, %s", GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP], IOP_offset_decode()); } +void IOPD_BNE(char *buf){sprintf(buf, "bne\t%s, %s, %s", GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP], IOP_offset_decode()); } +void IOPD_BLEZ(char *buf){sprintf(buf, "blez\t%s, %s", GPR_IOP_REG[DECODE_RS_IOP], IOP_offset_decode()); } +void IOPD_BGTZ(char *buf){sprintf(buf, "bgtz\t%s, %s", GPR_IOP_REG[DECODE_RS_IOP], IOP_offset_decode()); } +void IOPD_ADDI(char *buf){sprintf(buf, "addi\t%s, %s, 0x%04lX", GPR_IOP_REG[DECODE_RT_IOP], GPR_IOP_REG[DECODE_RS_IOP], DECODE_IMMED_IOP);} +void IOPD_ADDIU(char *buf){sprintf(buf, "addiu\t%s, %s, 0x%04lX", GPR_IOP_REG[DECODE_RT_IOP], GPR_IOP_REG[DECODE_RS_IOP], DECODE_IMMED_IOP);} +void IOPD_SLTI(char *buf){sprintf(buf, "slti\t%s, %s, 0x%04lX", GPR_IOP_REG[DECODE_RT_IOP], GPR_IOP_REG[DECODE_RS_IOP], DECODE_IMMED_IOP);} +void IOPD_SLTIU(char *buf){sprintf(buf, "sltiu\t%s, %s, 0x%04lX", GPR_IOP_REG[DECODE_RT_IOP], GPR_IOP_REG[DECODE_RS_IOP], DECODE_IMMED_IOP);} +void IOPD_ANDI(char *buf){sprintf(buf, "andi\t%s, %s, 0x%04lX", GPR_IOP_REG[DECODE_RT_IOP], GPR_IOP_REG[DECODE_RS_IOP], DECODE_IMMED_IOP);} +void IOPD_ORI(char *buf){sprintf(buf, "ori\t%s, %s, 0x%04lX", GPR_IOP_REG[DECODE_RT_IOP], GPR_IOP_REG[DECODE_RS_IOP], DECODE_IMMED_IOP); } +void IOPD_XORI(char *buf){sprintf(buf, "xori\t%s, %s, 0x%04lX", GPR_IOP_REG[DECODE_RT_IOP], GPR_IOP_REG[DECODE_RS_IOP], DECODE_IMMED_IOP); } +void IOPD_LUI(char *buf){sprintf(buf, "lui\t%s, 0x%04lX", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP); } +void IOPD_COP0(char *buf){IOP_DEBUG_CP0[DECODE_RS_IOP](buf);} +void IOPD_COP2(char *buf){IOP_DEBUG_CP2[((psxRegs.code) & 0x3F)](buf);} +void IOPD_LB(char *buf){sprintf(buf, "lb\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_LH(char *buf){sprintf(buf, "lh\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_LWL(char *buf){sprintf(buf, "lwl\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_LW(char *buf){sprintf(buf, "lw\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_LBU(char *buf){sprintf(buf, "lbu\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_LHU(char *buf){sprintf(buf, "lhu\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_LWR(char *buf){sprintf(buf, "lwr\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]);} +void IOPD_SB(char *buf){sprintf(buf, "sb\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]);} +void IOPD_SH(char *buf){sprintf(buf, "sh\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_SWL(char *buf){sprintf(buf, "swl\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_SW(char *buf){sprintf(buf, "sw\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_SWR(char *buf){sprintf(buf, "swr\t%s, 0x%04lX(%s)", GPR_IOP_REG[DECODE_RT_IOP], DECODE_IMMED_IOP, GPR_IOP_REG[DECODE_RS_IOP]);} +void IOPD_LWC2(char *buf){strcpy(buf, "lwc2");} +void IOPD_SWC2(char *buf){strcpy(buf, "swc2");} +//special table +void IOPD_SLL(char *buf) +{ + if (psxRegs.code == 0x00000000) + strcpy(buf, "nop"); + else + sprintf(buf, "sll\t%s, %s, 0x%02lX", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RT_IOP], DECODE_SA_IOP); +} +void IOPD_SRL(char *buf){sprintf(buf, "srl\t%s, %s, 0x%02lX", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RT_IOP], DECODE_SA_IOP); } +void IOPD_SRA(char *buf){sprintf(buf, "sra\t%s, %s, 0x%02lX", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RT_IOP], DECODE_SA_IOP);} +void IOPD_SLLV(char *buf){sprintf(buf, "sllv\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RT_IOP], GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_SRLV(char *buf){sprintf(buf, "srlv\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RT_IOP], GPR_IOP_REG[DECODE_RS_IOP]);} +void IOPD_SRAV(char *buf){sprintf(buf, "srav\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RT_IOP], GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_JR(char *buf){sprintf(buf, "jr\t%s", GPR_IOP_REG[DECODE_RS_IOP]);} +void IOPD_JALR(char *buf) +{ + int rd = DECODE_RD_IOP; + + if (rd == 31) + sprintf(buf, "jalr\t%s", GPR_IOP_REG[DECODE_RS_IOP]); + else + sprintf(buf, "jalr\t%s, %s", GPR_IOP_REG[rd], GPR_IOP_REG[DECODE_RS_IOP]); +} + +void IOPD_SYSCALL(char *buf){strcpy(buf, "syscall");} +void IOPD_BREAK(char *buf){strcpy(buf, "break");} +void IOPD_MFHI(char *buf){sprintf(buf, "mfhi\t%s", GPR_IOP_REG[DECODE_RD_IOP]); } +void IOPD_MTHI(char *buf){sprintf(buf, "mthi\t%s", GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_MFLO(char *buf){sprintf(buf, "mflo\t%s", GPR_IOP_REG[DECODE_RD_IOP]); } +void IOPD_MTLO(char *buf){sprintf(buf, "mtlo\t%s", GPR_IOP_REG[DECODE_RS_IOP]); } +void IOPD_MULT(char *buf){sprintf(buf, "mult\t%s, %s", GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]);} +void IOPD_MULTU(char *buf){sprintf(buf, "multu\t%s, %s", GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]);} +void IOPD_DIV(char *buf){sprintf(buf, "div\t%s, %s", GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]);} +void IOPD_DIVU(char *buf){sprintf(buf, "divu\t%s, %s", GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } + +void IOPD_ADD(char *buf) { sprintf(buf, "add\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } +void IOPD_ADDU(char *buf) { sprintf(buf, "addu\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } +void IOPD_SUB(char *buf) { sprintf(buf, "sub\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } +void IOPD_SUBU(char *buf) { sprintf(buf, "subu\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } +void IOPD_AND(char *buf) { sprintf(buf, "and\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } +void IOPD_OR(char *buf) { sprintf(buf, "or\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } +void IOPD_XOR(char *buf) { sprintf(buf, "xor\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } +void IOPD_NOR(char *buf) { sprintf(buf, "nor\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } +void IOPD_SLT(char *buf) { sprintf(buf, "slt\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } +void IOPD_SLTU(char *buf) { sprintf(buf, "sltu\t%s, %s, %s", GPR_IOP_REG[DECODE_RD_IOP], GPR_IOP_REG[DECODE_RS_IOP], GPR_IOP_REG[DECODE_RT_IOP]); } +//regimm + +void IOPD_BLTZ(char *buf) { sprintf(buf, "bltz\t%s, %s", GPR_IOP_REG[DECODE_RS_IOP], IOP_offset_decode()); } +void IOPD_BGEZ(char *buf) { sprintf(buf, "bgez\t%s, %s", GPR_IOP_REG[DECODE_RS_IOP], IOP_offset_decode()); } +void IOPD_BLTZAL(char *buf) { sprintf(buf, "bltzal\t%s, %s", GPR_IOP_REG[DECODE_RS_IOP], IOP_offset_decode()); } +void IOPD_BGEZAL(char *buf) { sprintf(buf, "bgezal\t%s, %s", GPR_IOP_REG[DECODE_RS_IOP], IOP_offset_decode()); } + +//cop0 + +void IOPD_MFC0(char *buf){ sprintf(buf, "mfc0\t%s, %s", GPR_IOP_REG[DECODE_RT_IOP], COP0_IOP_REG[DECODE_FS_IOP]); } +void IOPD_MTC0(char *buf){ sprintf(buf, "mtc0\t%s, %s", GPR_IOP_REG[DECODE_RT_IOP], COP0_IOP_REG[DECODE_FS_IOP]); } +void IOPD_CFC0(char *buf){ sprintf(buf, "cfc0\t%s, %s", GPR_IOP_REG[DECODE_RT_IOP], COP0_IOP_REG[DECODE_FS_IOP]); } +void IOPD_CTC0(char *buf){ sprintf(buf, "ctc0\t%s, %s", GPR_IOP_REG[DECODE_RT_IOP], COP0_IOP_REG[DECODE_FS_IOP]); } +void IOPD_RFE(char *buf){strcpy(buf, "rfe");} +//cop2 +void IOPD_BASIC(char *buf){IOP_DEBUG_CP2BSC[DECODE_RS_IOP](buf);} +void IOPD_RTPS(char *buf){strcpy(buf, "rtps");} +void IOPD_NCLIP(char *buf){strcpy(buf, "nclip");} +void IOPD_OP(char *buf){strcpy(buf, "op");} +void IOPD_DPCS(char *buf){strcpy(buf, "dpcs");} +void IOPD_INTPL(char *buf){strcpy(buf, "intpl");} +void IOPD_MVMVA(char *buf){strcpy(buf, "mvmva");} +void IOPD_NCDS(char *buf){strcpy(buf, "ncds");} +void IOPD_CDP(char *buf){strcpy(buf, "cdp");} +void IOPD_NCDT(char *buf){strcpy(buf, "ncdt");} +void IOPD_NCCS(char *buf){strcpy(buf, "nccs");} +void IOPD_CC(char *buf){strcpy(buf, "cc");} +void IOPD_NCS(char *buf){strcpy(buf, "ncs");} +void IOPD_NCT(char *buf){strcpy(buf, "nct");} +void IOPD_SQR(char *buf){strcpy(buf, "sqr");} +void IOPD_DCPL(char *buf){strcpy(buf, "dcpl");} +void IOPD_DPCT(char *buf){strcpy(buf, "dpct");} +void IOPD_AVSZ3(char *buf){strcpy(buf, "avsz3");} +void IOPD_AVSZ4(char *buf){strcpy(buf, "avsz4");} +void IOPD_RTPT(char *buf){strcpy(buf, "rtpt");} +void IOPD_GPF(char *buf){strcpy(buf, "gpf");} +void IOPD_GPL(char *buf){strcpy(buf, "gpl");} +void IOPD_NCCT(char *buf){strcpy(buf, "ncct");} +//cop2 basic +void IOPD_MFC2(char *buf){strcpy(buf, "mfc2");} +void IOPD_CFC2(char *buf){strcpy(buf, "cfc2");} +void IOPD_MTC2(char *buf){strcpy(buf, "mtc2");} +void IOPD_CTC2(char *buf){strcpy(buf, "ctc2");} +//null +void IOPD_NULL(char *buf){strcpy(buf, "????");} + +} // end Namespace R3000A \ No newline at end of file diff --git a/pcsx2/DebugTools/DisR5900.cpp b/pcsx2/DebugTools/DisR5900.cpp new file mode 100644 index 0000000000..fd2849c3f4 --- /dev/null +++ b/pcsx2/DebugTools/DisR5900.cpp @@ -0,0 +1,1089 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +using namespace std; + +#include "Debug.h" +#include "R5900.h" +#include "VU.h" + +const char * const disRNameCP2f[] = { + "VF00", "VF01", "VF02", "VF03", "VF04", "VF05", "VF06", "VF07", + "VF08", "VF09", "VF10", "VF11", "VF12", "VF13", "VF14", "VF15", + "VF16", "VF17", "VF18", "VF19", "VF20", "VF21", "VF22", "VF23", + "VF24", "VF25", "VF26", "VF27", "VF28", "VF29", "VF30", "VF31"}; + +const char * const disRNameCP2i[] = { + "VI00", "VI01", "VI02", "VI03", "VI04", "VI05", "VI06", "VI07", + "VI08", "VI09", "VI10", "VI11", "VI12", "VI13", "VI14", "VI15", + "Status", "MAC", "Clip", "*RES*", "R", "I", "Q", "*RES*", + "*RES*", "*RES*", "TPC", "CMSAR0", "FBRST", "VPU-STAT", "*RES*", "CMSAR1"}; + +const char * const CP2VFnames[] = { "x", "y", "z", "w" }; + +namespace R5900 +{ + +// Names of registers +const char * const disRNameGPR[] = { + "r0", "at", "v0", "v1", "a0", "a1","a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5","t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5","s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp","fp", "ra", "hi", "lo"}; // lo,hi used in rec + +const char * const disRNameCP0[] = { + "Index" , "Random" , "EntryLo0" , "EntryLo1", "Context" , "PageMask" , "Wired" , "*RES*", + "BadVAddr" , "Count" , "EntryHi" , "Compare" , "Status" , "Cause" , "ExceptPC" , "PRevID", + "Config" , "LLAddr" , "WatchLo" , "WatchHi" , "*RES*" , "*RES*" , "*RES*" , "Debug", + "DEPC" , "PerfCnt" , "ErrCtl" , "CacheErr", "TagLo" , "TagHi" , "ErrorEPC" , "DESAVE"}; + +const char * const disRNameCP1[] = { + "FPR0" , "FPR1" , "FPR2" , "FPR3" , "FPR4" , "FPR5" , "FPR6" , "FPR7", + "FPR8" , "FPR9" , "FPR10", "FPR11", "FPR12", "FPR13", "FPR14", "FPR15", + "FPR16", "FPR17", "FPR18", "FPR19", "FPR20", "FPR21", "FPR22", "FPR23", + "FPR24", "FPR25", "FPR26", "FPR27", "FPR28", "FPR29", "FPR30", "FPR31"}; + +const char * const disRNameCP1c[] = { + "FRevID", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", + "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", + "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", + "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "*RES*", "FStatus"}; + +// Type definition of our functions +#define DisFInterface (string& output, u32 code) +#define DisFInterfaceT (string&, u32) +#define DisFInterfaceN (output, code) + +typedef void (*TdisR5900F)DisFInterface; + +// These macros are used to assemble the disassembler functions +#define MakeDisF(fn, b) \ + void fn DisFInterface { \ + ssprintf(output, "(%8.8x) ", code); \ + b; \ + } + +#undef _Target_ +#undef _Branch_ +#undef _Funct_ +#undef _Rd_ +#undef _Rt_ +#undef _Rs_ +#undef _Sa_ +#undef _Im_ + +#define _Funct_ ((code ) & 0x3F) // The funct part of the instruction register +#define _Rd_ ((code >> 11) & 0x1F) // The rd part of the instruction register +#define _Rt_ ((code >> 16) & 0x1F) // The rt part of the instruction register +#define _Rs_ ((code >> 21) & 0x1F) // The rs part of the instruction register +#define _Sa_ ((code >> 6) & 0x1F) // The sa part of the instruction register +#define _Im_ ( code & 0xFFFF) // The immediate part of the instruction register + + +#define _rRs_ cpuRegs.GPR.r[_Rs_].UL[1], cpuRegs.GPR.r[_Rs_].UL[0] // Rs register +#define _rRt_ cpuRegs.GPR.r[_Rt_].UL[1], cpuRegs.GPR.r[_Rt_].UL[0] // Rt register +#define _rRd_ cpuRegs.GPR.r[_Rd_].UL[1], cpuRegs.GPR.r[_Rd_].UL[0] // Rd register +#define _rSa_ cpuRegs.GPR.r[_Sa_].UL[1], cpuRegs.GPR.r[_Sa_].UL[0] // Sa register + +#define _rFs_ cpuRegs.CP0.r[_Rd_] // Fs register + +#define _rRs32_ cpuRegs.GPR.r[_Rs_].UL[0] // Rs register +#define _rRt32_ cpuRegs.GPR.r[_Rt_].UL[0] // Rt register +#define _rRd32_ cpuRegs.GPR.r[_Rd_].UL[0] // Rd register +#define _rSa32_ cpuRegs.GPR.r[_Sa_].UL[0] // Sa register + + +#define _nRs_ _rRs_, disRNameGPR[_Rs_] +#define _nRt_ _rRt_, disRNameGPR[_Rt_] +#define _nRd_ _rRd_, disRNameGPR[_Rd_] +#define _nSa_ _rSa_, disRNameGPR[_Sa_] +#define _nRd0_ _rFs_, disRNameCP0[_Rd_] + +#define _nRs32_ _rRs32_, disRNameGPR[_Rs_] +#define _nRt32_ _rRt32_, disRNameGPR[_Rt_] +#define _nRd32_ _rRd32_, disRNameGPR[_Rd_] +#define _nSa32_ _rSa32_, disRNameGPR[_Sa_] + +#define _I_ _Im_, _Im_ +#define _Target_ ((cpuRegs.pc & 0xf0000000) + ((code & 0x03ffffff) * 4)) +#define _Branch_ (cpuRegs.pc + 4 + ((short)_Im_ * 4)) +#define _OfB_ _Im_, _nRs_ + +#define _Fsf_ ((code >> 21) & 0x03) +#define _Ftf_ ((code >> 23) & 0x03) + +// sap! it stands for string append. It's not a friendly name but for now it makes +// the copy-paste marathon of code below more readable! +#define _sap( str ) ssappendf( output, str, params + +#define dName(i) _sap("%-7s\t") i); +#define dGPR128(i) _sap("%8.8x_%8.8x_%8.8x_%8.8x (%s),") cpuRegs.GPR.r[i].UL[3], cpuRegs.GPR.r[i].UL[2], cpuRegs.GPR.r[i].UL[1], cpuRegs.GPR.r[i].UL[0], disRNameGPR[i]) +#define dGPR64(i) _sap("%8.8x_%8.8x (%s),") cpuRegs.GPR.r[i].UL[1], cpuRegs.GPR.r[i].UL[0], disRNameGPR[i]) +#define dGPR64U(i) _sap("%8.8x_%8.8x (%s),") cpuRegs.GPR.r[i].UL[3], cpuRegs.GPR.r[i].UL[2], disRNameGPR[i]) +#define dGPR32(i) _sap("%8.8x (%s),") cpuRegs.GPR.r[i].UL[0], disRNameGPR[i]) + +#define dCP032(i) _sap("%8.8x (%s),") cpuRegs.CP0.r[i], disRNameCP0[i]) + +#define dCP132(i) _sap("%f (%s),") fpuRegs.fpr[i].f, disRNameCP1[i]) +#define dCP1c32(i) _sap("%8.8x (%s),") fpuRegs.fprc[i], disRNameCP1c[i]) +#define dCP1acc() _sap("%f (ACC),") fpuRegs.ACC.f) + +#define dCP2128f(i) _sap("w=%f z=%f y=%f x=%f (%s),") VU0.VF[i].f.w, VU0.VF[i].f.z, VU0.VF[i].f.y, VU0.VF[i].f.x, disRNameCP2f[i]) +#define dCP232x(i) _sap("x=%f (%s),") VU0.VF[i].f.x, disRNameCP2f[i]) +#define dCP232y(i) _sap("y=%f (%s),") VU0.VF[i].f.y, disRNameCP2f[i]) +#define dCP232z(i) _sap("z=%f (%s),") VU0.VF[i].f.z, disRNameCP2f[i]) +#define dCP232w(i) _sap("w=%f (%s),") VU0.VF[i].f.w, disRNameCP2f[i]) +#define dCP2ACCf() _sap("w=%f z=%f y=%f x=%f (ACC),") VU0.ACC.f.w, VU0.ACC.f.z, VU0.ACC.f.y, VU0.ACC.f.x) +#define dCP232i(i) _sap("%8.8x (%s),") VU0.VI[i].UL, disRNameCP2i[i]) +#define dCP232iF(i) _sap("%f (%s),") VU0.VI[i].F, disRNameCP2i[i]) +#define dCP232f(i, j) _sap("Q %s=%f (%s),") CP2VFnames[j], VU0.VF[i].F[j], disRNameCP2f[i]) + +#define dHI64() _sap("%8.8x_%8.8x (%s),") cpuRegs.HI.UL[1], cpuRegs.HI.UL[0], "hi") +#define dLO64() _sap("%8.8x_%8.8x (%s),") cpuRegs.LO.UL[1], cpuRegs.LO.UL[0], "lo") +#define dImm() _sap("%4.4x (%d),") _Im_, _Im_) +#define dTarget() _sap("%8.8x,") _Target_) +#define dSa() _sap("%2.2x (%d),") _Sa_, _Sa_) +#define dSa32() _sap("%2.2x (%d),") _Sa_+32, _Sa_+32) +#define dOfB() _sap("%4.4x (%8.8x (%s)),") _Im_, cpuRegs.GPR.r[_Rs_].UL[0], disRNameGPR[_Rs_]) +#define dOffset() _sap("%8.8x,") _Branch_) +#define dCode() _sap("%8.8x,") (code >> 6) & 0xffffff) +#define dSaR() _sap("%8.8x,") cpuRegs.sa) + +struct sSymbol { + u32 addr; + char name[32]; +}; + +static sSymbol *dSyms = NULL; +static int nSymAlloc = 0; +static int nSyms = 0; + +void disR5900AddSym(u32 addr, const char *name) { + + assert( strlen(name) < 32 ); + + if( nSyms+1 >= nSymAlloc ) + { + // Realloc by threshold block sizes. + nSymAlloc += 64 + (nSyms / 4); + dSyms = (sSymbol*)realloc(dSyms, sizeof(sSymbol) * (nSymAlloc)); + } + + if (dSyms == NULL) return; + dSyms[nSyms].addr = addr; + strncpy(dSyms[nSyms].name, name, 32); + nSyms++; +} + +void disR5900FreeSyms() { + if (dSyms != NULL) { free(dSyms); dSyms = NULL; } + nSymAlloc = 0; + nSyms = 0; +} + +const char *disR5900GetSym(u32 addr) { + int i; + + if (dSyms == NULL) return NULL; + for (i=0; i laddr) { + laddr = dSyms[i].addr; + j = i; + } + } + if (j == -1) return NULL; + return dSyms[j].name; +} + +void dFindSym( string& output, u32 addr ) +{ + const char* label = disR5900GetSym( addr ); + if( label != NULL ) + output.append( label ); +} + +#define dAppendSym(addr) dFindSym( output, addr ) + +/********************************************************* +* Arithmetic with immediate operand * +* Format: OP rt, rs, immediate * +*********************************************************/ +MakeDisF(disADDI, dName("ADDI"); dGPR64(_Rt_); dGPR32(_Rs_); dImm();) +MakeDisF(disADDIU, dName("ADDIU"); dGPR64(_Rt_); dGPR32(_Rs_); dImm();) +MakeDisF(disANDI, dName("ANDI"); dGPR64(_Rt_); dGPR64(_Rs_); dImm();) +MakeDisF(disORI, dName("ORI"); dGPR64(_Rt_); dGPR64(_Rs_); dImm();) +MakeDisF(disSLTI, dName("SLTI"); dGPR64(_Rt_); dGPR64(_Rs_); dImm();) +MakeDisF(disSLTIU, dName("SLTIU"); dGPR64(_Rt_); dGPR64(_Rs_); dImm();) +MakeDisF(disXORI, dName("XORI"); dGPR64(_Rt_); dGPR64(_Rs_); dImm();) +MakeDisF(disDADDI, dName("DADDI"); dGPR64(_Rt_); dGPR64(_Rs_); dImm();) +MakeDisF(disDADDIU, dName("DADDIU"); dGPR64(_Rt_); dGPR64(_Rs_); dImm();) + +/********************************************************* +* Register arithmetic * +* Format: OP rd, rs, rt * +*********************************************************/ +MakeDisF(disADD, dName("ADD"); dGPR64(_Rd_); dGPR32(_Rs_); dGPR32(_Rt_);) +MakeDisF(disADDU, dName("ADDU"); dGPR64(_Rd_); dGPR32(_Rs_); dGPR32(_Rt_);) +MakeDisF(disDADD, dName("DADD"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disDADDU, dName("DADDU"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disSUB, dName("SUB"); dGPR64(_Rd_); dGPR32(_Rs_); dGPR32(_Rt_);) +MakeDisF(disSUBU, dName("SUBU"); dGPR64(_Rd_); dGPR32(_Rs_); dGPR32(_Rt_);) +MakeDisF(disDSUB, dName("DSUB"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disDSUBU, dName("DSDBU"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disAND, dName("AND"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disOR, dName("OR"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disXOR, dName("XOR"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disNOR, dName("NOR"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disSLT, dName("SLT"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disSLTU, dName("SLTU"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) + +/********************************************************* +* Jump to target * +* Format: OP target * +*********************************************************/ +MakeDisF(disJ, dName("J"); dTarget(); dAppendSym(_Target_);) +MakeDisF(disJAL, dName("JAL"); dTarget(); dGPR32(31); dAppendSym(_Target_);) + +/********************************************************* +* Register jump * +* Format: OP rs, rd * +*********************************************************/ +MakeDisF(disJR, dName("JR"); dGPR32(_Rs_); dAppendSym(cpuRegs.GPR.r[_Rs_].UL[0]);) +MakeDisF(disJALR, dName("JALR"); dGPR32(_Rs_); dGPR32(_Rd_); dAppendSym(cpuRegs.GPR.r[_Rs_].UL[0]);) + +/********************************************************* +* Register mult/div & Register trap logic * +* Format: OP rs, rt * +*********************************************************/ +MakeDisF(disDIV, dName("DIV"); dGPR32(_Rs_); dGPR32(_Rt_);) +MakeDisF(disDIVU, dName("DIVU"); dGPR32(_Rs_); dGPR32(_Rt_);) +MakeDisF(disMULT, dName("MULT"); dGPR32(_Rs_); dGPR32(_Rt_); dGPR32(_Rd_);) +MakeDisF(disMULTU, dName("MULTU"); dGPR32(_Rs_); dGPR32(_Rt_); dGPR32(_Rd_);) + +/********************************************************* +* Load higher 16 bits of the first word in GPR with imm * +* Format: OP rt, immediate * +*********************************************************/ +MakeDisF(disLUI, dName("LUI"); dGPR64(_Rt_); dImm();) + +/********************************************************* +* Move from HI/LO to GPR * +* Format: OP rd * +*********************************************************/ +MakeDisF(disMFHI, dName("MFHI"); dGPR64(_Rd_); dHI64();) +MakeDisF(disMFLO, dName("MFLO"); dGPR64(_Rd_); dLO64();) + +/********************************************************* +* Move to GPR to HI/LO & Register jump * +* Format: OP rs * +*********************************************************/ +MakeDisF(disMTHI, dName("MTHI"); dHI64(); dGPR64(_Rs_);) +MakeDisF(disMTLO, dName("MTLO"); dLO64(); dGPR64(_Rs_);) + +/********************************************************* +* Shift arithmetic with constant shift * +* Format: OP rd, rt, sa * +*********************************************************/ +MakeDisF(disSLL, if (code) { dName("SLL"); dGPR64(_Rd_); dGPR32(_Rt_); dSa(); } else { dName("NOP"); }) +MakeDisF(disDSLL, dName("DSLL"); dGPR64(_Rd_); dGPR64(_Rt_); dSa();) +MakeDisF(disDSLL32, dName("DSLL32"); dGPR64(_Rd_); dGPR64(_Rt_); dSa32();) +MakeDisF(disSRA, dName("SRA"); dGPR64(_Rd_); dGPR32(_Rt_); dSa();) +MakeDisF(disDSRA, dName("DSRA"); dGPR64(_Rd_); dGPR64(_Rt_); dSa();) +MakeDisF(disDSRA32, dName("DSRA32"); dGPR64(_Rd_); dGPR64(_Rt_); dSa32();) +MakeDisF(disSRL, dName("SRL"); dGPR64(_Rd_); dGPR32(_Rt_); dSa();) +MakeDisF(disDSRL, dName("DSRL"); dGPR64(_Rd_); dGPR64(_Rt_); dSa();) +MakeDisF(disDSRL32, dName("DSRL32"); dGPR64(_Rd_); dGPR64(_Rt_); dSa32();) + +/********************************************************* +* Shift arithmetic with variant register shift * +* Format: OP rd, rt, rs * +*********************************************************/ +MakeDisF(disSLLV, dName("SLLV"); dGPR64(_Rd_); dGPR32(_Rt_); dGPR32(_Rs_);) +MakeDisF(disDSLLV, dName("DSLLV"); dGPR64(_Rd_); dGPR64(_Rt_); dGPR32(_Rs_);) +MakeDisF(disSRAV, dName("SRAV"); dGPR64(_Rd_); dGPR32(_Rt_); dGPR32(_Rs_);) +MakeDisF(disDSRAV, dName("DSRAV"); dGPR64(_Rd_); dGPR64(_Rt_); dGPR32(_Rs_);) +MakeDisF(disSRLV, dName("SRLV"); dGPR64(_Rd_); dGPR32(_Rt_); dGPR32(_Rs_);) +MakeDisF(disDSRLV, dName("DSRLV"); dGPR64(_Rd_); dGPR64(_Rt_); dGPR32(_Rs_);) + +/********************************************************* +* Load and store for GPR * +* Format: OP rt, offset(base) * +*********************************************************/ +MakeDisF(disLB, dName("LB"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLBU, dName("LBU"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLH, dName("LH"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLHU, dName("LHU"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLW, dName("LW"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLWU, dName("LWU"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLWL, dName("LWL"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLWR, dName("LWR"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLD, dName("LD"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLDL, dName("LDL"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLDR, dName("LDR"); dGPR64(_Rt_); dOfB();) +MakeDisF(disLQ, dName("LQ"); dGPR128(_Rt_); dOfB();) +MakeDisF(disSB, dName("SB"); dGPR64(_Rt_); dOfB();) +MakeDisF(disSH, dName("SH"); dGPR64(_Rt_); dOfB();) +MakeDisF(disSW, dName("SW"); dGPR64(_Rt_); dOfB();) +MakeDisF(disSWL, dName("SWL"); dGPR64(_Rt_); dOfB();) +MakeDisF(disSWR, dName("SWR"); dGPR64(_Rt_); dOfB();) +MakeDisF(disSD, dName("SD"); dGPR64(_Rt_); dOfB();) +MakeDisF(disSDL, dName("SDL"); dGPR64(_Rt_); dOfB();) +MakeDisF(disSDR, dName("SDR"); dGPR64(_Rt_); dOfB();) +MakeDisF(disSQ, dName("SQ"); dGPR128(_Rt_); dOfB();) + +/********************************************************* +* Register branch logic * +* Format: OP rs, rt, offset * +*********************************************************/ +MakeDisF(disBEQ, dName("BEQ"); dGPR64(_Rs_); dGPR64(_Rt_); dOffset();) +MakeDisF(disBNE, dName("BNE"); dGPR64(_Rs_); dGPR64(_Rt_); dOffset();) + +/********************************************************* +* Moves between GPR and COPx * +* Format: OP rt, rd * +*********************************************************/ +MakeDisF(disMFC0, dName("MFC0"); dGPR32(_Rt_); dCP032(_Rd_);) +MakeDisF(disMTC0, dName("MTC0"); dCP032(_Rd_); dGPR32(_Rt_);) + +/********************************************************* +* Register branch logic * +* Format: OP rs, offset * +*********************************************************/ + +MakeDisF(disBGEZ, dName("BGEZ"); dGPR64(_Rs_); dOffset();) +MakeDisF(disBGEZAL, dName("BGEZAL"); dGPR64(_Rs_); dOffset();) +MakeDisF(disBGTZ, dName("BGTZ"); dGPR64(_Rs_); dOffset();) +MakeDisF(disBLEZ, dName("BLEZ"); dGPR64(_Rs_); dOffset();) +MakeDisF(disBLTZ, dName("BLTZ"); dGPR64(_Rs_); dOffset();) +MakeDisF(disBLTZAL, dName("BLTZAL"); dGPR64(_Rs_); dOffset();) + + +/********************************************************* +* Register branch logic Likely * +* Format: OP rs, offset * +*********************************************************/ + + +MakeDisF(disBEQL, dName("BEQL"); dGPR64(_Rs_); dGPR64(_Rt_); dOffset();) +MakeDisF(disBNEL, dName("BNEL"); dGPR64(_Rs_); dGPR64(_Rt_); dOffset();) +MakeDisF(disBLEZL, dName("BLEZL"); dGPR64(_Rs_); dOffset();) +MakeDisF(disBGTZL, dName("BGTZL"); dGPR64(_Rs_); dOffset();) +MakeDisF(disBLTZL, dName("BLTZL"); dGPR64(_Rs_); dOffset();) +MakeDisF(disBGEZL, dName("BGEZL"); dGPR64(_Rs_); dOffset();) +MakeDisF(disBLTZALL, dName("BLTZALL"); dGPR64(_Rs_); dOffset();) +MakeDisF(disBGEZALL, dName("BGEZALL"); dGPR64(_Rs_); dOffset();) + + +/********************************************************* +* COP0 opcodes * +* * +*********************************************************/ + +MakeDisF(disBC0F, dName("BC0F"); dOffset();) +MakeDisF(disBC0T, dName("BC0T"); dOffset();) +MakeDisF(disBC0FL, dName("BC0FL"); dOffset();) +MakeDisF(disBC0TL, dName("BC0TL"); dOffset();) + +MakeDisF(disTLBR, dName("TLBR");) +MakeDisF(disTLBWI, dName("TLBWI");) +MakeDisF(disTLBWR, dName("TLBWR");) +MakeDisF(disTLBP, dName("TLBP");) +MakeDisF(disERET, dName("ERET");) +MakeDisF(disEI, dName("EI");) +MakeDisF(disDI, dName("DI");) + +/********************************************************* +* COP1 opcodes * +* * +*********************************************************/ + +#define _Ft_ _Rt_ +#define _Fs_ _Rd_ +#define _Fd_ _Sa_ + +MakeDisF(disMFC1, dName("MFC1"); dGPR64(_Rt_); dCP132(_Fs_);) +MakeDisF(disCFC1, dName("CFC1"); dGPR64(_Rt_); dCP1c32(_Fs_);) +MakeDisF(disMTC1, dName("MTC1"); dCP132(_Fs_); dGPR64(_Rt_);) +MakeDisF(disCTC1, dName("CTC1"); dCP1c32(_Fs_); dGPR64(_Rt_);) + +MakeDisF(disBC1F, dName("BC1F");) +MakeDisF(disBC1T, dName("BC1T");) +MakeDisF(disBC1FL, dName("BC1FL");) +MakeDisF(disBC1TL, dName("BC1TL");) + +MakeDisF(disADDs, dName("ADDs"); dCP132(_Fd_); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disSUBs, dName("SUBs"); dCP132(_Fd_); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disMULs, dName("MULs"); dCP132(_Fd_); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disDIVs, dName("DIVs"); dCP132(_Fd_); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disSQRTs, dName("SQRTs"); dCP132(_Fd_); dCP132(_Ft_);) +MakeDisF(disABSs, dName("ABSs"); dCP132(_Fd_); dCP132(_Fs_);) +MakeDisF(disMOVs, dName("MOVs"); dCP132(_Fd_); dCP132(_Fs_);) +MakeDisF(disNEGs, dName("NEGs"); dCP132(_Fd_); dCP132(_Fs_);) +MakeDisF(disRSQRTs, dName("RSQRTs"); dCP132(_Fd_); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disADDAs, dName("ADDAs"); dCP1acc(); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disSUBAs, dName("SUBAs"); dCP1acc(); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disMULAs, dName("MULAs"); dCP1acc(); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disMADDs, dName("MADDs"); dCP132(_Fd_); dCP1acc(); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disMSUBs, dName("MSUBs"); dCP132(_Fd_); dCP1acc(); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disMADDAs, dName("MADDAs"); dCP1acc(); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disMSUBAs, dName("MSUBAs"); dCP1acc(); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disCVTWs, dName("CVTWs"); dCP132(_Fd_); dCP132(_Fs_);) +MakeDisF(disMAXs, dName("MAXs"); dCP132(_Fd_); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disMINs, dName("MINs"); dCP132(_Fd_); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disCFs, dName("CFs"); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disCEQs, dName("CEQs"); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disCLTs, dName("CLTs"); dCP132(_Fs_); dCP132(_Ft_);) +MakeDisF(disCLEs, dName("CLEs"); dCP132(_Fs_); dCP132(_Ft_);) + +MakeDisF(disCVTSw, dName("CVTSw"); dCP132(_Fd_); dCP132(_Fs_);) + +/********************************************************* +* Load and store for COP1 * +* Format: OP rt, offset(base) * +*********************************************************/ + +MakeDisF(disLWC1, dName("LWC1"); dCP132(_Rt_); dOffset();) +MakeDisF(disSWC1, dName("SWC1"); dCP132(_Rt_); dOffset();) + +/********************************************************* +* Conditional Move * +* Format: OP rd, rs, rt * +*********************************************************/ + +MakeDisF(disMOVZ, dName("MOVZ"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disMOVN, dName("MOVN"); dGPR64(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) + +/********************************************************* +* MMI opcodes * +* * +*********************************************************/ + +MakeDisF(disMULT1, dName("MULT1");) +MakeDisF(disMULTU1, dName("MULTU1");) + +/********************************************************* +* MMI0 opcodes * +* * +*********************************************************/ + +MakeDisF(disPADDW, dName("PADDW");) +MakeDisF(disPADDH, dName("PADDH");) +MakeDisF(disPADDB, dName("PADDB");) + +MakeDisF(disPADDSW, dName("PADDSW");) +MakeDisF(disPADDSH, dName("PADDSH");) +MakeDisF(disPADDSB, dName("PADDSB");) + +MakeDisF(disPSUBW, dName("PSUBW");) +MakeDisF(disPSUBH, dName("PSUBH");) +MakeDisF(disPSUBB, dName("PSUBB");) + +MakeDisF(disPSUBSW, dName("PSUBSW");) +MakeDisF(disPSUBSH, dName("PSUBSH");) +MakeDisF(disPSUBSB, dName("PSUBSB");) + +MakeDisF(disPCGTW, dName("PCGTW");) +MakeDisF(disPCGTH, dName("PCGTH");) +MakeDisF(disPCGTB, dName("PCGTB");) + +MakeDisF(disPMAXW, dName("PMAXW");) +MakeDisF(disPMAXH, dName("PMAXH");) + +MakeDisF(disPEXTLW, dName("PEXTLW"); dGPR128(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disPEXTLH, dName("PEXTLH"); dGPR128(_Rd_); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disPEXTLB, dName("PEXTLB");) +MakeDisF(disPEXTS, dName("PEXTS");) + +MakeDisF(disPPACW, dName("PPACW");) +MakeDisF(disPPACH, dName("PPACH");) +MakeDisF(disPPACB, dName("PPACB");) +MakeDisF(disPPACS, dName("PPACS");) + +/********************************************************* +* MMI1 opcodes * +* * +*********************************************************/ + +MakeDisF(disPADSBH, dName("PADSBH");) + +MakeDisF(disPABSW, dName("PABSW");) +MakeDisF(disPABSH, dName("PABSH");) + +MakeDisF(disPCEQW, dName("PCEQW");) +MakeDisF(disPCEQH, dName("PCEQH");) +MakeDisF(disPCEQB, dName("PCEQB");) + +MakeDisF(disPMINW, dName("PMINW");) +MakeDisF(disPMINH, dName("PMINH");) + +MakeDisF(disPADDUW, dName("PADDUW");) +MakeDisF(disPADDUH, dName("PADDUH");) +MakeDisF(disPADDUB, dName("PADDUB");) + +MakeDisF(disPSUBUW, dName("PSUBUW");) +MakeDisF(disPSUBUH, dName("PSUBUH");) +MakeDisF(disPSUBUB, dName("PSUBUB");) + +MakeDisF(disPEXTUW, dName("PEXTUW"); dGPR128(_Rd_); dGPR64U(_Rs_); dGPR64U(_Rt_);) +MakeDisF(disPEXTUH, dName("PEXTUH"); dGPR128(_Rd_); dGPR64U(_Rs_); dGPR64U(_Rt_);) +MakeDisF(disPEXTUB, dName("PEXTUB");) + +MakeDisF(disQFSRV, dName("QFSRV");) + +/********************************************************* +* MMI2 opcodes * +* * +*********************************************************/ + +MakeDisF(disPMADDW, dName("PMADDW");) +MakeDisF(disPMADDH, dName("PMADDH");) + +MakeDisF(disPSLLVW, dName("PSLLVW");) +MakeDisF(disPSRLVW, dName("PSRLVW");) + +MakeDisF(disPMFHI, dName("PMFHI");) +MakeDisF(disPMFLO, dName("PMFLO");) + +MakeDisF(disPINTH, dName("PINTH");) + +MakeDisF(disPMULTW, dName("PMULTW");) +MakeDisF(disPMULTH, dName("PMULTH");) + +MakeDisF(disPDIVW, dName("PDIVW");) +MakeDisF(disPDIVH, dName("PDIVH");) + +MakeDisF(disPCPYLD, dName("PCPYLD"); dGPR128(_Rd_); dGPR128(_Rs_); dGPR128(_Rt_);) + +MakeDisF(disPAND, dName("PAND"); dGPR128(_Rd_); dGPR128(_Rs_); dGPR128(_Rt_);) +MakeDisF(disPXOR, dName("PXOR"); dGPR128(_Rd_); dGPR128(_Rs_); dGPR128(_Rt_);) + +MakeDisF(disPMSUBW, dName("PMSUBW");) +MakeDisF(disPMSUBH, dName("PMSUBH");) + +MakeDisF(disPHMADH, dName("PHMADH");) +MakeDisF(disPHMSBH, dName("PHMSBH");) + +MakeDisF(disPEXEW, dName("PEXEW");) +MakeDisF(disPEXEH, dName("PEXEH");) + +MakeDisF(disPREVH, dName("PREVH");) + +MakeDisF(disPDIVBW, dName("PDIVBW");) + +MakeDisF(disPROT3W, dName("PROT3W");) + +/********************************************************* +* MMI3 opcodes * +* * +*********************************************************/ + +MakeDisF(disPMADDUW, dName("PMADDUW");) + +MakeDisF(disPSRAVW, dName("PSRAVW");) + +MakeDisF(disPMTHI, dName("PMTHI");) +MakeDisF(disPMTLO, dName("PMTLO");) + +MakeDisF(disPINTEH, dName("PINTEH");) + +MakeDisF(disPMULTUW, dName("PMULTUW");) +MakeDisF(disPDIVUW, dName("PDIVUW");) + +MakeDisF(disPCPYUD, dName("PCPYUD"); dGPR128(_Rd_); dGPR128(_Rt_); dGPR128(_Rs_);) + +MakeDisF(disPOR, dName("POR"); dGPR128(_Rd_); dGPR128(_Rs_); dGPR128(_Rt_);) +MakeDisF(disPNOR, dName("PNOR"); dGPR128(_Rd_); dGPR128(_Rs_); dGPR128(_Rt_);) + +MakeDisF(disPEXCH, dName("PEXCH");) +MakeDisF(disPEXCW, dName("PEXCW");) + +MakeDisF(disPCPYH, dName("PCPYH"); dGPR128(_Rd_); dGPR128(_Rt_);) + +/********************************************************* +* COP2 opcodes * +* * +*********************************************************/ + +#define _Ft_ _Rt_ +#define _Fs_ _Rd_ +#define _Fd_ _Sa_ + +#define _X code>>24 +#define _Y code>>23 +#define _Z code>>22 +#define _W code>>21 + +MakeDisF(disLQC2, dName("LQC2"); dCP2128f(_Rt_); dOfB();) +MakeDisF(disSQC2, dName("SQC2"); dCP2128f(_Rt_); dOfB();) + +MakeDisF(disQMFC2, dName("QMFC2");) +MakeDisF(disQMTC2, dName("QMTC2");) +MakeDisF(disCFC2, dName("CFC2"); dGPR32(_Rt_); dCP232i(_Fs_);) +MakeDisF(disCTC2, dName("CTC2"); dCP232i(_Fs_); dGPR32(_Rt_);) + +MakeDisF(disBC2F, dName("BC2F");) +MakeDisF(disBC2T, dName("BC2T");) +MakeDisF(disBC2FL, dName("BC2FL");) +MakeDisF(disBC2TL, dName("BC2TL");) + +// SPEC1 +MakeDisF(disVADD, dName("VADD");) +MakeDisF(disVADDx, dName("VADDx"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) +MakeDisF(disVADDy, dName("VADDy"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) +MakeDisF(disVADDz, dName("VADDz"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) +MakeDisF(disVADDw, dName("VADDw"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) +MakeDisF(disVADDq, dName("VADDq"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232iF(REG_Q);) +MakeDisF(disVADDi, dName("VADDi"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232iF(REG_I);) +MakeDisF(disVSUB, dName("VSUB");) +MakeDisF(disVSUBx, dName("VSUBx"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) +MakeDisF(disVSUBy, dName("VSUBy"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) +MakeDisF(disVSUBz, dName("VSUBz"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) +MakeDisF(disVSUBw, dName("VSUBw"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) +MakeDisF(disVSUBq, dName("VSUBq");) +MakeDisF(disVSUBi, dName("VSUBi");) +MakeDisF(disVMADD, dName("VMADD");) +MakeDisF(disVMADDx, dName("VMADDx"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232x(_Ft_);) +MakeDisF(disVMADDy, dName("VMADDy"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232y(_Ft_);) +MakeDisF(disVMADDz, dName("VMADDz"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232z(_Ft_);) +MakeDisF(disVMADDw, dName("VMADDw"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232w(_Ft_);) +MakeDisF(disVMADDq, dName("VMADDq");) +MakeDisF(disVMADDi, dName("VMADDi");) +MakeDisF(disVMSUB, dName("VMSUB");) +MakeDisF(disVMSUBx, dName("VMSUBx");) +MakeDisF(disVMSUBy, dName("VMSUBy");) +MakeDisF(disVMSUBz, dName("VMSUBz");) +MakeDisF(disVMSUBw, dName("VMSUBw");) +MakeDisF(disVMSUBq, dName("VMSUBq");) +MakeDisF(disVMSUBi, dName("VMSUBi");) +MakeDisF(disVMAX, dName("VMAX");) +MakeDisF(disVMAXx, dName("VMAXx");) +MakeDisF(disVMAXy, dName("VMAXy");) +MakeDisF(disVMAXz, dName("VMAXz");) +MakeDisF(disVMAXw, dName("VMAXw");) +MakeDisF(disVMAXi, dName("VMAXi");) +MakeDisF(disVMINI, dName("VMINI");) +MakeDisF(disVMINIx, dName("VMINIx");) +MakeDisF(disVMINIy, dName("VMINIy");) +MakeDisF(disVMINIz, dName("VMINIz");) +MakeDisF(disVMINIw, dName("VMINIw");) +MakeDisF(disVMINIi, dName("VMINIi");) +MakeDisF(disVMUL, dName("VMUL");) +MakeDisF(disVMULx, dName("VMULx");) +MakeDisF(disVMULy, dName("VMULy");) +MakeDisF(disVMULz, dName("VMULz");) +MakeDisF(disVMULw, dName("VMULw");) +MakeDisF(disVMULq, dName("VMULq");) +MakeDisF(disVMULi, dName("VMULi");) +MakeDisF(disVIADD, dName("VIADD");) +MakeDisF(disVIADDI, dName("VIADDI");) +MakeDisF(disVISUB, dName("VISUB");) +MakeDisF(disVIAND, dName("VIAND");) +MakeDisF(disVIOR, dName("VIOR");) +MakeDisF(disVOPMSUB, dName("VOPMSUB");) +MakeDisF(disVCALLMS, dName("VCALLMS");) +MakeDisF(disVCALLMSR, dName("VCALLMSR");) + +// SPEC2 +MakeDisF(disVADDA, dName("VADDA");) +MakeDisF(disVADDAx, dName("VADDAx");) +MakeDisF(disVADDAy, dName("VADDAy");) +MakeDisF(disVADDAz, dName("VADDAz");) +MakeDisF(disVADDAw, dName("VADDAw");) +MakeDisF(disVADDAq, dName("VADDAq");) +MakeDisF(disVADDAi, dName("VADDAi");) +MakeDisF(disVMADDA, dName("VMADDA");) +MakeDisF(disVMADDAx, dName("VMADDAx"); dCP2ACCf(); dCP2128f(_Fs_); dCP232x(_Ft_);) +MakeDisF(disVMADDAy, dName("VMADDAy"); dCP2ACCf(); dCP2128f(_Fs_); dCP232y(_Ft_);) +MakeDisF(disVMADDAz, dName("VMADDAz"); dCP2ACCf(); dCP2128f(_Fs_); dCP232z(_Ft_);) +MakeDisF(disVMADDAw, dName("VMADDAw"); dCP2ACCf(); dCP2128f(_Fs_); dCP232w(_Ft_);) +MakeDisF(disVMADDAq, dName("VMADDAq");) +MakeDisF(disVMADDAi, dName("VMADDAi");) +MakeDisF(disVSUBAx, dName("VSUBAx");) +MakeDisF(disVSUBAy, dName("VSUBAy");) +MakeDisF(disVSUBAz, dName("VSUBAz");) +MakeDisF(disVSUBAw, dName("VSUBAw");) +MakeDisF(disVMSUBAx, dName("VMSUBAx");) +MakeDisF(disVMSUBAy, dName("VMSUBAy");) +MakeDisF(disVMSUBAz, dName("VMSUBAz");) +MakeDisF(disVMSUBAw, dName("VMSUBAw");) +MakeDisF(disVITOF0, dName("VITOF0");) +MakeDisF(disVITOF4, dName("VITOF4");) +MakeDisF(disVITOF12, dName("VITOF12");) +MakeDisF(disVITOF15, dName("VITOF15");) +MakeDisF(disVFTOI0, dName("VFTOI0");) +MakeDisF(disVFTOI4, dName("VFTOI4");) +MakeDisF(disVFTOI12, dName("VFTOI12");) +MakeDisF(disVFTOI15, dName("VFTOI15");) +MakeDisF(disVMULA, dName("VMULA");) +MakeDisF(disVMULAx, dName("VMULAx"); dCP2ACCf(); dCP2128f(_Fs_); dCP232x(_Ft_);) +MakeDisF(disVMULAy, dName("VMULAy");) +MakeDisF(disVMULAz, dName("VMULAz");) +MakeDisF(disVMULAw, dName("VMULAw");) +MakeDisF(disVMOVE, dName("VMOVE"); dCP2128f(_Ft_); dCP2128f(_Fs_);) +MakeDisF(disVMR32, dName("VMR32");) +MakeDisF(disVDIV, dName("VDIV");) +MakeDisF(disVSQRT, dName("VSQRT"); dCP232f(_Ft_, _Ftf_);) +MakeDisF(disVRSQRT, dName("VRSQRT");) +MakeDisF(disVRNEXT, dName("VRNEXT");) +MakeDisF(disVRGET, dName("VRGET");) +MakeDisF(disVRINIT, dName("VRINIT");) +MakeDisF(disVRXOR, dName("VRXOR");) +MakeDisF(disVWAITQ, dName("VWAITQ");) + +/********************************************************* +* Special purpose instructions * +* Format: OP * +*********************************************************/ + +MakeDisF(disSYNC, dName("SYNC");) +MakeDisF(disBREAK, dName("BREAK");) +MakeDisF(disSYSCALL, dName("SYSCALL"); dCode();) +MakeDisF(disCACHE, ssappendf(output, "%-7s, %x,", params "CACHE", _Rt_); dOfB();) +MakeDisF(disPREF, dName("PREF");) + +MakeDisF(disMFSA, dName("MFSA"); dGPR64(_Rd_); dSaR();) +MakeDisF(disMTSA, dName("MTSA"); dGPR64(_Rs_); dSaR();) + +MakeDisF(disMTSAB, dName("MTSAB");dGPR64(_Rs_); dImm();) +MakeDisF(disMTSAH, dName("MTSAH");dGPR64(_Rs_); dImm();) + +MakeDisF(disTGE, dName("TGE"); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disTGEU, dName("TGEU"); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disTLT, dName("TLT"); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disTLTU, dName("TLTU"); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disTEQ, dName("TEQ"); dGPR64(_Rs_); dGPR64(_Rt_);) +MakeDisF(disTNE, dName("TNE"); dGPR64(_Rs_); dGPR64(_Rt_);) + +MakeDisF(disTGEI, dName("TGEI"); dGPR64(_Rs_); dImm();) +MakeDisF(disTGEIU, dName("TGEIU"); dGPR64(_Rs_); dImm();) +MakeDisF(disTLTI, dName("TLTI"); dGPR64(_Rs_); dImm();) +MakeDisF(disTLTIU, dName("TLTIU"); dGPR64(_Rs_); dImm();) +MakeDisF(disTEQI, dName("TEQI"); dGPR64(_Rs_); dImm();) +MakeDisF(disTNEI, dName("TNEI"); dGPR64(_Rs_); dImm();) + +/********************************************************* +* Unknown instruction (would generate an exception) * +* Format: ? * +*********************************************************/ +static MakeDisF(disNULL, dName("*** Bad OP ***");) + +TdisR5900F disR5900_MMI0[] = { // Subset of disMMI0 + disPADDW, disPSUBW, disPCGTW, disPMAXW, + disPADDH, disPSUBH, disPCGTH, disPMAXH, + disPADDB, disPSUBB, disPCGTB, disNULL, + disNULL, disNULL, disNULL, disNULL, + disPADDSW, disPSUBSW, disPEXTLW, disPPACW, + disPADDSH, disPSUBSH, disPEXTLH, disPPACH, + disPADDSB, disPSUBSB, disPEXTLB, disPPACB, + disNULL, disNULL, disPEXTS, disPPACS}; + +static void disMMI0( string& output, u32 code ) +{ + disR5900_MMI0[_Sa_]( output, code ); +} + +TdisR5900F disR5900_MMI1[] = { // Subset of disMMI1 + disNULL, disPABSW, disPCEQW, disPMINW, + disPADSBH, disPABSH, disPCEQH, disPMINH, + disNULL, disNULL, disPCEQB, disNULL, + disNULL, disNULL, disNULL, disNULL, + disPADDUW, disPSUBUW, disPEXTUW, disNULL, + disPADDUH, disPSUBUH, disPEXTUH, disNULL, + disPADDUB, disPSUBUB, disPEXTUB, disQFSRV, + disNULL, disNULL, disNULL, disNULL}; + +static void disMMI1( string& output, u32 code ) +{ + disR5900_MMI1[_Sa_]( output, code ); +} + +TdisR5900F disR5900_MMI2[] = { // Subset of disMMI2 + disPMADDW, disNULL, disPSLLVW, disPSRLVW, + disPMSUBW, disNULL, disNULL, disNULL, + disPMFHI, disPMFLO, disPINTH, disNULL, + disPMULTW, disPDIVW, disPCPYLD, disNULL, + disPMADDH, disPHMADH, disPAND, disPXOR, + disPMSUBH, disPHMSBH, disNULL, disNULL, + disNULL, disNULL, disPEXEH, disPREVH, + disPMULTH, disPDIVBW, disPEXEW, disPROT3W}; + +static void disMMI2( string& output, u32 code ) +{ + disR5900_MMI2[_Sa_]( output, code ); +} + +TdisR5900F disR5900_MMI3[] = { // Subset of disMMI3 + disPMADDUW, disNULL, disNULL, disPSRAVW, + disNULL, disNULL, disNULL, disNULL, + disPMTHI, disPMTLO, disPINTEH, disNULL, + disPMULTUW, disPDIVUW, disPCPYUD, disNULL, + disNULL, disNULL, disPOR, disPNOR, + disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disPEXCH, disPCPYH, + disNULL, disNULL, disPEXCW, disNULL}; + +static void disMMI3( string& output, u32 code ) +{ + disR5900_MMI3[_Sa_]( output, code ); +} + +TdisR5900F disR5900_MMI[] = { // Subset of disMMI + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disMMI0, disMMI2, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disMULT1, disMULTU1, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disMMI1, disMMI3, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL}; + +static void disMMI( string& output, u32 code ) +{ + disR5900_MMI[_Funct_]( output, code ); +} + + +TdisR5900F disR5900_COP0_BC0[] = { //subset of disCOP0 BC + disBC0F, disBC0T, disBC0FL, disBC0TL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, +}; + +static void disCOP0_BC0( string& output, u32 code ) +{ + disR5900_COP0_BC0[_Rt_]( output, code ); +} + +TdisR5900F disR5900_COP0_Func[] = { //subset of disCOP0 Function + disNULL, disTLBR, disTLBWI, disNULL, disNULL, disNULL, disTLBWR, disNULL, + disTLBP, disNULL, disNULL , disNULL, disNULL, disNULL, disNULL , disNULL, + disNULL, disNULL, disNULL , disNULL, disNULL, disNULL, disNULL , disNULL, + disERET, disNULL, disNULL , disNULL, disNULL, disNULL, disNULL , disNULL, + disNULL, disNULL, disNULL , disNULL, disNULL, disNULL, disNULL , disNULL, + disNULL, disNULL, disNULL , disNULL, disNULL, disNULL, disNULL , disNULL, + disNULL, disNULL, disNULL , disNULL, disNULL, disNULL, disNULL , disNULL, + disEI , disDI , disNULL , disNULL, disNULL, disNULL, disNULL , disNULL +}; +static void disCOP0_Func( string& output, u32 code ) +{ + disR5900_COP0_Func[_Funct_]( output, code ); +} + +TdisR5900F disR5900_COP0[] = { // Subset of disCOP0 + disMFC0, disNULL, disNULL, disNULL, disMTC0, disNULL, disNULL, disNULL, + disCOP0_BC0, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disCOP0_Func, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL}; + +static void disCOP0( string& output, u32 code ) +{ + disR5900_COP0[_Rs_]( output, code ); +} + +TdisR5900F disR5900_COP1_S[] = { //subset of disCOP1 S + disADDs, disSUBs, disMULs, disDIVs, disSQRTs, disABSs, disMOVs, disNEGs, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disRSQRTs, disNULL, + disADDAs, disSUBAs, disMULAs, disNULL, disMADDs, disMSUBs, disMADDAs, disMSUBAs, + disNULL, disNULL, disNULL, disNULL, disCVTWs, disNULL, disNULL, disNULL, + disMINs, disMAXs, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disCFs, disNULL, disCEQs, disNULL, disCLTs, disNULL, disCLEs, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, +}; + +static void disCOP1_S( string& output, u32 code ) +{ + disR5900_COP1_S[_Funct_]( output, code ); +} + +TdisR5900F disR5900_COP1_W[] = { //subset of disCOP1 W + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disCVTSw, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, +}; + +static void disCOP1_W( string& output, u32 code ) +{ + disR5900_COP1_W[_Funct_]( output, code ); +} + +TdisR5900F disR5900_COP1_BC1[] = { //subset of disCOP1 BC + disBC1F, disBC1T, disBC1FL, disBC1TL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, +}; + +static void disCOP1_BC1( string& output, u32 code ) +{ + disR5900_COP1_BC1[_Rt_]( output, code ); +} + +TdisR5900F disR5900_COP1[] = { // Subset of disCOP1 + disMFC1, disNULL, disCFC1, disNULL, disMTC1, disNULL, disCTC1, disNULL, + disCOP1_BC1, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disCOP1_S, disNULL, disNULL, disNULL, disCOP1_W, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL}; + +static void disCOP1( string& output, u32 code ) +{ + disR5900_COP1[_Rs_]( output, code ); +} + +TdisR5900F disR5900_COP2_SPEC2[] = { //subset of disCOP2 SPEC2 + disVADDAx, disVADDAy, disVADDAz, disVADDAw, disVSUBAx, disVSUBAy, disVSUBAz, disVSUBAw, + disVMADDAx, disVMADDAy, disVMADDAz, disVMADDAw, disVMSUBAx, disVMSUBAy, disVMSUBAz, disVMSUBAw, + disVITOF0, disVITOF4, disVITOF12, disVITOF15, disVFTOI0, disVFTOI4, disVFTOI12, disVFTOI15, + disVMULAx, disVMULAy, disVMULAz, disVMULAw, disNULL, disNULL, disNULL, disNULL, + disVADDAq, disVMADDAq, disVADDAi, disVMADDAi, disNULL, disNULL, disNULL, disNULL, + disVADDA, disVMADDA, disVMULA, disNULL, disNULL, disNULL, disNULL, disNULL, + disVMOVE, disVMR32, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disVDIV, disVSQRT, disVRSQRT, disVWAITQ, disNULL, disNULL, disNULL, disNULL, + disVRNEXT, disVRGET, disVRINIT, disVRXOR, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, +}; + +static void disCOP2_SPEC2( string& output, u32 code ) +{ + disR5900_COP2_SPEC2[(code & 0x3) | ((code >> 4) & 0x7c)]( output, code ); +} + +TdisR5900F disR5900_COP2_SPEC1[] = { //subset of disCOP2 SPEC1 + disVADDx, disVADDy, disVADDz, disVADDw, disVSUBx, disVSUBy, disVSUBz, disVSUBw, + disVMADDx, disVMADDy, disVMADDz, disVMADDw, disVMSUBx, disVMSUBy, disVMSUBz, disVMSUBw, + disVMAXx, disVMAXy, disVMAXz, disVMAXw, disVMINIx, disVMINIy, disVMINIz, disVMINIw, + disVMULx, disVMULy, disVMULz, disVMULw, disVMULq, disVMAXi, disVMULi, disVMINIi, + disVADDq, disVMADDq, disVADDi, disVMADDi, disVSUBq, disVMSUBq, disVSUBi, disVMSUBi, + disVADD, disVMADD, disVMUL, disVMAX, disVSUB, disVMSUB, disVOPMSUB, disVMINI, + disVIADD, disVISUB, disVIADDI, disNULL, disVIAND, disVIOR, disNULL, disNULL, + disVCALLMS, disVCALLMSR, disNULL, disNULL, disCOP2_SPEC2, disCOP2_SPEC2, disCOP2_SPEC2, disCOP2_SPEC2, +}; + +static void disCOP2_SPEC1( string& output, u32 code ) +{ + disR5900_COP2_SPEC1[_Funct_]( output, code ); +} + +TdisR5900F disR5900_COP2_BC2[] = { //subset of disCOP2 BC + disBC2F, disBC2T, disBC2FL, disBC2TL, disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, + disNULL, disNULL, disNULL , disNULL , disNULL, disNULL, disNULL, disNULL, +}; + +static void disCOP2_BC2( string& output, u32 code ) +{ + disR5900_COP2_BC2[_Rt_]( output, code ); +} + +TdisR5900F disR5900_COP2[] = { // Subset of disCOP2 + disNULL, disQMFC2, disCFC2, disNULL, disNULL, disQMTC2, disCTC2, disNULL, + disCOP2_BC2, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, disNULL, + disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, + disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1, disCOP2_SPEC1}; + +static void disCOP2( string& output, u32 code ) +{ + disR5900_COP2[_Rs_]( output, code ); +} + +TdisR5900F disR5900_REGIMM[] = { // Subset of disREGIMM + disBLTZ, disBGEZ, disBLTZL, disBGEZL, disNULL, disNULL, disNULL, disNULL, + disTGEI, disTGEIU, disTLTI, disTLTIU, disTEQI, disNULL, disTNEI, disNULL, + disBLTZAL, disBGEZAL, disBLTZALL, disBGEZALL, disNULL, disNULL, disNULL, disNULL, + disMTSAB, disMTSAH , disNULL, disNULL, disNULL, disNULL, disNULL, disNULL}; + +static void disREGIMM( string& output, u32 code ) +{ + disR5900_REGIMM[_Rt_]( output, code ); +} + +TdisR5900F disR5900_SPECIAL[] = { + disSLL, disNULL, disSRL, disSRA, disSLLV, disNULL, disSRLV, disSRAV, + disJR, disJALR, disMOVZ, disMOVN, disSYSCALL, disBREAK,disNULL, disSYNC, + disMFHI, disMTHI, disMFLO, disMTLO, disDSLLV, disNULL, disDSRLV, disDSRAV, + disMULT, disMULTU,disDIV, disDIVU, disNULL, disNULL, disNULL, disNULL, + disADD, disADDU, disSUB, disSUBU, disAND, disOR, disXOR, disNOR, + disMFSA , disMTSA, disSLT, disSLTU, disDADD, disDADDU,disDSUB, disDSUBU, + disTGE, disTGEU, disTLT, disTLTU, disTEQ, disNULL, disTNE, disNULL, + disDSLL, disNULL, disDSRL, disDSRA, disDSLL32, disNULL, disDSRL32,disDSRA32 }; + +static void disSPECIAL( string& output, u32 code ) +{ + disR5900_SPECIAL[_Funct_]( output, code ); +} + +TdisR5900F disR5900[] = { + disSPECIAL, disREGIMM, disJ , disJAL , disBEQ , disBNE , disBLEZ , disBGTZ , + disADDI , disADDIU , disSLTI, disSLTIU, disANDI, disORI , disXORI , disLUI , + disCOP0 , disCOP1 , disCOP2, disNULL , disBEQL, disBNEL, disBLEZL, disBGTZL, + disDADDI , disDADDIU, disLDL , disLDR , disMMI , disNULL, disLQ , disSQ , + disLB , disLH , disLWL , disLW , disLBU , disLHU , disLWR , disLWU , + disSB , disSH , disSWL , disSW , disSDL , disSDR , disSWR , disCACHE, + disNULL , disLWC1 , disNULL, disPREF , disNULL, disNULL, disLQC2 , disLD , + disNULL , disSWC1 , disNULL, disNULL , disNULL, disNULL, disSQC2 , disSD }; + +void disR5900F( string& output, u32 code ) +{ + disR5900[code >> 26]( output, code ); +} + +// returns a string representation of the cpuRegs current instruction. +// The return value of this method is *not* thread safe! +const string& DisR5900CurrentState::getString() +{ + result.clear(); + disR5900F( result, cpuRegs.code ); + return result; +} + +const char* DisR5900CurrentState::getCString() +{ + result.clear(); + disR5900F( result, cpuRegs.code ); + return result.c_str(); +} + +DisR5900CurrentState disR5900Current; + +} \ No newline at end of file diff --git a/pcsx2/DebugTools/DisR5900asm.cpp b/pcsx2/DebugTools/DisR5900asm.cpp new file mode 100644 index 0000000000..132ea554fc --- /dev/null +++ b/pcsx2/DebugTools/DisR5900asm.cpp @@ -0,0 +1,1192 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#ifdef __LINUX__ +#include +#endif + +#include "Debug.h" +#include "R5900.h" +#include "DisASM.h" +#include "R5900OpcodeTables.h" + +unsigned long opcode_addr; + +using namespace std; + +namespace R5900 +{ + +/* +//DECODE PROCUDURES + +//cop0 +#define DECODE_FS (DECODE_RD) +#define DECODE_FT (DECODE_RT) +#define DECODE_FD (DECODE_SA) +/// ******** + +#define DECODE_FUNCTION ((cpuRegs.code) & 0x3F) +#define DECODE_RD ((cpuRegs.code >> 11) & 0x1F) // The rd part of the instruction register +#define DECODE_RT ((cpuRegs.code >> 16) & 0x1F) // The rt part of the instruction register +#define DECODE_RS ((cpuRegs.code >> 21) & 0x1F) // The rs part of the instruction register +#define DECODE_SA ((cpuRegs.code >> 6) & 0x1F) // The sa part of the instruction register +#define DECODE_IMMED ( cpuRegs.code & 0xFFFF) // The immediate part of the instruction register +#define DECODE_OFFSET ((((short)DECODE_IMMED * 4) + opcode_addr + 4)) +#define DECODE_JUMP (opcode_addr & 0xf0000000)|((cpuRegs.code&0x3ffffff)<<2) +#define DECODE_SYSCALL ((opcode_addr & 0x03FFFFFF) >> 6) +#define DECODE_BREAK (DECODE_SYSCALL) +#define DECODE_C0BC ((cpuRegs.code >> 16) & 0x03) +#define DECODE_C1BC ((cpuRegs.code >> 16) & 0x03) +#define DECODE_C2BC ((cpuRegs.code >> 16) & 0x03) +*/ +/*************************CPUS REGISTERS**************************/ +static const char * const GPR_REG[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" +}; +static const char * const COP0_REG[32] ={ + "Index","Random","EntryLo0","EntryLo1","Context","PageMask", + "Wired","C0r7","BadVaddr","Count","EntryHi","Compare","Status", + "Cause","EPC","PRId","Config","C0r17","C0r18","C0r19","C0r20", + "C0r21","C0r22","C0r23","Debug","Perf","C0r26","C0r27","TagLo", + "TagHi","ErrorPC","C0r31" +}; +//floating point cop1 Floating point reg +static const char * const COP1_REG_FP[32] ={ + "f00","f01","f02","f03","f04","f05","f06","f07", + "f08","f09","f10","f11","f12","f13","f14","f15", + "f16","f17","f18","f19","f20","f21","f21","f23", + "f24","f25","f26","f27","f28","f29","f30","f31" +}; +//floating point cop1 control registers +static const char * const COP1_REG_FCR[32] ={ + "fcr00","fcr01","fcr02","fcr03","fcr04","fcr05","fcr06","fcr07", + "fcr08","fcr09","fcr10","fcr11","fcr12","fcr13","fcr14","fcr15", + "fcr16","fcr17","fcr18","fcr19","fcr20","fcr21","fcr21","fcr23", + "fcr24","fcr25","fcr26","fcr27","fcr28","fcr29","fcr30","fcr31" +}; + +//floating point cop2 reg +static const char * const COP2_REG_FP[32] ={ + "vf00","vf01","vf02","vf03","vf04","vf05","vf06","vf07", + "vf08","vf09","vf10","vf11","vf12","vf13","vf14","vf15", + "vf16","vf17","vf18","vf19","vf20","vf21","vf21","vf23", + "vf24","vf25","vf26","vf27","vf28","vf29","vf30","vf31" +}; +//cop2 control registers + +static const char * const COP2_REG_CTL[32] ={ + "vi00","vi01","vi02","vi03","vi04","vi05","vi06","vi07", + "vi08","vi09","vi10","vi11","vi12","vi13","vi14","vi15", + "Status","MACflag","ClipFlag","c2c19","R","I","Q","c2c23", + "c2c24","c2c25","TPC","CMSAR0","FBRST","VPU-STAT","c2c30","CMSAR1" +}; + +void P_COP2_Unknown( string& output ); +void P_COP2_SPECIAL2( string& output ); +void P_COP2_SPECIAL( string& output ); +void P_COP2_BC2( string& output ); + +//**************************************************************************** +//** COP2 - (VU0) ** +//**************************************************************************** +void P_QMFC2( string& output ); +void P_CFC2( string& output ); +void P_QMTC2( string& output ); +void P_CTC2( string& output ); +void P_BC2F( string& output ); +void P_BC2T( string& output ); +void P_BC2FL( string& output ); +void P_BC2TL( string& output ); +//*****************SPECIAL 1 VUO TABLE******************************* +void P_VADDx( string& output ); +void P_VADDy( string& output ); +void P_VADDz( string& output ); +void P_VADDw( string& output ); +void P_VSUBx( string& output ); +void P_VSUBy( string& output ); +void P_VSUBz( string& output ); +void P_VSUBw( string& output ); +void P_VMADDx( string& output ); +void P_VMADDy( string& output ); +void P_VMADDz( string& output ); +void P_VMADDw( string& output ); +void P_VMSUBx( string& output ); +void P_VMSUBy( string& output ); +void P_VMSUBz( string& output ); +void P_VMSUBw( string& output ); +void P_VMAXx( string& output ); +void P_VMAXy( string& output ); +void P_VMAXz( string& output ); +void P_VMAXw( string& output ); +void P_VMINIx( string& output ); +void P_VMINIy( string& output ); +void P_VMINIz( string& output ); +void P_VMINIw( string& output ); +void P_VMULx( string& output ); +void P_VMULy( string& output ); +void P_VMULz( string& output ); +void P_VMULw( string& output ); +void P_VMULq( string& output ); +void P_VMAXi( string& output ); +void P_VMULi( string& output ); +void P_VMINIi( string& output ); +void P_VADDq( string& output ); +void P_VMADDq( string& output ); +void P_VADDi( string& output ); +void P_VMADDi( string& output ); +void P_VSUBq( string& output ); +void P_VMSUBq( string& output ); +void P_VSUbi( string& output ); +void P_VMSUBi( string& output ); +void P_VADD( string& output ); +void P_VMADD( string& output ); +void P_VMUL( string& output ); +void P_VMAX( string& output ); +void P_VSUB( string& output ); +void P_VMSUB( string& output ); +void P_VOPMSUB( string& output ); +void P_VMINI( string& output ); +void P_VIADD( string& output ); +void P_VISUB( string& output ); +void P_VIADDI( string& output ); +void P_VIAND( string& output ); +void P_VIOR( string& output ); +void P_VCALLMS( string& output ); +void P_CALLMSR( string& output ); +//***********************************END OF SPECIAL1 VU0 TABLE***************************** +//******************************SPECIAL2 VUO TABLE***************************************** +void P_VADDAx( string& output ); +void P_VADDAy( string& output ); +void P_VADDAz( string& output ); +void P_VADDAw( string& output ); +void P_VSUBAx( string& output ); +void P_VSUBAy( string& output ); +void P_VSUBAz( string& output ); +void P_VSUBAw( string& output ); +void P_VMADDAx( string& output ); +void P_VMADDAy( string& output ); +void P_VMADDAz( string& output ); +void P_VMADDAw( string& output ); +void P_VMSUBAx( string& output ); +void P_VMSUBAy( string& output ); +void P_VMSUBAz( string& output ); +void P_VMSUBAw( string& output ); +void P_VITOF0( string& output ); +void P_VITOF4( string& output ); +void P_VITOF12( string& output ); +void P_VITOF15( string& output ); +void P_VFTOI0( string& output ); +void P_VFTOI4( string& output ); +void P_VFTOI12( string& output ); +void P_VFTOI15( string& output ); +void P_VMULAx( string& output ); +void P_VMULAy( string& output ); +void P_VMULAz( string& output ); +void P_VMULAw( string& output ); +void P_VMULAq( string& output ); +void P_VABS( string& output ); +void P_VMULAi( string& output ); +void P_VCLIPw( string& output ); +void P_VADDAq( string& output ); +void P_VMADDAq( string& output ); +void P_VADDAi( string& output ); +void P_VMADDAi( string& output ); +void P_VSUBAq( string& output ); +void P_VMSUBAq( string& output ); +void P_VSUBAi( string& output ); +void P_VMSUBAi( string& output ); +void P_VADDA( string& output ); +void P_VMADDA( string& output ); +void P_VMULA( string& output ); +void P_VSUBA( string& output ); +void P_VMSUBA( string& output ); +void P_VOPMULA( string& output ); +void P_VNOP( string& output ); +void P_VMONE( string& output ); +void P_VMR32( string& output ); +void P_VLQI( string& output ); +void P_VSQI( string& output ); +void P_VLQD( string& output ); +void P_VSQD( string& output ); +void P_VDIV( string& output ); +void P_VSQRT( string& output ); +void P_VRSQRT( string& output ); +void P_VWAITQ( string& output ); +void P_VMTIR( string& output ); +void P_VMFIR( string& output ); +void P_VILWR( string& output ); +void P_VISWR( string& output ); +void P_VRNEXT( string& output ); +void P_VRGET( string& output ); +void P_VRINIT( string& output ); +void P_VRXOR( string& output ); +//************************************END OF SPECIAL2 VUO TABLE**************************** + + +/* + CPU: Instructions encoded by opcode field. + 31---------26---------------------------------------------------0 + | opcode | | + ------6---------------------------------------------------------- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo +000 | *1 | *2 | J | JAL | BEQ | BNE | BLEZ | BGTZ | +001 | ADDI | ADDIU | SLTI | SLTIU | ANDI | ORI | XORI | LUI | +010 | *3 | *4 | *5 | --- | BEQL | BNEL | BLEZL | BGTZL | +011 | DADDI |DADDIU | LDL | LDR | *6 | --- | LQ | SQ | +100 | LB | LH | LWL | LW | LBU | LHU | LWR | LWU | +101 | SB | SH | SWL | SW | SDL | SDR | SWR | CACHE | +110 | --- | LWC1 | --- | PREF | --- | --- | LQC2 | LD | +111 | --- | SWC1 | --- | --- | --- | --- | SQC2 | SD | + hi |-------|-------|-------|-------|-------|-------|-------|-------| + *1 = SPECIAL, see SPECIAL list *2 = REGIMM, see REGIMM list + *3 = COP0 *4 = COP1 + *5 = COP2 *6 = MMI table +*/ + +/* + SPECIAL: Instr. encoded by function field when opcode field = SPECIAL + 31---------26------------------------------------------5--------0 + | = SPECIAL | | function| + ------6----------------------------------------------------6----- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo +000 | SLL | --- | SRL | SRA | SLLV | --- | SRLV | SRAV | +001 | JR | JALR | MOVZ | MOVN |SYSCALL| BREAK | --- | SYNC | +010 | MFHI | MTHI | MFLO | MTLO | DSLLV | --- | DSRLV | DSRAV | +011 | MULT | MULTU | DIV | DIVU | ---- | --- | ---- | ----- | +100 | ADD | ADDU | SUB | SUBU | AND | OR | XOR | NOR | +101 | MFSA | MTSA | SLT | SLTU | DADD | DADDU | DSUB | DSUBU | +110 | TGE | TGEU | TLT | TLTU | TEQ | --- | TNE | --- | +111 | DSLL | --- | DSRL | DSRA |DSLL32 | --- |DSRL32 |DSRA32 | + hi |-------|-------|-------|-------|-------|-------|-------|-------| +*/ + +/* + REGIMM: Instructions encoded by the rt field when opcode field = REGIMM. + 31---------26----------20-------16------------------------------0 + | = REGIMM | | rt | | + ------6---------------------5------------------------------------ + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo + 00 | BLTZ | BGEZ | BLTZL | BGEZL | --- | --- | --- | --- | + 01 | TGEI | TGEIU | TLTI | TLTIU | TEQI | --- | TNEI | --- | + 10 | BLTZAL| BGEZAL|BLTZALL|BGEZALL| --- | --- | --- | --- | + 11 | MTSAB | MTSAH | --- | --- | --- | --- | --- | --- | + hi |-------|-------|-------|-------|-------|-------|-------|-------| +*/ + +/* + MMI: Instr. encoded by function field when opcode field = MMI + 31---------26------------------------------------------5--------0 + | = MMI | | function| + ------6----------------------------------------------------6----- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo +000 | MADD | MADDU | --- | --- | PLZCW | --- | --- | --- | +001 | *1 | *2 | --- | --- | --- | --- | --- | --- | +010 | MFHI1 | MTHI1 | MFLO1 | MTLO1 | --- | --- | --- | --- | +011 | MULT1 | MULTU1| DIV1 | DIVU1 | --- | --- | --- | --- | +100 | MADD1 | MADDU1| --- | --- | --- | --- | --- | --- | +101 | *3 | *4 | --- | --- | --- | --- | --- | --- | +110 | PMFHL | PMTHL | --- | --- | PSLLH | --- | PSRLH | PSRAH | +111 | --- | --- | --- | --- | PSLLW | --- | PSRLW | PSRAW | + hi |-------|-------|-------|-------|-------|-------|-------|-------| + + *1 = see MMI0 table *2 = see MMI2 Table + *3 = see MMI1 table *4 = see MMI3 Table +*/ + +/* + MMI0: Instr. encoded by function field when opcode field = MMI & MMI0 + + 31---------26------------------------------10--------6-5--------0 + | | |function | MMI0 | + ------6----------------------------------------------------6----- + |--000--|--001--|--010--|--011--| lo +000 |PADDW | PSUBW | PCGTW | PMAXW | +001 |PADDH | PSUBH | PCGTH | PMAXH | +010 |PADDB | PSUBB | PCGTB | --- | +011 | --- | --- | --- | --- | +100 |PADDSW |PSUBSW |PEXTLW | PPACW | +101 |PADDSH |PSUBSH |PEXTLH | PPACH | +110 |PADDSB |PSUBSB |PEXTLB | PPACB | +111 | --- | --- | PEXT5 | PPAC5 | + hi |-------|-------|-------|-------| +*/ + +/* + MMI1: Instr. encoded by function field when opcode field = MMI & MMI1 + + 31---------26------------------------------------------5--------0 + | | |function | MMI1 | + ------6----------------------------------------------------6----- + |--000--|--001--|--010--|--011--| lo +000 | --- | PABSW | PCEQW | PMINW | +001 |PADSBH | PABSH | PCEQH | PMINH | +010 | --- | --- | PCEQB | --- | +011 | --- | --- | --- | --- | +100 |PADDUW |PSUBUW |PEXTUW | --- | +101 |PADDUH |PSUBUH |PEXTUH | --- | +110 |PADDUB |PSUBUB |PEXTUB | QFSRV | +111 | --- | --- | --- | --- | + hi |-------|-------|-------|-------| +*/ + +/* + MMI2: Instr. encoded by function field when opcode field = MMI & MMI2 + + 31---------26------------------------------------------5--------0 + | | |function | MMI2 | + ------6----------------------------------------------------6----- + |--000--|--001--|--010--|--011--| lo +000 |PMADDW | --- |PSLLVW |PSRLVW | +001 |PMSUBW | --- | --- | --- | +010 |PMFHI |PMFLO |PINTH | --- | +011 |PMULTW |PDIVW |PCPYLD | --- | +100 |PMADDH |PHMADH | PAND | PXOR | +101 |PMSUBH |PHMSBH | --- | --- | +110 | --- | --- | PEXEH | PREVH | +111 |PMULTH |PDIVBW | PEXEW |PROT3W | + hi |-------|-------|-------|-------| +*/ + +/* + MMI3: Instr. encoded by function field when opcode field = MMI & MMI3 + 31---------26------------------------------------------5--------0 + | | |function | MMI3 | + ------6----------------------------------------------------6----- + |--000--|--001--|--010--|--011--| lo +000 |PMADDUW| --- | --- |PSRAVW | +001 | --- | --- | --- | --- | +010 |PMTHI | PMTLO |PINTEH | --- | +011 |PMULTUW| PDIVUW|PCPYUD | --- | +100 | --- | --- | POR | PNOR | +101 | --- | --- | --- | --- | +110 | --- | --- | PEXCH | PCPYH | +111 | --- | --- | PEXCW | --- | + hi |-------|-------|-------|-------| + */ + +/* + COP0: Instructions encoded by the rs field when opcode = COP0. + 31--------26-25------21 ----------------------------------------0 + | = COP0 | fmt | | + ------6----------5----------------------------------------------- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo + 00 | MFC0 | --- | --- | --- | MTC0 | --- | --- | --- | + 01 | *1 | --- | --- | --- | --- | --- | --- | --- | + 10 | *2 | --- | --- | --- | --- | --- | --- | --- | + 11 | --- | --- | --- | --- | --- | --- | --- | --- | + hi |-------|-------|-------|-------|-------|-------|-------|-------| + *1=BC See BC0 list *2 = TLB instr, see TLB list +*/ +/* + BC0: Instructions encoded by the rt field when opcode = COP0 & rs field=BC0 + 31--------26-25------21 ----------------------------------------0 + | = COP0 | fmt | | + ------6----------5----------------------------------------------- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo + 00 | BC0F | BC0T | BC0FL | BC0TL | --- | --- | --- | --- | + 01 | --- | --- | --- | --- | --- | --- | --- | --- | + 10 | --- | --- | --- | --- | --- | --- | --- | --- | + 11 | --- | --- | --- | --- | --- | --- | --- | --- | + hi |-------|-------|-------|-------|-------|-------|-------|-------| +*/ +/* + C0=Instructions encode by function field when Opcode field=COP0 & rs field=C0 + 31---------26------------------------------------------5--------0 + | | | | + ------6----------------------------------------------------6----- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo +000 | --- | TLBR | TLBWI | --- | --- | --- | TLBWR | --- | +001 | TLBP | --- | --- | --- | --- | --- | --- | --- | +010 | --- | --- | --- | --- | --- | --- | --- | --- | +011 | ERET | --- | --- | --- | --- | --- | --- | --- | +100 | --- | --- | --- | --- | --- | --- | --- | --- | +101 | --- | --- | --- | --- | --- | --- | --- | --- | +110 | --- | --- | --- | --- | --- | --- | --- | --- | +111 | EI | DI | --- | --- | --- | --- | --- | --- | + hi |-------|-------|-------|-------|-------|-------|-------|-------| +*/ +/* + COP1: Instructions encoded by the fmt field when opcode = COP1. + 31--------26-25------21 ----------------------------------------0 + | = COP1 | fmt | | + ------6----------5----------------------------------------------- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo + 00 | MFC1 | --- | CFC1 | --- | MTC1 | --- | CTC1 | --- | + 01 | *1 | --- | --- | --- | --- | --- | --- | --- | + 10 | *2 | --- | --- | --- | *3 | --- | --- | --- | + 11 | --- | --- | --- | --- | --- | --- | --- | --- | + hi |-------|-------|-------|-------|-------|-------|-------|-------| + *1 = BC instructions, see BC1 list *2 = S instr, see FPU list + *3 = W instr, see FPU list +*/ +/* + BC1: Instructions encoded by the rt field when opcode = COP1 & rs field=BC1 + 31--------26-25------21 ----------------------------------------0 + | = COP1 | fmt | | + ------6----------5----------------------------------------------- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo + 00 | BC1F | BC1T | BC1FL | BC1TL | --- | --- | --- | --- | + 01 | --- | --- | --- | --- | --- | --- | --- | --- | + 10 | --- | --- | --- | --- | --- | --- | --- | --- | + 11 | --- | --- | --- | --- | --- | --- | --- | --- | + hi |-------|-------|-------|-------|-------|-------|-------|-------| +*/ +/* + FPU: Instructions encoded by the function field when opcode = COP1 + and rs = S + 31--------26-25------21 -------------------------------5--------0 + | = COP1 | = S | | function| + ------6----------5-----------------------------------------6----- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo +000 | ADD.S | SUB.S | MUL.S | DIV.S | SQRT.S| ABS.S | MOV.S | NEG.S | +001 | --- | --- | --- | --- | --- | --- | --- | --- | +010 | --- | --- | --- | --- | --- | --- |RSQRT.S| --- | +011 | ADDA.S| SUBA.S| MULA.S| --- | MADD.S| MSUB.S|MADDA.S|MSUBA.S| +100 | --- | --- | --- | --- | CVT.W | --- | --- | --- | +101 | MAX.S | MIN.S | --- | --- | --- | --- | --- | --- | +110 | C.F | --- | C.EQ | --- | C.LT | --- | C.LE | --- | +111 | --- | --- | --- | --- | --- | --- | --- | --- | + hi |-------|-------|-------|-------|-------|-------|-------|-------| +*/ +/* + FPU: Instructions encoded by the function field when opcode = COP1 + and rs = W + 31--------26-25------21 -------------------------------5--------0 + | = COP1 | = W | | function| + ------6----------5-----------------------------------------6----- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo +000 | --- | --- | --- | --- | --- | --- | --- | --- | +001 | --- | --- | --- | --- | --- | --- | --- | --- | +010 | --- | --- | --- | --- | --- | --- | --- | --- | +011 | --- | --- | --- | --- | --- | --- | --- | --- | +100 | CVT.S | --- | --- | --- | --- | --- | --- | --- | +101 | --- | --- | --- | --- | --- | --- | --- | --- | +110 | --- | --- | --- | --- | --- | --- | --- | --- | +111 | --- | --- | --- | --- | --- | --- | --- | --- | + hi |-------|-------|-------|-------|-------|-------|-------|-------| +*/ + +//************************************************************* +// COP2 TABLES :) [VU0 as a Co-Processor to the EE] +//************************************************************* +/* + COP2: Instructions encoded by the fmt field when opcode = COP2. + 31--------26-25------21 ----------------------------------------0 + | = COP2 | fmt | | + ------6----------5----------------------------------------------- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo + 00 | --- | QMFC2 | CFC2 | --- | --- | QMTC2 | CTC2 | --- | + 01 | *1 | --- | --- | --- | --- | --- | --- | --- | + 10 | *2 | *2 | *2 | *2 | *2 | *2 | *2 | *2 | + 11 | *2 | *2 | *2 | *2 | *2 | *2 | *2 | *2 | + hi |-------|-------|-------|-------|-------|-------|-------|-------| + *1 = BC instructions, see BC2 list *2 =see special1 table +*/ +void (*COP2PrintTable[32])( string& output ) = { + P_COP2_Unknown, P_QMFC2, P_CFC2, P_COP2_Unknown, P_COP2_Unknown, P_QMTC2, P_CTC2, P_COP2_Unknown, + P_COP2_BC2, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, + P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, + P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, P_COP2_SPECIAL, + + +}; +/* + BC2: Instructions encoded by the rt field when opcode = COP2 & rs field=BC1 + 31--------26-25------21 ----------------------------------------0 + | = COP2 | rs=BC2| | + ------6----------5----------------------------------------------- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo + 00 | BC2F | BC2T | BC2FL | BC2TL | --- | --- | --- | --- | + 01 | --- | --- | --- | --- | --- | --- | --- | --- | + 10 | --- | --- | --- | --- | --- | --- | --- | --- | + 11 | --- | --- | --- | --- | --- | --- | --- | --- | + hi |-------|-------|-------|-------|-------|-------|-------|-------| + */ +void (*COP2BC2PrintTable[32])( string& output ) = { + P_BC2F, P_BC2T, P_BC2FL, P_BC2TL, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, + P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, + P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, + P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, P_COP2_Unknown, +}; +/* + Special1 table : instructions encode by function field when opcode=COP2 & rs field=Special1 + 31---------26---------------------------------------------------0 + | =COP2 | rs=Special | + ------6---------------------------------------------------------- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo +000 |VADDx |VADDy |VADDz |VADDw |VSUBx |VSUBy |VSUBz |VSUBw | +001 |VMADDx |VMADDy |VMADDz |VMADDw |VMSUBx |VMSUBy |VMSUBz |VMSUBw | +010 |VMAXx |VMAXy |VMAXz |VMAXw |VMINIx |VMINIy |VMINIz |VMINIw | +011 |VMULx |VMULy |VMULz |VMULw |VMULq |VMAXi |VMULi |VMINIi | +100 |VADDq |VMADDq |VADDi |VMADDi |VSUBq |VMSUBq |VSUbi |VMSUBi | +101 |VADD |VMADD |VMUL |VMAX |VSUB |VMSUB |VOPMSUB|VMINI | +110 |VIADD |VISUB |VIADDI | --- |VIAND |VIOR | --- | --- | +111 |VCALLMS|CALLMSR| --- | --- | *1 | *1 | *1 | *1 | + hi |-------|-------|-------|-------|-------|-------|-------|-------| + *1=see special2 table +*/ +void (*COP2SPECIAL1PrintTable[64])( string& output ) = +{ + P_VADDx, P_VADDy, P_VADDz, P_VADDw, P_VSUBx, P_VSUBy, P_VSUBz, P_VSUBw, + P_VMADDx, P_VMADDy, P_VMADDz, P_VMADDw, P_VMSUBx, P_VMSUBy, P_VMSUBz, P_VMSUBw, + P_VMAXx, P_VMAXy, P_VMAXz, P_VMAXw, P_VMINIx, P_VMINIy, P_VMINIz, P_VMINIw, + P_VMULx, P_VMULy, P_VMULz, P_VMULw, P_VMULq, P_VMAXi, P_VMULi, P_VMINIi, + P_VADDq, P_VMADDq, P_VADDi, P_VMADDi, P_VSUBq, P_VMSUBq, P_VSUbi, P_VMSUBi, + P_VADD, P_VMADD, P_VMUL, P_VMAX, P_VSUB, P_VMSUB, P_VOPMSUB, P_VMINI, + P_VIADD, P_VISUB, P_VIADDI, P_COP2_Unknown,P_VIAND, P_VIOR, P_COP2_Unknown, P_COP2_Unknown, + P_VCALLMS, P_CALLMSR, P_COP2_Unknown,P_COP2_Unknown,P_COP2_SPECIAL2,P_COP2_SPECIAL2,P_COP2_SPECIAL2,P_COP2_SPECIAL2, + +}; +/* + Special2 table : instructions encode by function field when opcode=COp2 & rs field=Special2 + + 31---------26---------------------------------------------------0 + | =COP2 | rs=Special2 | + ------6---------------------------------------------------------- + |--000--|--001--|--010--|--011--|--100--|--101--|--110--|--111--| lo +0000 |VADDAx |VADDAy |VADDAz |VADDAw |VSUBAx |VSUBAy |VSUBAz |VSUBAw | +0001 |VMADDAx|VMADDAy|VMADDAz|VMADDAw|VMSUBAx|VMSUBAy|VMSUBAz|VMSUBAw| +0010 |VITOF0 |VITOF4 |VITOF12|VITOF15|VFTOI0 |VFTOI4 |VFTOI12|VFTOI15| +0011 |VMULAx |VMULAy |VMULAz |VMULAw |VMULAq |VABS |VMULAi |VCLIPw | +0100 |VADDAq |VMADDAq|VADDAi |VMADDAi|VSUBAq |VMSUBAq|VSUBAi |VMSUBAi| +0101 |VADDA |VMADDA |VMULA | --- |VSUBA |VMSUBA |VOPMULA|VNOP | +0110 |VMONE |VMR32 | --- | --- |VLQI |VSQI |VLQD |VSQD | +0111 |VDIV |VSQRT |VRSQRT |VWAITQ |VMTIR |VMFIR |VILWR |VISWR | +1000 |VRNEXT |VRGET |VRINIT |VRXOR | --- | --- | --- | --- | +1001 | --- | --- | --- | --- | --- | --- | --- | --- | +1010 | --- | --- | --- | --- | --- | --- | --- | --- | +1011 | --- | --- | --- | --- | --- | --- | --- | --- | +1100 | --- | --- | --- | --- | --- | --- | --- | --- | +1101 | --- | --- | --- | --- | --- | --- | --- | --- | +1110 | --- | --- | --- | --- | --- | --- | --- | --- | +1111 | --- | --- | --- | --- | --- | --- | --- | --- | + hi |-------|-------|-------|-------|-------|-------|-------|-------| +*/ +void (*COP2SPECIAL2PrintTable[128])( string& output ) = +{ + P_VADDAx ,P_VADDAy ,P_VADDAz ,P_VADDAw ,P_VSUBAx ,P_VSUBAy ,P_VSUBAz ,P_VSUBAw, + P_VMADDAx ,P_VMADDAy ,P_VMADDAz ,P_VMADDAw ,P_VMSUBAx ,P_VMSUBAy ,P_VMSUBAz ,P_VMSUBAw, + P_VITOF0 ,P_VITOF4 ,P_VITOF12 ,P_VITOF15 ,P_VFTOI0 ,P_VFTOI4 ,P_VFTOI12 ,P_VFTOI15, + P_VMULAx ,P_VMULAy ,P_VMULAz ,P_VMULAw ,P_VMULAq ,P_VABS ,P_VMULAi ,P_VCLIPw, + P_VADDAq ,P_VMADDAq ,P_VADDAi ,P_VMADDAi ,P_VSUBAq ,P_VMSUBAq ,P_VSUBAi ,P_VMSUBAi, + P_VADDA ,P_VMADDA ,P_VMULA ,P_COP2_Unknown,P_VSUBA ,P_VMSUBA ,P_VOPMULA ,P_VNOP, + P_VMONE ,P_VMR32 ,P_COP2_Unknown,P_COP2_Unknown,P_VLQI ,P_VSQI ,P_VLQD ,P_VSQD, + P_VDIV ,P_VSQRT ,P_VRSQRT ,P_VWAITQ ,P_VMTIR ,P_VMFIR ,P_VILWR ,P_VISWR, + P_VRNEXT ,P_VRGET ,P_VRINIT ,P_VRXOR ,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown, + P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown, + P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown, + P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown, + P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown, + P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown, + P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown, + P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown,P_COP2_Unknown, +}; + +//**************************TABLES CALLS*********************** + + +void disR5900Fasm(string& output, u32 code, u32 pc) +{ + string dbuf; + char obuf[48]; + + const u32 scode = cpuRegs.code; + opcode_addr = pc; + cpuRegs.code = code; + + sprintf(obuf, "%08X:\t", pc ); + output.assign( obuf ); + GetCurrentInstruction().disasm( output ); + + cpuRegs.code = scode; +} + +//************************************************************* +//************************COP2********************************** +void P_COP2_BC2( string& output ) +{ + COP2BC2PrintTable[DECODE_C2BC]( output ); +} +void P_COP2_SPECIAL( string& output ) +{ + COP2SPECIAL1PrintTable[DECODE_FUNCTION ]( output ); +} +void P_COP2_SPECIAL2( string& output ) +{ + COP2SPECIAL2PrintTable[(cpuRegs.code & 0x3) | ((cpuRegs.code >> 4) & 0x7c)]( output ); +} + +//**************************UNKNOWN**************************** +void P_COP2_Unknown( string& output ) +{ + output += "COP2 ??"; +} + + +//************************************************************* + +//*****************SOME DECODE STUFF*************************** + +void label_decode( string& output, u32 addr ) +{ + string buf; + ssprintf(buf, "0x%08X", addr); + const char* label = disR5900GetSym( addr ); + + if( label != NULL ) + { + output += label; + output += ' '; + } + + output += buf; +} + +void jump_decode( string& output ) +{ + label_decode( output, DECODE_JUMP ); +} + +void offset_decode( string& output ) +{ + label_decode( output, DECODE_OFFSET ); +} + +//*********************END OF DECODE ROUTINES****************** + +namespace OpcodeDisasm +{ + +void COP2( string& output ) +{ + COP2PrintTable[DECODE_RS]( output ); +} + +// Unkown Opcode! +void Unknown( string& output ) +{ + output += "?????"; +} + +void MMI_Unknown( string& output ) +{ + output += "MMI ??"; +} + +void COP0_Unknown( string& output ) +{ + output += "COP0 ??"; +} + +void COP1_Unknown( string& output ) +{ + output += "FPU ??"; +} + +// sap! it stands for string append. It's not a friendly name but for now it makes +// the copy-paste marathon of code below more readable! +#define _sap( str ) ssappendf( output, str, + +//********************* Standard Opcodes*********************** +void J( string& output ) { output += "j\t"; jump_decode(output);} +void JAL( string& output ) { output += "jal\t"; jump_decode(output);} +void BEQ( string& output ) { _sap("beq\t%s, %s, ") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); offset_decode(output); } +void BNE( string& output ) { _sap("bne\t%s, %s, ") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); offset_decode(output); } +void BLEZ( string& output ) { _sap("blez\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void BGTZ( string& output ) { _sap("bgtz\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void ADDI( string& output ) { _sap("addi\t%s, %s, 0x%04X") GPR_REG[DECODE_RT], GPR_REG[DECODE_RS], DECODE_IMMED);} +void ADDIU( string& output ) { _sap("addiu\t%s, %s, 0x%04X") GPR_REG[DECODE_RT], GPR_REG[DECODE_RS], DECODE_IMMED);} +void SLTI( string& output ) { _sap("slti\t%s, %s, 0x%04X") GPR_REG[DECODE_RT], GPR_REG[DECODE_RS], DECODE_IMMED); } +void SLTIU( string& output ) { _sap("sltiu\t%s, %s, 0x%04X") GPR_REG[DECODE_RT], GPR_REG[DECODE_RS], DECODE_IMMED); } +void ANDI( string& output ) { _sap("andi\t%s, %s, 0x%04X") GPR_REG[DECODE_RT], GPR_REG[DECODE_RS], DECODE_IMMED);} +void ORI( string& output ) { _sap("ori\t%s, %s, 0x%04X") GPR_REG[DECODE_RT], GPR_REG[DECODE_RS], DECODE_IMMED); } +void XORI( string& output ) { _sap("xori\t%s, %s, 0x%04X") GPR_REG[DECODE_RT], GPR_REG[DECODE_RS], DECODE_IMMED); } +void LUI( string& output ) { _sap("lui\t%s, 0x%04X") GPR_REG[DECODE_RT], DECODE_IMMED); } +void BEQL( string& output ) { _sap("beql\t%s, %s, ") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); offset_decode(output); } +void BNEL( string& output ) { _sap("bnel\t%s, %s, ") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); offset_decode(output); } +void BLEZL( string& output ) { _sap("blezl\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void BGTZL( string& output ) { _sap("bgtzl\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void DADDI( string& output ) { _sap("daddi\t%s, %s, 0x%04X") GPR_REG[DECODE_RT], GPR_REG[DECODE_RS], DECODE_IMMED); } +void DADDIU( string& output ) { _sap("daddiu\t%s, %s, 0x%04X") GPR_REG[DECODE_RT], GPR_REG[DECODE_RS], DECODE_IMMED); } +void LDL( string& output ) { _sap("ldl\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LDR( string& output ) { _sap("ldr\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LB( string& output ) { _sap("lb\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LH( string& output ) { _sap("lh\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LWL( string& output ) { _sap("lwl\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LW( string& output ) { _sap("lw\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LBU( string& output ) { _sap("lbu\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LHU( string& output ) { _sap("lhu\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LWR( string& output ) { _sap("lwr\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LWU( string& output ) { _sap("lwu\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SB( string& output ) { _sap("sb\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SH( string& output ) { _sap("sh\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SWL( string& output ) { _sap("swl\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SW( string& output ) { _sap("sw\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SDL( string& output ) { _sap("sdl\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SDR( string& output ) { _sap("sdr\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SWR( string& output ) { _sap("swr\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LD( string& output ) { _sap("ld\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SD( string& output ) { _sap("sd\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LQ( string& output ) { _sap("lq\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SQ( string& output ) { _sap("sq\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SWC1( string& output ) { _sap("swc1\t%s, 0x%04X(%s)") COP1_REG_FP[DECODE_FT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void SQC2( string& output ) { _sap("sqc2\t%s, 0x%04X(%s)") COP2_REG_FP[DECODE_FT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void PREF( string& output ) { output += "pref ---"; /*_sap("PREF\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[RS]); */} +void LWC1( string& output ) { _sap("lwc1\t%s, 0x%04X(%s)") COP1_REG_FP[DECODE_FT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +void LQC2( string& output ) { _sap("lqc2\t%s, 0x%04X(%s)") COP2_REG_FP[DECODE_FT], DECODE_IMMED, GPR_REG[DECODE_RS]); } +//********************END OF STANDARD OPCODES************************* + +void SLL( string& output ) +{ + if (cpuRegs.code == 0x00000000) + output += "nop"; + else + _sap("sll\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); +} + +void SRL( string& output ) { _sap("srl\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); } +void SRA( string& output ) { _sap("sra\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); } +void SLLV( string& output ) { _sap("sllv\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]); } +void SRLV( string& output ) { _sap("srlv\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]);} +void SRAV( string& output ) { _sap("srav\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]); } +void JR( string& output ) { _sap("jr\t%s") GPR_REG[DECODE_RS]); } + +void JALR( string& output ) +{ + int rd = DECODE_RD; + + if (rd == 31) + _sap("jalr\t%s") GPR_REG[DECODE_RS]); + else + _sap("jalr\t%s, %s") GPR_REG[rd], GPR_REG[DECODE_RS]); +} + + +void SYNC( string& output ) { output += "SYNC"; } +void MFHI( string& output ) { _sap("mfhi\t%s") GPR_REG[DECODE_RD]); } +void MTHI( string& output ) { _sap("mthi\t%s") GPR_REG[DECODE_RS]); } +void MFLO( string& output ) { _sap("mflo\t%s") GPR_REG[DECODE_RD]); } +void MTLO( string& output ) { _sap("mtlo\t%s") GPR_REG[DECODE_RS]); } +void DSLLV( string& output ) { _sap("dsllv\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]); } +void DSRLV( string& output ) { _sap("dsrlv\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]); } +void DSRAV( string& output ) { _sap("dsrav\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]); } +void MULT( string& output ) { _sap("mult\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]);} +void MULTU( string& output ) { _sap("multu\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]);} +void DIV( string& output ) { _sap("div\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void DIVU( string& output ) { _sap("divu\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void ADD( string& output ) { _sap("add\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void ADDU( string& output ) { _sap("addu\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void SUB( string& output ) { _sap("sub\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void SUBU( string& output ) { _sap("subu\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void AND( string& output ) { _sap("and\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void OR( string& output ) { _sap("or\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void XOR( string& output ) { _sap("xor\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void NOR( string& output ) { _sap("nor\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void SLT( string& output ) { _sap("slt\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void SLTU( string& output ) { _sap("sltu\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void DADD( string& output ) { _sap("dadd\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void DADDU( string& output ) { _sap("daddu\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void DSUB( string& output ) { _sap("dsub\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void DSUBU( string& output ) { _sap("dsubu\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void TGE( string& output ) { _sap("tge\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void TGEU( string& output ) { _sap("tgeu\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void TLT( string& output ) { _sap("tlt\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void TLTU( string& output ) { _sap("tltu\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void TEQ( string& output ) { _sap("teq\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void TNE( string& output ) { _sap("tne\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void DSLL( string& output ) { _sap("dsll\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); } +void DSRL( string& output ) { _sap("dsrl\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); } +void DSRA( string& output ) { _sap("dsra\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); } +void DSLL32( string& output ) { _sap("dsll32\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); } +void DSRL32( string& output ) { _sap("dsrl32\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); } +void DSRA32( string& output ) { _sap("dsra32\t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); } +void MOVZ( string& output ) { _sap("movz\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void MOVN( string& output ) { _sap("movn\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void MFSA( string& output ) { _sap("mfsa\t%s") GPR_REG[DECODE_RD]);} +void MTSA( string& output ) { _sap("mtsa\t%s") GPR_REG[DECODE_RS]);} +//*** unsupport (yet) cpu opcodes +void SYSCALL( string& output ) { output +="syscall ---";/*_sap("syscall\t0x%05X") DECODE_SYSCALL);*/} +void BREAK( string& output ) { output += "break ---";/*_sap("break\t0x%05X") DECODE_BREAK); */} +void CACHE( string& output ) { output += "cache ---";/*_sap("cache\t%s, 0x%04X(%s)") GPR_REG[DECODE_RT], DECODE_IMMED, GPR_REG[DECODE_RS]); */} +//************************REGIMM OPCODES*************************** +void BLTZ( string& output ) { _sap("bltz\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void BGEZ( string& output ) { _sap("bgez\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void BLTZL( string& output ) { _sap("bltzl\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void BGEZL( string& output ) { _sap("bgezl\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void TGEI( string& output ) { _sap("tgei\t%s, 0x%04X") GPR_REG[DECODE_RS], DECODE_IMMED); } +void TGEIU( string& output ) { _sap("tgeiu\t%s,0x%04X") GPR_REG[DECODE_RS], DECODE_IMMED); } +void TLTI( string& output ) { _sap("tlti\t%s, 0x%04X") GPR_REG[DECODE_RS], DECODE_IMMED); } +void TLTIU( string& output ) { _sap("tltiu\t%s,0x%04X") GPR_REG[DECODE_RS], DECODE_IMMED); } +void TEQI( string& output ) { _sap("teqi\t%s, 0x%04X") GPR_REG[DECODE_RS], DECODE_IMMED); } +void TNEI( string& output ) { _sap("tnei\t%s, 0x%04X") GPR_REG[DECODE_RS], DECODE_IMMED); } +void BLTZAL( string& output ) { _sap("bltzal\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void BGEZAL( string& output ) { _sap("bgezal\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void BLTZALL( string& output ) { _sap("bltzall\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void BGEZALL( string& output ) { _sap("bgezall\t%s, ") GPR_REG[DECODE_RS]); offset_decode(output); } +void MTSAB( string& output ) { _sap("mtsab\t%s, 0x%04X") GPR_REG[DECODE_RS], DECODE_IMMED);} +void MTSAH( string& output ) { _sap("mtsah\t%s, 0x%04X") GPR_REG[DECODE_RS], DECODE_IMMED);} + + +//***************************SPECIAL 2 CPU OPCODES******************* +const char* pmfhl_sub[] = { "lw", "uw", "slw", "lh", "sh" }; + +void MADD( string& output ) { _sap("madd\t%s, %s %s") GPR_REG[DECODE_RD],GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void MADDU( string& output ) { _sap("maddu\t%s, %s %s") GPR_REG[DECODE_RD],GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]);} +void PLZCW( string& output ) { _sap("plzcw\t%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS]); } +void MADD1( string& output ) { _sap("madd1\t%s, %s %s") GPR_REG[DECODE_RD],GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void MADDU1( string& output ) { _sap("maddu1\t%s, %s %s") GPR_REG[DECODE_RD],GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void MFHI1( string& output ) { _sap("mfhi1\t%s") GPR_REG[DECODE_RD]); } +void MTHI1( string& output ) { _sap("mthi1\t%s") GPR_REG[DECODE_RS]); } +void MFLO1( string& output ) { _sap("mflo1\t%s") GPR_REG[DECODE_RD]); } +void MTLO1( string& output ) { _sap("mtlo1\t%s") GPR_REG[DECODE_RS]); } +void MULT1( string& output ) { _sap("mult1\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void MULTU1( string& output ) { _sap("multu1\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]);} +void DIV1( string& output ) { _sap("div1\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void DIVU1( string& output ) { _sap("divu1\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +//that have parametres that i haven't figure out how to display... +void PMFHL( string& output ) { _sap("pmfhl.%s \t%s") pmfhl_sub[DECODE_SA], GPR_REG[DECODE_RD]); } +void PMTHL( string& output ) { _sap("pmthl.%s \t%s") pmfhl_sub[DECODE_SA], GPR_REG[DECODE_RS]); } +void PSLLH( string& output ) { _sap("psllh \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); } +void PSRLH( string& output ) { _sap("psrlh \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);} +void PSRAH( string& output ) { _sap("psrah \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);} +void PSLLW( string& output ) { _sap( "psllw \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);} +void PSRLW( string& output ) { _sap( "psrlw \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);} +void PSRAW( string& output ) { _sap( "psraw \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);} +//***************************END OF SPECIAL OPCODES****************** +//*************************MMI0 OPCODES************************ + +void PADDW( string& output ){ _sap( "paddw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSUBW( string& output ){ _sap( "psubw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PCGTW( string& output ){ _sap( "pcgtw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PMAXW( string& output ){ _sap( "pmaxw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PADDH( string& output ){ _sap( "paddh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSUBH( string& output ){ _sap( "psubh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PCGTH( string& output ){ _sap( "pcgth\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PMAXH( string& output ){ _sap( "pmaxh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PADDB( string& output ){ _sap( "paddb\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSUBB( string& output ){ _sap( "psubb\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PCGTB( string& output ){ _sap( "pcgtb\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PADDSW( string& output ){ _sap( "paddsw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSUBSW( string& output ){ _sap( "psubsw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PEXTLW( string& output ){ _sap( "pextlw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PPACW( string& output ) { _sap( "ppacw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PADDSH( string& output ){ _sap( "paddsh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSUBSH( string& output ){ _sap( "psubsh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PEXTLH( string& output ){ _sap( "pextlh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PPACH( string& output ) { _sap( "ppach\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PADDSB( string& output ){ _sap( "paddsb\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSUBSB( string& output ){ _sap( "psubsb\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PEXTLB( string& output ){ _sap( "pextlb\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PPACB( string& output ) { _sap( "ppacb\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PEXT5( string& output ) { _sap( "pext5\t%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]); } +void PPAC5( string& output ) { _sap( "ppac5\t%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]); } +//**********END OF MMI0 OPCODES********************************* +//**********MMI1 OPCODES************************************** +void PABSW( string& output ){ _sap( "pabsw%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]); } +void PCEQW( string& output ){ _sap( "pceqw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PMINW( string& output ){ _sap( "pminw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PADSBH( string& output ){ _sap( "padsbh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PABSH( string& output ){ _sap( "pabsh%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]); } +void PCEQH( string& output ){ _sap( "pceqh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PMINH( string& output ){ _sap( "pminh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PCEQB( string& output ){ _sap( "pceqb\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PADDUW( string& output ){ _sap( "padduw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSUBUW( string& output ){ _sap( "psubuw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PEXTUW( string& output ){ _sap( "pextuw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PADDUH( string& output ){ _sap( "padduh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSUBUH( string& output ){ _sap( "psubuh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PEXTUH( string& output ){ _sap( "pextuh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PADDUB( string& output ){ _sap( "paddub\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSUBUB( string& output ){ _sap( "psubub\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PEXTUB( string& output ){ _sap( "pextub\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void QFSRV( string& output ) { _sap( "qfsrv\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +//********END OF MMI1 OPCODES*********************************** +//*********MMI2 OPCODES*************************************** +void PMADDW( string& output ){ _sap( "pmaddw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSLLVW( string& output ){ _sap( "psllvw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PSRLVW( string& output ){ _sap( "psrlvw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PMSUBW( string& output ){ _sap( "msubw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PMFHI( string& output ){ _sap( "pmfhi\t%s") GPR_REG[DECODE_RD]); } +void PMFLO( string& output ){ _sap( "pmflo\t%s") GPR_REG[DECODE_RD]); } +void PINTH( string& output ){ _sap( "pinth\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PMULTW( string& output ){ _sap( "pmultw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PDIVW( string& output ){ _sap( "pdivw\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PCPYLD( string& output ){ _sap( "pcpyld\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PMADDH( string& output ){ _sap( "pmaddh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PHMADH( string& output ){ _sap( "phmadh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PAND( string& output ){ _sap( "pand\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PXOR( string& output ){ _sap( "pxor\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PMSUBH( string& output ){ _sap( "pmsubh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PHMSBH( string& output ){ _sap( "phmsbh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PEXEH( string& output ){ _sap( "pexeh\t%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]); } +void PREVH( string& output ){ _sap( "prevh\t%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]); } +void PMULTH( string& output ){ _sap( "pmulth\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PDIVBW( string& output ){ _sap( "pdivbw\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PEXEW( string& output ){ _sap( "pexew\t%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]); } +void PROT3W( string& output ){ _sap( "prot3w\t%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]); } +//*****END OF MMI2 OPCODES*********************************** +//*************************MMI3 OPCODES************************ +void PMADDUW( string& output ){ _sap("pmadduw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]); } +void PSRAVW( string& output ){ _sap("psravw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], GPR_REG[DECODE_RS]); } +void PMTHI( string& output ){ _sap("pmthi\t%s") GPR_REG[DECODE_RS]); } +void PMTLO( string& output ){ _sap("pmtlo\t%s") GPR_REG[DECODE_RS]); } +void PINTEH( string& output ){ _sap("pinteh\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PMULTUW( string& output ){ _sap("pmultuw\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PDIVUW( string& output ){ _sap("pdivuw\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PCPYUD( string& output ){ _sap("pcpyud\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void POR( string& output ){ _sap("por\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PNOR( string& output ){ _sap("pnor\t%s, %s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); } +void PEXCH( string& output ){ _sap("pexch\t%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]);} +void PCPYH( string& output ){ _sap("pcpyh\t%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]);} +void PEXCW( string& output ){ _sap("pexcw\t%s, %s") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT]);} +//**********************END OF MMI3 OPCODES******************** + +//**************************************************************************** +//** COP0 ** +//**************************************************************************** +void MFC0( string& output ){ _sap("mfc0\t%s, %s") GPR_REG[DECODE_RT], COP0_REG[DECODE_FS]); } +void MTC0( string& output ){ _sap("mtc0\t%s, %s") GPR_REG[DECODE_RT], COP0_REG[DECODE_FS]); } +void BC0F( string& output ){ output += "bc0f\t"; offset_decode(output); } +void BC0T( string& output ){ output += "bc0t\t"; offset_decode(output); } +void BC0FL( string& output ){ output += "bc0fl\t"; offset_decode(output); } +void BC0TL( string& output ){ output += "bc0tl\t"; offset_decode(output); } +void TLBR( string& output ){ output += "tlbr";} +void TLBWI( string& output ){ output += "tlbwi";} +void TLBWR( string& output ){ output += "tlbwr";} +void TLBP( string& output ){ output += "tlbp";} +void ERET( string& output ){ output += "eret";} +void DI( string& output ){ output += "di";} +void EI( string& output ){ output += "ei";} +//**************************************************************************** +//** END OF COP0 ** +//**************************************************************************** +//**************************************************************************** +//** COP1 - Floating Point Unit (FPU) ** +//**************************************************************************** +void MFC1( string& output ){ _sap("mfc1\t%s, %s") GPR_REG[DECODE_RT], COP1_REG_FP[DECODE_FS]); } +void CFC1( string& output ){ _sap("cfc1\t%s, %s") GPR_REG[DECODE_RT], COP1_REG_FCR[DECODE_FS]); } +void MTC1( string& output ){ _sap("mtc1\t%s, %s") GPR_REG[DECODE_RT], COP1_REG_FP[DECODE_FS]); } +void CTC1( string& output ){ _sap("ctc1\t%s, %s") GPR_REG[DECODE_RT], COP1_REG_FCR[DECODE_FS]); } +void BC1F( string& output ){ output += "bc1f\t"; offset_decode(output); } +void BC1T( string& output ){ output += "bc1t\t"; offset_decode(output); } +void BC1FL( string& output ){ output += "bc1fl\t"; offset_decode(output); } +void BC1TL( string& output ){ output += "bc1tl\t"; offset_decode(output); } +void ADD_S( string& output ){ _sap("add.s\t%s, %s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]);} +void SUB_S( string& output ){ _sap("sub.s\t%s, %s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]);} +void MUL_S( string& output ){ _sap("mul.s\t%s, %s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]);} +void DIV_S( string& output ){ _sap("div.s\t%s, %s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void SQRT_S( string& output ){ _sap("sqrt.s\t%s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FT]); } +void ABS_S( string& output ){ _sap("abs.s\t%s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS]); } +void MOV_S( string& output ){ _sap("mov.s\t%s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS]); } +void NEG_S( string& output ){ _sap("neg.s\t%s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS]);} +void RSQRT_S( string& output ){_sap("rsqrt.s\t%s, %s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]);} +void ADDA_S( string& output ){ _sap("adda.s\t%s, %s") COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void SUBA_S( string& output ){ _sap("suba.s\t%s, %s") COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void MULA_S( string& output ){ _sap("mula.s\t%s, %s") COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void MADD_S( string& output ){ _sap("madd.s\t%s, %s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void MSUB_S( string& output ){ _sap("msub.s\t%s, %s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void MADDA_S( string& output ){_sap("madda.s\t%s, %s") COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void MSUBA_S( string& output ){_sap("msuba.s\t%s, %s") COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void CVT_W( string& output ){ _sap("cvt.w.s\t%s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS]); } +void MAX_S( string& output ){ _sap("max.s\t%s, %s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]);} +void MIN_S( string& output ){ _sap("min.s\t%s, %s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]);} +void C_F( string& output ){ _sap("c.f.s\t%s, %s") COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void C_EQ( string& output ){ _sap("c.eq.s\t%s, %s") COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void C_LT( string& output ){ _sap("c.lt.s\t%s, %s") COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void C_LE( string& output ){ _sap("c.le.s\t%s, %s") COP1_REG_FP[DECODE_FS], COP1_REG_FP[DECODE_FT]); } +void CVT_S( string& output ){ _sap("cvt.s.w\t%s, %s") COP1_REG_FP[DECODE_FD], COP1_REG_FP[DECODE_FS]); } +//**************************************************************************** +//** END OF COP1 ** +//**************************************************************************** + +} // End namespace R5900::OpcodeDisasm + +//**************************************************************************** +//** COP2 - (VU0) ** +//**************************************************************************** +void P_QMFC2( string& output ){ _sap("qmfc2\t%s, %s") GPR_REG[DECODE_RT], COP2_REG_FP[DECODE_FS]); } +void P_CFC2( string& output ){ _sap("cfc2\t%s, %s") GPR_REG[DECODE_RT], COP2_REG_CTL[DECODE_FS]); } +void P_QMTC2( string& output ){ _sap("qmtc2\t%s, %s") GPR_REG[DECODE_RT], COP2_REG_FP[DECODE_FS]); } +void P_CTC2( string& output ){ _sap("ctc2\t%s, %s") GPR_REG[DECODE_RT], COP2_REG_CTL[DECODE_FS]); } +void P_BC2F( string& output ){ output += "bc2f\t"; offset_decode(output); } +void P_BC2T( string& output ){ output += "bc2t\t"; offset_decode(output); } +void P_BC2FL( string& output ){ output += "bc2fl\t"; offset_decode(output); } +void P_BC2TL( string& output ){ output += "bc2tl\t"; offset_decode(output); } +//******************************SPECIAL 1 VUO TABLE**************************************** +#define _X ((cpuRegs.code>>24) & 1) +#define _Y ((cpuRegs.code>>23) & 1) +#define _Z ((cpuRegs.code>>22) & 1) +#define _W ((cpuRegs.code>>21) & 1) + +const char *dest_string(void) +{ + static char str[5]; + int i = 0; + + if(_X) str[i++] = 'x'; + if(_Y) str[i++] = 'y'; + if(_Z) str[i++] = 'z'; + if(_W) str[i++] = 'w'; + str[i++] = 0; + + return (const char *)str; +} + +char dest_fsf() +{ + const char arr[4] = { 'x', 'y', 'z', 'w' }; + return arr[(cpuRegs.code>>21)&3]; +} + +char dest_ftf() +{ + const char arr[4] = { 'x', 'y', 'z', 'w' }; + return arr[(cpuRegs.code>>23)&3]; +} + +void P_VADDx( string& output ){_sap("vaddx.%s %s, %s, %sx") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VADDy( string& output ){_sap("vaddy.%s %s, %s, %sy") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VADDz( string& output ){_sap("vaddz.%s %s, %s, %sz") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VADDw( string& output ){_sap("vaddw.%s %s, %s, %sw") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VSUBx( string& output ){_sap("vsubx.%s %s, %s, %sx") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VSUBy( string& output ){_sap("vsuby.%s %s, %s, %sy") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VSUBz( string& output ){_sap("vsubz.%s %s, %s, %sz") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VSUBw( string& output ){_sap("vsubw.%s %s, %s, %sw") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMADDx( string& output ){_sap("vmaddx.%s %s, %s, %sx") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMADDy( string& output ){_sap("vmaddy.%s %s, %s, %sy") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMADDz( string& output ){_sap("vmaddz.%s %s, %s, %sz") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMADDw( string& output ){_sap("vmaddw.%s %s, %s, %sw") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMSUBx( string& output ){_sap("vmsubx.%s %s, %s, %sx") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMSUBy( string& output ){_sap("vmsuby.%s %s, %s, %sy") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMSUBz( string& output ){_sap("vmsubz.%s %s, %s, %sz") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMSUBw( string& output ){_sap("vmsubw.%s %s, %s, %sw") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMAXx( string& output ){_sap("vmaxx.%s %s, %s, %sx") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMAXy( string& output ){_sap("vmaxy.%s %s, %s, %sy") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMAXz( string& output ){_sap("vmaxz.%s %s, %s, %sz") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMAXw( string& output ){_sap("vmaxw.%s %s, %s, %sw") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMINIx( string& output ){_sap("vminix.%s %s, %s, %sx") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMINIy( string& output ){_sap("vminiy.%s %s, %s, %sy") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); ;} +void P_VMINIz( string& output ){_sap("vminiz.%s %s, %s, %sz") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMINIw( string& output ){_sap("vminiw.%s %s, %s, %sw") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMULx( string& output ){_sap("vmulx.%s %s,%s,%sx") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMULy( string& output ){_sap("vmuly.%s %s,%s,%sy") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMULz( string& output ){_sap("vmulz.%s %s,%s,%sz") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMULw( string& output ){_sap("vmulw.%s %s,%s,%sw") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMULq( string& output ){_sap("vmulq.%s %s,%s,Q") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VMAXi( string& output ){_sap("vmaxi.%s %s,%s,I") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VMULi( string& output ){_sap("vmuli.%s %s,%s,I") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VMINIi( string& output ){_sap("vminii.%s %s,%s,I") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VADDq( string& output ){_sap("vaddq.%s %s,%s,Q") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VMADDq( string& output ){_sap("vmaddq.%s %s,%s,Q") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VADDi( string& output ){_sap("vaddi.%s %s,%s,I") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VMADDi( string& output ){_sap("vmaddi.%s %s,%s,I") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VSUBq( string& output ){_sap("vsubq.%s %s,%s,Q") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VMSUBq( string& output ){_sap("vmsubq.%s %s,%s,Q") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VSUbi( string& output ){_sap("vsubi.%s %s,%s,I") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VMSUBi( string& output ){_sap("vmsubi.%s %s,%s,I") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS]); } +void P_VADD( string& output ){_sap("vadd.%s %s, %s, %s") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMADD( string& output ){_sap("vmadd.%s %s, %s, %s") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMUL( string& output ){_sap("vmul.%s %s, %s, %s") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMAX( string& output ){_sap("vmax.%s %s, %s, %s") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VSUB( string& output ){_sap("vsub.%s %s, %s, %s") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMSUB( string& output ){_sap("vmsub.%s %s, %s, %s") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VOPMSUB( string& output ){_sap("vopmsub.xyz %s, %s, %s") COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMINI( string& output ){_sap("vmini.%s %s, %s, %s") dest_string(),COP2_REG_FP[DECODE_FD], COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VIADD( string& output ){_sap("viadd %s, %s, %s") COP2_REG_CTL[DECODE_SA], COP2_REG_CTL[DECODE_FS], COP2_REG_CTL[DECODE_FT]);} +void P_VISUB( string& output ){_sap("visub %s, %s, %s") COP2_REG_CTL[DECODE_SA], COP2_REG_CTL[DECODE_FS], COP2_REG_CTL[DECODE_FT]);} +void P_VIADDI( string& output ){_sap("viaddi %s, %s, 0x%x") COP2_REG_CTL[DECODE_FT], COP2_REG_CTL[DECODE_FS], DECODE_SA);} +void P_VIAND( string& output ){_sap("viand %s, %s, %s") COP2_REG_CTL[DECODE_SA], COP2_REG_CTL[DECODE_FS], COP2_REG_CTL[DECODE_FT]);} +void P_VIOR( string& output ){_sap("vior %s, %s, %s") COP2_REG_CTL[DECODE_SA], COP2_REG_CTL[DECODE_FS], COP2_REG_CTL[DECODE_FT]);} +void P_VCALLMS( string& output ){output += "vcallms";} +void P_CALLMSR( string& output ){output += "callmsr";} +//***********************************END OF SPECIAL1 VU0 TABLE***************************** +//******************************SPECIAL2 VUO TABLE***************************************** +void P_VADDAx( string& output ){_sap("vaddax.%s ACC,%s,%sx") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VADDAy( string& output ){_sap("vadday.%s ACC,%s,%sy") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VADDAz( string& output ){_sap("vaddaz.%s ACC,%s,%sz") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VADDAw( string& output ){_sap("vaddaw.%s ACC,%s,%sw") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VSUBAx( string& output ){_sap("vsubax.%s ACC,%s,%sx") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VSUBAy( string& output ){_sap("vsubay.%s ACC,%s,%sy") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VSUBAz( string& output ){_sap("vsubaz.%s ACC,%s,%sz") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VSUBAw( string& output ){_sap("vsubaw.%s ACC,%s,%sw") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMADDAx( string& output ){_sap("vmaddax.%s ACC,%s,%sx") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMADDAy( string& output ){_sap("vmadday.%s ACC,%s,%sy") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMADDAz( string& output ){_sap("vmaddaz.%s ACC,%s,%sz") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMADDAw( string& output ){_sap("vmaddaw.%s ACC,%s,%sw") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMSUBAx( string& output ){_sap("vmsubax.%s ACC,%s,%sx") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMSUBAy( string& output ){_sap("vmsubay.%s ACC,%s,%sy") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMSUBAz( string& output ){_sap("vmsubaz.%s ACC,%s,%sz") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMSUBAw( string& output ){_sap("vmsubaw.%s ACC,%s,%sw") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VITOF0( string& output ){_sap("vitof0.%s %s, %s") dest_string(),COP2_REG_FP[DECODE_FT], COP2_REG_FP[DECODE_FS]); } +void P_VITOF4( string& output ){_sap("vitof4.%s %s, %s") dest_string(),COP2_REG_FP[DECODE_FT], COP2_REG_FP[DECODE_FS]); } +void P_VITOF12( string& output ){_sap("vitof12.%s %s, %s") dest_string(),COP2_REG_FP[DECODE_FT], COP2_REG_FP[DECODE_FS]); } +void P_VITOF15( string& output ){_sap("vitof15.%s %s, %s") dest_string(),COP2_REG_FP[DECODE_FT], COP2_REG_FP[DECODE_FS]); } +void P_VFTOI0( string& output ) {_sap("vftoi0.%s %s, %s") dest_string(),COP2_REG_FP[DECODE_FT], COP2_REG_FP[DECODE_FS]); } +void P_VFTOI4( string& output ) {_sap("vftoi4.%s %s, %s") dest_string(),COP2_REG_FP[DECODE_FT], COP2_REG_FP[DECODE_FS]); } +void P_VFTOI12( string& output ){_sap("vftoi12.%s %s, %s") dest_string(),COP2_REG_FP[DECODE_FT], COP2_REG_FP[DECODE_FS]); } +void P_VFTOI15( string& output ){_sap("vftoi15.%s %s, %s") dest_string(),COP2_REG_FP[DECODE_FT], COP2_REG_FP[DECODE_FS]); } +void P_VMULAx( string& output ){_sap("vmulax.%s ACC,%s,%sx") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMULAy( string& output ){_sap("vmulay.%s ACC,%s,%sy") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMULAz( string& output ){_sap("vmulaz.%s ACC,%s,%sz") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMULAw( string& output ){_sap("vmulaw.%s ACC,%s,%sw") dest_string(),COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]);} +void P_VMULAq( string& output ){_sap("vmulaq.%s ACC %s, Q") dest_string(), COP2_REG_FP[DECODE_FS]); } +void P_VABS( string& output ){_sap("vabs.%s %s, %s") dest_string(),COP2_REG_FP[DECODE_FT], COP2_REG_FP[DECODE_FS]);} +void P_VMULAi( string& output ){_sap("vmulaq.%s ACC %s, I") dest_string(), COP2_REG_FP[DECODE_FS]); } +void P_VCLIPw( string& output ){_sap("vclip %sxyz, %sw") COP2_REG_FP[DECODE_FS], COP2_REG_FP[DECODE_FT]);} +void P_VADDAq( string& output ){_sap("vaddaq.%s ACC %s, Q") dest_string(), COP2_REG_FP[DECODE_FS]); } +void P_VMADDAq( string& output ){_sap("vmaddaq.%s ACC %s, Q") dest_string(), COP2_REG_FP[DECODE_FS]); } +void P_VADDAi( string& output ){_sap("vaddai.%s ACC %s, I") dest_string(), COP2_REG_FP[DECODE_FS]); } +void P_VMADDAi( string& output ){_sap("vmaddai.%s ACC %s, Q") dest_string(), COP2_REG_FP[DECODE_FS]); } +void P_VSUBAq( string& output ){_sap("vsubaq.%s ACC %s, Q") dest_string(), COP2_REG_FP[DECODE_FS]); } +void P_VMSUBAq( string& output ){_sap("vmsubaq.%s ACC %s, Q") dest_string(), COP2_REG_FP[DECODE_FS]); } +void P_VSUBAi( string& output ){_sap("vsubai.%s ACC %s, I") dest_string(), COP2_REG_FP[DECODE_FS]); } +void P_VMSUBAi( string& output ){_sap("vmsubai.%s ACC %s, I") dest_string(), COP2_REG_FP[DECODE_FS]); } +void P_VADDA( string& output ){_sap("vadda.%s ACC %s, %s") dest_string(), COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMADDA( string& output ){_sap("vmadda.%s ACC %s, %s") dest_string(), COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMULA( string& output ){_sap("vmula.%s ACC %s, %s") dest_string(), COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VSUBA( string& output ){_sap("vsuba.%s ACC %s, %s") dest_string(), COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VMSUBA( string& output ){_sap("vmsuba.%s ACC %s, %s") dest_string(), COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VOPMULA( string& output ){_sap("vopmula.xyz %sxyz, %sxyz") COP2_REG_FP[DECODE_FS],COP2_REG_FP[DECODE_FT]); } +void P_VNOP( string& output ){output += "vnop";} +void P_VMONE( string& output ){_sap("vmove.%s, %s, %s") dest_string(), COP2_REG_FP[DECODE_FT],COP2_REG_FP[DECODE_FS]); } +void P_VMR32( string& output ){_sap("vmr32.%s, %s, %s") dest_string(), COP2_REG_FP[DECODE_FT],COP2_REG_FP[DECODE_FS]); } +void P_VLQI( string& output ){_sap("vlqi %s%s, (%s++)") COP2_REG_FP[DECODE_FT], dest_string(), COP2_REG_CTL[DECODE_FS]);} +void P_VSQI( string& output ){_sap("vsqi %s%s, (%s++)") COP2_REG_FP[DECODE_FS], dest_string(), COP2_REG_CTL[DECODE_FT]);} +void P_VLQD( string& output ){_sap("vlqd %s%s, (--%s)") COP2_REG_FP[DECODE_FT], dest_string(), COP2_REG_CTL[DECODE_FS]);} +void P_VSQD( string& output ){_sap("vsqd %s%s, (--%s)") COP2_REG_FP[DECODE_FS], dest_string(), COP2_REG_CTL[DECODE_FT]);} +void P_VDIV( string& output ){_sap("vdiv Q, %s%c, %s%c") COP2_REG_FP[DECODE_FS], dest_fsf(), COP2_REG_FP[DECODE_FT], dest_ftf());} +void P_VSQRT( string& output ){_sap("vsqrt Q, %s%c") COP2_REG_FP[DECODE_FT], dest_ftf());} +void P_VRSQRT( string& output ){_sap("vrsqrt Q, %s%c, %s%c") COP2_REG_FP[DECODE_FS], dest_fsf(), COP2_REG_FP[DECODE_FT], dest_ftf());} +void P_VWAITQ( string& output ){output += "vwaitq";} +void P_VMTIR( string& output ){_sap("vmtir %s, %s%c") COP2_REG_CTL[DECODE_FT], COP2_REG_FP[DECODE_FS], dest_fsf());} +void P_VMFIR( string& output ){_sap("vmfir %s%c, %s") COP2_REG_FP[DECODE_FT], dest_string(), COP2_REG_CTL[DECODE_FS]);} +void P_VILWR( string& output ){_sap("vilwr %s, (%s)%s") COP2_REG_CTL[DECODE_FT], COP2_REG_CTL[DECODE_FS], dest_string());} +void P_VISWR( string& output ){_sap("viswr %s, (%s)%s") COP2_REG_CTL[DECODE_FT], COP2_REG_CTL[DECODE_FS], dest_string());} +void P_VRNEXT( string& output ){_sap("vrnext %s%s, R") COP2_REG_CTL[DECODE_FT], dest_string());} +void P_VRGET( string& output ){_sap("vrget %s%s, R") COP2_REG_CTL[DECODE_FT], dest_string());} +void P_VRINIT( string& output ){_sap("vrinit R, %s%s") COP2_REG_CTL[DECODE_FS], dest_string());} +void P_VRXOR( string& output ){_sap("vrxor R, %s%s") COP2_REG_CTL[DECODE_FS], dest_string());} +//************************************END OF SPECIAL2 VUO TABLE**************************** + +} diff --git a/pcsx2/DebugTools/DisVU0Micro.cpp b/pcsx2/DebugTools/DisVU0Micro.cpp new file mode 100644 index 0000000000..d348de6648 --- /dev/null +++ b/pcsx2/DebugTools/DisVU0Micro.cpp @@ -0,0 +1,83 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Debug.h" + +static char ostr[1024]; + +// Type deffinition of our functions +#define DisFInterface (u32 code, u32 pc) +#define DisFInterfaceT (u32, u32) +#define DisFInterfaceN (code, pc) + +typedef char* (*TdisR5900F)DisFInterface; + +// These macros are used to assemble the disassembler functions +#define MakeDisF(fn, b) \ + char* fn DisFInterface { \ + sprintf (ostr, "%8.8x %8.8x:", pc, code); \ + b; /*ostr[(strlen(ostr) - 1)] = 0;*/ return ostr; \ + } + +//Lower/Upper instructions can use that.. +#define _Ft_ ((code >> 16) & 0x1F) // The rt part of the instruction register +#define _Fs_ ((code >> 11) & 0x1F) // The rd part of the instruction register +#define _Fd_ ((code >> 6) & 0x1F) // The sa part of the instruction register + +#define dName(i) sprintf(ostr, "%s %-7s,", ostr, i) +#define dNameU(i) { char op[256]; sprintf(op, "%s.%s%s%s%s", i, _X ? "x" : "", _Y ? "y" : "", _Z ? "z" : "", _W ? "w" : ""); sprintf(ostr, "%s %-7s,", ostr, op); } + + +#define dCP2128f(i) sprintf(ostr, "%s w=%f z=%f y=%f x=%f (%s),", ostr, VU0.VF[i].f.w, VU0.VF[i].f.z, VU0.VF[i].f.y, VU0.VF[i].f.x, disRNameCP2f[i]) +#define dCP232x(i) sprintf(ostr, "%s x=%f (%s),", ostr, VU0.VF[i].f.x, disRNameCP2f[i]) +#define dCP232y(i) sprintf(ostr, "%s y=%f (%s),", ostr, VU0.VF[i].f.y, disRNameCP2f[i]) +#define dCP232z(i) sprintf(ostr, "%s z=%f (%s),", ostr, VU0.VF[i].f.z, disRNameCP2f[i]) +#define dCP232w(i) sprintf(ostr, "%s w=%f (%s),", ostr, VU0.VF[i].f.w, disRNameCP2f[i]) +#define dCP2ACCf() sprintf(ostr, "%s w=%f z=%f y=%f x=%f (ACC),", ostr, VU0.ACC.f.w, VU0.ACC.f.z, VU0.ACC.f.y, VU0.ACC.f.x) +#define dCP232i(i) sprintf(ostr, "%s %8.8x (%s),", ostr, VU0.VI[i].UL, disRNameCP2i[i]) +#define dCP232iF(i) sprintf(ostr, "%s %f (%s),", ostr, VU0.VI[i].F, disRNameCP2i[i]) +#define dCP232f(i, j) sprintf(ostr, "%s Q %s=%f (%s),", ostr, CP2VFnames[j], VU0.VF[i].F[j], disRNameCP2f[i]) +#define dImm5() sprintf(ostr, "%s %d,", ostr, (code >> 6) & 0x1f) +#define dImm11() sprintf(ostr, "%s %d,", ostr, code & 0x7ff) +#define dImm15() sprintf(ostr, "%s %d,", ostr, ( ( code >> 10 ) & 0x7800 ) | ( code & 0x7ff )) + +#define _X ((code>>24) & 0x1) +#define _Y ((code>>23) & 0x1) +#define _Z ((code>>22) & 0x1) +#define _W ((code>>21) & 0x1) + +#define _Fsf_ ((code >> 21) & 0x03) +#define _Ftf_ ((code >> 23) & 0x03) + + +/********************************************************* +* Unknown instruction (would generate an exception) * +* Format: ? * +*********************************************************/ +//extern char* disNULL DisFInterface; +static MakeDisF(disNULL, dName("*** Bad OP ***");) + +#include "DisVUmicro.h" +#include "DisVUops.h" +#include "VU.h" + +_disVUOpcodes(VU0); +_disVUTables(VU0); + diff --git a/pcsx2/DebugTools/DisVU1Micro.cpp b/pcsx2/DebugTools/DisVU1Micro.cpp new file mode 100644 index 0000000000..17721e3aee --- /dev/null +++ b/pcsx2/DebugTools/DisVU1Micro.cpp @@ -0,0 +1,122 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Debug.h" +#include "VUmicro.h" + +static char ostr[1024]; + +// Type deffinition of our functions +#define DisFInterface (u32 code, u32 pc) +#define DisFInterfaceT (u32, u32) +#define DisFInterfaceN (code, pc) + +typedef char* (*TdisR5900F)DisFInterface; + +// These macros are used to assemble the disassembler functions +#define MakeDisF(fn, b) \ + char* fn DisFInterface { \ + if( !CHECK_VU1REC ) sprintf (ostr, "%8.8x %8.8x:", pc, code); \ + else ostr[0] = 0; \ + b; /*ostr[(strlen(ostr) - 1)] = 0;*/ return ostr; \ + } + +//Lower/Upper instructions can use that.. +#define _Ft_ ((code >> 16) & 0x1F) // The rt part of the instruction register +#define _Fs_ ((code >> 11) & 0x1F) // The rd part of the instruction register +#define _Fd_ ((code >> 6) & 0x1F) // The sa part of the instruction register + +#define dName(i) sprintf(ostr, "%s %-12s", ostr, i); \ + +#define dNameU(i) { \ + char op[256]; sprintf(op, "%s.%s%s%s%s", i, _X ? "x" : "", _Y ? "y" : "", _Z ? "z" : "", _W ? "w" : ""); \ + sprintf(ostr, "%s %-12s", ostr, op); \ +} + +#define dCP2128f(i) { \ + if( CHECK_VU1REC ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \ + else sprintf(ostr, "%s w=%f (%8.8x) z=%f (%8.8x) y=%f (%8.8xl) x=%f (%8.8x) (%s),", ostr, VU1.VF[i].f.w, VU1.VF[i].UL[3], VU1.VF[i].f.z, VU1.VF[i].UL[2], VU1.VF[i].f.y, VU1.VF[i].UL[1], VU1.VF[i].f.x, VU1.VF[i].UL[0], disRNameCP2f[i]); \ +} \ + +#define dCP232x(i) { \ + if( CHECK_VU1REC ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \ + else sprintf(ostr, "%s x=%f (%s),", ostr, VU1.VF[i].f.x, disRNameCP2f[i]); \ +} \ + +#define dCP232y(i) { \ + if( CHECK_VU1REC ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \ + else sprintf(ostr, "%s y=%f (%s),", ostr, VU1.VF[i].f.y, disRNameCP2f[i]); \ +} \ + +#define dCP232z(i) { \ + if( CHECK_VU1REC ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \ + else sprintf(ostr, "%s z=%f (%s),", ostr, VU1.VF[i].f.z, disRNameCP2f[i]); \ +} + +#define dCP232w(i) { \ + if( CHECK_VU1REC ) sprintf(ostr, "%s %s,", ostr, disRNameCP2f[i]); \ + else sprintf(ostr, "%s w=%f (%s),", ostr, VU1.VF[i].f.w, disRNameCP2f[i]); \ +} + +#define dCP2ACCf() { \ + if( CHECK_VU1REC ) sprintf(ostr, "%s ACC,", ostr); \ + else sprintf(ostr, "%s w=%f z=%f y=%f x=%f (ACC),", ostr, VU1.ACC.f.w, VU1.ACC.f.z, VU1.ACC.f.y, VU1.ACC.f.x); \ +} \ + +#define dCP232i(i) { \ + if( CHECK_VU1REC ) sprintf(ostr, "%s %s,", ostr, disRNameCP2i[i]); \ + else sprintf(ostr, "%s %8.8x (%s),", ostr, VU1.VI[i].UL, disRNameCP2i[i]); \ +} + +#define dCP232iF(i) { \ + if( CHECK_VU1REC ) sprintf(ostr, "%s %s,", ostr, disRNameCP2i[i]); \ + else sprintf(ostr, "%s %f (%s),", ostr, VU1.VI[i].F, disRNameCP2i[i]); \ +} + +#define dCP232f(i, j) { \ + if( CHECK_VU1REC ) sprintf(ostr, "%s %s%s,", ostr, disRNameCP2f[i], CP2VFnames[j]); \ + else sprintf(ostr, "%s %s=%f (%s),", ostr, CP2VFnames[j], VU1.VF[i].F[j], disRNameCP2f[i]); \ +} + +#define dImm5() sprintf(ostr, "%s %d,", ostr, (s16)((code >> 6) & 0x10 ? 0xfff0 | ((code >> 6) & 0xf) : (code >> 6) & 0xf)) +#define dImm11() sprintf(ostr, "%s %d,", ostr, (s16)(code & 0x400 ? 0xfc00 | (code & 0x3ff) : code & 0x3ff)) +#define dImm15() sprintf(ostr, "%s %d,", ostr, ( ( code >> 10 ) & 0x7800 ) | ( code & 0x7ff )) + +#define _X ((code>>24) & 0x1) +#define _Y ((code>>23) & 0x1) +#define _Z ((code>>22) & 0x1) +#define _W ((code>>21) & 0x1) + +#define _Fsf_ ((code >> 21) & 0x03) +#define _Ftf_ ((code >> 23) & 0x03) + +/********************************************************* +* Unknown instruction (would generate an exception) * +* Format: ? * +*********************************************************/ +//extern char* disNULL DisFInterface; +static MakeDisF(disNULL, dName("*** Bad OP ***");) + +#include "DisVUmicro.h" +#include "DisVUops.h" + +_disVUOpcodes(VU1); +_disVUTables(VU1); + diff --git a/pcsx2/DebugTools/DisVUmicro.h b/pcsx2/DebugTools/DisVUmicro.h new file mode 100644 index 0000000000..15caffe9ca --- /dev/null +++ b/pcsx2/DebugTools/DisVUmicro.h @@ -0,0 +1,209 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + + +#define _disVUTables(VU) \ + \ +/****************/ \ +/* LOWER TABLES */ \ +/****************/ \ + \ +TdisR5900F dis##VU##LowerOP_T3_00_OPCODE[32] = { \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + dis##VU##MI_MOVE , dis##VU##MI_LQI , dis##VU##MI_DIV , dis##VU##MI_MTIR, \ + dis##VU##MI_RNEXT , disNULL , disNULL , disNULL , /* 0x10 */ \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , dis##VU##MI_MFP , dis##VU##MI_XTOP , dis##VU##MI_XGKICK, \ + dis##VU##MI_ESADD , dis##VU##MI_EATANxy, dis##VU##MI_ESQRT, dis##VU##MI_ESIN, \ +}; \ + \ +TdisR5900F dis##VU##LowerOP_T3_01_OPCODE[32] = { \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + dis##VU##MI_MR32 , dis##VU##MI_SQI , dis##VU##MI_SQRT , dis##VU##MI_MFIR, \ + dis##VU##MI_RGET , disNULL , disNULL , disNULL , /* 0x10 */ \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , dis##VU##MI_XITOP, disNULL , \ + dis##VU##MI_ERSADD, dis##VU##MI_EATANxz, dis##VU##MI_ERSQRT, dis##VU##MI_EATAN, \ +}; \ + \ +TdisR5900F dis##VU##LowerOP_T3_10_OPCODE[32] = { \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , dis##VU##MI_LQD , dis##VU##MI_RSQRT, dis##VU##MI_ILWR, \ + dis##VU##MI_RINIT , disNULL , disNULL , disNULL , /* 0x10 */ \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + dis##VU##MI_ELENG , dis##VU##MI_ESUM , dis##VU##MI_ERCPR, dis##VU##MI_EEXP, \ +}; \ + \ +TdisR5900F dis##VU##LowerOP_T3_11_OPCODE[32] = { \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , dis##VU##MI_SQD , dis##VU##MI_WAITQ, dis##VU##MI_ISWR, \ + dis##VU##MI_RXOR , disNULL , disNULL , disNULL , /* 0x10 */ \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + dis##VU##MI_ERLENG, disNULL , dis##VU##MI_WAITP, disNULL , \ +}; \ + \ +MakeDisF(dis##VU##LowerOP_T3_00, dis##VU##LowerOP_T3_00_OPCODE[_Fd_] DisFInterfaceN) \ +MakeDisF(dis##VU##LowerOP_T3_01, dis##VU##LowerOP_T3_01_OPCODE[_Fd_] DisFInterfaceN) \ +MakeDisF(dis##VU##LowerOP_T3_10, dis##VU##LowerOP_T3_10_OPCODE[_Fd_] DisFInterfaceN) \ +MakeDisF(dis##VU##LowerOP_T3_11, dis##VU##LowerOP_T3_11_OPCODE[_Fd_] DisFInterfaceN) \ + \ +TdisR5900F dis##VU##LowerOP_OPCODE[64] = { \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , /* 0x10 */ \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , /* 0x20 */ \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + dis##VU##MI_IADD , dis##VU##MI_ISUB , dis##VU##MI_IADDI, disNULL , /* 0x30 */ \ + dis##VU##MI_IAND , dis##VU##MI_IOR , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + dis##VU##LowerOP_T3_00, dis##VU##LowerOP_T3_01, dis##VU##LowerOP_T3_10, dis##VU##LowerOP_T3_11, \ +}; \ + \ +MakeDisF(dis##VU##LowerOP, dis##VU##LowerOP_OPCODE[code & 0x3f] DisFInterfaceN) \ + \ +TdisR5900F dis##VU##MicroL[] = { \ + dis##VU##MI_LQ , dis##VU##MI_SQ , disNULL , disNULL, \ + dis##VU##MI_ILW , dis##VU##MI_ISW , disNULL , disNULL, \ + dis##VU##MI_IADDIU, dis##VU##MI_ISUBIU, disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ + dis##VU##MI_FCEQ , dis##VU##MI_FCSET , dis##VU##MI_FCAND, dis##VU##MI_FCOR, /* 0x10 */ \ + dis##VU##MI_FSEQ , dis##VU##MI_FSSET , dis##VU##MI_FSAND, dis##VU##MI_FSOR, \ + dis##VU##MI_FMEQ , disNULL , dis##VU##MI_FMAND, dis##VU##MI_FMOR, \ + dis##VU##MI_FCGET , disNULL , disNULL , disNULL, \ + dis##VU##MI_B , dis##VU##MI_BAL , disNULL , disNULL, /* 0x20 */ \ + dis##VU##MI_JR , dis##VU##MI_JALR , disNULL , disNULL, \ + dis##VU##MI_IBEQ , dis##VU##MI_IBNE , disNULL , disNULL, \ + dis##VU##MI_IBLTZ , dis##VU##MI_IBGTZ , dis##VU##MI_IBLEZ, dis##VU##MI_IBGEZ, \ + disNULL , disNULL , disNULL , disNULL, /* 0x30 */ \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ + dis##VU##LowerOP , disNULL , disNULL , disNULL, /* 0x40*/ \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, /* 0x50 */ \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, /* 0x60 */ \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, /* 0x70 */ \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ + disNULL , disNULL , disNULL , disNULL, \ +}; \ + \ + \ +MakeDisF(dis##VU##MicroLF, dis##VU##MicroL[code >> 25] DisFInterfaceN) \ + \ + \ +/****************/ \ +/* UPPER TABLES */ \ +/****************/ \ + \ +TdisR5900F dis##VU##_UPPER_FD_00_TABLE[32] = { \ + dis##VU##MI_ADDAx, dis##VU##MI_SUBx , dis##VU##MI_MADDAx, dis##VU##MI_MSUBAx, \ + dis##VU##MI_ITOF0, dis##VU##MI_FTOI0, dis##VU##MI_MULAx , dis##VU##MI_MULAq , \ + dis##VU##MI_ADDAq, dis##VU##MI_SUBAq, dis##VU##MI_ADDA , dis##VU##MI_SUBA , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ +}; \ + \ +TdisR5900F dis##VU##_UPPER_FD_01_TABLE[32] = { \ + dis##VU##MI_ADDAy , dis##VU##MI_SUBy , dis##VU##MI_MADDAy, dis##VU##MI_MSUBAy, \ + dis##VU##MI_ITOF4 , dis##VU##MI_FTOI4 , dis##VU##MI_MULAy , dis##VU##MI_ABS , \ + dis##VU##MI_MADDAq, dis##VU##MI_MSUBAq, dis##VU##MI_MADDA , dis##VU##MI_MSUBA , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ +}; \ + \ +TdisR5900F dis##VU##_UPPER_FD_10_TABLE[32] = { \ + dis##VU##MI_ADDAz , dis##VU##MI_SUBz , dis##VU##MI_MADDAz, dis##VU##MI_MSUBAz, \ + dis##VU##MI_ITOF12, dis##VU##MI_FTOI12, dis##VU##MI_MULAz , dis##VU##MI_MULAi , \ + dis##VU##MI_ADDAi, dis##VU##MI_SUBAi , dis##VU##MI_MULA , dis##VU##MI_OPMULA, \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ +}; \ + \ +TdisR5900F dis##VU##_UPPER_FD_11_TABLE[32] = { \ + dis##VU##MI_ADDAw , dis##VU##MI_SUBw , dis##VU##MI_MADDAw, dis##VU##MI_MSUBAw, \ + dis##VU##MI_ITOF15, dis##VU##MI_FTOI15, dis##VU##MI_MULAw , dis##VU##MI_CLIP , \ + dis##VU##MI_MADDAi, dis##VU##MI_MSUBAi, disNULL , dis##VU##MI_NOP , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ +}; \ + \ +MakeDisF(dis##VU##_UPPER_FD_00, dis##VU##_UPPER_FD_00_TABLE[_Fd_] DisFInterfaceN) \ +MakeDisF(dis##VU##_UPPER_FD_01, dis##VU##_UPPER_FD_01_TABLE[_Fd_] DisFInterfaceN) \ +MakeDisF(dis##VU##_UPPER_FD_10, dis##VU##_UPPER_FD_10_TABLE[_Fd_] DisFInterfaceN) \ +MakeDisF(dis##VU##_UPPER_FD_11, dis##VU##_UPPER_FD_11_TABLE[_Fd_] DisFInterfaceN) \ + \ +TdisR5900F dis##VU##MicroU[] = { \ + dis##VU##MI_ADDx , dis##VU##MI_ADDy , dis##VU##MI_ADDz , dis##VU##MI_ADDw, \ + dis##VU##MI_SUBx , dis##VU##MI_SUBy , dis##VU##MI_SUBz , dis##VU##MI_SUBw, \ + dis##VU##MI_MADDx , dis##VU##MI_MADDy , dis##VU##MI_MADDz , dis##VU##MI_MADDw, \ + dis##VU##MI_MSUBx , dis##VU##MI_MSUBy , dis##VU##MI_MSUBz , dis##VU##MI_MSUBw, \ + dis##VU##MI_MAXx , dis##VU##MI_MAXy , dis##VU##MI_MAXz , dis##VU##MI_MAXw, /* 0x10 */ \ + dis##VU##MI_MINIx , dis##VU##MI_MINIy , dis##VU##MI_MINIz , dis##VU##MI_MINIw, \ + dis##VU##MI_MULx , dis##VU##MI_MULy , dis##VU##MI_MULz , dis##VU##MI_MULw, \ + dis##VU##MI_MULq , dis##VU##MI_MAXi , dis##VU##MI_MULi , dis##VU##MI_MINIi, \ + dis##VU##MI_ADDq , dis##VU##MI_MADDq , dis##VU##MI_ADDi , dis##VU##MI_MADDi, /* 0x20 */ \ + dis##VU##MI_SUBq , dis##VU##MI_MSUBq , dis##VU##MI_SUBi , dis##VU##MI_MSUBi, \ + dis##VU##MI_ADD , dis##VU##MI_MADD , dis##VU##MI_MUL , dis##VU##MI_MAX, \ + dis##VU##MI_SUB , dis##VU##MI_MSUB , dis##VU##MI_OPMSUB, dis##VU##MI_MINI, \ + disNULL , disNULL , disNULL , disNULL , /* 0x30 */ \ + disNULL , disNULL , disNULL , disNULL , \ + disNULL , disNULL , disNULL , disNULL , \ + dis##VU##_UPPER_FD_00, dis##VU##_UPPER_FD_01, dis##VU##_UPPER_FD_10, dis##VU##_UPPER_FD_11, \ +}; \ + \ + \ +MakeDisF(dis##VU##MicroUF, dis##VU##MicroU[code & 0x3f] DisFInterfaceN) \ + diff --git a/pcsx2/DebugTools/DisVUops.h b/pcsx2/DebugTools/DisVUops.h new file mode 100644 index 0000000000..fb43a9d69a --- /dev/null +++ b/pcsx2/DebugTools/DisVUops.h @@ -0,0 +1,197 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + + +#define _disVUOpcodes(VU) \ + \ +/*****************/ \ +/* LOWER OPCODES */ \ +/*****************/ \ + \ +MakeDisF(dis##VU##MI_DIV, dName("DIV"); dCP232f(_Fs_, _Fsf_); dCP232f(_Ft_, _Ftf_);) \ +MakeDisF(dis##VU##MI_SQRT, dName("SQRT"); dCP232f(_Ft_, _Ftf_);) \ +MakeDisF(dis##VU##MI_RSQRT, dName("RSQRT"); dCP232f(_Fs_, _Fsf_); dCP232f(_Ft_, _Ftf_);) \ +MakeDisF(dis##VU##MI_IADDI, dName("IADDI"); dCP232i(_Ft_); dCP232i(_Fs_); dImm5();) \ +MakeDisF(dis##VU##MI_IADDIU, dName("IADDIU"); dCP232i(_Ft_); dCP232i(_Fs_); dImm15();) \ +MakeDisF(dis##VU##MI_IADD, dName("IADD"); dCP232i(_Fd_); dCP232i(_Fs_); dCP232i(_Ft_);) \ +MakeDisF(dis##VU##MI_IAND, dName("IAND"); dCP232i(_Fd_); dCP232i(_Fs_); dCP232i(_Ft_);) \ +MakeDisF(dis##VU##MI_IOR, dName("IOR"); dCP232i(_Fd_); dCP232i(_Fs_); dCP232i(_Ft_);) \ +MakeDisF(dis##VU##MI_ISUB, dName("ISUB"); dCP232i(_Fd_); dCP232i(_Fs_); dCP232i(_Ft_);) \ +MakeDisF(dis##VU##MI_ISUBIU, dName("ISUBIU"); dCP232i(_Ft_); dCP232i(_Fs_); dImm15();) \ +MakeDisF(dis##VU##MI_MOVE, if (_Fs_ == 0 && _Ft_ == 0) { dNameU("NOP"); } else { dNameU("MOVE"); dCP2128f(_Ft_); dCP2128f(_Fs_); }) \ +MakeDisF(dis##VU##MI_MFIR, dNameU("MFIR"); dCP2128f(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_MTIR, dNameU("MTIR"); dCP232i(_Ft_); dCP232f(_Fs_, _Fsf_);) \ +MakeDisF(dis##VU##MI_MR32, dNameU("MR32"); dCP2128f(_Ft_); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_LQ, dNameU("LQ"); dCP2128f(_Ft_); dCP232i(_Fs_); dImm11();) \ +MakeDisF(dis##VU##MI_LQD, dNameU("LQD"); dCP2128f(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_LQI, dNameU("LQI"); dCP2128f(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_SQ, dNameU("SQ"); dCP2128f(_Fs_); dCP232i(_Ft_); dImm11(); ) \ +MakeDisF(dis##VU##MI_SQD, dNameU("SQD"); dCP2128f(_Fs_); dCP232i(_Ft_);) \ +MakeDisF(dis##VU##MI_SQI, dNameU("SQI"); dCP2128f(_Fs_); dCP232i(_Ft_);) \ +MakeDisF(dis##VU##MI_ILW, dNameU("ILW"); dCP232i(_Ft_); dImm11(); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_ISW, dNameU("ISW"); dCP232i(_Ft_); dImm11(); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_ILWR, dNameU("ILWR"); dCP232i(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_ISWR, dNameU("ISWR"); dCP232i(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_LOI, dName("LOI"); ) \ +MakeDisF(dis##VU##MI_RINIT, dNameU("RINIT"); dCP232i(REG_R); dCP232f(_Fs_, _Fsf_);) \ +MakeDisF(dis##VU##MI_RGET, dNameU("RGET"); dCP232i(REG_R); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_RNEXT, dNameU("RNEXT"); dCP232i(REG_R); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_RXOR, dNameU("RXOR"); dCP232i(REG_R); dCP232f(_Fs_, _Fsf_);) \ +MakeDisF(dis##VU##MI_WAITQ, dName("WAITQ"); ) \ +MakeDisF(dis##VU##MI_FSAND, dName("FSAND"); dCP232i(_Ft_); dCP232i(REG_STATUS_FLAG); sprintf(ostr, "%s %.3x,", ostr, code&0xfff); ) \ +MakeDisF(dis##VU##MI_FSEQ, dName("FSEQ"); dCP232i(_Ft_); dCP232i(REG_STATUS_FLAG); sprintf(ostr, "%s %.3x,", ostr, code&0xfff);) \ +MakeDisF(dis##VU##MI_FSOR, dName("FSOR"); dCP232i(_Ft_); dCP232i(REG_STATUS_FLAG); sprintf(ostr, "%s %.3x,", ostr, code&0xfff);) \ +MakeDisF(dis##VU##MI_FSSET, dName("FSSET"); dCP232i(REG_STATUS_FLAG);) \ +MakeDisF(dis##VU##MI_FMAND, dName("FMAND"); dCP232i(_Ft_); dCP232i(REG_MAC_FLAG); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_FMEQ, dName("FMEQ"); dCP232i(_Ft_); dCP232i(REG_MAC_FLAG); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_FMOR, dName("FMOR"); dCP232i(_Ft_); dCP232i(REG_MAC_FLAG); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_FCAND, dName("FCAND"); dCP232i(1); sprintf(ostr, "%s %8.8x,", ostr, code&0xffffff); ) \ +MakeDisF(dis##VU##MI_FCEQ, dName("FCEQ"); dCP232i(1); dCP232i(REG_CLIP_FLAG);) \ +MakeDisF(dis##VU##MI_FCOR, dName("FCOR"); dCP232i(1); dCP232i(REG_CLIP_FLAG);) \ +MakeDisF(dis##VU##MI_FCSET, dName("FCSET"); dCP232i(REG_CLIP_FLAG); sprintf(ostr, "%s %.6x,", ostr, code&0xffffff); ) \ +MakeDisF(dis##VU##MI_FCGET, dName("FCGET"); dCP232i(_Ft_); dCP232i(REG_CLIP_FLAG);) \ +MakeDisF(dis##VU##MI_IBEQ, dName("IBEQ"); dImm11(); dCP232i(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_IBGEZ, dName("IBEZ"); dImm11(); dCP232i(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_IBGTZ, dName("IBGTZ"); dImm11(); dCP232i(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_IBLEZ, dName("IBLEZ"); dImm11(); dCP232i(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_IBLTZ, dName("IBLTZ"); dImm11(); dCP232i(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_IBNE, dName("IBNE"); dImm11(); dCP232i(_Ft_); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_B, dName("B"); dImm11();) \ +MakeDisF(dis##VU##MI_BAL, dName("BAL"); dImm11(); dCP232i(_Ft_);) \ +MakeDisF(dis##VU##MI_JR, dName("JR"); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_JALR, dName("JALR"); dCP232i(_Ft_); dCP232i(_Fs_); ) \ +MakeDisF(dis##VU##MI_MFP, dNameU("MFP"); dCP2128f(_Ft_); dCP232i(REG_P);) \ +MakeDisF(dis##VU##MI_WAITP, dName("WAITP"); ) \ +MakeDisF(dis##VU##MI_ESADD, dName("ESADD"); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_ERSADD, dName("ERSADD"); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_ELENG, dName("ELENG"); dCP2128f(_Fs_); ) \ +MakeDisF(dis##VU##MI_ERLENG, dName("ERLENG"); dCP2128f(_Fs_); ) \ +MakeDisF(dis##VU##MI_EATANxy, dName("EATANxy"); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_EATANxz, dName("EATANxz"); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_ESUM, dName("ESUM"); dCP232i(_Fs_); ) \ +MakeDisF(dis##VU##MI_ERCPR, dName("ERCPR"); dCP232f(_Fs_, _Fsf_); ) \ +MakeDisF(dis##VU##MI_ESQRT, dName("ESQRT"); dCP232f(_Fs_, _Fsf_); ) \ +MakeDisF(dis##VU##MI_ERSQRT, dName("ERSQRT"); dCP232f(_Fs_, _Fsf_); ) \ +MakeDisF(dis##VU##MI_ESIN, dName("ESIN"); dCP232f(_Fs_, _Fsf_); ) \ +MakeDisF(dis##VU##MI_EATAN, dName("EATAN"); dCP232f(_Fs_, _Fsf_); ) \ +MakeDisF(dis##VU##MI_EEXP, dName("EEXP"); dCP232f(_Fs_, _Fsf_); ) \ +MakeDisF(dis##VU##MI_XITOP, dName("XITOP"); dCP232i(_Ft_);) \ +MakeDisF(dis##VU##MI_XGKICK, dName("XGKICK"); dCP232i(_Fs_);) \ +MakeDisF(dis##VU##MI_XTOP, dName("XTOP"); dCP232i(_Ft_);) \ + \ + \ +/*****************/ \ +/* UPPER OPCODES */ \ +/*****************/ \ + \ +MakeDisF(dis##VU##MI_ABS, dNameU("ABS"); dCP2128f(_Ft_); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_ADD, dNameU("ADD"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_ADDi, dNameU("ADDi"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_ADDq, dNameU("ADDq"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232iF(REG_Q);) \ +MakeDisF(dis##VU##MI_ADDx, dNameU("ADDx"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_ADDy, dNameU("ADDy"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_ADDz, dNameU("ADDz"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_ADDw, dNameU("ADDw"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_ADDA, dNameU("ADDA"); dCP2ACCf(); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_ADDAi, dNameU("ADDAi"); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_ADDAq, dNameU("ADDAq"); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_Q);) \ +MakeDisF(dis##VU##MI_ADDAx, dNameU("ADDAx"); dCP2ACCf(); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_ADDAy, dNameU("ADDAy"); dCP2ACCf(); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_ADDAz, dNameU("ADDAz"); dCP2ACCf(); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_ADDAw, dNameU("ADDAw"); dCP2ACCf(); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_SUB, dNameU("SUB"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_SUBi, dNameU("SUBi"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_SUBq, dNameU("SUBq"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232iF(REG_Q);) \ +MakeDisF(dis##VU##MI_SUBx, dNameU("SUBx"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_SUBy, dNameU("SUBy"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_SUBz, dNameU("SUBz"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_SUBw, dNameU("SUBw"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_SUBA, dNameU("SUBA"); dCP2ACCf(); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_SUBAi, dNameU("SUBAi"); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_SUBAq, dNameU("SUBAq"); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_Q);) \ +MakeDisF(dis##VU##MI_SUBAx, dNameU("SUBAx"); dCP2ACCf(); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_SUBAy, dNameU("SUBAy"); dCP2ACCf(); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_SUBAz, dNameU("SUBAz"); dCP2ACCf(); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_SUBAw, dNameU("SUBAw"); dCP2ACCf(); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_MUL, dNameU("MUL"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_MULi, dNameU("MULi"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_MULq, dNameU("MULq"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232iF(REG_Q);) \ +MakeDisF(dis##VU##MI_MULx, dNameU("MULx"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_MULy, dNameU("MULy"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_MULz, dNameU("MULz"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_MULw, dNameU("MULw"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_MULA, dNameU("MULA"); dCP2ACCf(); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_MULAi, dNameU("MULAi"); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_MULAq, dNameU("MULAq"); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_Q);) \ +MakeDisF(dis##VU##MI_MULAx, dNameU("MULAx"); dCP2ACCf(); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_MULAy, dNameU("MULAy"); dCP2ACCf(); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_MULAz, dNameU("MULAz"); dCP2ACCf(); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_MULAw, dNameU("MULAw"); dCP2ACCf(); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_MADD, dNameU("MADD"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_MADDi, dNameU("MADDi"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_MADDq, dNameU("MADDq"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_Q);) \ +MakeDisF(dis##VU##MI_MADDx, dNameU("MADDx"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_MADDy, dNameU("MADDy"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_MADDz, dNameU("MADDz"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_MADDw, dNameU("MADDw"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_MADDA, dNameU("MADDA"); dCP2ACCf(); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_MADDAi, dNameU("MADDAi"); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_MADDAq, dNameU("MADDAq"); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_Q);) \ +MakeDisF(dis##VU##MI_MADDAx, dNameU("MADDAx"); dCP2ACCf(); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_MADDAy, dNameU("MADDAy"); dCP2ACCf(); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_MADDAz, dNameU("MADDAz"); dCP2ACCf(); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_MADDAw, dNameU("MADDAw"); dCP2ACCf(); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_MSUB, dNameU("MSUB"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_MSUBi, dNameU("MSUBi"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_MSUBq, dNameU("MSUBq"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_Q);) \ +MakeDisF(dis##VU##MI_MSUBx, dNameU("MSUBx"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_MSUBy, dNameU("MSUBy"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_MSUBz, dNameU("MSUBz"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_MSUBw, dNameU("MSUBw"); dCP2128f(_Fd_); dCP2ACCf(); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_MSUBA, dNameU("MSUBA"); dCP2ACCf(); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_MSUBAi, dNameU("MSUBAi"); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_MSUBAq, dNameU("MSUBAq"); dCP2ACCf(); dCP2128f(_Fs_); dCP232iF(REG_Q);) \ +MakeDisF(dis##VU##MI_MSUBAx, dNameU("MSUBAx"); dCP2ACCf(); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_MSUBAy, dNameU("MSUBAy"); dCP2ACCf(); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_MSUBAz, dNameU("MSUBAz"); dCP2ACCf(); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_MSUBAw, dNameU("MSUBAw"); dCP2ACCf(); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_MAX, dNameU("MAX"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_MAXi, dNameU("MAXi"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_MAXx, dNameU("MAXx"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_MAXy, dNameU("MAXy"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_MAXz, dNameU("MAXz"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_MAXw, dNameU("MAXw"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_MINI, dNameU("MINI"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_MINIi, dNameU("MINIi"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232iF(REG_I);) \ +MakeDisF(dis##VU##MI_MINIx, dNameU("MINIx"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232x(_Ft_);) \ +MakeDisF(dis##VU##MI_MINIy, dNameU("MINIy"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232y(_Ft_);) \ +MakeDisF(dis##VU##MI_MINIz, dNameU("MINIz"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232z(_Ft_);) \ +MakeDisF(dis##VU##MI_MINIw, dNameU("MINIw"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP232w(_Ft_);) \ +MakeDisF(dis##VU##MI_OPMULA, dNameU("OPMULA"); dCP2ACCf(); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_OPMSUB, dNameU("OPMSUB"); dCP2128f(_Fd_); dCP2128f(_Fs_); dCP2128f(_Ft_);) \ +MakeDisF(dis##VU##MI_NOP, dName("NOP");) \ +MakeDisF(dis##VU##MI_FTOI0, dNameU("FTOI0"); dCP2128f(_Ft_); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_FTOI4, dNameU("FTOI4"); dCP2128f(_Ft_); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_FTOI12, dNameU("FTOI12"); dCP2128f(_Ft_); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_FTOI15, dNameU("FTOI15"); dCP2128f(_Ft_); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_ITOF0, dNameU("ITOF0"); dCP2128f(_Ft_); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_ITOF4, dNameU("ITOF4"); dCP2128f(_Ft_); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_ITOF12, dNameU("ITOF12"); dCP2128f(_Ft_); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_ITOF15, dNameU("ITOF15"); dCP2128f(_Ft_); dCP2128f(_Fs_);) \ +MakeDisF(dis##VU##MI_CLIP, dNameU("CLIP"); dCP2128f(_Fs_); dCP232w(_Ft_);) \ + diff --git a/pcsx2/DebugTools/Makefile.am b/pcsx2/DebugTools/Makefile.am new file mode 100644 index 0000000000..84b2acf084 --- /dev/null +++ b/pcsx2/DebugTools/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = -I@srcdir@/../ -I@srcdir@/../common/ +noinst_LIBRARIES = libDebugTools.a + +libDebugTools_a_SOURCES = \ +DisR3000A.cpp DisR5900.cpp DisVU0Micro.cpp \ +DisR3000asm.cpp DisR5900asm.cpp DisVU1Micro.cpp \ +Debug.h DisVUmicro.h DisASM.h DisVUops.h diff --git a/pcsx2/Decode_XA.cpp b/pcsx2/Decode_XA.cpp new file mode 100644 index 0000000000..59bbb6faf0 --- /dev/null +++ b/pcsx2/Decode_XA.cpp @@ -0,0 +1,322 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +//============================================ +//=== Audio XA decoding +//=== Kazzuya +//============================================ +//=== Modified by linuzappz +//============================================ + +#include + +#include "Decode_XA.h" + +#ifdef __WIN32__ +#pragma warning(disable:4244) +#endif + +typedef unsigned char U8; +typedef unsigned short U16; +typedef unsigned long U32; + +#define NOT(_X_) (!(_X_)) +#define CLAMP(_X_,_MI_,_MA_) {if(_X_<_MI_)_X_=_MI_;if(_X_>_MA_)_X_=_MA_;} + +//============================================ +//=== ADPCM DECODING ROUTINES +//============================================ + +static double K0[4] = { + 0.0, + 0.9375, + 1.796875, + 1.53125 +}; + +static double K1[4] = { + 0.0, + 0.0, + -0.8125, + -0.859375 +}; + +#define BLKSIZ 28 /* block size (32 - 4 nibbles) */ + +//=========================================== +void ADPCM_InitDecode( ADPCM_Decode_t *decp ) +{ + decp->y0 = 0; + decp->y1 = 0; +} + +//=========================================== +#define SH 4 +#define SHC 10 + +#define IK0(fid) ((int)((-K0[fid]) * (1<> 4) & 0x0f; + range = (filter_range >> 0) & 0x0f; + + fy0 = decp->y0; + fy1 = decp->y1; + + for (i = BLKSIZ/4; i; --i) { + long y; + long x0, x1, x2, x3; + + y = *blockp++; + x3 = (short)( y & 0xf000) >> range; x3 <<= SH; + x2 = (short)((y << 4) & 0xf000) >> range; x2 <<= SH; + x1 = (short)((y << 8) & 0xf000) >> range; x1 <<= SH; + x0 = (short)((y << 12) & 0xf000) >> range; x0 <<= SH; + + x0 -= (IK0(filterid) * fy0 + (IK1(filterid) * fy1)) >> SHC; fy1 = fy0; fy0 = x0; + x1 -= (IK0(filterid) * fy0 + (IK1(filterid) * fy1)) >> SHC; fy1 = fy0; fy0 = x1; + x2 -= (IK0(filterid) * fy0 + (IK1(filterid) * fy1)) >> SHC; fy1 = fy0; fy0 = x2; + x3 -= (IK0(filterid) * fy0 + (IK1(filterid) * fy1)) >> SHC; fy1 = fy0; fy0 = x3; + + CLAMP( x0, -32768<> SH; destp += inc; + CLAMP( x1, -32768<> SH; destp += inc; + CLAMP( x2, -32768<> SH; destp += inc; + CLAMP( x3, -32768<> SH; destp += inc; + } + decp->y0 = fy0; + decp->y1 = fy1; +} + +static int headtable[4] = {0,2,8,10}; + +//=========================================== +static void xa_decode_data( xa_decode_t *xdp, unsigned char *srcp ) { + const U8 *sound_groupsp; + const U8 *sound_datap, *sound_datap2; + int i, j, k, nbits; + U16 data[4096], *datap; + short *destp; + + destp = xdp->pcm; + nbits = xdp->nbits == 4 ? 4 : 2; + + if (xdp->stereo) { // stereo + for (j=0; j < 18; j++) { + sound_groupsp = srcp + j * 128; // sound groups header + sound_datap = sound_groupsp + 16; // sound data just after the header + + for (i=0; i < nbits; i++) { + datap = data; + sound_datap2 = sound_datap + i; + if ((xdp->nbits == 8) && (xdp->freq == 37800)) { // level A + for (k=0; k < 14; k++, sound_datap2 += 8) { + *(datap++) = (U16)sound_datap2[0] | + (U16)(sound_datap2[4] << 8); + } + } else { // level B/C + for (k=0; k < 7; k++, sound_datap2 += 16) { + *(datap++) = (U16)(sound_datap2[ 0] & 0x0f) | + ((U16)(sound_datap2[ 4] & 0x0f) << 4) | + ((U16)(sound_datap2[ 8] & 0x0f) << 8) | + ((U16)(sound_datap2[12] & 0x0f) << 12); + } + } + ADPCM_DecodeBlock16( &xdp->left, sound_groupsp[headtable[i]+0], data, + destp+0, 2 ); + + datap = data; + sound_datap2 = sound_datap + i; + if ((xdp->nbits == 8) && (xdp->freq == 37800)) { // level A + for (k=0; k < 14; k++, sound_datap2 += 8) { + *(datap++) = (U16)sound_datap2[0] | + (U16)(sound_datap2[4] << 8); + } + } else { // level B/C + for (k=0; k < 7; k++, sound_datap2 += 16) { + *(datap++) = (U16)(sound_datap2[ 0] >> 4) | + ((U16)(sound_datap2[ 4] >> 4) << 4) | + ((U16)(sound_datap2[ 8] >> 4) << 8) | + ((U16)(sound_datap2[12] >> 4) << 12); + } + } + ADPCM_DecodeBlock16( &xdp->right, sound_groupsp[headtable[i]+1], data, + destp+1, 2 ); + + destp += 28*2; + } + } + } + else { // mono + for (j=0; j < 18; j++) { + sound_groupsp = srcp + j * 128; // sound groups header + sound_datap = sound_groupsp + 16; // sound data just after the header + + for (i=0; i < nbits; i++) { + datap = data; + sound_datap2 = sound_datap + i; + if ((xdp->nbits == 8) && (xdp->freq == 37800)) { // level A + for (k=0; k < 14; k++, sound_datap2 += 8) { + *(datap++) = (U16)sound_datap2[0] | + (U16)(sound_datap2[4] << 8); + } + } else { // level B/C + for (k=0; k < 7; k++, sound_datap2 += 16) { + *(datap++) = (U16)(sound_datap2[ 0] & 0x0f) | + ((U16)(sound_datap2[ 4] & 0x0f) << 4) | + ((U16)(sound_datap2[ 8] & 0x0f) << 8) | + ((U16)(sound_datap2[12] & 0x0f) << 12); + } + } + ADPCM_DecodeBlock16( &xdp->left, sound_groupsp[headtable[i]+0], data, + destp, 1 ); + + destp += 28; + + datap = data; + sound_datap2 = sound_datap + i; + if ((xdp->nbits == 8) && (xdp->freq == 37800)) { // level A + for (k=0; k < 14; k++, sound_datap2 += 8) { + *(datap++) = (U16)sound_datap2[0] | + (U16)(sound_datap2[4] << 8); + } + } else { // level B/C + for (k=0; k < 7; k++, sound_datap2 += 16) { + *(datap++) = (U16)(sound_datap2[ 0] >> 4) | + ((U16)(sound_datap2[ 4] >> 4) << 4) | + ((U16)(sound_datap2[ 8] >> 4) << 8) | + ((U16)(sound_datap2[12] >> 4) << 12); + } + } + ADPCM_DecodeBlock16( &xdp->left, sound_groupsp[headtable[i]+1], data, + destp, 1 ); + + destp += 28; + } + } + } +} + +//============================================ +//=== XA SPECIFIC ROUTINES +//============================================ +typedef struct { +U8 filenum; +U8 channum; +U8 submode; +U8 coding; + +U8 filenum2; +U8 channum2; +U8 submode2; +U8 coding2; +} xa_subheader_t; + +#define SUB_SUB_EOF (1<<7) // end of file +#define SUB_SUB_RT (1<<6) // real-time sector +#define SUB_SUB_FORM (1<<5) // 0 form1 1 form2 +#define SUB_SUB_TRIGGER (1<<4) // used for interrupt +#define SUB_SUB_DATA (1<<3) // contains data +#define SUB_SUB_AUDIO (1<<2) // contains audio +#define SUB_SUB_VIDEO (1<<1) // contains video +#define SUB_SUB_EOR (1<<0) // end of record + +#define AUDIO_CODING_GET_STEREO(_X_) ( (_X_) & 3) +#define AUDIO_CODING_GET_FREQ(_X_) (((_X_) >> 2) & 3) +#define AUDIO_CODING_GET_BPS(_X_) (((_X_) >> 4) & 3) +#define AUDIO_CODING_GET_EMPHASIS(_X_) (((_X_) >> 6) & 1) + +#define SUB_UNKNOWN 0 +#define SUB_VIDEO 1 +#define SUB_AUDIO 2 + +//============================================ +static int parse_xa_audio_sector( xa_decode_t *xdp, + xa_subheader_t *subheadp, + unsigned char *sectorp, + int is_first_sector ) { + if ( is_first_sector ) { + switch ( AUDIO_CODING_GET_FREQ(subheadp->coding) ) { + case 0: xdp->freq = 37800; break; + case 1: xdp->freq = 18900; break; + default: xdp->freq = 0; break; + } + switch ( AUDIO_CODING_GET_BPS(subheadp->coding) ) { + case 0: xdp->nbits = 4; break; + case 1: xdp->nbits = 8; break; + default: xdp->nbits = 0; break; + } + switch ( AUDIO_CODING_GET_STEREO(subheadp->coding) ) { + case 0: xdp->stereo = 0; break; + case 1: xdp->stereo = 1; break; + default: xdp->stereo = 0; break; + } + + if ( xdp->freq == 0 ) + return -1; + + ADPCM_InitDecode( &xdp->left ); + ADPCM_InitDecode( &xdp->right ); + + xdp->nsamples = 18 * 28 * 8; + if (xdp->stereo == 1) xdp->nsamples /= 2; + } + xa_decode_data( xdp, sectorp ); + + return 0; +} + +//================================================================ +//=== THIS IS WHAT YOU HAVE TO CALL +//=== xdp - structure were all important data are returned +//=== sectorp - data in input +//=== pcmp - data in output +//=== is_first_sector - 1 if it's the 1st sector of the stream +//=== - 0 for any other successive sector +//=== return -1 if error +//================================================================ +long xa_decode_sector( xa_decode_t *xdp, + unsigned char *sectorp, int is_first_sector ) { + if (parse_xa_audio_sector(xdp, (xa_subheader_t *)sectorp, sectorp + sizeof(xa_subheader_t), is_first_sector)) + return -1; + + return 0; +} + +/* EXAMPLE: +"nsamples" is the number of 16 bit samples +every sample is 2 bytes in mono and 4 bytes in stereo + +xa_decode_t xa; + + sectorp = read_first_sector(); + xa_decode_sector( &xa, sectorp, 1 ); + play_wave( xa.pcm, xa.freq, xa.nsamples ); + + while ( --n_sectors ) + { + sectorp = read_next_sector(); + xa_decode_sector( &xa, sectorp, 0 ); + play_wave( xa.pcm, xa.freq, xa.nsamples ); + } +*/ diff --git a/pcsx2/Decode_XA.h b/pcsx2/Decode_XA.h new file mode 100644 index 0000000000..40262f205d --- /dev/null +++ b/pcsx2/Decode_XA.h @@ -0,0 +1,43 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +//============================================ +//=== Audio XA decoding +//=== Kazzuya +//============================================ + +#ifndef DECODEXA_H +#define DECODEXA_H + +typedef struct { + long y0, y1; +} ADPCM_Decode_t; + +typedef struct { + int freq; + int nbits; + int stereo; + int nsamples; + ADPCM_Decode_t left, right; + short pcm[16384]; +} xa_decode_t; + +long xa_decode_sector( xa_decode_t *xdp, + unsigned char *sectorp, + int is_first_sector ); + +#endif diff --git a/pcsx2/Docs/ChangeLog.txt b/pcsx2/Docs/ChangeLog.txt new file mode 100644 index 0000000000..4c9c3ea7f2 --- /dev/null +++ b/pcsx2/Docs/ChangeLog.txt @@ -0,0 +1,2851 @@ +[ Legend: ] +[ + Added feature ] +[ * Improved/changed feature ] +[ - Bug fixed (we hope) ] +[ ! Attention (Notes) ] +[ M Maintenance ] +[ dates are dd/mm/yyyy !!! ] + +ChangeLog: + v0.9.3: + * 26/11/06 [*] Version 0.9.3 started :) + (Refraction) + v0.9.2: + * 30/10/06:[*] Improved the way Unpacks are handled slightly, just so its a little quicker (not much) + [-] MMI bug causing corrupt characters on Max Payne fixed + [-] Performance counters corrected in INT and REC + [-] Branches in a branch delay slot no longer results in a crash + [*] Changed Run CD to Run CD/DVD, ps2 doesnt use CD's much :P + [*] Removed "Advanced" from the menu, not required anymore (at the moment) + [-] Small alteration to IPU transfers, ending the transfer too quickly + [-] Made a seperate branchtest for INT, fixed the VU not running on the bios in INT + (Refraction - Zerofrog) + + * 16/10/06:[-] Counter bugz again, seemed to effect the text in xmen + [-] Fixed an ipu bug which stopped the GTA San Andreas videos working + [-] couple more smaller unpack changes to remove a hack + (Refraction) + + * 15/10/06:[*] Moved some declarations from patch.c to patch.h + [-] Fixed problem with XML patch loading (see above). + [+] Merged my WIP cheat finder dialog (note WIP). + (Gigaherz) + + * 11/10/06:[+] Added XML Patch support + [-] More unpack fixes for GT3/GT4, Xmen, Midnight Club 2, FFX sky, Whiplash and more :) + (Refraction - Gigaherz) + + * 05/10/06:[-] Fixed VIF Unpack skipping write cycle counting (Fixes XMen 2 amongst other SPS) + [-] Small counter change, am i ever going to get peace from these :( + (Refraction) + + * 03/10/06:[-] Fixed MFIFO issue where VIF/GIF might not be ready + [*] Improved Guitar Hero fix, seems MFIFO isnt too keen on sliced transfers (especially on GIF) + [*] Modified VS2005 Project file so it compiles - Thanks to CKemu for doing that :P + (Refraction - CKemu) + + * 02/10/06:[*] Various changes in cdvd.c. Most notable is the implementation of cdvd.PwOff. + It is actualy INTR_STAT and has "completition" bits that are Ack'ed by cdvdman interrupt handler. + ...to be continued. + (Florin) + + * 30/09/06:[*] Corrected performance counter register reads on COP0 (3 regs are accessed by 1) - Lego Racers 2 + [-] Altered the order DIV on IOP rec handles signs - Lego Racers 2 + (Refraction) + + * 29/09/06:[-] Fixed condition where MFIFO could get ahead of itself/not have enough data available + (fixes Guitar Hero) + [-] Fixed up DMA Stalls between VIF/IPU/GIF, games such as Theme Park World should work properly now. + [+] Added cpu detection for Dual core Opterons, Athlon X2's and Intel Core 2 Duo's (no perf effect) + [+] Added saqibs sound changes so ADMA and ADPCM streams work. + [-] Yes, more counter fixes, fed up of these :P + [-] DMA transfers fixed when a QWC is already given, it transfers the QWC then ends (Fixes Ridge Racer 5 a bit) + [-] Small GSCSR changes, not sure what will be fixed. + [-] Event where the DMA could be terminated during a stall on VIF + [-] Various Rec + MTGS + IPU Fixes + (Refraction - Saqib - ZeroFrog) + + * 25/07/06:[-] Changed Vif0 COL/ROW mem write addresses, they were going to the same QW + [*] Made Vif stalls use cycles to delay, due to the spiderman fix we dont want these interrupting instantly! + (fixes Flatout BIFCO) + [-] Call/Ret now update the ASR status in the CHCR register + [-] Couple of small fixes to Tag BUSERR, altho i hope we never need them :P + [+] Added seperate EE counter logging + (Refraction) + +v0.9.1: + * 28/06/06:[+] MAJOR CHANGE: All recompilers and memory code rewritten. + eeRec now does block analysis, constant propagation, liveness + analysis, mmx/xmm reg caching, etc. + vuRec is about 10x faster than older vurec. Both VU0 and + VU1 recs work and share the same code. + iopRec - does same as eeRec, except is simpler + Altogether, 3D scenes are 2-4x faster than 0.9 release. + (zerofrog) + * 09/05/06:[*] Moved repository to SVN. Play nice. Any questions... you know where to find me + [+] new developer blogs from me and auMatt + (Florin-auMatt) + + * 05/05/06:[*] Changed names of the SSBUS registers in PsxHw.c + [!] dev1 is rom bus, dev5 is cdvd bus, dev9 is ata|smap|dvr|uart|flash bus + (Florin) + + * 04/05/06:[+] Added atad and dev9 functions names to psxinterpreter.c + (Florin) + + * 01/05/06:[+] Added *pass-through* MagicGate support. It will work with pre-decrypted files + (Florin) + + * 30/04/06:[-] Fixed (almost:P) the debug bios for remote debugging in WinMain.c + [*] Added PSX detection in Misc.c + [+] Added DevBlog.txt file to Docs. Please read and contribute. + (Florin) + + * 15/04/06:[M] Removed some *shit*; please don't commit *.suo/aps/opt/plg/ncb/exp files + [*] added/fixed some scmd commands in cdvd.c + (Florin) + + * 13/04/06:[*] Implemented CDVD Scmd 0x3E(set OSDVER for PSTwo), Thanks to loser for info. + (auMatt) + + * 12/04/06:[!] Uploaded to CVS the latest version, (fixed vc2005 project, + converted all files to CR/LF ending) + [!] When commiting to CVS, please write same text to comment as in here + [*] Changed the _MSC_VER version displaying; hoping ms will keep it linear :) + [*] Implemented properly CDVD Scmd 0x36 (get OSDVER for PStwo) + (Florin) + + * 10/04/06:[!] Changed to version 0.9.1 + [-] Fixed some Vif FIFO bugs and moved VIF interrupt to check the channel has ended + [-] Fixed some counter bugs (better now i can test on my ps2!) + [!] This code is not perfectly stable and contains debug printouts! It is purely for the other + devs who dont have the latest code ;) + (Refraction) + + v0.9: + * 02/04/06:[+] VU0 Rec added, still buggy + [*] VU1 Speedups and fixes, needs more testing but seems better + [*] CDVD Timing Improved wait times for commands reduced. + [-] Misc eeRec bug fixes made + [*] Vif stalls greatly improved loads more games now work! + [*] Counters modified with gates and improved accuracy + [*] Cycle counting in EE/IOP done on branches to cut download + 2D speed up by over double! + [-] Misc VIF unpack fixes + [-] GIF Stalls should now work correctly + [+] Virtual Memory for Windows XP users, quicker than TLB + [*] Difference CPU options can be mixed and matched by users choice + [-] SIF communication fixed, games can now be booted via the bios (in most cases) + [!] Lots of other fixes have been made by due to lazyness we didnt document! :D + [+] MTGS and Dual Core modes added which are VERY experimental! + [!] This is the FINAL code for 0.9 enjoy! + (Refraction, ZeroFrog, Saqib) + * 15/02/06:[+] Multithreaded GS/Dual Core Support + (zerofrog) + * 17/01/06:[-] Some changes in CDVD tray status + (efp) + + * 16/01/06:[-] Fixed sticky flags. + [*] Implemented/Fixed a lot of vu functions in vurec. VuRec now properly checks for underflow/overflow + (zerofrog) + * 15/01/06:[-] Bug fixes in VU rec/interp (Ebit, xgkick). Added 24bit precision for VUs. + [*] Changed all math functions to use the C++ floating point versions. + (zerofrog) + * 14/01/06:[-] VU Flag fixes + double removal (it needed doing) from zerofrog + [-] Disabled an old PSX Counter hack that i left in some how! + [*] Changed SIF reg code to something simpler and less time consuming + (Refraction) + * 13/01/06:[*] Fixed mac update flags for vu interpreter. + (zerofrog) + + * 12/01/06:[-] Changed IPU.c, thread syncing issue fixed by zerofrog + [*] Altered SBUS so it is triggered on PSX dma + [*] Removed a load of IOP hacks, should improve compatability in some cases + [!] A lot of changes have been made since 05/09/05 but weve lost track, whoops + (Refraction Saqib zerofrog) + + * 05/01/06:[*] Added GSreadFIFO2 for faster reads from GS. + [-] Fixed the multi-threaded IPU and added sync primitives for interrupts and DMA. + The green squares should be gone too. + (zerofrog) + + * 05/09/05:[-] Fixed VU-Rec crash if the pc overflowed the vumem, thanks to fumofumo! + [*] New Icon for 0.9 made by CKemu + [+] Vsync rate now selectable, current Vsync speed is default but less compatable in + some cases, option selects real vsync speed, syncs sound correctly. + (Refraction) + + * 03/09/05:[-] IOP rec & VU0rec fix, x86ptr being set in the wrong place, also a couple of vu-rec op fixes + thanks to fumofumo on irc. + [-] Slowed VSync down to actual speed, so sound isnt half the speed of the graphics, + a few more changes to counters as well, BOR works again, just slower ;p + (Refraction) + + * 30/08/05:[-] Few more changes to the counters, seems much better again, last fix killed stuff + [-] VSync rate now actually changes between PAL and NTSC, before it was stuck in NTSC + [-] Couple of changes in VIF + * 24/08/05:[-] Readded setting of Done on VIF1 TTE, added the same on VIF0 + [-] Applied VIF1 FBRST fix to VIF0 too + [-] Fixed problem causing Beats Of Rage to skip and altered slow sound prob again. + (Refraction) + + * 23/08/05:[-] Did some more timing fixes, they should be a LOT more compatable now + [-] Small change to FBRST, was causing crashes if VIF was finished. + [+] Added counter copy to hold on SBUS interrupt + (Refraction) + + * 21/08/05:[-] Few fixes in the eeRec, shouldnt crash on Kingdom Hearts now, Thanks to fumofumo! + [-] Fixed a bug in FBRST and Vif1, altho theres still an interrupting problem somewhere :/ + [-] Int VU now uses Single Precision (its faster) with no loss of graphics. + [-] Made sure MAC, CLIP and STATUS flags are all located on VU0. + [*] Enabled linuz's SSE unpack code, gives a nice little speed boost, works ok :) + [-] VU execute blocks limited to 128, seems a little faster, no loss of graphics, seems + to fix a few looping infinately issues (VU not breaking) (GiGaHeRz) + [-] Hack removed on TLBP, should hopefully work ok, doesnt seem to break anything (GiGaHeRz) + [+] Added SPU2 hack, should fix some games, not compatable with videos tho. removed fast dvd + [-] More counter stuff, also added gating to IOP counters, not totally sure on it. + [-] Added a couple of checks in VU & eeRec to make sure the constant registers werent overwritten + [-] Temp fix for the dma alignment error on GIF + [-] modified dma timings, qwc / BIAS could mess up if qwc = 1 + [-] Singled out some problematic VU-Rec ops that cause FFX to hang and borkey graphics. + (Refraction) + + * 12/08/05:[*] Few more timing things, music shouldnt be slow anymore on games and cleaned up + counters a bit. + [+] Added SBUS register logging on IOP side + [-] Temp fixed a MAC flag problem in vu-rec till linuz sorts it properly + [*] Committed new logo, nice job CKEmu ;) + [*] Vu-Rec now supports non SSE2 processors thanks to kekko for the changes! + (refraction) + + * 05/08/05:[+] Added faster Cd/DVD reads to the Advanced options (Refraction) + [-] The previous DVD/CD change by ref was just a hack, so i commited the old + version again + [-] I've commented out RECOMPILE_VUMI_LOADSTORE on iVUmicro.h, instead + of commenting it out in iVU1micro.c as ref did on last update, + that way is better until i fix the real problem + [*] Improved vuDIV for .x to .x cases + [+] Added PEXTRW/PINSRW + (linuzappz) + + * 05/08/05:[-] Fixed a couple of VU-Rec ops, swapped some over to the interpreter to fix 2d + (thanks to fumofumo for the tipoff) + [-] Yes more timing stuff :P think its as compatable as its going to get now. + [*] DVD/CD reads now considerably faster, some loading times down by 1600%!!! + Compatability still good :) + [-] Fixed a bug in 128bit hardware reads + [-] Fixed a bug i made in VU which messed graphics up eg Frequency and Sega Tennis + (refraction) + + * 04/08/05:[-] More IOP/EE timing fixes, Thanks for the info linuz + [-] Vsync speeds shouldnt be as insane as they were, the target was set as 1/4 the rate + instead of 1/2! + [!] 32bit timer interrupt checks arent working properly eg. overflow only works + when checking if they are equal, on target only works on overflow :/ but the checking + method which was used in SafeIOP is more accurate. + (refraction) + + * 03/08/05:[-] Fixed FTOX in VUrec + (linuzappz) + + * 31/07/05:[-] Reverted to old Branchtarget's, seems to stop the RecUNKs + [-] Fixed the timing a bit between IOP & EE, shouldn't need SafeIOP anymore :D + [-] Removed Pointless While loops on GIF & SPR, also fixed condition where SPR0 + couldn't use destination chain mode + [!] Gating on the IOP needs checking/completing, i have no docs on it so + i can't check the values for it, they seem different to EE :/ + [-] Removed GS Stall checking on GIF, could have caused it not to send data. + [*] Added code for faster V4-32 unpacks when CL == WL + [*] x86 code kept in int format for rec instead of U8 to INT conversion (GiGaHeRz) + (refraction) + + * 21/07/05:[-] Fixed setting of the Double Buffer + [-] Fixed the Branchtarget's for R3000A and R5900 + [*] Altered some stuff in vuflags, seems to give a speed boost :P + (refraction) + + * 18/07/05:[-] Removed some useless & 0xffff on Hw.c + [-] Reverted Sif changes to the old code, + gotta speak about this with refraction + [+] Added VIF1_STAT at vif1Write32, to handled the FDR bit + [-] VIF1 now clears qwc with "from memory" transfer + (linuzappz) + + * 17/07/05:[-] Fixed a bug in VIF where done could be unset + [-] Fixed a couple of bugs in VU + [*] Rewrote SIF0, seems to be a bit more compatable as well + as more readable + [-] Altered the loops for vu(1/0)exec, seems to fix some graphics + without comprimising compatability + (refraction) + + * 12/07/05:[-] Fixed VIF stop on stall occurance (refraction) + [-] Added two common funcs for source chain dmas + [-] Fixed REFE and END (even once again) in dmas + [-] Fixed interrupt i bit issue in Vif1 (on multiple i bits) + (linuzappz) + + * 08/07/05:[-] MFIFO now wraps around the ring buffer when it's full, + in both GIF/VIF1 + (linuzappz) + + * 06/07/05:[-] Bug in DIRECT/HL caused vif errors + [-] Recomitted old VIF0 IRQ stuff, seemed to cause problems + (Refraction) + + * 05/07/05:[-] Fixed IRQ setting for VIF0 transfers (Refraction) + [*] Removed Call/Ret from SPR1, not used (Refraction) + [+] Added some more comments to DMA stuff (Refraction) + [-] Reimplemented the old vifunpack code, since saqib's one + had problems 'in pieces' transfers + [-] ElfHeader now loads the data though the program headers only + [-] Removed tadr+= 16 on refe/end on dmas + [!] I'll start commenting more the code from now on, so it'll + be easier for us to understand every part of pcsx2, others + pls do the same kthx + (Refraction-linuzappz) + + * 02/07/05:[-] Added saqib vif fixes + [-] Fixed Stall canceling stat bits (VIF_STAT_INT) + [-] Fixed Stall bits clearing + (Refraction-saqib-linuzappz) + + * 29/06/05:[*] iR3000A now has defines same as iR5900.c + [+] Added PSXCNT_LOG + (linuzappz) + + * 28/06/05:[+] Added InterlaceHack (usefull for Dinasty Warriors 3) + [+] Added SafeCnts flag, which makes very accurate iop counters, + but they make pcsx2 slower + [+] Added FastIopClk which sets the PsxClk to 86864000 + [!] There are sorta hacks and will go away in time when i figure + out how to really fix them + (linuzappz) + + * 28/06/05:[+] Added SIO plugin specs. Should be followed by compatible plugins :P + (Florin) + + * 22/06/05:[-] Console supports colors ;) + (linuzappz) + + * 19/06/05:[-] Fixed D/T flags (added interrupts) at VU0/1 + (linuzappz) + + * 18/06/05:[-] Rather small fixes to last update in vif, replaced cyclenum + with _vif->cl, also fixed a timing issue in vif0 + (linuzappz) + + * 14/06/05:[-] Alot of bugs fixed in VIF. Masking was not correct so was + filling write. VIF0 was incorrect. The VIF0 Fifo was always being set to 0. + [-] dmaSPR1 now handles Transfer Tag option. + (saqib and Refraction) + + * 31/05/05:[-] Fixed UNPACK's with masking + (linuzappz) + + * 29/05/05:[-] Fixed a few small timing issues with VIF1 + [-] Refixed end source chain tag to add 16 to tadr + (linuzappz) + + * 28/05/05:[*] Uncommented GS dma irq code + [-] Fixed some switch cases at iVUmicro.c + [-] Tested/Fixed DIS_S/MAX_S/MIN_S at iFPU.c + (linuzappz) + + * 28/05/05:[*] removed CVT_S regcaching from iFPU.c. That doesn't seem to work properly :~ + [*] Added MAX_S , MIN_S to reg caching iFPU.c . Not tested but should work + (shadow) + + * 28/05/05:[+] Added PNOR and impoved a few more MMI opcodes + [+] Few more opcodes to ix86_sse.c as well + (shadow) + + * 26/05/05:[+] added emulated hardware cd/dvd sector decryption routines + (should make the playstation2 logo display correctly now amoung other things) + [-] fixed cdvdReadKey to get correct args sent to it, also behaves a little differently depending on args + (loser) + + * 26/05/05:[*] fixed a silly bug in iMMI.c pmaxh,pminh opcodes + [*] optimized PCEQB,PCEQH,PCEQW,PCGTB,PCGTH,PCGTW + [*] few more addes to ix86_sse.c . Linuz seemed to discovered new opcodes :P + (shadow) + + * 25/05/05:[+] Few opcodes added in ix86_sse.c .Few of them still needs recheck (shadow- Gabest) + [*] Added the following SSE,SSE2 opcodes to iMMI.c . + paddub,padduh,paddh,pcpyld,pmaxh,pminh,paddsb,paddsh,psubsb,psubsw,paddb,paddw,psubsb,psubsh + psubb,psubb,psubh,psubw + [*] More opcodes in SSE2 . PCPYUD,PSRLW,PSRLH,PSRAH,PSRAW,PSLLH ,PSLLW ,PCEQB,PCEQH,PCEQW,PCGTB,PCGTH,PCGTW + (shadow) + + * 24/05/05:[*] PSXCLK now can be changed at the ini, defaults + to the right value (36864000) if it's 0 anyways + [-] Fixed bug at Interpreter.c for sstates + (linuzappz) + + * 23/05/05:[-] fixed a bug in iMMi.c PADDSD,PSUBSD doesn't exist in ix86 (who added them?) + [*] Added a new prefix in ix86_SSE opcodes, it is now more clear to understand what each opcode do + that also helped to find that linuz had added an SSE2 instruction in iFPU.c bad linuz ;P + [-] PSMAXSW,PSMINSW was writing to a XMM register (the SSE version writes to MMX register) fixed.. + [+] a few more SSE2 instructions needed for iMMI.c added in ix86_sse.c . + Linuz code them properly pls ;) + (shadow) + + * 23/05/05:[+] Added Devc++ 4.9.9.2 project files for compiling mingw32 with IDE :) + [-] Added a few missing defines + [!] still my project file can't make as fast exe as linuz's sse build. can't figure why yet + the problem should me around Makefile.win :~ + (shadow) + + * 21/05/05:[-] More PS1 compat fixes + [!] Seems the GPU->GS is done by the PS1DRV, gotta find out more + (linuzappz) + + * 19/05/05:[+] PS1 games kinda boot now as well ;) + (linuzappz) + + * 16/05/05:[-] Misc GUI fixes + (linuzappz) + + * 15/05/05:[-] More FPU regcaching stuff + [!] Finally changed version to 0.9 :D + (linuzappz) + + v0.8: + * 10/05/05:[-] Added some more code for FPU regcaching, still unused + [!] Code is closed today until release, only bugfixes now + (linuzappz) + + * 08/05/05:[-] More fixes/improvemets to VUrec/iFPU + (linuzappz) + + * 07/05/05:[-] COP2 now is recompiled when VUrec is enabled + (linuzappz) + + * 05/05/05:[-] Fixed a rather small bug in VIF1 unpack + [-] VU random functions are more correct now, thx goes to _Riff_ and saqib + [-] Sio arranged better for sstates + [*] Modified "FireWire" syms to "FW" + (linuzappz) + + * 04/05/05:[-] Timings in VIF1 should be more accurate now + [-] Fixed bug in the elf loading filter + (linuzappz) + + * 02/05/05:[*] recommited the old reccop2.c (shadow) + + * 02/05/05:[-] Fixed bug in REF/REFS dma at VIF1/GS MFIFO + (saqib) + + * 02/05/05:[*] Cpudialog will disable the checkboxes if the requested features not found + (shadow) + + * 01/05/05:[*] Created a new <> folder to clean up some stuff in the existing interface + Folders became better now and included project files only for vcnet2003 so far... + [*] Moved all patchbrowser source to patchbrowser.c and added language support for it + [*] CpuDialog is now At CpuDlg.c . Redone the Dialog a lotta better ;) + [*] The remaining console,thread,patches configure are now part of the main menu + [*] Organize cpudetection code a bit better. Cpudetection is now done in winmain.c + (shadow) + * 01/05/05:[*] Even more VUrec stuff + [+] Added some more Stats.c info per frame + [-] Fixed some MFIFO bugs remaining + (linuzappz) + + * 30/04/05:[*] More work on VUrec, flags are somehow implemented, PSHUFD is now + used to unpack XYZW/IQ stuff and bug fixes as well :) + (linuzappz) + + * 29/04/05:[-] Updated VIF0 code as VIF1 + [-] VIF1 doens't clears str on MFIFO (saqib) + [-] Fixed some MFIFO bugs in both GS/VIF1(saqib) + [*] Cleaned up dma## functions + (linuzappz) + + * 28/04/05:[-] end source chain tag seems not to touch tadr + [-] Vif1 dmaIrq seems ok, need to test it more tough + (linuzappz) + + * 26/04/05:[+] Added BC1 ops at rec + [+] Started some optimizations for UNPACK + [-] Skipping vif mode was still a bit buggy, seems ok now + (linuzappz) + + * 25/04/05:[*] VIF1 dma is kinda more asynchonous now + (linuzappz) + + * 22/04/05:[+] Added some cache code, not used by default, + it's just for testing atm + (linuzappz) + + * 21/04/05:[-] Readded SysPrintf for Cd/DvdReads + [-] Fixed NReady busy stuff, thx to loser + (linuzappz) + + * 19/04/05:[-] More VUrec refixing/work + (linuzappz) + + * 18/04/05:[+] added CDVDreadSubQ, CDVDgetTOC, CDVDctrlTrayOpen, CDVDctrlTrayClose + to the cdvd plugin definitions + [*] changed CDVDgetType to CDVDgetDiskType + [-] fixed NVM (eeprom) access from cdvd stuff + [+] added reading of mecha version from an external file + [-] fixed raw dvd sector readng for dvd9 discs (otp and ptp) + [+] added hw-reg read/write for DecSet register 0x1f40203A + [+] made cdSeek change the current cdvd sector + [*] incremented cdvd plugin version and brought cdvd plugins in cvs up to date + (loser) + [-] __WIN32__ should not be used over PS2Etypes.h, + __MSCW32__ or __MINGW32__ must be used there + (linuzappz) + + * 16/04/05:[-] xyzw stalls was kinda wrong ;P seems ok now + [-] Fixed a bit vurecs + (linuzappz) + + * 16/04/05:[+] Added GSsetCSR + (saqib-linuzappz) + + * 14/04/05:[-] Fixed some small bugs over VUops.c for the regsNum stuff, + thx goes to saqib + [-] Really fixed skipping vif this time :P + [-] Fixed DIV/RSQRT exceptions (saqib-linuzappz) + (linuzappz) + + * 13/04/05:[-] Fixed skipping write mode at Vif + [-] Fixed unpack overflowing + (linuzappz) + [-] updated all visual studio project files to have the correct source/lib/includes + also removed unused dirs and files. now even 'debug config' will build ok :) + (loser) + + * 10/04/05:[-] Fixed stalls for VF regs, xyzw must be handled separatedly + (linuzappz) + + * 04/04/05:[-] Kinda reworked VU MAC flags, also fixed RSQRT neg prob, thx to Riff/saqib + [-] Fixes to VifDma cl/wl stuff + (linuzappz) + + * 03/04/05:[*] Modified a bit how branch works on VUs + (linuzappz) + + * 02/04/05:[-] Some fixes to VU flags/pipelines + (linuzappz) + + * 30/03/05:[*] Cleaned up a bit writes to VIFX regs + [-] Fixed a small bug in vif1 stalls + [-] Commented readclock win32 code at CDVD.c + [-] Fixed two small issues with branches in interp + (linuzappz) + + * 29/03/05:[*] Added some fixes from LDChen to hConsole/WM_PAINT stuff + [*] Modified GetToc stuff in cdvd to support dvds + [*] cdvdTD uses lsn now + (linuzappz) + + * 28/03/05:[+] Added placetopatch == 1, which means patches will be applied every vsync ;) + (linuzappz) + + * 27/03/05:[*] Improved cpu detection for Amd's 64 using BrandID. Most of the models should be + detected correct now (at least my cpu does ;P) (shadow) + + * 24/03/05:[-] Fixed ISUBIU :D + [-] Some fixes to flags, status was fecked, now seems better + [-] Commented the GS dma IRQ again, seems to cause more probs, will + fix it later + (linuzappz) + + * 21/03/05:[-] Fixed VIFX_CODE reg + [+] Added previous normal vu fixes to vurec + [*] Added EFU pipeline to VUmicro, kinda untested + [-] Hopefully fixed GS dma IRQ + (linuzappz) + + * 21/03/05:[-] Fixed cdReadKey function to emulate correctly. Now games should boot using + Execute with non Modded Bios. + (auMatt & Loser) + + * 20/03/05:[-] Started reg caching for VUrec ;D + [*] Rearrenged a bit rec32/64, added a common 'x86' dir + (linuzappz) + + * 18/03/05:[-] VU1micro was being executed instead of VU0, bad linuz bad :P + [-] Fixed VU's JR/JALR/BAL :D + (linuzappz) + + * 17/03/05:[-] Cleanup/speedup/fixup from last fixes :P + [-] VIF0 has no from mode ;) + (linuzappz) + + * 16/03/05:[*] Added some more debug info to CDVD + (auMatt) + + * 15/03/05:[*] Added an evil vuDouble function to convert from vu float format + to ieee 754 format ;) + [-] Reworked MAX/MINI + [+] Added experimental work for VU pipelines + (linuzappz) + + * 14/03/05:[-] Added the pipelines VU1 bug to VU0 + (linuzappz) + + * 12/03/05:[-] Fixed some pipelines bug within VU1 + (linuzappz) + + * 10/03/05:[-] Fixed small bug over VIF1 dma FIFO + (linuzappz) + + * 08/03/05:[-] Fixed vu flushing over vuExecMicro + (linuzappz) + + * 06/03/05:[-] Fixed UNPACK V4_5 with mask + (linuzappz) + + * 04/03/05:[-] Small fixes to VUrec + (linuzappz) + + * 01/03/05:[+] Added new VS2005 sln and .vcproj to build the 32bit version of + pcsx2 in VS2005 Beta1 (You have to use different projects to build) + [*] Changed some of the files to make them compile in VS2005. + (GoldFinger) + + * 23/02/05:[-] Commented hack at VSync + [+] Added interleave mode for SPR + (linuzappz) + + * 20/02/05:[*] Changed IRQ defines to functions + [-] VIF irq by vifcode seems ok now + [+] Added offset/difference with mask UNPACK modes in vif + [-] SPR1 was transfer /4 the size it should have :/ + (linuzappz) + + * 16/02/05:[-] Fixed some VPU-STAT related issues + [+] 'sstates' dir is now created at init + (linuzappz) + + * 15/02/05:[+] Another DNAS by nachbrenner + (linuzappz) + + * 14/02/05:[+] Added offset/difference UNPACK modes in vif + (linuzappz) + + * 14/02/05:[*] 64bit rec back to x86-64 better this way + [*] Updated vsnet2005beta1 project files to compile with x86-64 dir + [*] Added a few pragma warnings disable to a few files to correct some silly vsnet2005beta1 + warnings (blah MS deprecated stdio pffff) (shadow) + + * 10/02/05:[-] Fixed bug in CDVD_findfile + (linuzappz) + + * 09/02/05:[*] GSvsync is now called on the vsync start + (linuzappz) + + * 08/02/05:[*] QWC is set to 0 after a normal transfer in GS/SPR1/VIF1 + and MADR is increased qwc*16 as well + (linuzappz) + + * 07/02/05:[*] Changed a bit the CPU dialog. Now it is better i guess :) . Linuz update the pcsx2.po again pls :D + [*] Added a __VCNET2005__ definition cause vcnet2005beta1 doesn't support inline asm.. + That only effects Cpudetection.c and Gte.c (not much since it has c code instead to use). + Cpudetection must be written on ml64 to be useable on vcnet2005. + [*] Added prelimary vcnet2005beta1 project files. gettext disabled cause we still doesn't have a + 64bit version for it + [*] Small fixes around and wholaaa i produced the first buggy pcsx2_64bit.exe :) (shadow) + + + * 07/02/05:[+] Added another DNAS version thx to Nachbrenner + [-] Fixed SPR0 chain mode + [-] Fixed intc/dmac interrupts that gets cleared right away + [-] Cleaned up langs a bit, only spanish works so far now + [+] Added 1f402007 reg, "BREAK", still not really handled ;P, + thx to matt + (linuzappz) + + * 31/01/05:[-] Disabled recompile functions recBGEZ() and recBLEZ() from iR3000.c . That will make + recompiler not to crash after the players selection screen in Virtua Fighter 4. Thanks + to JayteeMaster for bug tracing it ;) (shadow) + + * 30/01/05:[-] Fixed some more bugs in IPU, some streams had a few problems, now they are ok. + Quality of ipu playback also improved(thanks JayteeMaster for pointing it out) + (asadr aka saqib) + + * 26/01/05;[*] Merged a big part of linuz's amd64 rec to the normal rec (shadow) + + * 25/01/05:[-] Finally fixed IPU. Mpegs and IPU streams run + so do PSS files.Some fixes to MMI and Interpreter (asadr) + + * 23/01/05:[-] Fixed vurec crash on pcs that doesn't support SSE2 + [*] General cleanup on ix86-32 rec. Goldrec removed since no progress has been made + lately (shadow) + + * 20/01/05:[+] Added x86-64 rec, still unclean, but i'll leave for holidays this + saturday, so ^^ + (linuzappz) + + * 19/01/05:[-] Fix for end chain mode at vif, still gotta check it + (linuzappz) + + * 16/01/05:[-] Lots of fixes to VUmicro, thx to Refraction for pointig out + some bugs ^^ + [*] Commited ix86 changes for x64 + (linuzappz) + + * 13/01/05:[-] WriteNVM now takes address in param[1], thx to auMatt + (linuzappz) + + * 06/01/05:[-] Fixed long types at VU.h + [-] Removed memLUT decl + [!] Notice long is 8bytes long in amd64, so we should + stick to the u32/s32 types instead of using long pls :) + (linuzappz) + + * 06/01/05:[-] Fixed SBUS IRQ at iop + [-] dmaGetAddr now uses memLUTR + (linuzappz) + + * 05/01/05:[*] Changed some stuff for amd64, still more to come + [+] Added more memRead/Write funcs for speed + [-] Fixed some iVUmicro bugs + [-] Fixed QFSRV/MTSAH/MTSAB + (linuzappz) + + * 03/01/05:[+] Implemented MFLAG for VU0 + [-] Vsync is now genereted on boths cpus at the same time + (linuzappz) + + * 03/01/05:[-] Commented dma irq stuff, fecks some games, gotta recheck that + (linuzappz) + + * 22/12/04:[-] Fixed bug in branch address in vus + (linuzappz) + + v0.7: + * 18/12/04:[*] Added GSread8/16/GSwrite8/16, GSgifTransfer1 now has two args, + added the addr and pMem points to the VU1 mem + (linuzappz) + + * 16/12/04:[*] Pofis improved his Patchbrowser (shadow) + + * 06/12/04:[-] Fixed Langs support for win32 + (linuzappz) + + * 06/12/04:[*] Added support for 1.90 Bios NVM. + (auMatt) + + * 06/12/04:[+] Added Patch Browser for win32 . Made by Pofis (shadow) + + * 05/12/04:[*] NVM always loads rom %Bios%.NVM + [*] Cd/Dvd Reads are now again displayed on console, + that is very helpfull for testing :) + (linuzappz) + + * 28/11/04:[-] BC2T/F/TL/FL also check for VU1 to be stopped + [-] VU1 memory is masked on micro load/store instructions, + dunno yet about VU0, gotta test ;) + (linuzappz) + + * 26/11/04:[-] VU D/T flags are handled now (kindaof :P). + (linuzappz) + + * 20/11/04:[-] RTC timezone fixed (gigaherz) + + * 19/11/04:[-] Fixed vu branch negative overflows + (linuzappz) + + * 17/11/04:[*] Fixed some stuff in logging so now + -Memcards sysprintf messages Are now MEMCARDS_LOG define and enable with PAD_LOG + -Cdreads Dvdreads are now in CDR_LOG + -Vus Overflow enable from CPU_LOG + That means less logs at runtime and no more complains from nasty betatesters :D (shadow) + + + * 17/11/04:[*] fixed linuz bug in naming the memcards different in 2 different places + Memcards now are Mcd001.ps2 and Mcd002.ps2 in emu and McdDialog (shadow) + + * 16/11/04:[-] Fixed NVM File Load and Creation. Defaults to BIOS name als now. + (auMatt) + + * 15/11/04:[+] Added another DNAS at Misc.c. + (nachbrenner) + + * 14/11/04:[-] VU0/1 reset shouldn't reset the micro memory. + [*] Commented the LT_OpcodePrintTable call on Interpreter.c, + for speed :) + (linuzappz) + + * 12/11/04:[-] Second MCD works fine now. + (linuzappz) + + * 11/11/04:[-] Finally fixed SIF1 bug :D:D + (linuzappz) + + * 10/11/04:[+] Added MingW32 support ;). + (linuzappz) + + * 10/11/04:[-] Fixed NVM loading/reading/writing and also fixed v12 NVM issues. + (auMatt) + + * 09/11/04:[+] Added BiosVersion var. + [*] Sif stuff now gets saved over sstates. + (linuzappz) + + * 09/11/04:[-] Fixed bug in ModelID code in CDVD + (florin-auMatt) + + * 07/11/04:[-] Fixed bug that caused to interrupt twice before + handling the first interrupt. + (linuzappz) + + * 05/11/04:[-] Small fix for broken elfs. + (linuzappz) + + * 04/11/04:[-] CDVD was interrupting dma too much :P. + (linuzappz) + + * 03/11/04:[*] Added FREEZE_SIZE. + (gabest-linuzappz) + + * 03/11/04:[+] Added a couple of DNAS at Misc.c. + (nachbrenner) + + * 02/11/04:[+] Added hack for QFC in GS ;). + (linuzappz) + + * 01/11/04:[+] Implemented VU0 Interlocks over CFC2/CTC2/QMFC2/QMTC2. + (linuzappz) + + * 31/10/04:[-] Fixed v12 bios running. + (auMatt) + + * 30/10/04:[-] Fixed v9-v10 bios running. + (auMatt) + + * 28/10/04:[-] Really fixed SIF SMFLAG/MSFLAG :P:D + (linuzappz) + + * 28/10/04:[-] Fixed MCDS :D + (asadr) + + * 26/10/04:[*] Added rom2/erom support + (auMatt/linuzappz) + + * 23/10/04:[*] Added/Fixed Model Number Reading + (auMatt/Florin) + + * 18/10/04:[-] Fixed SIF SMFLAG/MSFLAG, needs testing tho + (linuzappz) + + * 06/10/04:[*] More BIOS detection added.(auMatt) + + * 04/10/04:[-] Fixed EXL bug + [-] Fixed SIF0/1 when fifo got filled up + (linuzappz) + + * 01/10/04:[-] CdReadConfig/CdWriteConfig now uses NVM ^^ + [!] Bios configurations now gets saved/loaded from NVM (nvm.raw) + (linuzappz) + + * 1/10/04:[+] Started coding the memcard manager. Far from finish but will be better soon :D + [*] Change settings . Default memcard is now a ps2 memcard. (shadow) + + * 30/09/04:[-] Newer bios will now work with pcsx2. Pads fixed, more + compatibility. (asadr) + + * 27/09/04:[-] VU0/1 Reset hopefull fixed + (linuzappz) + + * 16/09/04:[*] NVM now is readed/written from nvm.raw. + [-] ReadILink/GetMecaconVersion are really implemented ^^ + (auMatt-linuzappz) + + * 15/09/04:[*] Rewritten LoadConfig-SaveConfig to use ini instead of registry. + [*] Removed DeleteRegistry button. For obvious reasons :D (shadow) + + * 15/09/04:[-] Pads finally fixed - Fixed PADwin Plugin Required! + Updated stuff at PsxSio2.c and Sio.c (Pad Hack still + there but won't be used) asadr + + * 13/09/04:[+] Added an option for setting the main thread priority, usefull at work :P + (linuzappz) + + * 13/09/04:[*] Fixes to SCMD's (auMatt) + * 02/09/04:[*] Finally commited asadr's IPU changes + [-] Fixed small bug in memory dump for win32 + (linuzappz) + + * 30/08/04:[*] Added Deci2 Call back into the Interpreter.c for printf.(auMatt) + + * 18/08/04:[*] fixed finaly the IOP disasm. Should be correct now... + [+] for stuff for amd64 porting.notice that pcsx2_2003_amd64 project just check for 64bit + portability.Can't produce 64bit exe yet.. (shadow) + + * 15/08/04:[-] fixed the IOP-EE disasm problem.(for ppl with vc != 2003 you must include + DisR3000asm.c and DismAsm.h in your project files) + [!] Still have to finish the correct IOP disasm :D + [+] Added small framework for x86-64. Still nothing that useable :D (shadow) + + * 04/08/04:[-] PadHack now works again :) + [-] Sio2 now supports states again + (linuzappz) + + * 02/08/04:[*] F2 will print the selected state on the console + [*] Added overflow messages for ISW/ILW/ILWR/ISWR + [*] FBRST should reset the vus when VU0 Reset/VU1 Reset + bits are set, but i don't really know have much it's + resetted :P + (linuzappz) + + * 30/07/04:[-] Vif1 MPG lacked a flush + (linuzappz) + + * 28/06/04:[+] Added GSprintf, GSgetDriverInfo and PADgsDriverInfo + [+] Deci2Call 0x10 gets printed ;) + [-] Some fixes to CDVD reading + (linuzappz) + + * 24/06/04:[-] Fixed some bugs over interrupts and + exceptions + (linuzappz) + + * 22/06/04:[-] Couple of bugs fixed over IPU1 dma + [-] psHu32(INTC_STAT) changed to INTC_IRQ + (asadr-linuzappz) + + * 16/06/04:[-] Counters regs are returned in 16 bits + [+] Also added them for hwRead16 + [-] Counter only resets when writing to the mode reg + when the value & 0x3ff differs from the actual mode + [-] Fixed BCR interrupting for CDVD ;) + (linuzappz) + + * 14/06/04:[*] Enabled SSE Vif at i386.asm + [*] Added florin's work over mcds, still needs + a cleanup :) + (linuzappz) + + * 10/06/04:[*] CSRr is OR'd with 0x8 always over VSyncEnd + (linuzappz) + + * 08/06/04:[*] VU pointer gets aligned now + (linuzappz) + + * 07/06/04:[*] vuJALR now uses _Fs_*8 + [*] VSync now interrupt for INTC_STAT & 0x1 + [!] Based of debugging Aura for Laura :) + (linuzappz) + + * 07/06/04:[*] Detection for Chinese Bios. + (auMatt) + + * 04/06/04:[-] Cleaned a bit VIF1transfer, and now when + DIRECT/DIRECTHL cmd is misaligned it skips it + [*] Added USBirqHandler, USB specs v2 now + (linuzappz) + + * 04/06/04:[-] VU1 regs now are mapped to VU0 mem + (linuzappz) + + * 02/06/06:[*] finished most of the iops disasm works :P + [*] increase the firewire reg size . seems there are more regs ;p (shadow) + + * 25/05/04:[-] Fixed big stupid bug in counters ;P + Now they're accurate + (linuzappz) + + * 25/05/04:[*] Included new BIOS detection 'P'. For Free and Public BIOS Images. + (auMatt) + + * 20/05/04:[*] BCR now decrements for each cdvdReadSector, thx to Roor + (linuzappz) + + * 18/05/04:[*] Removed PsxBios2.c/h, and cleaned Irx funcs from ElfHeader.c/h + [+] Added cpuTestHwInts/cpuTestINTCInts/cpuTestDMACInts + [+] More work over mcds + [!] Now cpuBranchTest doesn't checks for irqs + (linuzappz) + + * 17/05/04:[-] Fixed bug psx counters, thx gold + (linuzappz) + + * 15/05/04:[+] Added IOP disasm in the debugger... + [!] Not yet finished and i disasm using R5900disasm atm. I will code it correctly soon (shadow) + + * 12/05/04:[*] More optimizations to the FPU rec + [*] BIAS was commented over Counters.c :/ lol + [-] Disabled EMMS_TRACE, it's buggy, dunno why tho + (linuzappz) + + * 11/05/04:[*] Now Saving/Loading FPU CW only for the ops that really need it + [*] Added some optimizations to jumps over rec + [*] Added LQC2/SQC2 + (linuzappz) + + * 08/05/04:[-] Fixed states loading/saving from the menu + (linuzappz) + + * 03/05/04:[*] Uncommented the sio2 1d100 hack + (linuzappz) + + * 3/05/04:[+] started reorganize the src. Lotta unneccesary stuff removed(like HLE) and lotta + organized. still needs a lot of work to clean but it's a start (shadow) + + * 02/05/04:[+] Added D3/D4 defines at Hw.h + (linuzappz) + + * 1/05/04:[+] Added a Delete Registry Button. It deletes pcsx2,gssoft settings + (shadow) + + * 30/04/04:[-] Small bugfix for cpu speed detection, thx to Rainer Aglas + (linuzappz) + + * 27/04/04:[+] Implemented ReadNVM/WriteNVM, untested though + (linuzappz) + + * 15/04/04:[-] SPUdma timings changed to 80, thx _Riff_ + (linuzappz) + + * 13/04/04:[*] Several changes for IPU + [+] IsBIOS now belongs to Misc.c + (asadr-linuzappz) + + [*] Dev9Null/USBLinuz/FireWire Plugins Configure now shows + Message. + [*] Configure Menu Shows USB and FireWire Options. + (auMatt) + + * 12/04/04:[-] Fixed SPU2async cycle, was getting reseted wrongly, + thx _Riff_ + [+] Added SPU2irqCallback + (linuzappz) + + * 08/04/04:[-] Unhacked sio2 for 1d100 recv1 mode + (linuzappz) + + * 07/04/04:[+] FireWire IRQ is implemented ok now + [-] Forgot dev9Interrupt over R3000A.c + (linuzappz) + + * 05/04/04:[+] Added Firewire plugin protocol. (shadow) + + * 02/04/04:[-] VIF-FIFO transfers seems to work better ;) need testing + [+] Added GSreadFIFO + (linuzappz) + + * 30/03/04:[*] sstates now use CRC as well :) + [*] emuLog.txt now goes at 'logs' dir + (linuzappz) + + * 29/03/04:[*] GSdma now waits qwords transferred cycles + before clearing CHCR and triggering, + OPH and APATH are handled as well now. + (linuzappz) + + * 25/03/04:[*] Some rewritte over Sio.c + (linuzappz) + + * 24/03/04:[-] ostr size is now 1024 in Dis*.c + [!] Finally v0.7 :D + (linuzappz) + + v0.6: + * 21/03/04:[-] About dialog was cropping the testers, thx CK :) + (linuzappz) + + * 19/03/04:[+] Added a PadHack option at the cpu dialog + [-] Couple of fixes for release + (linuzappz) + + * 16/03/04:[-] CRC had a bug, now it's ok, sorry :) + [-] Fixed bug in memInit, thx gold :) + [-] LoadState now loads the tlbs + (linuzappz) + + * 16/03/04:[-] Fixed another silly bug in loadElfFile ;) + (linuzappz) + + * 15/03/04:[+] Added texts on the console Title to make patch makers life easier :P (shadow) + + * 15/03/04:[-] Fixed a free in loadElfFile + (linuzappz) + + * 14/03/04:[*] loadElfFile now reads the whole file first + [-] DMA4/7 interrupt timings are more accurate now + (linuzappz) + + * 12/03/04:[+] Added i386.asm, only used on linux so far, + compiles using nasm, it replaces the inline + assembling for Vif + [-] Fixed bug in UNPACK for skipping write + (linuzappz) + + * 11/03/04:[*] Patches names are now using crc, + instead of ie.: + Ridge Racer V PAL was SCES_500.00.pnach, + now it is 5BBC2F40.pnach + this way games with the same code + will not get confused + [-] Fixed bug in disR5900GetUpperSym + (linuzappz) + + * 10/03/04:[+] Added SQ/LQ over iVUmicro + (linuzappz) + + * 09/03/04:[-] sio2Reset was missing from psxHwReset + [-] Fixed sio pad swaps for 0x1100 mode + (linuzappz) + + * 08/03/04:[*] Modified the DEV9irq stuff + (linuzappz) + + * 05/03/04:[-] DEV9irq now issues a SBUS_IRQ + [*] malloc memory is now aligned to 16bytes + (linuzappz) + + * 02/03/04:[-] Added presaving for MMI ops that needed that + [-] vuJR/vuJALR now masks Fs with ~0x7 + (linuzappz) + + * 02/03/04:[+] stats.txt now dumps the Cpu mode + (linuzappz) + + * 01/03/04:[-] dmaGetAddr now uses memLUT + [+] Finally added USB plugins ;) + (linuzappz) + + * 26/02/04:[-] Fixed VUops that didn't have presaving + (linuzappz) + + * 22/02/04:[+] More ops at DisVUops.h + [-] VifDma.c is cleaner now, also fixed some + stuff for savestates + [-] Added some remaining vars at cpuBranchTest + to cpuRegs for savestates + (linuzappz) + + * 21/02/04:[-] Fixed ITOF0 + (linuzappz) + + * 20/02/04:[-] Fixed savestates ;) + (linuzappz) + + * 19/02/04:[*] VUflags now should handle overflow/userflow ok + (linuzappz) + + * 17/02/04:[*] Improved and fixed sio2 stuff + [-] Fixed _vuMFIR + (linuzappz) + + * 16/02/04:[-] DIV could crash when divisor == 0.0, + as well as ERSADD + (linuzappz) + + * 13/02/04:[-] Fixed FCAND over VU1ops.c + [*] Merged VU1ops/VU0ops/Vops to VUops + [*] Cleaned up VUflags.c + [-] hackedRecv now defaults to 0x1100 + (linuzappz) + + * 09/02/04:[-] VifDma.c MPG now clears the VU0/1 Mem though Cpu->ClearVU0/1 + (linuzappz) + + * 09/02/04:[-] More SCMD functions added. Still require working code for them. :) + (auMatt) + + * 06/02/04:[-] More iVUmicro.c opcodes, fixes, and stuff :) + (linuzappz) + + * 06/02/04:[-] Fixed Bios Detection for HK Bios. + (auMatt) + + * 06/02/04:[-] Fixed CdRom/CDVD Interrupt for 0x41000200 chcr, + now it interrupts after the read ends + (linuzappz) + + * 05/02/04:[+] Added FALSE/TRUE in GRecCommon.h + [+] Added more 16bit ops to x86.c/h + [+] Fixed SQD/SQI and implemented IOR/IAND over iVUmicro + [-] BIAS is now 2 again + [+] Now only MARK is writable at Hw.c, for vif0/1Regs + (linuzappz) + + * 03/02/04:[-] Fixed FALSE to GREC_FALSE in GRecMain.c + [-] Added break to F5 ;) + [-] Commented 'ERL Set' SysPrintf + (linuzappz) + + * 02/02/04:[-] Included stdarg.h in GRecCommon.h + [+] Added some scmds to CDVD.c (auMatt) + [+] cdvd.Status now changes to CDVD_STATUS_SEEK_COMPLETE, + after a CdRead CMD + (linuzappz) + + * 16/01/04:[+] Added LQI/LQD/SQI/SQD over Recompiler + [-] Updated EE BIAS to 8, need to test this + [!] SQI/SQD are diabled still, because they are still buggy + and i don't have more time... vacations :D:D + (linuzappz) + + * 15/01/04:[+] Added code for dma interrupts, for IPU FDEC, still commented + (linuzappz) + + * 14/01/04:[+] Temporary fix for PADS. F5 now change the pad mode on the fly. if your game doesn't work + switch it from there. (shadow) + + * 12/01/04:[*] FIFO is now really 128bit as it should be :) + (linuzappz) + + * 09/01/04:[-] Fixed VU->VI[0] != 0 bug in VU1, VU0, Macromode, JALR, BAL, LQI, LQD, SQI, SQD were setting VU->VI[0]. + Address in VCALLMS was incorrect, was causing crash in VF4. + (asadr) + + * 09/01/04:[*] Start to convert VIF stuff to functions instead of macros, right now I just converted + them duplicating the code for VIF0/1 and it is working but it is not the best yet, + commiting just so others can debug VIF while I finish the work, did some minor fixes + on VIF too. + [*] The only major function needed to be reduced to one is VIF0transfer/VIF1transfer, all + others are ok (I think). + (GoldFinger) + + * 09/01/04:[-] Fixed LQC2 for _Fs_ == 0 + [*] Fixed several SIF bugs (NoComp) + (linuzappz) + + * 06/01/04:[-] Fixed UNPACK modes in VIF.c, the Indeterminate fields are now set to 1 by default. (asadr) + + * 04/01/04:[-] Fixed a bug in LQD and LQI when _Ft_ was 0. + (GoldFinger) + + * 04/01/04:[*] Modified the VIFregister structure, both VIFs(0,1) have the exactly same + structure, just VIF0 does not use some of the registers, so I unified the + structures for easy reading and less bugs. + (GoldFinger) + + * 31/12/03:[-] Added USB API at PS2Edefs.h ;) + [+] More ops at DisVUops.h + [*] UNPACK now flushes the VU micro + (linuzappz) + + * 30/12/03:[-] Fixed savestates in Misc.c and added fixed VIFdma.h + (asadr) + + * 29/12/03:[-] Fixed bug for new vu code in savestates + (linuzappz) + + * 27/12/03:[-] Fixed bug in my last update for R5900.c + (linuzappz) + + * 26/12/03:[*] VUflags are now updated when Rd == 0 as well, changed only + at VU1ops.c + [*] VU1Regs/VU0Regs is now VURegs + [!] I think we should merge VU1ops/VU0ops/Vops + (linuzappz) + + * 24/12/03:[-] Fixed DMA8 for PsxDma.c + [*] LUI is now as it was before asadr's update + [*] readded 'if Log' over debugI at Interpreter.c + (linuzappz) + + * 18/12/03:[*] Restructured VU code and VIF. Fixed stuff in Interpreter + and added CTC2 VU1 Microinstruction caller. + (asadr) + + * 18/12/03:[*] Improvements to TLB code. + (linuzappz) + + * 17/12/03:[-] Fixed iCOP0.h define to __iCOP0__ + [+] Added disR5900GetUpperSym + (linuzappz) + + * 08/12/03:[+] SSE recompile of vus started. Some opcodes works some yet not. + Missing flags that i gonna add soon. Gives some speed boost :) + (shadow) + + * 05/12/03:[-] Fixed bug in GS for CSR stuff + (linuzappz) + + * 03/12/03:[+] Added Interlock for CFC2/CTC2 + [-] Fixed CFC2/CTC2/LQC2/SQC2, they now + check for zero registers + [!] This should fix the 'R0 is not zero' + and the 'VU memory overflow' hopefully ;) + (linuzappz) + + * 03/12/03:[+] You now disable vu0 macromode too when you disable vu recs by the + checkbox iR5900.c + [-] corrected some more bugg0rs of linuzappz sse instructions ix86_sse.c + [!] careful linuzappz's code. Don't ever trust him cause he is a lazy bugg0r + His mind is only for pampita!:) (shadow) + + * 02/12/03:[+] Added UNPCKLPSRtoR and MOVLHPSRtoR, there you go expert :) + (linuzappz) + + * 01/12/03:[-] Fixed savestates + (linuzappz) + + * 30/11/03:[+] a few sse instructions in sse.c + a much cleaner code in reccop2.c (shadow) + + * 28/11/03:[-] Recommited old VUops.h with asadr's EATANxy/EATANxz bugfixes + [!] asadr, the VUops.h you commited was in html format, + and some opcodes you change were only to slow down things, + commit other ones that need fixes. + (linuzappz) + + * 28/11/03:[-] Fixed alot of bugs in VUops.h, hopefully some stuff will now + work as it should (asadr) + + * 27/11/03:[*] fixed some stuff in recCOP2.c (shadow) + + * 25/11/03:[-] Fixed bug in Vif.c, masks hopefully are ok now ;) + (linuzappz) + + * 25/11/03:[-] Fixed Interpreter.c ix86.h include + [-] Added to GRecBasic.h a newline at end of file, linux + complains else ;P + (linuzappz) + + * 24/11/03:[+] Added cpu detection in interpeter.c too so now you can use SSE,MMX code in + interpreter too without the fear that your pc doesn't support it ;P (shadow) + + * 22/11/03:[+] By Nachbrenner request i added a memory patcher in Debugger.Now you can make patches + while pcsx2 is running. It is not yet finished as it can only patch 32bit data but + all the patches so far are 32bit so no problem ;P (shadow) + [!] Update Common.h . We are up to pcsx2 0.6 now! :) + + v0.5: + * 21/11/03:[-] Fixed Vif.c bugs :) + [!] Source code closed for release 0.5, + only bugfixes con be submitted + (linuzappz) + + * 21/11/03:[*] Started optimizing vif.c but I find so many strange stuff (problably bugs), please + linuz, check the comments I added on the vif.c file, and tell me what to do. + (GoldFinger) + + * 20/11/03:[*] Reorganize the cpu dialog a bit and added a new option. Disable vu recompiler + with disable the recompile of vu and will save some games from freeze when + vu memory overflows (quite often) :P . This will be removed when vu problems will + solve :P (shadow) + + * 19/11/03:[-] Fixed bug in savestates + (linuzappz) + + * 18/11/03:[*] Removed the old reg caching code including the sources, so now the only rec + that works is the normal one. + [*] GoldRec is progressing, I did lots of changes to make the progress better, + right now nothing works. + (GoldFinger) + + * 14/11/03:[*] Modified GSfreezeData, now it's plugin dependant + [+] Added SPU2freeze/DEV9freeze + [-] Fixed bug in inifile_read, hi shadow bugg0r :P + [-] Fixed bug in Sio2 + (linuzappz) + + * 14/11/03:[*] change pcsx2 patch system to use *.pnach files.Now you need a patches dir in your + pcsx2. (shadow) + [!] Am i the only one that write a changelog here?? (shadow) + + * 13/11/03:[-] fixed savestate naming in win32 and linux (shadow) + + * 06/11/03:[+] cleaned up a bit rec vu and enabled vu0 micro (shadow) + + * 06/11/03:[-] Workaround in VifDma.h for FiFo transfer ;) + (linuzappz) + + * 04/11/03:[*] Changed the way we handle SPR TTE transfers, also + MFIFO_VIFtransfer transfer always the ptag+2 + [-] Unhacked Sio.c for PADs ;) + [!] Now you gotta use the PADwinKeyb from PADwin at cvs + (linuzappz) + + * 03/11/03:[-] Fixed ba8R16 bug in Memory.c + [*] Enabled VU1 micro recompilation + [*] dmaGetAddr goes though memLUTR now + (linuzappz) + + * 02/11/03:[-] Fixed MSUBA had the same problem as MSUB, iFPU.c + A = A - B * C != A = B * C - A :) + (GoldFinger) + + * 01/11/03:[+] Addded cpu speed detection in cpu detection code + [!] Linuz fix the cpu_detection.c to work with linux pls ;p + [!] Goldfinger was right.Police 24/7 is okay with MSUB now :) (shadow) + + + * 01/11/03:[-] Fixed MSUB (MADD was ok), iFPU.c + Expert, please try in Police 24/7... :) + (GoldFinger) + + * 31/10/03:[-] Fixed PCPYLD, MMI.c + (linuzappz) + + * 30/10/03:[-] disable MADD,MSUB from iFPU. that ops was causing bugs in police 24/7. + Can't figure why, can someone? (shadow) + + * 29/10/03:[-] Commented D/T flags for VUmicro + (linuzappz) + + * 28/10/03:[-] Fixed memory rec issue, blah, shadow was right again :P + (linuzappz) + + * 27/10/03:[-] More fixes to memory stuff, hwregs for 128bit + [-] Fixed vifNum == 0 for UNPACKs + [-] DEV9_R_DEV now goes though DEV9read16 :) + [+] FiFo VIF1 can read data now ;) + [-] Fixed rec mem limit, shadow was right ;P + [!] Try Make Your Dream Home now ;) + (linuzappz) + + * 26/10/03:[-] Fixed Memory stuff, now it works ok :) + (linuzappz) + + * 25/10/03:[*] Some changes to memory cards. It's not what you all expect:P + (Florin) + + * 25/10/03:[*] Improved more the cpudetection routine at the Supporting Instruction sets features + [-] Improved a bit the MMI and fixed some bugs that prevent PII cpus to work (shadow) + + * 23/10/03:[-] Fixed a bug in Memory, untested actually ;), should fix + the loader rec bug + (linuzappz) + + * 24/10/03:[*] Improved the cpudetection routine in goldrec.Now more info appears :) + (shadow) + + * 23/10/03:[*] Improved VIF for transfers in parts + [-] Fixed some bugs in DisVUops.h ;) + [-] Fixed a bug in Memory, VU1.Mem was twice, thx shadow :D + (linuzappz) + + * 21/10/03:[-] Scratchpad memory was set bigger that it really is + [-] Uncommented a few tlb related printfs just in case ;) + (linuzappz) + + * 20/10/03:[*] Rewritten several Memory.c code, it's very untested, so far + i only tested bios, so tell me what's broken now :) + (linuzappz) + + * 18/10/03:[*] Fixed the rec for use with the tlb code, but this is a no go, slow as hell we must + change the whole tlb stuff, please linuz, lets think better before implementing this. + (GoldFinger) + + * 14/10/03:[*] Modified the memRead functions, now they're better for tlbs + misses, but note that they have one more arg, so gold, you'll + have to update the rec + (linuzappz) + + * 12/10/03:[*] Removed the new module in cvs called GoldRec. Now GoldRec is a directory under ix86-32. + (GoldFinger) + + * 10/10/03:[*] Removed the new recompiler from the main project and removed its dependencies, now there + is a new module in cvs called GoldRec, you must check it out. + (GoldFinger) + + * 10/10/03:[*] Improved TLB stuff a lot ;) + (linuzappz) + + * 07/10/03:[-] Fixed bug for recompiler in psx writes from Memory.c + (linuzappz) + + * 06/10/03:[*] VU0/1 now uses the VURegs struct + [+] Added the possibility to load the System.map from ps2linux + [+] Added TLB exceptions + [-] Fixed psxM accesses from Memory.c + (linuzappz) + + * 04/10/03:[*] rewrote the recompiled vu micro startup code and separate it + [-] remove the 3dnow code for reccop2 as it created more problems that it actually solved + [+] qmf2,qmtc2 is now done in SSE too . (shadow) + + * 01/10/03:[+] Handle for the EDI flag at ERET, a guess actually ;) + (linuzappz) + + * 30/09/03:[*] Reg caching works with bios and almost everything as normal rec does, need to + check better what does not work. + [*] Splitted iR5900.c into several .c files for easy navigation and for sanity + purposes as iR5900.c was HUGE. + [-] Fix LOTS of reg caching bugs, the main one was the comparission of 64bits registers. + (GoldFinger) + + * 30/09/03:[*] Added a base interrupt delay for the dma7 (spu2) + [*] More improvements to VifDma.h + (linuzappz) + + * 29/09/03:[-] Reimplemented Interrupt latency over R5900.c + [-] Fixed load/store unsigned addr to signed one over VUops.h + (linuzappz) + + * 28/09/03:[-] Fixed dmaIrq's for Source Chain mode + (linuzappz) + + * 26/09/03:[-] Fix the speed optimization problem that was in softCall function in Bios.c, + added a #pragma to disable optimization just to that function and everything + is working again as it should (linuz, please check if this pragma will interfere + with linux) + [*] The win32 project is back like before, the new Recompiler project was removed and + everything is working. + (GoldFinger) + + * 26/09/03:[+] Added two keys (F11, F12) for Opening/Closing the CDVD tray, + only for linux so far. + (linuzappz) + + * 25/09/03:[*] Sio2 fixes. Now the mcs are 'seen' as PS2 cards but unformated(able:P) + (Florin) + + * 25/09/03:[*] Modified the project structure under win32, I separated the recompiler from + the main project into a static lib, so now I can work with two projects instead + of one and enable optimizations. Now register caching works much better as I + enable speed optimizations in the Recompiler project but had to disable it in + the main project, the real problem is the global optimizations, this way I am + proving that the problem is not on my code... :) + [-] Fix lots of reg caching bugs and commented some of the reg caching instructions + that are buggy. Lots of demos work now, P51, colors, colors15 and maybe some others. + (GoldFinger) + + * 23/09/03:[-] Changed the MessageBox in recFunctions.c to SysMessage + [+] FIFO for VIF0/1 now works :) + (linuzappz) + + * 23/09/03:[*] Splitted the bios files so the code is the .c files and not on the .h files + [-] Fixed several bugs in reg caching and improved the routines + [!] Visual Studio .NET (dont know the others) speed optimizations are + messing with the code so it is recommended to build release mode + without Speed optimizations + (GoldFinger) + + * 23/09/03:[-] Commented the Syms in BiosInit.h, since they are only + for scph10000 + [-] Fixed ret DMA op + [+] Added iVUmicro, for recompiler + (linuzappz) + + * 22/09/03:[*] Added ExecuteVU0/VU1Block in R5900cpu, R5900.c + [+] Destination Chain for SPR0 dma and added Vif masking + [!] Gold, please merge the beta changes i sent you for + the iVUmicro.c/h + (linuzappz) + + * 20/09/03:[*] Now pad2 works in lle mode [tested with bios browser] + [+] New SCMDs and MC commands + [-] Fixes in sio, sio2, cdvd, etc. + [!] Now mcs appear as not inserted, but they are wip:P + SecrCardAuth() works fine;) + (Florin) + + * 19/09/03:[-] Small bugfix in Hw.h, u32 qwc -> u16 qwc; u16 pad; + [+] Small hack in Memory.c for ba000006 + (linuzappz) + + * 17/09/03:[-] Corrected a unpack bug i forgot ;) + [+] Implemented mskpath3 / m3r flags + [-] Fixed rom1.bin lookup for linux + (linuzappz) + + * 16/09/03:[-] Linuz fixed the macro bug hanging Visual Studio + (Gold-linuz) + + * 16/09/03:[-] More bugfixes/additions to Vif/VUs + (linuzappz) + + * 15/09/03:[-] Fixes lots of bugs in Reg Caching, now other demos work. + (GoldFinger) + + * 13/09/03:[-] Some fixes to the subq in cdvd.c (Florin) + + * 13/09/03:[-] Fixed the reg caching bug in tut1. + [*] Changed the DEFINE for reg caching for the config expert put on the CPU + screen, now we have both way to test. Thanks Expert. + (GoldFinger) + + * 13/09/03:[+] Added an option for enable regcaching or not.Now goldfinger should make + support for that!! (shadow) + + * 12/09/03:[-] Fixed the normal rec bug + [+] Added lots of new instructions for reg caching now all tutorials work + (demo 1 is strange), 3stars work also. + (GoldFinger) + + * 12/09/03:[-] Small bugfixes to Vif/VUs + (linuzappz) + + * 10/09/03:[*] More work on ipu (Florin) + + * 10/09/03:[-] New recompiler code wasn't compiling over linux, now it's ok + [-] Fixed VU memory64 writes masks + [+] Added Stats.c/h, it will create a stats.txt with some stats info, + if you define a NOSTATS in Common.h that will not be used + (linuzappz) + + * 09/09/03:[*] Improved the filter of ELF loading in GUI + [*] Register caching started to work, tutorial demo2a is working, others are coming. + [*] Commied ix86.c again with previous fix as linuz removed it. + (GoldFinger) + + * 09/09/03:[-] Fixed __int64 to u64 in Misc.c + [-] Fixed small bug in Vif.h + [-] Fixed bug in GS.c, bios is ok now + [+] Implemented INTC_STAT/MASK and DMAC_STAT for 64bits + [!] I commited the last ix86.c/h, afaik you only + reformatted it gold, please don't reformat my + code + (linuzappz) + + * 09/09/03:[-] Disabled host support for bios as it is buggy. + [*] Added/fixed SCMDs (2,3,1A) in cdvd.c (Florin) + + * 08/09/03:[+] Vif0/1 regs are now mapped to hardware + (linuzappz) + + * 08/09/03:[-] fixed bug in cpu ops debugger. Now all opcodes should appear .(For you goldfinger!) + (shadow) + + * 07/09/03:[+] Completed phase 1/2 of adding host support for bios. (Florin) + + * 06/09/03:[-] Fixed bug in rec when using ezmpeg + * 06/09/03:[*] Re-structuring the whole recompiler to make it easier for + debug and for the sake of understanding + (GoldFinger) + + * 06/09/03:[*] Many things fixed/changed in ipu files (Florin) + + * 05/09/03:[+] Hot keys for savestates in win32 + F1 -> savestate + F2 -> change the slot + F3 -> loadstate + [+] checkbox for enable the patches in cpu dialog + (shadow) + + * 05/09/03:[+] Some more VUmicro debugging + (linuzappz) + + * 05/09/03:[+] Added VDEC & BDEC; now m2v files work but ipu files do not + (Florin) + + * 05/09/03:[-] bug fixed in ifpu.h (shadow) + + * 04/09/03:[+] Added Init of Plugins before Loading of + savestates. + [-] LoadOthers in WinMain.c had a plugin init + missing fixed that. + [!] Fixed GSsoft aswell. And increased vRam size from 4*1024*1024 + to 2*4*1024*1024 as OpenGL doesn't handle wrapping around in + memory. + (asadr) + + * 04/09/03:[+] HSync stuff, and better CSR/IMR handling + [+] Few more FPU insts in ix86.c/h + [!] Please tell me if something is now screwed :) + (linuzappz) + + * 04/09/03:[-] ipu fixes to dmas and vdec (Florin) + + * 02/09/03:[-] fixed the patch system + (goldfinger) + + * 02/09/03:[*] working savestates in win32 :) + (shadow) + + * 01/09/03:[*] Working savestates for linux :) + (linuzappz) + + * 02/09/03:[-] Bug fixes in IPU.c (still hacky when returning BP) + Now ezmpeg gets to VDEC;) (Florin) + + * 01/09/03:[-] Fixed old bug in Sio.c/CdRom.c + (linuzappz) + + * 30/08/03:[+] sceCdReadSubQ <- that is a bad name; it look more like + a gettoc entry (SCMD2) (Florin) + + * 29/08/03:[+] Started the register caching implementation, not ready to test yet + some new x86 opcodes to x86.c and .h iR5900.c is completely changed + a new define for iR5900.c is used to enable reg caching. + (GoldFinger) + + * 29/08/03:[-] Removed 3DNOW code is the FPU since 3DNOW is 64bits, + and FPU is 32bits + [+] Workaround in ElfHeader.c for pukklink, so it will + load ok with Run Cd + [+] Savestates :D + [-] Maybe fixed patches, blah :P + (linuzappz) + + * 27/08/03:[+] CdGetToc + CdReadSubQ + [*] Changed PS2Edefs specs 0.4.3 / CDVD v3 + [*] Changed back the CDVDgetTD function to have a 2nd param: cdvdTD + (Florin) + + * 26/08/03:[-] Fixed patching bug in BiosInit.h that was used only for scph10000 + (Florin) + + * 26/08/03:[*] SPR address in DMAs are now masked with 0x0fffffff + (linuzappz) + + * 25/08/03:[-] Fixed patch stuff + (shadow-linuzappz) + + * 25/08/03:[-] Fixed IDEC bitstream decoding. Now, all *.ipu files + should work fine (Florin) + + * 25/08/03:[-] Fixed VIF Transfers to include MARK reg in VIF0 aswell. + (asadr) + + * 22/08/03:[*] Changed the SSE opcodes to Macro mode (thanks Linuz) and linuz added some more + (GoldFinger) + + * 22/08/03:[-] Changed DEV9 stuff to use DEV9 plugins + (linuzappz) + + * 21/08/03:[+] Added SSE instructions ADDPSRtoR, ADDPSMtoR, SUBPSRtoR, SUBPSMtoR, MULPSRtoR, + MULPSMtoR, MINPSRtoR, MINPSMtoR, MAXPSRtoR, MAXPSMtoR, SQRTPSRtoR, SQRTPSMtoR, + RSQRTPSRtoR, RSQRTPSMtoR, RCPPSRtoR, RCPPSMtoR, CVTPS2PIRtoR, CVTPS2PIMtoR, + CVTPIPS2RtoR, CVTPIPS2MtoR, CMPEQPSRtoR, CMPEQPSMtoR, CMPLTPSRtoR, + CMPLTPSMtoR, CMPLEPSRtoR, CMPLEPSMtoR to ix86.c ( need to compile and check ) :P + (GoldFinger) + + * 21/08/03:[+] Added Patch.c + (shadow-linuzappz) + + * 19/08/03:[+] Fixed bug in PsxHw.h, thanks to psycho_tr + (linuzappz) + + * 15/08/03:[+] More to SMAP + (linuzappz) + + * 14/08/03:[-] Fixed Debugger/RDebugger + [!] There is still one bug left i saw in the RDebugger, + that's the threads are not ended ok, but i'll leave + that to you florin :) + (linuzappz) + + * 12/08/03:[*] Changed the time in CDVD.c and some WIN32 defines to __WIN32__ + (Florin) + + * 08/08/03:[+] Started SMAP/DEV9 stuff + (linuzappz) + + * 06/08/03:[-] Fixed MFIFO for GS dma + (linuzappz) + + v0.41: + * 05/08/03:[-] Uncommented some sif WIP stuff :) + [*] Addeded 0x00100008 PC for cpuExecuteBios as well + [-] Fixed silly bug in CDVD.c for DvdReads + (linuzappz) + + v0.4: + * 29/07/03:[-] Fixed bug in Sif code ;) + (linuzappz) + + * 26/07/03:[-] Fixed the dma memory checking for the scratchpad, + and implemented it over the remaining dmas + (linuzappz) + + * 25/07/03:[-] GSdma now ends when a transfer is outside the memory + [-] Bugfix to Sif1 dma + [-] Some changes in the ICFG write code, and + when D5_CHCR == 0, psxSu32(0x30) = 0x40000; + (linuzappz) + + * 22/07/03:[-] Counters fixes/improvements + [*] Improvements on CdRead/DvdRead + [*] Better Sif1/0 handling + (linuzappz) + + * 22/07/03:[+] New PS2Edefs 0.4.0 :) + (linuzappz) + + * 20/07/03:[-] DvdRead was really 2064 :P + (linuzappz) + + * 19/07/03:[-] Fixed BIG stupid bug in FiFo.c that i left there:P + [!] I'm ashamed:( + [*] Fixed getBits function with back buffer;) + [!] I'm proud of that B-) (Florin) + + * 18/07/03:[-] Fixed includes for Mpeg.c/yuv2rgb.c + [-] Modified DvdRead, uses 2048 blocksize, experimental ;) + [-] CDVDgetTD had to return s32, not u32 + [*] Some reorganization for the rec + (linuzappz) + + * 18/07/03:[-] Fixed some memory allocation bug in IPU.c + [i think there are more, searching...:P] (Florin) + + * 17/07/03:[-] Fixed another Sio bug ;) + [-] Fixed MULT1/MULTU1, s64 was really s32 + [-] Commented out the interrupt delay thingy ;), was causing + troubles with Kengo 2 + [+] Implemented DvdRead + (linuzappz) + + * 17/07/03:[-] Fixed bug in FiFo.c that caused flickering between movies + [*] Fixed color conversion (Florin) + + * 16/07/03:[-] Now we can boot games though bios :) + [-] Fixed 32bits shifts in Interpreter.c + (linuzappz) + + * 16/07/03:[*] Fixed (somehow:P) IDEC so that .ipu files works a bit (Florin) + + * 15/07/03:[*] Changed cdvdLoc stuff to lsn + [-] PsxDma10 now will just return if bcr < 4 + [-] Corrections to IPU1/0 dma transfers, untested but should be fine ;) + [!] Breaks compat with current cdvd plugs + (linuzappz) + + * 15/07/03:[+] CSC + PACK ipu commands Fixes to bitstream decoding (Florin) + + * 14/07/03:[-] Really implemented MFIFO now ;P + [!] mfifo.elf works just fine now, also tekkentag reaches a bit further + (linuzappz) + + * 14/07/03:[+] Some more work on IntraDECoding for IPU (florin) + + * 14/07/03:[-] Some cleaning over Sif.c + [+] MFIFO implemented in both GS/VIF + [-] Fixes to xpadman + (linuzappz) + + * 13/07/03:[*] VUmicro code now can run in "async mode" + [-] Fixed TTE in SPR1 dma + (linuzappz) + + * 10/07/03:[-] Fixed bug in R5900.c when HLE mode was on + [-] fileio ain't crashing anymore when the file is not there + [-] Fixed interrupts on VifDma.h + (linuzappz) + + * 09/07/03:[-] Fixed bug in GS dma transfers when TTE is enabled. + Was passing wrong size of qword now it is GSgifTransfer3(ptag+2, 4) + + * 08/07/03:[*] In Win32 cdvdReadRTC gets correct time. (Florin) + + * 08/07/03:[-] Fixed stupid bug in SPR1 dma, thanks florin :) + (linuzappz) + + * 07/07/03:[-] Fixed bug in Vif UNPACK cmd, tops was *16 ;) + [*] GSgifTransfer/2 => GSgifTransfer1/2/3 (PATH1/2/3) + [*] Updated GS.c with VifDma.h asadr changes, commented the TTE + transfer by now + [*] Updated as well the SPR1 dma + [!] Note that now all vu1 demos work :D + (linuzappz) + + * 06/07/03:[+] Implemented cdvdReadKey + (linuzappz) + + * 05/07/03:[+] VCALLMS/VCALLMSR now are implemented + [*] SPU dma4 now has a delay base of 0x10000 + [-] VifDma.h is now mostly as before asadr rewrote it + last time, since that way had problems with the TTE chcr + flag, now go check the bios Browser ;) + (linuzappz) + + * 04/07/03:[-] IOP won't die now after cpuRegs.cycle overflows :), R5900.c + (linuzappz) + + * 03/07/03:[-] Fixed WriteRTC and bug in Sio.c + (linuzappz) + + * 03/07/03:[+] Some IPU commands (BCLR,FDEC,SETIQ,SETVQ,SETTH) & IPU fifo + (Florin) + + * 02/07/03:[-] Cleaned a bit VifDma.h + [*] Modified the DmaExec macros, now the Dmas take care + to interrupt and to clear the STR bit + (linuzappz) + + * 30/06/03:[*] Usb hack ;), PsxHw.c + (linuzappz) + + * 29/06/03:[*] Modified Iop DMAs for async processing, only for spu/spu2 by now + [-] Vif dma refe/end now sets the tadr to 0, tek4 loops else, but + now is crashing after ;) + [!] sven shows one screen now, and others want to ;) + (linuzappz) + + * 29/06/03:[-] Removed the experimental code i added yesterday from CDVD.c/h + [*] Improved EE/IOP Rcnts + (linuzappz) + + * 28/06/03:[*] More on iop rcnts rewrite + [+] Experimental code in CdRead, now if the lsn sector is + the same than the last one readed it will add nSectors + to it, ie. it readed 2 sectors from 0:2:16; and then + it issues another cdread with the same lsn, it will read + from 0:2:18. + (linuzappz) + + * 27/06/03:[*] Started iop rcnts rewrite, untested and more to come ;) (linuzappz) + + * 27/06/03:[-] Some fixes on iop rcnts + [+] Implemented the SPU2async func + (linuzappz) + + * 26/06/03:[-] Really fixed HLE mode ;). (linuzappz) + + * 26/06/03:[-] Small CDVD.c corrections. (linuzappz) + + * 24/06/03:[*] Fixed elf loader at least not to crash at bad elfs. (Florin) + + * 23/06/03:[*] Added "rom1.bin" as a valid file for rom1 filesystem. (Florin) + + * 23/06/03:[-] Small fix to CDVD.c, maybe fixed a time out problem + with some cdvdman versions. + (linuzappz) + + * 20/06/03:[+] Some mcds work, PsxSio2.c, Sio.c + (linuzappz) + + * 17/06/03:[+] SPU dmas now call the correct funcs, PsxDma.c + (linuzappz) + + * 17/06/03:[-] Fixed Sio2, PADs are working :D, Sio.c, PsxSio2.c + [-] Fixed language in CDVD.c, now's english again :) + (linuzappz) + + * 16/06/03:[-] Better error handling over CDVD.c + [+] ROM1 stuff, Memory.c/h, PsxMem.c, iR3000A.c, iR5900.c + [!] PCSX2 now detects the rom1 in this way: ie. + you use scph10000.bin, then you must have + in the same dir scph10000.bin.rom1 or scph10000.rom1 + (linuzappz) + + * 16/06/03:[-] Rewrote the VIFtransfer and VIFdma again. Better speed and + compatibility this time (asadr). + + * 16/06/03:[*] Reoverwritten CDVD.c/h with my CDVD.c/h, + this is simpler, more direct and faster, + CdRom.c, PsxHw.c, R3000A.c, PsxCounters.c, + Memory.c + [-] Kinda workaround i think in the ICFG reg + (1450), PsxHw.c + [-] Removed VSyncHack at least ;), WinMain.c, + Common.h, ini.c + [!] Games start showing something :D:D + (linuzappz) + + * 15/06/03:[+] More dummy cdvd scmd (Florin) + + * 14/06/03:[-] Fixed SIF0 transfers, PsxDma.c, PsxHw.c/h. + [*] Cleaned up SIF1, R3000A.c, Sif.c + [+] Added a Run CD menuitem, WinMain.c, resource.h + [!] Bios shows something :D:D + (linuzappz) + + * 12/06/03:[-] Fixed bug in release version (unsafe compiler optimizations:P) + (Florin) + + * 11/06/03:[+] More functions on lle cdrom (cdread) (Florin) + + * 10/06/03:[+] Rudely overwritten linuzappz work in CDVD.c/.h + [!] Sorry man, i think i did the best...i created a framework + with names and so on. Also we have to talk about;) (Florin) + + * 10/06/03:[+] Started CDVD.c/h, moved old CDVD.h to CDVDlib.h, + PsxMem.c, PsxCommon.h (linuzappz) + + * 10/06/03:[*] Added new SPU2 dma functions plugin.c + [*] New PS2 plugins specifications 0.3.2 (shadow) + + * 09/06/03:[+] Add SPU2read/SPU2write to PsxMem.c + [-] Few fixes to PsxCounters.c + [+] Added PsxDma7, PsxHw.c, PsxDma.c (linuzappz) + + * 09/06/03:[-] Sif0 chaining fix (Florin) + + * 08/06/03:[+] Started with SIO2... (Florin) + + * 07/06/03:[-] Fixes to SIF0/SIF1 :D + [!] Now OSDSYS loads ok, and so does several modules in iop, + bios now gets stuck in a Deci2Call (reqsend) + (linuzappz) + + * 07/06/03:[-] Fixes0, Fixes1 (Florin;) + + * 06/06/03:[*] Fixed and improved the VIF Dma transfers and rewrote the VIF_Transfer. It's far + more compatible and fast ( all vu1 demos work:D ). (asadr) + + * 06/06/03:[*] Fixed sif1 (...waiting for a better solution;) (Florin) + + * 05/06/03:[-] Sif1 flags fixes (still buggy?) (Florin) + + * 04/06/03:[*] Some adjustments to PsxInterpreter.c (Florin) + + * 03/06/03:[-] Set the PSXCLK to 36.864 Mhz, by now, later PSXCLK should be + a variable, Common.h + [*] Improved iop hw maps, added DMA9/10, PsxDma.c/h, R3000A.c, PsxMem.c + [+] Added RCNT3/4/5 in PsxCounters.c/h, PsxBios.c, PsxHw.c/h + [+] Added a bit more of loggin in PsxInterpreter.c + [-] 0xba000006 now returns 1, for some bioses, Memory.c + [*] Improved dmaSIF1, SIF.c, and cleaned dmaSPR1, SPR.c + [!] Finally my name is on the v0.4 :P + (linuzappz) + + * 03/06/03:[-] Fixed GetPS2ElfName in Misc.c (Florin) + + * 22/05/03:[+] new flags code based in nsx2 flag code. vuflags.c vuflags.h + [*] vuops.h rewrote almost all the vu opcodes with new flags code Upper Instructions + should be okay but might still are some issues with the Lower instructions. + [-] added proper reset in vu1micro.c + [*] added the new vcnet files for compile properly with vuflags + (shadow) + + * 21/05/03:[-] Fully fixed of recSQ in iR5900.c. + [-] Fixed EMMS_TRACING on none 3DNOW machines, EMMS_TRACING now turned on again. + [*] Added CPU autodetection to iR5900.c + [!] #define CPU_3DNOW is not used now. + (Alexey Silinov) + + * 20/05/03:[-] Dummy fix of recSQ in iR5900.c. Recompiler back to work again. + (Alexey Silinov) + + * 10/05/03:[*] Improved fpu.c, just code optimizing in cvt_s, cvt_w, all branches opcodes and + some code organizing. + (GoldFinger) + + * 02/05/03:[+] Added some ioprps to Misc.c (Florin) + + * 02/05/03:[+] Some more work on Padman module 800100Xpadman.c , 800100Xpadman.h (shadow) + + * 30/04/03:[+] Added 2 more syscalls in BiosSysc.h (Florin) + + * 29/04/03:[+] More threads functions. (see threads.txt) + [!] To see threads switching, comment the 3 instructions + at the top of _ThreadHandler function (Bios.c) + (Florin) + + v0.3: + * 01/05/03:[-] Fixed dvd iso issue (roundup of numsectors ...+2047/2048) + in CDVDiso.c (Florin) + + * 30/04/03:[-] Fixed bug in CFC1, Fpu.c + [-] Fixed branches isns on VUops.h, US was instead of SS + [-] Added newline at oef for MMI.c, *mtapman.c + [-] Included in *loadfile.c + [*] Few changes over languages, Misc.c/h, WinMain.c + (linuzappz) + + * 29/04/03:[-] Added a workaround for the interrupt latency in the rec, + R5900.c + (linuzappz) + + * 28/04/03:[-] Fixed a bug in BiosInit.h, instead of 16 dmas there was 15 + [-] Counters 0,1,2 had interruption disabled, Counters.c + (linuzappz) + + * 27/04/03 [-] WinMain.c now only calls GetPS2Elf if on HLE mode + [-] parseCommandLine had a bug when not using HLE, ElfHeader.c + [*] Implemented memRead/Write128, Memory.c/h, iR5900.c + [*] Implemented a better way to count opcodes in iR5900.c + [*] Rewrote AddIntc/DmacHandler, RemoveIntc/DmacHandler, Bios* + (linuzappz) + + * 24/04/03:[*] More threads functions. (see threads.txt) (Florin) + + + * 23/04/03 [-] Undo the "VU0/1 control/integer regs are now the same" update, + VU*, DebugReg.c, (goldfinger-linuzappz) + [+] Implemented latency interrupt thingy ;), R5900.c/h + (linuzappz) + + * 21/04/03 [-] DMAC interrupt wasn't setting the cause in a0, Bios.c + [+] DMACTable is now being used with DefaultDmacHandler, Bios.c/h, + BiosInit.h, HLE.c + [+] Added dummy handles for mtapman901/3, Rpc_services.h, *mtapman* + [*] VIF0 code is now shared with VIF1 code, Vif.c, VifDma.h + (linuzappz) + + * 23/04/03:[*] Fixed RFU061_InitHeap, EndOfHeap, CreateThread, InitThreads + (Florin) + + * 21/04/03:[+] Started threads update (Bios.c/BiosInit.h/BiosSync.h/ + deci2_dbgp.c/ElfHeader.c/.h) (Florin) + [-] Fixed fifos a bit:) (FiFo.c/Hw.h) (Florin) + [*] Fixed VIF1 regs display in Hw.c. I know that those were handeled + but now is more clear (Asad-Florin) + + * 20/04/03:[-] Fixed a bug in WinMain.c. In case of the debuggers with HLE bios, + cpuExecuteBios() was not called, so the tlbs and all stuff + were not inited properly. (Florin) + + * 20/04/03:[-] Fixed a bug in VUmicro.h + [+] VIF1dma now handles 'from Memory' transfers, Vif.c + [-] F11 now un/sets Log, F12 un/sets symbol logging, WinMain.c + (linuzappz) + + * 19/04/03:[-] Fixed a bug in Interpreter.c + [-] VU0/1 control/integer regs are now the same, VU*, DebugReg.c + [*] Cleaned a bit GS.c + (linuzappz) + + * 17/04/03:[-] gsRead32 wasn't calling GSread32, lol :), GS.c + [+] Implemented more syscalls, as dummy for now, Bios.c/h, BiosSysc.h, + HLE.c + [*] Removed the CSR revision/intelace bits over GS.c + [+] Added GSirqCallback to PS2Edefs.h, GS.c, Plugins.c + [+] VIF1 cmd now handles the i bit, still not 100% correct, Vif.c + [-] Fixed several VIF1 bugs, Vif.c + (linuzappz) + + * 16/04/03:[*] biosInit is now called at hle_bootstrap, Bios.c, HLE.c, WinMain.c + [+] Added _TlbInit at biosInit, Bios.c + [*] Deci2Call is now better coded, Bios.c, BiosSysc.h + [-] Fixed some bugs over AddIntcHandler/AddDmacHandler, BiosSysc.h + [+] bios_SetSYSCALL is now implemented, BiosSysc.h + (linuzappz) + + * 17/04/03:[+] Breakpoint support for debugging bios. (Florin) + + * 16/04/03:[-] Readded an update that linuzappz missed in his src, + that was then used by many pcsx2 team members. [it is about my 16/03/2003's] + [+] sceCdReadIOPm in 80000595.c + (Florin) + + * 14/04/03:[*] Memory access bug in deci2 that made pcsx2 to crash. hi linuzappz + [+] MC functions fixes/dummies over 80000400mcserv.c + (Florin) + + * 12/04/03:[*] CP0Count now adds 2 every opcode, instead of 4 + (linuzappz) + + * 12/04/03:[-] Bios VSyncSetFlag is now fixed ok, Bios.c + [*] Started to implement new Bios code, Bios.c/h, BiosInit.h + BiosSysc.h, EEregs.h, HLE.c, R5900.c/h + [!] Please tell me if this update creates some incompatibility, + or something else works now, since the INTC/Exceptions code is + much better now :). + (linuzappz) + + * 08/04/03:[-] DMAC_STAT CIS? bits are now set after a DmaExec, Hw.c + [-] Fixed bug when a plugin returned -1, WinMain.c + [*] Cleaned a bit GSdma, GS.c + [-] Fixed bug in INTC interrupts, Bios.c + (linuzappz) + + * 07/04/03:[+] Added cpu opcodes debugger. Use the CPU ops button in the debugger + and it will create a cpuops.txt with the opcodes that used. + Debugger.c pcsx2.rc, cpuopdebug.c cpuopdebug.h . Now works only in + interpreter. (shadow) + + * 04/04/03:[+] Added LabelGreets/LabelAuthors to Misc.c/h, AboutDlg.c + [*] Languages code over pcsx2 is a bit better, WinMain.c + [+] Added Log to STDOUT over Logging dialog, pcsx2.rc, + Misc.c, resource.h + [+] Now we're emulating the VSync Start and the VSync End, Counters.c + (linuzappz) + + * 26/03/03:[*] Optimized PLZCW in mmi.c (GoldFinger) + [-] fixed MADD1 and MADDU1 in mmi.c (GoldFinger) + [-] PSLLH, PSRLH and PSRAH (sa needed to be the first 4 bits only), + mmi.c (GoldFinger) + [-] PADDSW, the manual seems to be wrong (well it is) so I tryed + to fix it, mmi.c (GoldFinger) + [-] PADDSB, PSUBSB, the comparision was wrong, mmi.c (GoldFinger) + [-] PADDSW, PSUBSW, PADDSH, PSUBSH, PADDSB, PSUBSB, PADDUW, PSUBUW, PADDUH, + PSUBUH, PADDUB, PSUBUB, the sum and sub need a conversion to the higher + value(s16,s32 and s64), mmi.c (GoldFinger) + [-] PMINH, the comparision needs to be signed, mmi.c (GoldFinger) + [-] PHMSBH, was wrong, fixed, mmi.c (GoldFinger) + [-] PEXEH, PREVH, was using _Rs_ when only _Rt_ should be, mmi.c (GoldFinger) + + * 26/03/03:[*] fixed fpu bugs. Hi linuzappz :) . Tmip is correct in interpreter mode finally + fpu.c (shadow) + + * 24/03/03:[+] Fixed Trap instructions, hi shadow :), also cleaned + up a bit the Interpreter.c (linuzappz) + + * 22/03/03:[+] Fixed the 'Load ELF File' issue, added the Memory Dump menu, + WinMain.c (Florin-linuzappz) + [!] I forgot the changelog entry in the last update, + please read it (linuzappz) + [!] I've updated again the pcsx2.po, florin sent one without the + linux port messages since i've never updated u the linux port ;) + + * 20/03/03:[+] Added GSsetWindowInfo, WinMain.c, PS2Edefs.h, Plugins.c + [+] Added MiltuLingual support, using gettext, WinMain.c, Plugins.c, + AboutDlg.c, ConfigDlg.c, pcsx2.rc, Memory.c, iR5900.c, iR3000A.c, + pcsx2.dsp, Sio.c, R5900.c, PsxMem.c, Hw.c, Common.h, ini.c, + resource.h, Win32.h + [-] Fixed IPU.c when IPU_LOG is not defined + [-] Fixed padman/loadfile.c when RPC_LOG is not defined + [*] Removed dummy memory area, Memory.c/h, PsxMem.c/h + (linuzappz) + + * 17/03/03:[*] add some more stuff to DisR5900asm.c . win debugger is better now (shadow) + [-] fix a bug in rec table (visubiu not exist) in macromode .. recCop2.c (shadow) + [!] recheck vu micromode tables. Hmm they seem correct (shadow) + + * 16/03/03:[+] Added "next" (aka "step over") support for deci2 debugging + [+] Added TTYP support (log redirection) (buggy;have to test more) + (Florin) + + * 15/03/03:[*] move loadfile to rpc using florins protocol hi florin;p (shadow) + + * 15/03/03:[*] Fixes to breakpoints in deci2 stuff (Florin) + + * 13/03/03:[+] More deci2 dbgp stuff (BREAK/CONTINUE/RUN) + [!] It is not fully tested. This is EXPERIMENTAL code. + [!] It may contain many bugs and there are also "known issues". + [*] changed names of shadow's padman files and made the according + changes to rpc_services.h & bios.c (Florin) + + * 12/03/03:[*] rewrote padman+xpadman according to florins protocol on RPC folder + [*] removed code from bios.c according to pad handling.. + [+] Add fix that make analog mode to work partially ;) (try turnip) (shadow) + + * 10/03/03:[+] Added more mem mappings to PsxMem.c & Memory.c in order + not to crash the emu when read from a not-covered area (Florin) + + * 10/03/03:[-] fix small bug in winmain that didn't let console to close winmain.c (shadow) + + * 04/03/03:[+] Added remaining UNPAKs and cleaned a bit, Vif.c/h (linuzappz) + + * 03/03/03:[*] replace console writes of IPU with IPU_LOG Ipu.c (Shadow) + + * 01/03/03:[-] Fixed partially FIFO bug; (unlogged change:P) + [!] I haven't fixed it all because is very messed up. + [+] Added a small fix to PsxMem.c in order to allow sifman to stay resident. + (Florin) + + * 28/02/03:[+] Added support for remote debugging with tcp/ip deci2 protocol. + [!] Not finished nor fully tested + [!] There's an issue on Win98 with winsock2 closing [have 2 check that] + [!] New src dir (rdebug\*.*), Win32\RDebugger.c/.h; + [!] also added ws2_32.lib to link libraries + (Florin) + + * 25/02/03:[+] Added 8bit DMAs to Hw.c + [+] More UNPACK cases to Vif.c (linuzappz) + + * 23/02/03:[+] Added BC0s in COP0.c + [!] the BC0s are still fake, but they should be ok ;) (linuzappz) + + * 22/02/03:[*] Now if the Recompiler fails to initialize it'll switch to interpreter (linuzappz) + + v0.2: + * 18/02/03:[-] Small fix in winmain.c in SysInit() causing a crash (shadow) + + * 12/02/03:[+] Added new DMA transfer codes to VIF.c, GS.c + [+] Added the new clipping code to the VU + [*] Fixed most of the VU ops. Better compatibility + [!] The new dma transfer code is 4-5% faster than the last one. :) + + * 12/02/03:[*] Fixed iR5900.c to include iR5900.h, moved some stuff from + ix86.h to iR5900.h + [-] Fixed CPU_LOG flags over iR5900.c/recCOP2.c + [+] Added some SysPrintfs over iR5900.c after x86Init + (linuzappz) + + * 26/01/03:[+] Added some defines in Hw.h (linuzappz) + + * 26/01/03:[-] Fixed include in IPU.c, 'common.h' for 'Common.h', + SJdata.c, 'rpc/...' '...' (linuzappz) + + * 04/02/03:[+] ix86.c: Added CPUID for linux. + SSE is autodetected now (linuzappz) + + * 28/01/03:[+] ix86.c: CPUID added. It will work if u don't change CPU on the FLY. :) + [*] ir5900.c: recompiler changed for CPUID. + [*] recCOP2.c: recompiler changed for CPUID. + (Alexey Silinov) + + * 27/01/03:[+] ir5900.c: recompile of DSRAV,PMINW,PMAXW added. + [!] I'am not sure in this code.Need find demos that use it. + [+] ix86.c: PANDNRtoR,PANDMRtoR added. + (Alexey Silinov) + + * 25/01/03:[-] ir5900.c: Some fpu opcodes for non 3DNOW version fixed with EMMS_TRACE on. + After Linuzappz request:memory opcodes + [-] (LB,LBU,LH,LHU,LW,LWU,.....,SB,SH,SW,...) fixed for version with EMMS_TRACE on. + [-] recCOP2.c: Bugfixed for non 3DNOW version(added another recCOP2SPECIAL1t,recCOP2SPECIAL2t tables). + + [!] Now EMMS_TRACE will work much better. + (Alexey Silinov) + + * 24/01/03:[+] ir5900.c: DSRA,DSRA32 recompilation added. + [*] shifts by _Imm_=0,_Rs_=0 optimized. + [+] PMFHI,PMFLO,PAND,PXOR,PMTHI,PMTLO,POR,PAND now can recompiled to SSE instructions. + [-] JUMPS bugfixed by putting SET_FPUSTATE before it, + because cpuBranchTest use FPU. Pillgen now ok with EMMS_TRACE. + [!] #define CPU_SSE in ix86.h if you have Pentium3/4 or Duron7/AthlonXP. + (Alexey Silinov) + + * 24/01/03: Fixed iR5900.c to compile without 3DNOW (linuzappz) + + * 24/01/03: Moved the emms's in Hw.c, GS.c, Memory.c to iR5900.c (linuzappz) + + * 24/01/03: Rewrote ini.c, same code as Pcsx now :) (linuzappz) + + * 24/01/03: Removed the browse info option over pcsx2.dsp (linuzappz) + + * 24/01/03: Some fixes in Hw.c, missings #ifdef HW_LOG/#endif, + reordered the ipu address and the IPU1 dma was wrong. (linuzappz) + + * 21/01/03: remove the fpu flags reorganize fpu.c a bit. Send code for using it + as reference to all the pcsx2 members. + + * 21/01/03:recCOP2.c + 75% of COP2 ops recompiled used 3DNOW. + only sign MAC flags updated. + Zero,Sticky will be done later. + (Alexey Silinov) + * 19/01/03: + ir5900.c: + Fixed recompiling of RSQRT_S for3DNOW,recPCPYUD(tskin.elf now ok), + recC_LE for x87 FPU(untitled.elf last part now ok). + Optimizing recompile of recADDI,recADDIU,recDADDI,recDADDIU, + recANDI,recORI,recXORI,... (check _Rs_,_Rt_= =0,!=0). + Optimiing recMOVZ,recMOVN only one JMP CC,.. needed. + Added #define EMMS_TRACE to. + Added #define ARITHMETICIMM_RECOMPILE,ARITHMETIC_RECOMPILE,etc. + (Alexey Silinov) + * 18/01/03: ir5900.c:Many recompiler opcodes fixed. + IPU DMA logging update. + (Alexey Silinov) + + * 18/01/03: Fixed fileio_lseek function in 80000001fileio.c. (Florin) + + * 16/01/03: Rewrite fpu and added two version of it. One with flags and + one without. The no flags version is much faster. + fpu.c ,r5900.h,common.h,r5900.c,ini, resources , winmain.c (shadow) + + * 15/01/03: Added memory mapping of IPU_CMD,IPU_CTRL,IPU_TOP,IPU_BP. + Added parsing of IPU commands. + (Alexey Silinov) + * 14/01/03: recMAX_S,recMIN_S,recC_F,recC_EQ,recC_LE,recC_LT-added. + recABS_S,recNEG_S-now don't use FPU. + ix86.c - 3DNOW opcodes added. + #ifdef CPU_3DNOW then FPU recompiled using 3DNOW instruction set. + Now we are ready TODO FPU register cache on CPU_3DNOW. + gs.c add emms before GS read,write. + (Alexey Silinov) + + * 14/01/03: Fixed stupid bug in 80000001fileio.c (Florin) + + * 13/01/03: Fixed some issues in CDVDiso.c & 80000597cdvdfsv.c (Florin) + + * 11/01/03: Fixed fileio [important] functions up to ioprp255 (Florin) + + * 11/01/03 some more MMI opcodes in rec (alexey silinov) + + * 10/01/03 recPADDUB-changed,recPADDUH-added (Alexey Silinov) + + + * 09/01/03 + My copy&paste bugs in recPCPYUD,recPAND,recPXOR fixed. + Now EMMS() instruction writing only before FPU commands if FPU state is MMX.With it size of recompiled code is reduced. + !!Added macros SET_FPUSTATE,SET_MMXSTATE it's must before MMX or FPU instruction. + Fixed many ops.ExtSign32to64() don't used anymore. it's faster and smaller to do CDQ(). + recLUI,recPCPYLD optimized. + Added recompilation of PMAXH,PMINH,PCGTB,PCGTH,PCGTW,PCEQB,PCEQH,PCEQW,PEXTLW,PEXTUW. + ir5900.c,ix86.c, ix86.h + (Alexey Silinov) + + + * 07/01/03: Fixed MADD, MADDA, MSUB, MSUBA opcodes in FPU.c ( asadr ) + + * 07/01/03: added MTSAB,MTSAH in interpreter.c (asadr) + + * 07/01/03: Added Flags in FPU.c, fixed SQRT, DIV, RSQRT opcodes in FPU.c ( asadr ) + + * 07/01/03: add some opcodes in interpreter.c (shadow) + + * 07/01/03: small fix in mult1,multu1 in MMI.c (shadow) + + * 07/01/03: more addes in recompiler iR5900.c (Alexey Silinov) + + * 07/01/03: fixed MULT,MULTU,MULT1,MULTU1 in rec and added DIV1,DIVU1 iR5900.c + (Alexey Silinov) + + * 07/01/03: add several MMI opcodes to recompiler iR5900.c ix86.c ix86.h + (Alexey Silinov) + + * 07/01/03: Fixed libpad version issues; added scePadInit2 support in Bios.c (Florin) + + * 06/01/03: Added sceDvdRead function to 80000595cdvdfsv.c, CDVDiso.h/.c + Moved sifcall_cdvdfsvA from Bios.c to 80000592cdvdfsv.c (Florin) + + * 05/01/03: Fixed libmc version 80000400mcserv.c + Added support for cdrom device in LoadHeap function 80000003sysmem.c (Florin) + + * 05/01/03: Added D and I Flag checking in vuDiv and SQRT opcodes and some more fixes ( asadr ) + + * 05/01/03: Merge all the vu code fixes to the better possible one vuops.h (shadow) + + * 05/01/03: Several vu fixes vuops.h (Alexey Silinov) + + * 04/01/03: Added temporary fix to vuDIV, vuRSQRT, vuERSADD, vuERCPR, ERSQRT ( asadr ) + + * 04/01/03: Added vuERCPRL, vuESUM , vuEATANxz, vuESIN, vuEATAN, vuEEXP, vuRXOR ( asadr ) + + * 04/01/03: Fixed GetPS2ElfName Misc.c + Romanian version pcsx2ro.rc & spell fixes in pcsx2.rc (Florin) + + * 03/01/03: Added callback support in Bios.c + Added function name display support (if you know other places, plz add...) + (Florin) + + * 02/01/03: Fixed sceCdRead (cdvdfsv1.4 and more?) 80000595cdvdfsv.c (Florin) + sceCdSeek, sceCdPause, sceCdStop, sceCdStatus + + * 02/01/03: Added SLTI,SLTIU,SLT,SLTU iR5900.c ( Alexey Silinov) + + * 02/01/03: Added SETS8R ix86.c ix86.h ( Alexey Silinov) + + * 01/01/03: YAH HAPPY NEW YEAR :) + + * 30/12/02: Added Dump memory button in Debug dialog over Debugger.c (Florin) + + * 29/12/02: Start work on Savestates Common.h, Misc.c. Need to include zlib.lib in project (shadow) + + * 29/12/02: Fix small bug In ERET. COP0.c (shadow) + + * 29/12/02: Some more Vu opcodes in the vuops.h (shadow) + + * 29/12/02: Added BC2F,BC2T,BC2TL,BC2FL not sure if i check the right bit althought Vu0.c (shadow) + + * 29/12/02: Added HLE opcode over DisR5900.c (linuzappz) + + * 29/12/02: Fixed some bugs on rec mode, Memory.c, PsxMem.c, + ix86.c/h, iR3000A.c, iR5900.c (linuzappz) + + * 29/12/02: Fixed JALR in Interpreter.c (linuzappz) + + * 28/12/02: Fixed stupid old bug in iR5900.c (linuzappz) + + * 26/12/02: Added compiler detection code (Win32/WinMain.c) (Florin) + + * 20/12/02: Refixed new Threads code, a bit hacky still ;), Bios.c/h, HLE.c (linuzappz) + + * 20/12/02: Refixed Counters.c, GS.c (linuzappz) + + * 20/12/02: Changed some cop0 names over DisR5900.c (linuzappz) + + * 20/12/02: Fixed EXL bit on cpuException, R5900.c (linuzappz) + + * 20/12/02: Fixed ERET, COP0.c, Interpreter.c, R5900.h (linuzappz) + + * 19/12/02: fix bugs in fileio system 80000001fileio.c (florin) + + * 19/12/02: rewrite MMI mmi.c (linuzappz) + + v0.1: + * 17/12/02: Small cleaning for release (linuzappz) + + * 17/12/02: Fixed a comment in CDVD.h (linuzappz) + + v0.046: + * 17/12/02: FIXED THAT BASTARD BUG with interpreter + R5900.c (shadow) + + * 16/12/02: Fixed CDVDFS_read function (CDVDisodrv.c). (Florin) + + * 07/12/02: Added support for SjDATA filesystem (SJdata.c/.h). + Small fixes in 80000003sysmem code. + Moved OpenPlugins call from debugger.c to WinMain.c + Added support for 0B001337cdvd rpc driver. (Florin) + + * 06/12/02: Fixed RPCVER for games that load ioprp.img. + Moved related code Bios.c to Misc.c. (Florin) + + * 05/12/02: Placed a guard in CDVDFS_init/CDVDisodrv.c + Added support for reading from cdvd in Elfheader.c/.h + Implemented RunCD option WinMain.c, GtkGui.c, Misc.c/.h (Florin) + + * 01/12/02: Made fileio_open more compatible with games (up to 2.0F/ioprp255) (Florin) + + * 30/11/02: Add test opcodes TGE,TGEU,TLT,TLTU,TEQ,TNE Intepreter.c (shadow) + + * 30/11/02: Add exceptions defines in R5900.h (shadow) + + * 01/12/02: Removed old VU0 code over VU0.c (linuzappz) + + * 31/11/02: Implemented CheckCdrom/LoadCdrom, still unfinished, Misc.c (linuzappz) + + v0.045: + * 31/11/02: Changed memRead32 for PSMu32 over Interpreter.c (linuzappz) + + * 31/11/02: Fixed CdRead, CDVDiso.c (linuzappz) + +// * 30/11/02: Added irqs for rcnt2/3, started thread HLE code rewrite, +// Bios.c, Counters.c (linuzappz) + + * 28/11/02: Fixed win32 fileio path, and some more small issues (linuzappz) + + * 27/11/02: Added BD0000X0 connection to 1000F0X0 and BC000000 ee memmap, + implemented direct rec clears, and added some more counters stuff, PsxMem.c/h, + Memory.c/h, iR3000A.c, iR5900.c, Counters.c/h, Hw.c, PsxHw.c (linuzappz) + + v0.044: + * 26/11/02: Added RPC handler for 80000595cdvdfsv + Fixes in iso handling system (read function can read more than 16KB) + Fixed issue with deci2call no.3 (junk re-printing) (Florin) + * 20/11/02: Automatic setting of RPCVER + (problems: 1. SDK samples do not load an ioprp; default is "2000";) + 2. starwars game load a ioprp.img; so RPCVER="0000") + Fixed bug in CDVDiso.c/CdRead(); check for null pointer + Fixed issue with the Joliet filesystem (Florin) + * 09/11/02: Added iso parsing & filesystem driver from libcdvd library by Hiryu & Sjeep + Added credits (Hiryu&Sjeep) to Aboutbox & Readme.txt + Changed the order of opening of plugins in Plugins.c/OpenPlugins() + Added (or moved code from bios.c) to handle RPC user & standard code + -works (or should work; needs intensive testing): + 014D704Enaplink, 80000001fileio, + 80000003sysmem, 80000592cdvdfsv, + 80000597cdvdfsv + -dummy (to be implemented; only params display): + 0B0110C5sjpcm, 0badca11amigamod(shadow), + 80000400mcserv,80000596cdvdfsv, + 80000601osdsnd + Added RPC_LOG logging stuff to debug.h, pcsx2.rc & resource.h + Added kputs handling for scePrintf in Deci2Call syscall in bios.c + (Florin) + + v0.043: + * 06/11/02: add some work for amigamod.irx HLE based on Florin + RPC code (shadow) + + * 21/10/02: implement some more opcodes for VU + flags in many opcodes + (shadow) + + * 19/10/02: Added more opcodes to VUops.h (from VU0.c), and started the implementation of flags, + VUflags.h, VU* (shadow-linuzappz) + * 12/10/02: Added fix for several bioses, Hw.c, removed hack for 30002R.bin R5900.c (linuzappz) + + * 06/10/02: Added 0xb2000000 map area as a 0x12000000 mirror, Memory.c (linuzappz) + + * 06/10/02: Added/Fixed some VIF1 unpack cases, Vif.c (linuzappz) + + * 06/10/02: Started Rewrite of SetGsCrt, Bios.c (linuzappz) + + * 05/10/02: Improved gsReads/Writes GS.c (linuzappz) + + * 28/09/02: Added iop HLE code for exceptions in PsxBios.c (linuzappz) + + * 24/09/02: Added ps2 bios check for config dialog in ConfigDlg.c/GtkGui.c (Florin) + + * 23/09/02: Added command line parsing for main's argc, argv in ElfHeader.c (Florin) + + * 14/09/02: Fixed small issue in PsxBios2.c/h, Bios.c (linuzappz) + + * 13/09/02: Fixed bug in Bios.c for sif_call (linuzappz) + + v0.042: + * 25/08/02: Fixed VU SQRT/RSQRT, also added ERSQRT, VUops.h (linuzappz) + + * 24/08/02: Added several stuff for IOP hle (linuzappz) + + * 09/08/02: Added: PADDSW, PSUBSW, PPACW, PADDSH, PSUBSH, PEXTLH, PPACH, PADDSB, PSUBSB, + PPACB, PEXT5, PPAC5, PABSW, PCEQW, PMINW, PADSBH, PABSH, PCEQH, PMINH, + PCEQB, PSUBUW, PADDUH, PSUBUH, PSUBUB, PEXTUB, QFSRV, PSLLVW, PSRLVW, + PINTH, PROT3W, PSRAVW, PEXCW, PEXCH, PINTEH, PMTHI, PMTLO, PEXEW, + PREVH, PEXEH, in MMI.c (GoldFinger) + + * 09/08/02: Changed: PADDUB to conform to defines (GoldFinger) + + v0.041: + * 11/08/02: More to VUops.h (shadow) + + * 10/08/02: Rewrote L/R Stores/Loads (linuzappz) + + * 05/08/02: Add some more opcodes to VUops.h some demos works again(shadow) + + * 04/08/02: Added VUops.h, VU0.c/h, still uncomplete (linuzappz) + + * 04/08/02: Added GSgifTransfer2 for PATH1, VU1micro.c, PS2Edefs.h, Plugins.c (linuzappz) + + * 03/08/02: Added unpack v4-32, Vif.c (linuzappz) + + * 02/08/02: Rewrote partially VUmicrocode, structures and stuff, VU1/0micro.c, VUmicro.h (linuzappz) + + * 02/08/02: Small change on Misc.c (linuzappz) + + * 01/08/02: cpuExecuteBios is now called before the LoadElf, WinMain.c (linuzappz) + + * 01/08/02: Added a couple of HW_LOGS in Hw.c (linuzappz) + + * 01/08/02: Fixed MFC2/CFC2 in DisR3000A.c (linuzappz) + + * 01/08/02: Added small hack on R3000A.c (linuzappz) + + * 01/08/02: Fixed bug in StartThread Bios.c (linuzappz) + + v0.040: + * 31/07/02: Small change in PsxInterpreter.c and PsxHw.c (linuzappz) + + * 31/07/02: Added CDVDgetTN and CDVDgetTD, PS2Edefs.h, Plugins.c, CdRom.c/h (linuzappz) + + * 30/07/02: Fixed Gte.c, now includes PsxCommon.h (linuzappz) + + * 30/07/02: Added GPU_LOG to Debug.h (linuzappz) + + * 30/07/02: IOP now has vsyncs, PsxCounters.c/h (linuzappz) + + * 30/07/02: Added PsxGPU.c/h, moved the GPU_xx stuff from PsxBios.c, PsxDma.c (linuzappz) + + * 24/07/02: Added psxSIF1transfer to R3000A.c,, Sif.c, still not working (linuzappz) + + * 24/07/02: Added a couple of address more to PsxHw.c (linuzappz) + + * 24/07/02: Added zeroEx func to trace writes to stdout, PsxInterpreter.c (linuzappz) + + * 24/07/02: Improved bios_write, PsxBios.c (linuzappz) + + * 24/07/02: PsxMem.c now handles the full 4mb of the bios mem (linuzappz) + + * 24/07/02: Hack for 30002R bios, R5900.c (linuzappz) + + * 24/07/02: Fixed BNEL, BEQL in disR5900.c (linuzappz) + + * 22/07/02: Remove biosCall, now use only biosException, Bios.c/h, Interpreter.c, + R5900.c (linuzappz) + + * 21/07/02: Intialaze of VU1 vu1microc,vumicro.h,R5900.c (shadow) + + * 21/07/02: implement VIF0transfer same way as VIF1transfer vif.c (shadow) + + * 20/07/02: Add debug logs in vu0,vu1 micromode + vu0 execute vu0micro.c, + vu1micro.c,vumicro.h (shadow) + + * 18/07/02: Clean up vu0 macromode vu0.c (shadow) + + * 19/07/02: PsxMem.c now handles 0xbf80xxxx hw addresses (linuzappz) + + * 17/07/02: IOP PRid is 0x1f (not 0x2), R3000A.c (linuzappz) + + * 17/07/02: More to VU1micro.c (shadow) + + * 14/07/02: Added psxMemRLUT/psxMemWLUT instead of only psxMemLUT, PsxMem.c/h (linuzappz) + + * 11/07/02: Added MADD1, MADDU1, PMFHL, PCGTW, PMAXW, PADDH, PSUBH, PCGTH, + PMAXH, PADDB, PCGTB, MMI.c (goldfinger) + + v0.039: + * 08/07/02: Commented the 0xe000 hw dma test, for jasper, Hw.c (linuzappz) + + * 08/07/02: Changed PADreadStatus for PADstartPoll/PADpoll, Plugins.c, PS2Edefs.h, Bios.c, + PsxBios.c, Sio.c (linuzappz) + + * 07/07/02: More to Vif1 dma and added vu1ExecMicro, Vif.c, VU1micro.c/h (linuzappz) + + * 06/07/02: Rewritten/updated iop code, CdRom.c/h, Common.h, Iop*, Psx*, Debug.h, + Decode_XA.c/h, DisR3000A.c, ElfHeader.c, Gte.c/h, Mdec.c/h, R3000A.c, + R5900.c/h, Sio.c/h, iR3000A.c, Bios.c, pcsx2.dsp (linuzappz) + + * 05/07/02: Added CDVDreadTrack/getBuffer and now we're using CDVD plugin, PS2Edefs.h, + Plugins.c, ConfigDlg.c, ini.c pcsx2.rc (linuzappz) + + * 05/07/02: Optimized recANDI and completed the mmi rec tables, iR5900.c,ix86.c/h (linuzappz) + + * 06/07/02: Add VU opcodes tables vu1micro.c,vu0micro.c,vumicro.h (shadow) + + * 03/07/02: Added MADDU, PMTHL, PLZCW, PSLLH, PSRLH, PSRAH, PSLLW, PSRLW, PSRAW in MMI.c(goldfinger) + + * 03/07/02: Small change in MULT1, MULTU1 for speed, MMI.c(goldfinger) + + * 03/07/02: Fixed MADD, PLZCW in MMI.c(goldfinger) + + * 03/07/02: Fixed signed extend in DIVU, SLL, SLLV, Interpreter.c(goldfinger) + + * 03/07/02: Fixed C_F, FPU.c(goldfinger) + + * 04/07/02: Fixed silly bug on Bios.c, also added a workaround on Counters.c (linuzappz) + + * 04/07/02: Refixed CVT_W/S, FPU.c (linuzappz) + + * 03/07/02: Fixed DIV/DIVU and implemented MOVZ/MOVN in iR5900.c (linuzappz) + + v0.038: + * 02/07/02: Small change on CVT_W, FPU.c (linuzappz) + + * 01/07/02: Changed a bit IPU.c/h, added the dmaIPUs on Hw.c and added IPU_LOG, Debug.h, + resource.h, pcsx2.rc, Common.h (linuzappz) + + * 30/06/02: Add the remaining vif regs in the structure vif.h (shadow) + + * 30/06/02: Framework for IPU ipu.c,ipu.h (shadow) + + v0.037: + * 30/06/02: Added a couple of MMI opcodes to iR5900.c, ix86.c/h, also added one more cdvd server, + Bios.c (linuzappz) + + * 29/06/02: Added most of the FPU opcodes on the recompiler, fixed DSRLV/DSLLV, and also added + LQ, SQ, MFHI, MFLO, MTLO, MTHI, iR5900.c, ix86.c/h (linuzappz) + + * 28/06/02: Refixed LDL,LDR,SDL,SDR, Interpreter.c (linuzappz) + + * 27/06/02: Fixed bug in ABS, FPU.c and completed more fpu opcodes on DisR5900.c + fix to vu0 (vu0.c) + (shadow-linuzappz) + + * 27/06/02: Fixed fileio for dummy files, Bios.c (linuzappz) + + * 26/06/02: Changes/(fixes?) to some opcodes in interpreter.c SRA,DSRA,MULT,MULTU + ,SDL,SDR,LDR,LDL.At least cubemastah is correct now!(GoldFinger) + + * 20/06/02: Fixed the new Logging, modified the files on the last entry + WinMain.c (linuzappz) + + * 18/06/02: Realtime Logging Debugger.c,resource.h,pcsx2.rc,ini.c, + Common.h,debug.h (you must enable it!)(need to fixed) (shadow) + + * 18/06/02: Add some work for sjpcm module on Bios.c (shadow) + + v0.36: + * 13/06/02: Fix basic3d on Bios.c (linuzappz) + + * 09/06/02: Fix for call/ret dma in GS.c (shadow) + + * 06/06/02: Started CDVD plugin API on PS2Edefs.h (linuzappz) + + * 06/06/02: Started HLE.c/h, Interpreter.c, Common.h, iR5900.c, InterTables.c/h (linuzappz) + + * 04/06/02: Added some more syscalls on Bios.c (linuzappz) + + * 02/06/02: Fixed some bugs on SPR.c, Sif.c, Vif.c (linuzappz) + + * 02/06/02: More work on DisR5900asm.c (shadow) + + * 02/06/02: More stuff on Vif.c/h and SPR.c (linuzappz) + + * 01/06/02: Started SIFdmas Hw.c, Sif.c/h, Common.h, added SIF_LOG to Debug.h, + also removed the BREAK code since the bios doesn't handles that, Interpreter.c (linuzappz) + + * 01/06/02: Added PLZCW to MMI.c (linuzappz) + + * 01/06/02: RFU060 is now as before, added INTC and DMAC stuff, added INTC regs 0x1000f000 and + 0x1000f010, and DMAC 0x1000e010, Hw.c/h, Bios.c, Counters.c, R5900.c/h (linuzappz) + + * 31/05/02: Thread stuff is better now, added RFU061 and fixed RFU060, Bios.c (linuzappz) + + * 30/05/02: VU0 stuff is almost complete on DisR5900asm.c (shadow) + + * 30/05/02: Set gp on ElfHeader.c (linuzappz) + + * 30/05/02: Renamed _LOG to EMU_LOG, Debug.h, WinMain.c (linuzappz) + + * 28/05/02: Implemented GetCop0/iGetCop0 bios calls, Bios.c (linuzappz) + + * 28/05/02: Added names to GPR/COP0 regs, R5900.h, COP0.c, Bios.c, Interpreter.c, + FPU.c, DisR5900.c, R5900.c, VU0.c, MMI.c, iR5900.c, ElfHeader.c (linuzappz) + + * 28/05/02: Fixed PSUBW, PSUBB, added PADDW, PEXTLB, PEXTUH, MMI.c, PERF + on Interpreter.c, and TLBWR, TLBR to COP0.c (linuzappz) + + * 28/05/02: disasm.c/h is now DisR5900asm.c, Debugger.c, Debug.h, also added symbols + support for it (linuzappz) + + v0.35: + * 27/05/02: INCREASE VERSION ON 0.036! WinMain.c (shadow) + + * 27/05/02: add partialy support for VU0 in Win32 disasm.fix a bug in tables for MMI + disasm.c disasm.h (shadow) + + * 27/05/02: add VU1 register debugger. resource.h, pcsx2.rc,debugger.h, debugreg.c (shadow) + + * 27/05/02: Started real threads stuff on Bios.c (linuzappz) + + * 27/05/02: Rewrote dmaGIF/dmaVIF1, GS.c/h, Vif.c/h, Hw.c/h (linuzappz) + + * 27/05/02: add Macros for VU0Mem + Some new VU0 opcodes VISWR,VILWR, (shadow - [TyRaNiD] ) + + * 27/05/02: add some new Defines (_Id_ _Is_ _It_ _Imm5_) and some new opcodes + VIADD,VISUB,VIOR,VIAND,VIADDDI,VFOI12,VFOI15,VABS (shadow) + + * 26/05/02: Added support for Symbols on DisR5900.c, ElfHeader.c, Debug.h, + also refixed the qwc on SPR.c (linuzappz) + + * 26/05/02: Added some cdvdfsv stuff on Bios.c, also set the gs revision to 3 in + CSR, GS.c (linuzappz) + + * 25/05/02: CTC2/CFC2 to DisR5900.c (linuzappz) + + * 25/05/02: Added BREAK opcode on Interpreter.c (linuzappz) + + * 25/05/02: Now the b0000000 to b2000000 address are handled as a 10000000 mirror, + Hw.c, Memory.c (linuzappz) + + * 25/05/02: Removed some old code on R5900.c, rewrote a bit the ElfHeader.c (linuzappz) + + * 24/05/02: Add gsWrite... on bios_SetGsCrt (tyranid) + + * 24/05/02: MULT1/MULTU1 are now ok MMI.c (linuzappz) + + * 24/05/02: Added 1000f000 addr on hwRead32, Hw.c (linuzappz) + + * 23/05/02: add VU0 opcodes VSUBi,VSUBq,VMULA,VMULAi,VMULAq,VMSUB,VMSUBi, + VMSUBq,VMADDA,VMAX,VMINI,VADDA,VADDAi,VADDAq,VADDAx,VADDAy, + VADDAz,VADDAw,VSUBA,VSUBAi,VSUBAq,VSUBAx,VSUBAy,VSUBAz,VSUBAw, + VMSUBA,VMSUBAx,VMSUBAy,VMSUVAz,VMSUBAw,VMSUBAi,VMSUBAq (shadow) + + * 23/05/02: add VU0-1 status. (vu0.h , vumicro.h) (shadow) + + * 23/05/02: Added VU0/1micro.c/VUmicro.h (shadow) + + * 23/05/02: Fixed bios_isceSifDmaStat (return -1), also started some work on the + zero-exception stuff, Bios.c, IopBios.c, R5900.c (linuzappz) + + * 23/05/02: VRNEXT should be fine now, VU0.c (linuzappz) + + * 23/05/02: Small addition on Bios.c, 0x80000003 cmd on bios_isceSifSetDma (linuzappz) + + * 22/05/02: Added define __VU0_H__ on VU0.h (linuzappz) + + * 22/05/02: Rewritten GSdma/dmaGIF stuff, now only GSgifTransfer exists, added dmaGIF to GS.c, also added + GIF_LOG to Debug.h, Plugins.c/h (linuzappz) + + * 21/05/02: Fixed one bug in WinMain.c, now can rerun a elf ok (linuzappz) + + * 21/05/02: More stuff to Vif.c (linuzappz) + + * 20/05/02: Fixed one bug in biosException (linuzappz) + + * Started SPRdmas, SPR.c/h, Hw.c, Common.h, also added SPR_LOG, Debug.h (linuzappz) + + * 'call' dmaChain mode in dmaVIF1 should be right now (linuzappz) + + * More changes to the GS plugin API, GSdmaGIF/GSdma, PS2Edefs.h, Plugins.c, Hw.c, Vif.c, + also fixed little bug in SPU2init (linuzappz) + + * Small change in GSmakeSnapshot, now pass char *path (linuzappz) + + v0.34: + + * add more opcodes to Disasm DisR5900.c (shadow) + + * correct MTSA in interpreter interpreter.c (_Rs_ and not _Rd_) (shadow) + + * fixed FPU register debugger debugreg.c .Now registers appears float (shadow) + + * Some more stuff on Bios.c/Counters.c, timer1 intc, fixed bug in CreateSema (semaid stuff), + and added iSignalSema (linuzappz) + + * Added some VU0 opcodes to DisR5900.c (linuzappz) + + * Fixed bug in FPU.c, BC1XX branch delay (linuzappz) + + * Started Vif0/1 dmas, Hw.c, Vif.c/h, Common.h, added VIF_LOG to Debug.h (linuzappz) + + * Fixed VSUBx, VMULz, renamed VMONE to VMOVE, VU0.c (linuzappz) + + * Moved COP2 from Interpreter.c to VU0.c, and MMI to MMI.c (linuzappz) + + * fixed VU0 register debugger debugreg.c (Linuzappz-shadow) + + * Added ExecuteBlock and removed ExecuteBios on R5900cpu, R59000.h, Interpreter.c + and iR5900.c, also added psxExecuteBios, R5900.c, WndMain.c (linuzappz) + + * Some fixes to VU0.c, _Rd_ == 0, not _Rd_ != 0 (linuzappz) + + * Added MTHI1/MTLO1, MMI.c (linuzappz) + + * Rewrote Memory.c (linuzappz) + + * Improved iR5900.c branchs, and updated ix86.c/h (linuzappz) + + * Fixed bug in MULTU, (u64) stuff (linuzappz) + + * Interrupt Exception code was wrong, 0 -> 0x400 (linuzappz) + + * Started ChangeLog + diff --git a/pcsx2/Docs/License.txt b/pcsx2/Docs/License.txt new file mode 100644 index 0000000000..7d1f8605f8 --- /dev/null +++ b/pcsx2/Docs/License.txt @@ -0,0 +1,342 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + + diff --git a/pcsx2/Docs/PCSX2 FAQ 0.9.4.rtf b/pcsx2/Docs/PCSX2 FAQ 0.9.4.rtf new file mode 100644 index 0000000000..50c55e0d7d --- /dev/null +++ b/pcsx2/Docs/PCSX2 FAQ 0.9.4.rtf @@ -0,0 +1,172 @@ +{\rtf1\ansi\ansicpg1253\deff0\deflang1032{\fonttbl{\f0\froman\fprq2\fcharset161{\*\fname Times New Roman;}Times New Roman Greek;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fcharset161{\*\fname Arial;}Arial Greek;}} +{\colortbl ;\red0\green0\blue255;} +{\*\generator Msftedit 5.41.15.1515;}\viewkind4\uc1\pard\b\f0\fs44 PCSX2 0.9.4 FAQ\par +\par +\b0\fs28 In this document we will try to answer the most common questions our end users have. This is mostly a copy/paste from the Emuforums thread created by CKemu with some updates and corrections.\par +\b\fs44\par +\par +\par +Can I 'Play' Games?\b0\par +\fs28 \par +If by 'playing' a game, you mean: at full speed with sound, perfect graphics, etc, then \b NO\b0 . Come back next year.\par + \par +Since this release (PCSX2 0.9.4) compatibility has increased greatly. Many games will now go 'in-game' or at least to some form of menu. Whilst games may get 'in-game', they will not run at 'playable' speeds, due to the complex nature of PS2 emulation, and the lack of modern hardware that is powerful enough to emulate such a console.\par + \par +Speed has recently increased significantly, for near 'fullspeed' games, we recommend you examine the {\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/95668-post-your-pcsx2-0-9-4-screenshots-here.html"}}{\fldrslt{\cf1\ul screenshots thread}}}\cf0\ulnone\f0\fs28 for speeds.\par + \par +\b\fs44 Is game 'X' Working?\b0\fs28\par + \par +Before posting this, check the {\field{\*\fldinst{HYPERLINK "http://www.pcsx2.net/compat.php?p=1&c=$&s1=1&s2=1&s3=1&s4=1&s5=1" }}{\fldrslt{\cf1\ul compatibility list}}}\cf0\ulnone\f0\fs28 you will be able to see how far your game goes, if you can't get it as far as we can, check your configuration against the {\field{\*\fldinst{HYPERLINK "http://www.pcsx2.net/guide.php" }}{\fldrslt{\cf1\ul Configuration Guide}}}\cf0\ulnone\f0\fs28 , if you still have no luck, you may post the question. If your game is not in the compatibility list then I\rquote m afraid you are on your own, we cannot guarantee it working either way so it is impossible to tell if we can help you at all.\par + \par +\b\fs44 Does PCSX2 play PlayStation 1 (PSOne / PSX / PS1) Games?\b0\fs28\par + \par +\b No\b0 , simple as that. PCSX2's primary goal is to emulate the PS2, which would eventually include PlayStation 1 games, however at this time the key focus is to make PlayStation 2 games 'run' with a high degree of compatibility, including support for such features as USB device support (EyeToy, Special Controllers), DEV9 (Network, HDD) etc.\par + \par +If you wish to play PlayStation 1 games, there are extremely compatible, fast and stable emulators already in existance, and I recommend:\par + \par +{\field{\*\fldinst{HYPERLINK "http://psxemulator.gazaxian.com/" }}{\fldrslt{\cf1\ul pSX}}}\cf0\ulnone\f0\fs28 - New emulator, with a growing level of compatibility and is very simple to use.\par +{\field{\*\fldinst{HYPERLINK "http://epsxe.com/" }}{\fldrslt{\cf1\ul ePSXe}}}\cf0\ulnone\f0\fs28 - The famous and widely supported PS1 emulator, use this if you love features, and plugins!\par +{\field{\*\fldinst{HYPERLINK "http://batard.psxfanatics.com/" }}{\fldrslt{\cf1\ul PSXeven}}}\cf0\ulnone\f0\fs28 - Created by Xeven, supports the PSEmu Pro plugin system, and often has increased compatibilty over ePSXe with some specific games.\par + \par +\par +\b\fs44 My game worked in 0.9.2 but not in 0.9.4 why?\b0\fs28\par + \par +Due to changes in the emulator some games may not work as well as they did in the previous release, as you will find with most emulation projects, you fix one game, you break another somewhere. So the best solution to this is, play your game on the version it worked best for you.\par + \par +\b\fs44 PCSX2 doesn't work on my Athlon XP (or other non SSE2 chips)\b0\fs28\par + \par +Due to the nature of the VM (pcsx2.exe), which heavily uses SSE2, this version of the emulator will \b NOT\b0 work for you, however the TLB (pcsx2t.exe) build should work for you. Note when using a \b non\b0 SSE2 cpu to run PCSX2, make sure you select the \b non\b0 SSE2 versions of the plugins.\par + \par +\par +\b\fs44 ZeroGS looks REALLY ugly!! - What gives?\par +\b0\fs28 \par +\par +ZeroGS renders to native PS2 resolution, that\rquote s it's default render target, when you set the window size to say 1024x768, ZeroGS still renders to native PS2 resolution, but stretches that resolution to fit the window size. What you are used to is setting your window size to x*y and the target resolution also changes accordingly.\par + \par +The so called 'AA' ZeroGS uses, isn't actually anti aliasing, it simply ups the render target size, so you end up with a higher resolution image, thus increasing it's quality (albeit blocky).\par + \par +So you're thinking, well what gives? To be honest, you do, the forum user, so many people whine about speed (oh no my aging rig..or general abuse thrown at the mods / developers), it was decided that in order to maintain speed, the render target and window resolution would remain unlinked, despite the obvious fuglyness of it.\par + \par +\b\fs44 Where is the BIOS?\par +\b0\fs28 \par +It is Illegal to ask for a BIOS, as the BIOS is copyright of SONY. If you wish to use a REAL BIOS with PCSX2, you can dump it from your own PS2 (there are guides {\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/83608-rfs-guide-dumping-your-ps2-bios-over-lan.html" }}{\fldrslt{\cf1\ul here}}}\cf0\ulnone\f0\fs28 and {\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/84994-guide-dump-bios-through-usb.html" }}{\fldrslt{\cf1\ul here}}}\cf0\ulnone\f0\fs28 . Or find it by yourself.\par + \par +\b\fs44 No SCPH10,000 Support?!\par +\b0\fs28 \par +The SCPH10,000 BIOS doesn't have all the libraries many games need to load, thus for better results, use a more modern BIOS version.\par + \par +\b\fs44 Do I need ROM1, ROM2, EROM?\par +\b0\fs28 \par +If the console is warning you about not having these files, do not panic, all you need is the main ROM0 (the 4,096kb main BIOS file). You can dump the ROM1, ROM2, EROM from your own PS2 console, again guides and tools are on the internet, use {\field{\*\fldinst{HYPERLINK "http://www.google.com" }}{\fldrslt{\cf1\ul Google}}}\cf0\ulnone\f0\fs28\par +\par +\b\fs44 Is PCSX2 finally using the new .p2b BIOS format?\par +\par +\b0\fs28 Unfortunately, florin the team member responsible for this feature got held back and it did not make it in the release. Nevertheless, you can extract the contents of your .p2b BIOS file using 7zip, so you are still able to use your own complete BIOS files\par +\par + \par +\b\fs44 Where can I get games?\b0\fs28\par + \par +From your local computer game shop, where else?\par + \par +This forum does not support warez or piracy, helping someone get links to such files, asking for such files, or linking to illegal material will be warned then banned if the violation is repeated. There are \b MANY\b0 great games out there, and you owe it to the talented creators to buy their games, and many you can get very cheaply, in platinum editions or second hand.\par + \par +\b\fs44 Can I be a betatester?\b0\fs28\par + \par +Ask this and the answer will always be \b NO\b0 . Keep asking it and you'll find yourself with a big fat ban.\par + \par +\b\fs44 What are the system requirements?\b0\fs28\par + \par +We recommend the following:\par + \par +\b Minimal Specs:\par +\b0 AMD XP/64 or Intel Pentium 4 (VM Built will not work with [B]non[/B] SSE2 CPU's)\par +512MB of RAM\par +Pixel Shader 1.4 supporting card (GSdx Graphics Plugin Only)\par + \par +\b Recommended Specs:\b0 (For reasonable performance in many games, but not all)\par +AMD64 X2 or Intel Conroe E6600+ (Multi Threading is supported in PCSX2)\par +512-1024MB of RAM (more RAM allows for VM mode to be stable)\par +Pixel Shader 2.0 supporting card (recommended GeForce 6600-8800 or equivalent ATi card)\par + \par +\b Possible Ideal Specs:\b0\par +64bit OS such as Vista or winXP64, to allow for future support of 64bit recompilers.\par +Future AMD or Intel Quad Core maybe needed, but currently the support of 4 threads is not handled by PCSX2, and the effect of threading out EE / VU is not yet known.\par +We don't forsee you requiring a GPU more powerful than the current generation of cards, up to and including the nVidia GeForce 8800.\par + \par +A more in-depth guide to system specifications can be found {\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/84457-will-emulator-run-fast-my-computer.html" }}{\fldrslt{\cf1\ul here}}}\cf0\ulnone\f0\fs28 .\par + \par +\b\fs44 How do I compile the SVN source?\par +\b0\fs28 \par +We do not support SVN builds on this forum. Only official builds are permitted to be discussed. This is mainly to stop confusion, people 'handing out' betas, n00b questions when dealing with errors, as it's hard to keep track of multiple 0.x.x versions.\par + \par +Besides, if you need to ask that question, then trust me, you don't want to be bothered. It's very very hard.\par + \par +\b\fs44 What is going on with PCSX2?\par +\b0\fs28\par +\i Why aren't we getting lots of info, or is PCSX2 dead?\i0\par +\pard\fi-360\li720\tx720\lang2057\f1\'b7\tab\lang1032\f0 When something 'special' happens, one of the beta-testers will post shots and information.\par +\pard\lang2057\f1\'b7\tab\lang1032\f0 When something super-dupa mega super hyper turbo mega mega happens, shots will be posted like \i super mega\i0 lightning fast on the main {\field{\*\fldinst{HYPERLINK "http://www.pcsx2.net/"}}{\fldrslt{\cf1\ul news page}}}\cf0\ulnone\f0\fs28 .\par +\lang2057\f1\'b7\tab\lang1032\f0 If no shots are being posted, assume that the progress is slow but steady, and whilst stuff is happening, it doesn't result in anything that you'd be able to see (eg techy nerd stuff).\par +\lang2057\f1\'b7\tab\lang1032\f0 If nothing is being posted whatsoever, assume that \b A:\b0 The developers have lives (yeah I didn't believe that either), \b B:\b0 The developers are taking a rest.\par +\lang2057\f1\'b7\tab\lang1032\f0 What if \b SHOCK HORROR\b0 no news has been posted for ages???!!!!!! Assume any of the above or most of the team was killed.\par +\lang2057\f1\'b7\tab\lang1032\f0 What if I have programming knowledge, and could understand the technical changes being made? Well then go browse to the SVN, and aquire the \b changelog.txt\b0 .\par +\par +\b\fs44 When will the next version be released?\par +\b0\fs28 \par +The authors are writing this in there spare time. If they knew, they would inform you, normally a release is made when significant progress and a big difference in 'output' is shown, eg, when you the end user will be able to see more stuff running.\par + \par +\b\fs44 Why bother making releases when it is not finished?\b0\fs28\par + \par +In a nutshell, it keeps the "Gimme the next release \b NOW!\b0 " crowd from getting too annoying. More importantly it alls folks to see how things are progressing first hand.\par + \par +\b\fs44 How do I make a patch?\par +\b0\fs28 \par +Making a patch will \b not\b0 make a game 'playable' but may allow you further into it. Patches can be used to skip videos that don't play, or other simple bugs, they will not make '3D' perfect or make sound super duppa!\par + \par +{\field{\*\fldinst{HYPERLINK "http://www.pcsx2.net/nachbrenner/" }}{\fldrslt{\cf1\ul Nachbrenner's Site for making Patches}}}\cf0\ulnone\f0\fs28\par +{\field{\*\fldinst{HYPERLINK "http://www.ngemu.com/forums/showthread.php?t=62837" }}{\fldrslt{\cf1\ul Simple Guide for Video Skip Patches}}}\cf0\ulnone\f0\fs28\par + \par +\b\fs44 My Sprites have black borders!\b0\fs28\par + \par +This happens when you have \b Linear Texture Filtering\b0 (LTF) turned on in GSdx, to solve this, turn off LTF. This is most often the cause of black squares or borders around sprites in 2D and 2.5D fighting games.\par + \par +\b\fs44 Every Game I run crashes the emulator instantly!\par +\b0\fs28 \par +This is often caused by people having the \b NLOOP0\b0 hack enabled in the GS plugin (ZeroGS KOSMOS / GSdx), and using the Run>Execute menu. By using Run>Execute the emulator first boots the BIOS, which will crash when you have NLOOP0 enabled. Simply use File>RunCD menu to avoid crashes, and this is the recommended option for anygame.\par + \par +If you wish to run the BIOS, simply \b DISABLE\b0 NLOOP0 in your GS plugin first!\par + \par +\b\fs44 Useful Links\par +\b0\fs28 \par +BIOS Guides:\par + \par +{\field{\*\fldinst{HYPERLINK "http://www.ngemu.com/forums/showthread.php?t=65015" }}{\fldrslt{\cf1\ul CKemu's guide to using the PS2 BIOS}}}\cf0\ulnone\f0\fs28 \par +{\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/84994-guide-dump-bios-through-usb.html" }}{\fldrslt{\cf1\ul RealOne's Guide to USB BIOS Dumping}}}\cf0\ulnone\f0\fs28\par +{\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/83608-rfs-guide-dumping-your-ps2-bios-over-lan.html" }}{\fldrslt{\cf1\ul Reichfuher's guide to dumping your Playstation 2 BIOS}}}\cf0\ulnone\f0\fs28\par +\par +Error/Problem Guides:\par + \par +{\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/67512-common-problems-solutions-guide.html" \\\\l "post912874" }}{\fldrslt{\cf1\ul Refraction's guide to common problems and errors.}}}\cf0\ulnone\f0\fs28\par +{\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/84457-will-emulator-run-fast-my-computer.html" }}{\fldrslt{\cf1\ul Will the emulator run fast on my computer?}}}\cf0\ulnone\f0\fs28\par +{\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/70174-why-pcsx2-slow.html" }}{\fldrslt{\cf1\ul Why is PCSX2 Slow?}}}\cf0\ulnone\f0\fs28\par +\par +Memory Card Guides:\par + \par +{\field{\*\fldinst{HYPERLINK "http://www.ngemu.com/forums/showthread.php?t=64482" }}{\fldrslt{\cf1\ul Convert GameFAQ saves to PCSX2 memorycard}}}\cf0\ulnone\f0\fs28 {\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/69844-load-us-ntsc-final-fantasy-x-gamesaves-any-ffx-version.html" }}{\fldrslt{\cf1\ul Load US memcard saves on any region version of FFX and FFX-2}}}\cf0\ulnone\f0\fs28\par + \par +Plugins/Patch Guides:\par + \par +{\field{\*\fldinst{HYPERLINK "http://www.ngemu.com/forums/showthread.php?t=62837" }}{\fldrslt{\cf1\ul Guide to making patches for PCSX2}}}\cf0\ulnone\f0\fs28\par +{\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/1065199-post74.html" }}{\fldrslt{\cf1\ul TwinPad Keyboard Plugin Latest Version}}}\cf0\ulnone\f0\fs28\par +{\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/ps2-plugin-questions-troubleshooting/87274-lilypad-new-pad-plugin-lame-name.html" }}{\fldrslt{\cf1\ul Lilypad plugin}}}\cf0\ulnone\f0\fs28\par +{\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/ps2-plugin-questions-troubleshooting/94273-megapad-plugin.html" }}{\fldrslt{\cf1\ul MegaPad plugin}}}\cf0\ulnone\f0\fs28\par + \par +Reviews:\par + \par +{\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/76251-pcsx2-review-cpu-power-does-matter.html" }}{\fldrslt{\cf1\ul PCSX2 Review Part 1: CPU Power Matters}}}\cf0\ulnone\f0\fs28\par +{\field{\*\fldinst{HYPERLINK "http://forums.ngemu.com/pcsx2-official-forum/80417-pcsx2-review-part2-gpu-power-also-matter.html" }}{\fldrslt{\cf1\ul PCSX2 Review Part 2: GPU Power Also Matters}}}\cf0\ulnone\f0\fs28\par +\par +\par +\f2\fs20\par +} + \ No newline at end of file diff --git a/pcsx2/Docs/PS2Edefs.txt b/pcsx2/Docs/PS2Edefs.txt new file mode 100644 index 0000000000..5442da8297 --- /dev/null +++ b/pcsx2/Docs/PS2Edefs.txt @@ -0,0 +1,443 @@ + PS2E Definitions v0.3.0 (beta) + ------------------------------ + + Author: linuzappz@pcsx.net + +Note: Refer to PS2Edefs.h for more info. + +1) API Funcs +2) FAQs +3) Notes + +1) API Funcs: + --------- + + Common stuff: + ------------ + +// u32 CALLBACK PS2EgetLibType(void); + +Gets the plugin type, from the following defines: + +#define PS2E_LT_GS 0x01 +#define PS2E_LT_PAD 0x02 +#define PS2E_LT_SPU2 0x04 +#define PS2E_LT_CDVD 0x08 +#define PS2E_LT_DEV9 0x10 +#define PS2E_LT_USB 0x20 +#define PS2E_LT_FIREWIRE 0x40 + +Note that the types can be ORed so you can make an +ie. GS/PAD plugin in one dll. + +// u32 CALLBACK PS2EgetLibVersion(void); + +Will get the plugin version: + +const unsigned char version = 1; // GS library v1 +const unsigned char revision = VERSION; +const unsigned char build = BUILD; // increase that with each version + +return version<<16|revision<<8|build; + +'version' is the GS plugin API version, as this is beta, +it should be 1. +'revision' and 'build' are plugin defined values. + +// char* CALLBACK PS2EgetLibName(void); + +Get the LibName, ie. "GSsoftdx Driver"; + + + + GS plugin API: + ------------- + +Basic funcs +----------- + +// s32 CALLBACK GSinit(); + +Inits the plugin, return 0 on success, else -1. + +// s32 CALLBACK GSopen(void *pDsp, char *Title); + +Opens the plugin, return 0 on success, else -1. +The plugin must fill the 'pDsp' arg (32bit) that'll be +passed to other plugins (*1), this is OS dependant (*2). + +On Win32: pass a HWND value, ie: + *(long*)pDsp = (long)GShwnd; +On Linux: pass a Display value, ie: + *(long*)pDsp = (long)display; + +*1 Even if this value is passed to every plugin, this +may not be used by the plugins. + +*2 This could change anyways, ie. maybe you can code a +GS/PAD plugin for a speacial library, so the pDsp +will be a value that you need to communicate between +them (if you need to do this). + +// void CALLBACK GSclose(); + +Close the plugin. + +// void CALLBACK GSshutdown(); + +Shutdown the plugin. + +// void CALLBACK GSvsync(); + +Called on every VSync. + +// void CALLBACK GSgifTransfer(u32 *pMem, u32 size); + +Transfer 'size' qwords (128bit) from 'pMem' to the Gif. + +// void CALLBACK GSgifTransfer2(u32 *pMem); + +Transfer a qwords (128bit) block from 'pMem' to the Gif. + +// void CALLBACK GSwrite32(u32 mem, u32 value); + +Writes to address 'mem' data 'value', 32bit. +Addresses can range from 0x12000000 to 0x14000000. + +// void CALLBACK GSwrite64(u32 mem, u64 value); + +Writes to address 'mem' data 'value', 64bit. +Addresses can range from 0x12000000 to 0x14000000. + +// u32 CALLBACK GSread32(u32 mem); + +Returns 32bit from address 'mem'. +Addresses can range from 0x12000000 to 0x14000000. + +// u64 CALLBACK GSread64(u32 mem); + +Returns 64bit from address 'mem'. +Addresses can range from 0x12000000 to 0x14000000. + +Extended funcs +-------------- + +// void CALLBACK GSkeyEvent(keyEvent *ev); + +Gets called when there is a keyEvent from the PAD plugin + +// void CALLBACK GSmakeSnapshot(char *path); + +Makes an snapshot of the vRam, can be a with the full +vRam or just the view area (as you prefer), to the dir +'path'. + + +// #ifdef __WIN32__ +// s32 CALLBACK GSsetWindowInfo(winInfo *info); +// #endif + +Windows only function, will pass the info struct to the +GS plugin, if the plugin will use this info it should +return 1 (TRUE), else the emu will destroy the window +handle and let the plugin take care of that. +This function must be called before the GSopen. +Note that the emu should hide the hMenu and the hStatusWnd +if it they are set. +The position and size of the window is not specified and +should be reset by the plugin, but the window passed should +be in a normal state, not maximized nor minimized. +After a GSclose the emu must destroy the window and +recreate it. + +// void CALLBACK GSconfigure(); + +Configure the plugin. + +// void CALLBACK GSabout(); + +Shows an About Dialog of the plugin. + +// s32 CALLBACK GStest(); + +Returns 0 if the plugin should work ok, else -1. + + + + PAD plugin API: -=[ OBSOLETE ]=- + -------------- + +Basic funcs +----------- + +// s32 CALLBACK PADinit(u32 flags); + +Inits the plugin, return 0 on success, else -1. + +// s32 CALLBACK PADopen(void *pDsp); + +Opens the plugin, return 0 on success, else -1. +The 'pDsp' is a value from the GS plugin (refer to +GSopen). + +// void CALLBACK PADclose(); + +Close the plugin. + +// void CALLBACK PADshutdown(); + +Shutdown the plugin. + +// keyEvent* CALLBACK PADkeyEvent(); + +Called every vsync, return NULL if no event happened. + +// u8 CALLBACK PADstartPoll(int pad); + +Starts polling data from the PAD, 'pad' can be 1 (pad1) +or 2 (pad2). +Returns first byte from buffer; + +// u8 CALLBACK PADpoll(u8 value); + +Returns next byte from buffer; +Refer to "pad and mem card data.txt" or "padmem.txt", +for info about value/buffer data. + +// u32 CALLBACK PADquery(); + +returns: 1 if supported pad1 + 2 if supported pad2 + 3 if both are supported + +Extended funcs +-------------- + +// void CALLBACK PADconfigure(); + +Configure the plugin. + +// void CALLBACK PADabout(); + +Shows an About Dialog of the plugin. + +// s32 CALLBACK PADtest(); + +Returns 0 if the plugin should work ok, else -1. + + + + SIO plugin API: + -------------- + +Basic funcs +----------- + +// s32 CALLBACK SIOinit(u32 port, u32 slot, SIOchangeSlotCB f); + +Inits the plugin, return 0 on success, else -1. +port/slot combination will be used to load the corresponding + configuration data from ini. It's like an id for the instance. +'f' is a callback function that is used by SIO_TYPE_MTAP capable + plugins to change active slot in emulator (since sio is emu-controled). + +// s32 CALLBACK SIOopen(void *pDsp); + +Opens the plugin, return 0 on success, else -1. +The 'pDsp' is a value from the GS plugin (refer to +GSopen). + +// void CALLBACK SIOclose(); + +Close the plugin. + +// void CALLBACK SIOshutdown(); + +Shutdown the plugin. + +// u8 CALLBACK SIOstartPoll(u8 value); + +Starts polling data from the SIO, 'value' is + 0x01, 0x21, 0x61 and 0x81 corresponding to pad, mtap, rm and mc. +Returns first byte from buffer; + +// u8 CALLBACK SIOpoll(u8 value); + +Returns next byte from buffer; +Refer to "pad and mem card data.txt", "padmem.txt" or + "padspecs.txt" (http://ps2dev.ps2-scene.org/padspecs.txt), +for info about value/buffer data. + +// u32 CALLBACK SIOquery(); + +#define SIO_TYPE_PAD 0x00000001 +#define SIO_TYPE_MTAP 0x00000004 +#define SIO_TYPE_RM 0x00000040 +#define SIO_TYPE_MC 0x00000100 + +returns: ORed value of SIO_TYPE_xxxx - the capabilities of the plugin +eg. a remote control plugin will return SIO_TYPE_PAD | SIO_TYPE_RM + +Extended funcs +-------------- + +// void CALLBACK SIOconfigure(); + +Configure the plugin. + +// void CALLBACK SIOabout(); + +Shows an About Dialog of the plugin. + +// s32 CALLBACK SIOtest(); + +Returns 0 if the plugin should work ok, else -1. + + + + SPU2 plugin API: + --------------- + +TODO :) + +Basic funcs +----------- + +// s32 CALLBACK SPU2init(); +// s32 CALLBACK SPU2open(void *pDsp); +// void CALLBACK SPU2close(); +// void CALLBACK SPU2shutdown(); +// void CALLBACK SPU2update(); +// void CALLBACK SPU2dma(u32 *dmaAddr, char *pRam); +// void CALLBACK SPU2write(u32 mem, u16 value); +// u16 CALLBACK SPU2read(u32 mem); + +Extended funcs +-------------- + +// void CALLBACK SPU2configure(); +// void CALLBACK SPU2about(); +// s32 CALLBACK SPU2test(); + + + + CDVD plugin API: + --------------- + +Basic funcs +----------- + +// s32 CALLBACK CDVDinit(); + +Inits the plugin, return 0 on success, else -1. + +// s32 CALLBACK CDVDopen(); + +Opens the plugin, return 0 on success, else -1. + +// void CALLBACK CDVDclose(); + +Close the plugin. + +// void CALLBACK CDVDshutdown(); + +Shutdown the plugin. + +// s32 CALLBACK CDVDreadTrack(u32 lsn, int mode); + +Starts reading from the specified 'lsn' sector location, +return 0 on success, else -1. + +// u8* CALLBACK CDVDgetBuffer(); + +Gets a pointer to the buffer with the sector data +readed by a previously CDVDreadTrack call. +The buffer size depends on the mode used for readTrack. + +note: return can be NULL (for async modes) + +// s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ* subq); + +Read subq data from disc at 'lsn' location (only cds have subq data), +return 0 on success, else -1. + +// s32 CALLBACK CDVDgetTN(cdvdTN *Buffer); + +Get the the cdvdTN data for the currect CdRom, +return 0 on success, else -1. + +// s32 CALLBACK CDVDgetTD(u8 Track, cdvdLoc *Buffer); + +Get the the cdvdTD data for the 'Track' track in the current CdRom, +return 0 on success, else -1. + +// s32 CALLBACK CDVDgetTOC(void* toc); + +Get ps2 style toc from disc, return 0 on success, else -1. +(ps2 toc isnt quite the same as a normal disc toc, +especially for dvds) + +// s32 CALLBACK CDVDgetDiskType(); + +Returns disktype in the format CDVD_TYPE_xxxx + +// s32 CALLBACK CDVDgetTrayStatus(); + +Returns tray status in the format CDVD_TRAY_xxxx + +// s32 CALLBACK CDVDctrlTrayOpen(); + +Opens disc tray, return 0 on success, else -1. + +// s32 CALLBACK CDVDctrlTrayClose(); + +Closes disc tray, return 0 on success, else -1. + + +Extended funcs +-------------- + +// void CALLBACK CDVDconfigure(); + +Configure the plugin. + +// void CALLBACK CDVDabout(); + +Shows an About Dialog of the plugin. + +// s32 CALLBACK CDVDtest(); + +Returns 0 if the plugin should work ok, else -1. + + +2) FAQs + + * What's the right open/close sequence? + 1. CDVD + 2. GS + 3. PAD1/2 + 4. SPU2 + + * Where to start coding a plugin? + Get an open source plugin, mine are all open source, + so you can freely base on them. + + * Why GSgifTransfer2 exists? + GSgifTransfer2 is used by the XGKICK VU1 opcode, and + it doesn't has a size, the GSgifTransfer is used by he GIF + and the VIF dmas, and it has a size, so that's why :). + +3) Notes + + * CDVDgetBuffer should be used after CDVDreadTrack, + like this: + CDVDreadTrack(time); + ptr = CDVDgetBuffer(); + + but not like this; + ptr = CDVDgetBuffer(); + CDVDreadTrack(time); + + * Errors should be checked for both CDVDreadTrack and CDVDgetBuffer, + and not only CDVDreadTrack. + diff --git a/pcsx2/Docs/RemoteDebugging.txt b/pcsx2/Docs/RemoteDebugging.txt new file mode 100644 index 0000000000..339b400b48 --- /dev/null +++ b/pcsx2/Docs/RemoteDebugging.txt @@ -0,0 +1,105 @@ +ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» +ºNOTE: 1. this is an internal pcsx2 team document, for developers ONLYº +º 2. lamers/gamers are excluded º +º 3. DECI2 will not run your games:P º +ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ + +RemoteDebugging +=============== + +This is a new feature available for debugging applications within pcsx2 emu. + Using a remote debugger is not wellcomed unless it is more featured. + Fortunately, there are such debuggers. Currently added support is for win32 + target only; also, only TCP/IP remote connection and only for Sony's DECI2 + app-level protocol. Future addition could be GNU debugger remote support. + +Anyway, pcsx2 has a new menu item that allows one to start a debugging session + by specifying the listen port [default for DECI2 is 8510]. Pcsx2 will act like + a server for the remote debugger. It is set to accept connections on any IP. + For local debugging sessions, use IP=127.0.0.1 aka IP=localhost. On the + other hand, if a pcsx2 would be listening on an active IP in the Internet, + anyone could connect to it;) Only one connection is supported. After the + connection is closed by the client/remote tool, the only thing to do is to + "admire" the log window and then close it/restart the emu. + +So, to start a debug session: + - first start the emu [quite reasonable; because it will be the server]. There + are some issues if you start the client first or if you didn't close ALL the + client subprograms in the last debugging session. + - choose from the menu, Debug->Remote Debugging. A dialog will pop asking for + the port to use. Also, if you have a non-HLE bios selected in the Config box, + ie. a true bios, there will be also an option weather to debug the bios. You + have 2 choices: + -not checked=Run Bios + before any debug is performed a bios run. ie. the emu + will run the bios up to the shell loading or a bit + further, to the moment when all the iop modules are + loaded. You will have then a full environment loaded. + -checked=Debug Bios + nothing is done further and the PC=0xBFC00000. + - after that a connection log window will be shown and you can follow the + connection data exchange. First, the emu is placed in a wait state. + - this is the moment you have to run the client. It will open the connection. + The emu accepts the connection and the DECI2 protocol is on;) + You'll see how the client queries for registers values & memory areas. + - the situation gets complicated since you can do many things from now on: + +Debug bios: + - start with a bios selected and the check box checked (see above). + - PC=0xBFC00000 and you can trace the bios now. Anyway, there is no breakpoints + support for the bios as the client "thinks" you are debugging the real PS2, + so you cannot write to bios area. BTW: the breakpoints are implemented by + patching the code with a BREAK instruction. Since you cannot write such instr + you cannot put a breakpoint on the bios, cannot run to address and so on... +Run bios: + - start with a bios selected and the check box not checked (see above). + - wait till the emu runs the bios...it will take some time. After THAT the + connection log window will be displayed. + - PC=0x82000 or smth like that...you can check the environment set up while + running the bios. The modules window for instance. And various memory + structures. Also, this is the entrypoint of the shell loading. Try to trace + it;) +Debug an application: + - start with HLEbios selected (see above) + - since the debugger (the client) does debug whatever you want to. You can + load the elf to debug with pcsx2 loader (ie. with File->Load ELF file or + from the CDVD). But you will need to know the entrypoint to force it in + remote debugger (set PC to cursor or to address). This way is not recomended. + I like to use the loader of the debugger since it knows to load the source + also if available and does not cause troubles with setting the PC. Also, if + the ELF has symbols, it can run it to main;) + - now, you're on your own. Use step by step, step over, breakpoints, run to + cursor, run to address etc. HAPPY DEBUGGING! + +KNOWN ISSUES and NOTES +====================== + - use CDVDnull or another plugin as you like to have or not a src for cdvd + loading;) + - pcsx2 does not have support to debug the IOP. You can do this now but it is + a hard job. That is due to the fact that PS2 can run every processor + independently (ie. one can be stopped) and pcsx2 cannot. The emu runs either + all the procs or nothing. So, in the debugger, the debugged processor is on + stop and others are "running". When there will be added support for debugging + the IOP, also only one processor will run. + - do not step through the program very fast. Because of the communication speed + and a reentrancy issue, this will get the debugger in a FALSE run state. That + means that the debugger "thinks" that the PS2 (ie. pcsx2;)) is running, + although it is not. The simple solution to this is to press STOP/BREAK button + in the debugger. Notice also that some run states might be also true! When + you are stepping over a big loop...that might take some time, so if you stop + the emu you will get in the middle of the loop:P + - you can also notice the low speed that can also be met in the pcsx2 debugger. + That's because Cpu->Step() is used. + - also, notice that you cannot debug in recompiler mode! ONLY interpreter mode + works! + - IOP modules loading is not supported and the files loaded from host directory + are also loaded in pcsx2 way, ie. from host\ directory; not through remote + debugger. + - if you try to debug the bios and the code is not displayed...scroll down + in order to have only valid addresses on the screen; ie. the top address + to be 0xBFC00000 and in emu communication log there will be no ADDRESS ERROR. + + +=============================== +Florin (florinsasu@yahoo.com) +2003/04/17 diff --git a/pcsx2/Docs/Translating.txt b/pcsx2/Docs/Translating.txt new file mode 100644 index 0000000000..9fb98e10a2 --- /dev/null +++ b/pcsx2/Docs/Translating.txt @@ -0,0 +1,41 @@ + Translating PCSX2 + ----------------- + +Just some small notes for translators. + +First download the translator package from the download section of http://www.pcsx2.net + +PCSX2 translations are based on the gettext library: +http://www.gnu.org/software/gettext. + +The main file to translate is pcsx2.po which cames in the package you downloaded, +note that you must place the translated +strings over the msgstr and leave untouched the msgid. + +Example: + +msgid "Memory Card 1" +msgstr "Your Translation goes here" + +To test the translation use the msgfmt.exe utility to +convert the translated pcsx2.po file to a pcsx2.mo file +with this command: 'msgfmt pcsx2.po -o pcsx2.mo -v', after translating +the file please send us the .po and .mo file and please ask before +translating it, maybe someone has already started it +for your language. + +Version update for 0.9.2/3: + +I have rewritten the contents of the Translation doc to ease reading +and brung it up to date with the current GUI. Translations will work in 0.9.2 +but some Menu options may still show as their original names (fixed in 0.9.3). + + + +If you have any problems contact us and we'll try to +help you. + +Regards +linuzappz,shadow,refraction + +Contact me at : refraction@gmail.com or visit us on efnet #pcsx2 \ No newline at end of file diff --git a/pcsx2/Docs/devblog.txt b/pcsx2/Docs/devblog.txt new file mode 100644 index 0000000000..130345c7b4 --- /dev/null +++ b/pcsx2/Docs/devblog.txt @@ -0,0 +1,273 @@ +Developers Blog +=============== + +This file is mainly an R&D document containing developers findings. The aim is + to have it as much accurate as possible. Each item has to contain a number(ID), + the name of developer, revisions history and a short description. +Also, this document will serve as a pinboard - will contain coding rules, + explanations on implementation of different parts of the emulator, suggestions, + questions and ideas. + + +============================================================================== +============================================================================== +============================================================================== + +[ ID ] 4 +[name] Florin +[desc] PS2 info dumping project +[hist] 2006-05-08 initial version + +The emulator needs some information from a Playstation2 console. +We used to require the 'bios' software. In order to get better results + more and more information was needed. So here are some guidelines + on what we need and what we don't need from the console. +Also, a new dumper and handling of these infos in the emu are in work. + +a. The so-called 'bios', is actualy rom0 found in consoles, devkits, + test-kits and PSX at address 0xBFC00000 on both EE and IOP. + [!] Note that on 75xxx the EE side bios mismatch zone fields, + so it is recomended to make a IOP side dump. +b. Rom1 and Erom contain DvdPlayer related software and are located + starting with 0xBE000000 on PS2s from 18000 and up. Over various + versions, the size of rom1 and size and position of erom changed. + They used to be dumped separately, the new dumper will simplify this, + there'll only one file containing both rom1 and erom (2M or 4M). + [!] Note that on 70xxx and 75xxx (PStwo's) the dumping of erom from EE + is not possible, the correct dump is done only from IOP! + [!] rom1 and erom are identical for same generation for all zones. + In order to be able to run the dvdplayer code in the emulator, it needs + to be pre-decrypted. EROMDRV and DVDELF will have some 'decrypt' patches. +c. NVM (Non Volatile Memory) is 1K of code and provide valuable info about + console model and region. 10000 and 15000 don't contain the model name. + PStwo's contain the zone codes making possible to have a single version + of the rom for all zones. +d. Rom2 is found only in Chinese PS2s (50009, 70009...) and contains + a font file: GB18030. It is located at 0xBE400000. It was (w/o + justification) assumed that it is as big as rom1. There is no need to + dump it on any other console than chinese. The size is to be calculated + from the filesystem (as rom0 and rom1, it has romfs). +e. BBA (BroadBand Adapter) info is not needed to be dumped. At least not + for the emulator itself, maybe for the DEV9 plugin. Anyway, it's just + a MAC address that can be made up. +f. The current dumper is reading CDVD Mecha version. This will be replaced + by a more complete information file that will contain the following info: + from [EE] + CPU revision - revision code from COP0->PRId register + easier read from (u16)GetCop0(15) + high byte is 0x2E for EE + low byte is: high nibble major, low nibble minor of revision + FPU revision - revision code from COP1->control register 0 + same description as for cpu PRId + VUs revision - hum, i'm not aware of any revision for them :( + VUs memory - local and micro memory sizes are not detected + GS revision - upper halfword in CSR register + high byte is ID = 0x55 + low bytes is major.minor (per nibbles) + GS mem - is 4M + IPU revision - hum, i'm not aware of any revision for IPU :( + Caches - instruction and data cache sizes are read from + COP0->Config register + easier read from GetCop0(16). + MEM - total RAM available + easier read with Syscall +127 - GetMemorySize() + the values are: 32M for console and test-kits, + 128M for devkits and 64M for PSX + the size is not roughly detected. As devkits have multiple + boot modes (128M or 64M). + from [IOP] + CPU revision - revision code from COP0->PRId register + high byte seems to be 0 + low byte is major.minor (per nibbles) + initial japs have major=1, + most of the consoles have major=2, + 75xxx have a new IOP processor with major=3 + Caches - are known to be 4K-I$; 1K-D$ + 0xFFFE0130 is CACHE_CONFIG register... + MEM - total RAM available + easier to read with QueryMemSize() + the values are: 2M for consoles and test-kits, + 8M for devkits and PSX + again, the size is incorrectly read :) + SPU mem - 2M (any method of detection?) + CDVD mecha - version of CDVD controller + Scmd 0x03 with subcode 0x00 (outsize=4) + the first byte is the MG-encryption code + second is major, third is minor, fourth is [2b completed] + meka - i don't know; not supported on early consoles + Scmd 0x03 with subcode 0x90 (outsize=1+1) + DEV9 revision- type of DEV9 device: 0x2x for PCMCIA, 0x3x for EXPBAY + *(vu16*)BF80146E + USB OHCI rev - Hc_revision of the OHCI controller + *(vu32*)0xBF801600 + ILINK rev/id - I'm not sure of this one, on ps2's that don't have + firewire anymore, the value is 0xFF; on 10000 is random + *(vu32*)0xBF808480 and *(vu32*)0xBF808500 (looks like mirror) + +g. The dumper will sign the dumped content, so John Doe cannot play easily + with the bios data ;) The emu works correctly with good data/food. + +h. I'm thinking of a method to check for modified content (dumps made with a + chipped console). Probably, it will be done in the emu and presented as a + warrning. + + +============================================================================== +============================================================================== +============================================================================== + +[ ID ] 3 +[name] auMatt +[desc] PS2 NVM Data not 100% +[hist] 2006-05-07 initial version + + ³v0-v8 ³ v9- ³ Bits ³ Bytes º +ÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍØÍÍÍÍÍÍÍØÍÍÍÍÍÍÍØÍÍÍÍÍÍ͹ +ÍÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍØÍÍÍÍÍÍÍØÍÍÍÍÍÍÍØÍÍÍÍÍÍ͹ +PS1 Disc Spd ³ 0x300 ³ 0x2c0 ³ ³ º +PS1 Texture ³ 0x300 ³ 0x2c0 ³ ³ º +Video Output ³ 0x300 ³ 0x2c0 ³ ³ º +SPDIF ³ 0x310 ³ 0x2c0 ³ 0 ³ º +A/R ³ 0x310 ³ 0x2c0 ³ 1 & 2 ³ º +Language ³ 0x311 ³ 0x2c1 ³ All ³ º +TimeZone ³ 0x312 ³ 0x2c2 ³ ³ º +Summer Time ³ 0x312 ³ 0x2c2 ³ 3 ³ º +Time Format ³ 0x312 ³ 0x2c2 ³ 5 ³ º +DateNotation ³ 0x312 ³ 0x2c2 ³ 6 & 7 ³ º +TimeZone ³ 0x313 ³ 0x2c3 ³ All ³ º +TimeZone ³ 0x315 ³ 0x2c5 ³ All ³ º +Model Number ³ 0x1a0 ³ 0x1b0 ³ ³ 16 º +Console ID ³ 0x1c8 ³ 0x1f0 ³ ³ 8 º +ILink ID ³ 0x1c0 ³ 0x1e0 ³ ³ 8 º +Date Stamp ³ 0x180 ³ ³ ³ 16 º +Date Stamp ³ 0x1e0 ³ ³ ³ 16 º +Date Stamp ³ 0x1f0 ³ ³ ³ 16 º +Rem. Control ³ ³ 0x2c4 ³ 5 ³ º +Checksum ³ 0x31f ³ 0x2cf ³ All ³ º + +Checksum is calculated by the previous 15 bytes added together, then ANDed with 0xFF +Serial Number of Console can be obtained from bytes 7,6 & 5 of the Console ID in dec. + + +============================================================================== +============================================================================== +============================================================================== + +[ ID ] 2 +[name] Florin +[desc] PS2 bios versioning +[hist] 2006-04-30 initial version + +MG zone - the MagicGate decryption zone. You can find the code as one of the bits + in the byte at offset +1C in MagicGate encrypted files +PS1 drv - the letter for PS1VERx files in PStwo rom0 +ROMVER - has the following format VVvvZTYYYYMMDD in 14 bytes + VV, vv are the version of the bios in BCD + Z is zone code, see below + T is type of the console: C - consumer console, D - devkit/test console + YYYYMMDD is date of the bios + - BxDATA-SYSTEM, BxEXEC-SYSTEM, BxEXEC-DVDPLAYER uses Z code in place of 'x' + I: default, Japan; A: Usa, Asia; E: Europe, Oceania, Russia; C: China +VERSTR - the code from that file in rom0 +OSDVER - VVvvZlng is the format of the file for scph5xxxx. in PStwo the last 4 letters + are read from NVM, offset +180 7 chars (ROMVER:1|OSDVER:4|VERSTR:1|DVDID:1) +mecha - version read from CDVD SCmd 0x03:0x00 + +The following table is far from complete... + + ³Japan ³ USA ³AusNz ³ UK ³Europe³Korea ³ HK ³Taiwan³Russia³China ³Mexic º +ÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍ͹ +ÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍ͹ +MG zone ³ 0/J ³ 1/U ³ 3/O ³ 2/E ³ 2/E ³ 4/A ³ 4/A ³ 4/A ³ 5/R ³ 6/C ³ 7/M º +PS1 drv ³ J ³ A ³ E ³ E ³ E ³ H ³ H ³ H ³ E ³ C ³ A º +ROMVER ³0 J ³1 A ³2 E ³2 E ³2 E ³1 H ³1 H ³1 H ³2 E ³3 C ³1 A º +VERSTR ³ J ³ A ³ E ³ E ³ E ³ J ³ J ³ J ³ E ³ J ³ A º +OSDVER ³0 Jjpn³1 Aeng³2 Eeng³2 Eeng³2 Eeng³5 Kkor³6 Htch³6 Htch³4 Rrus³3 Csch³ Aspa?º +ÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +v0 scph³10000 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + mecha³0.12.0ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + ver³0100JCÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + date³000117ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + scph³15000 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + mecha³0.18.0ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + ver³0101JCÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + date³000217ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + scph³18000 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + mecha³0.22.0ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + ver³0120JCÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + date³001027ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +ÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍ͹ +v1 scph³30000 ³30001 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +v2 ³30000 ³30001 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +v3 ³30000 ³30001 ³30002 ³30003 ³30004 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +ÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍ͹ +v4 scph³30000 ³30001 ³30002 ³30003 ³30004 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + mecha³ ³ ³ ³ ³ ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + ver³ ³ ³ ³0120EC³ ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + date³ ³ ³ ³000902³ ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + ³35000 ³35001 ³35002 ³35003 ³35004 ³35005 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +ÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍ͹ +v5 ³30000 ³30001 ³30002 ³30003 ³30004 ³30005 ³ ³ ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + scph³30000R³30001R³30002R³30003R³30004R³30005R³30006R³30007RÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + mecha³0.32.0³ ³ ³2.32.0³ ³ ³ ³ ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + ver³0160JC³ ³ ³0160EC³0160EC³ ³ ³ ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + date³011004³ ³ ³011004³011004³ ³ ³ ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +v6 ³ ³ ³ ³ ³ ³ ³ ³ ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +ÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍ͹ +v7 scph³37000 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + ³39000 ³39001 ³39002 ³39003 ³39004 ³39005 ³39006 ³39007 ³39008 ÃÄÄÄÄÄÄÅÄÄÄÄÄĶ + mecha³ ³1.36.0³ ³ ³ ³ ³ ³ ³ ÃÄÄÄÄÄÄÅÄÄÄÄÄĶ + ver³ ³0160AC³ ³ ³ ³ ³ ³ ³ ÃÄÄÄÄÄÄÅÄÄÄÄÄĶ + date³ ³020207³ ³ ³ ³ ³ ³ ³ ÃÄÄÄÄÄÄÅÄÄÄÄÄĶ + ÃÄÄÄÄÄÄ´39001NÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄ´39010Nº +ÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +v8 scph³39000 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄ´39006 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ + mecha³0.38.0³ ³ ³ ³ ³ ³ ³ ³ ³ ³ º + ver³0160JC³ ³ ³ ³ ³ ³ ³ ³ ³ ³ º + date³020426³ ³ ³ ³ ³ ³ ³ ³ ³ ³ º +ÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍ͹ +v9 scph³50000 ³50001 ³50002 ³50003 ³50004 ³50005N³50006 ³50007 ³50008 ³50009 ÃÄÄÄÄÄĶ + ÃÄÄÄÄÄÄ´50001NÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄ´50010Nº + ³55000 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄ´55005N³55006 ³55007 ÃÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +ÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄĶ +v10 scph³ ³50001 ³50002 ³50003 ³50004 ³ ³50006 ³50007 ³ ³50009 ³50010 º + mecha³ ³ ³ ³ ³ ³ ³4.54.0³4.??.0³ ³6.??.0³ º + ver³ ³ ³ ³ ³0190EC³ ³0190HC³0190HC³ ³0190CC³ º + date³ ³ ³ ³ ³030623³ ³030623³030623³ ³030623³ º +v11 ³ ³ ³ ³ ³50004 ³ ³ ³ ³ ³ ³ ºMexic USA +ÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍÎÍÍÍÍÍÍÑÍÍÍÍÍÍ» +v12 scph³70000 ÃÄÄÄÄÄÄ´70002 ³70003 ³70004 ³70005 ³70006 ³70007 ³70008 ³70009 ÃÄÄÄÄÄÄ´70011 ³70012 º + mecha³ ÃÄÄÄÄÄÄ´3.62.0³2.62.0³2.??.0³ ³4.64.0³ ³5.??.0³ ÃÄÄÄÄÄÄ´ ³1.60.0º + ver³0200JCÃÄÄÄÄÄÄ´0200EC³0200EC³0200EC³ ³0200HC³ ³0200EC³ ÃÄÄÄÄÄÄ´ ³0200ACº + date³040614ÃÄÄÄÄÄÄ´040614³040614³040614³ ³040614³ ³040614³ ÃÄÄÄÄÄÄ´ ³040614º +v13 scph³ ³70001 ³70002 ³70003 ³70004 ³ ³ ³ ³ ³ ³ ÌÍÍÍÍÍÍÏÍÍÍÍÍͼ +ÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍ͹ +v14 scph³75000 ³75001 ³75002 ³75003 ³75004 ³75005 ³75006 ³75007 ³75008 ÃÄÄÄÄÄÄ´75010 º + mecha³ ³1.66.0³3.66.0³ ³ ³ ³ ³ ³ ÃÄÄÄÄÄÄ´ º + ver³ ³0220AC³0220EC³ ³ ³ ³ ³ ³ ÃÄÄÄÄÄÄ´ º + date³ ³050620³050620³ ³ ³ ³ ³ ³ ÃÄÄÄÄÄÄ´ º +ÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄĽ + + +============================================================================== +============================================================================== +============================================================================== + +[ ID ] 1 +[name] Florin +[desc] CVS management rules +[hist] 2006-04-30 initial version + +Please do not commit on CVS temporary files or those made by development tools + (IDEs). Eg: *.suo/aps/opt/plg/ncb/exp +Keep it clean and before commiting a new module see the previous structure and + naming rules. If unsure about how to do any opperation please ASK. please ASK! +There's no rush ;) +Also, I'm considering switching to SVN, as CVS seems to be *abandoned* by + sourceforge.net +Any change to sources has to be commited to the public CVS. The risk of + stealing is faced by any open-source developer - the difference is the + quality! The original will always stand up ;) +Try not to break the source by your patches. Although linux and 64-bit versions + are not maintained atm, your changes should be done with all versions in mind. +Happy coding :) diff --git a/pcsx2/Docs/readme 0.9.4.txt b/pcsx2/Docs/readme 0.9.4.txt new file mode 100644 index 0000000000..2048b28cb2 --- /dev/null +++ b/pcsx2/Docs/readme 0.9.4.txt @@ -0,0 +1,146 @@ +PCSX2 is a PlayStation 2 emulator for Windows and Linux, started by the same team that brought you PCSX (a Sony PlayStation 1 emulator). + +The PCSX2 project attempts to allow PS2 code to be executed on your computer, thus meaning you can put a PS2 DVD or CD into your computers drive, and boot it up! + + +Overview + +The project has been running for more than five years now, and since it's initial release has grown in compatibility. From initially just being able to run a few public domain demos, it's current state enables many games to boot and actually go in game, such as the 'famous' Final Fantasy X or Devil May Cry 3. You can always visit the PCSX2 homepage (http://www.pcsx2.net) to check the latest compatibility status of games with more than 1800 titles tested. + + +Configuration + +A very detailed guide is available on the PCSX2 homepage which is already translated in several languages! +You can consult it here: http://www.pcsx2.net/guide.php + + +Status + +PCSX2 has come a long way since its’ starting point back at 2002.Current features include: + +•Separate recompilers for Emotion Engine (EE) , Vector Unit 0 (VU0) and Vector Unit 1 (VU1). +•Dual core support, with the Graphics Synthesizer (GS) running on a second thread. +•Usage of MMX,SSE1,SSE2 and limited SSE3 extensions +•Proper SPU2 emulation featuring Auto DMA and Time Scaling +•Full gamepad support featuring Dual Shock 2,analog controls and even supporting analog movement over keyboard (using some external plugins) +•Patch system for creating cheats and for workarounds on games + +Sections that still need work: + +•Dev9 functions, such as HDD and Ethernet (partially implemented) support +•Firewire emulation (quite low on the list though) +•USB emulation (very partially implemented) +•Image Processing Unit (IPU) emulation which is responsible for the FMV playback.It has been implemented but it is buggy and slow +•MIPS cache needs to be properly implemented (barely works at this time) + + + + +How can you help + +As most of you are aware, the PCSX2 team is working on this project at the expense of their free time and provide it without charging the program’s use. +If you want to show your appreciation to these people and motivate them, you can donate any amount of money you feel is right to the team’s paypal account found in the official site. +These funds will be used so the team members can get more modern and new hardware in order to test and debug more efficiently and even implement new features (just like dual core support for example). + +If you are a programmer and you are interested in helping the PCSX2 team by making additions or corrections to the code, you are free to browse through the public SVN repository here (http://sourceforge.net/projects/pcsx2) after taking into account PCSX2 is under the GNU General Public Licence (GPL) v2. + + + +The Coding Team + +Below you can see 3 tables, showing the current team members who are actively coding at the present time, the current team members who have been inactive for some time and the older team members who for some reason quit along the way, which include the previous project leader Linuzappz to whom we send our best regards :) + + +Current active team members: + +Nickname Real Name Place Occupation Comments + +florin Florin Sasu Romania co-coder Master of HLE. Master of cd code and bios HLE.. +saqib - Pakistan Main Coder Fixing bugs around (FPU, Interpreter, VUs...) +Nachnbrenner - Germany co-coder patch freak :P +aumatt Australia co-coder a bit of everything mostly handles CDVD cmds +refraction Alex Brown England co-coder General Coding DMA/VIF etc +zerofrog - USA co-coder Recompilers, ZeroGS, x86-64, linux, optimizations, general fixes and new features + +Current inactive team members: + +Nickname Real Name Place Occupation Comments + +Shadow George Moralis Greece co-coder Master of cpu, master of bugs, general coding… +Goldfinger - Brazil co-coder MMI,FPU and general stuff +loser - Australia co-coder obscure cdvd related stuff + +Ex team members: + +Nickname Real Name Place Occupation Comments + +Linuzappz - Argentina Main Coder Master of The GS emulation and so many others.. +basara - - co-coder Recompiler programmer. general coding +[TyRaNiD] - - co-coder GS programmer.General coding +Roor - - co-coder General coding + +Additional coding: +F|RES, Pofis, Gigaherz, Nocomp, _Riff_, fumofumo + +The Beta Tester Team + +Beta testers are people (slaves/mindless grunts :P) who constantly test new PCSX2 beta builds to report any new bugs, regressions or improvements. While this might sound simple to most, what many people do not know is that testers also debug with the coders, maintain the huge game compatibility list, create dumps and logs for the coders and so much more. As above, active, inactive and ex members are listed alphabetically + +Current active members: + +Bositman, CKemu, Crushtest, Falcon4Ever, GeneralPlot, Prafull, RPGWizard, RudyX, Parotaku + + +Current inactive team members: + +Belmont, Knuckles, Raziel + + +Ex team members: +Chaoscode, CpUMasteR, EFX , Elly, JegHegy, Razorblade, Seta San, Snake875 + + +Additional thanks and credits + +Duke of NAPALM: For the 3d stars demo. The first demo that worked in pcsx2 :) +Tony Saveski (dreamtime): For his great ps2tutorials!! +F|res: Author of dolphin, a big thanks from shadow.. +Now3d: The guy that helped shadow at his first steps.. +Keith: Who believed in us.. +Bobbi & Thorgal: For hosting us, for the old page design and so many other things +Sjeep: Help and info +BGnome: Help testing stuff +Dixon: Design of the old pcsx2 page, and the pcsx2.net domain +Bositman: PCSX2 beta tester :) (gia sou bositman pare ta credits sou ) +No-Reccess: Nice guy and great demo coder :) +NSX2 team: For their help with VU ;) +Razorblade: For the old PCSX2 logo & icon. +Snake: He knows what for :P +Ector: Awesome emu :) +Zezu: A good guy. Good luck with your emu :P +Hiryu & Sjeep: For their libcdvd (iso parsing and filesystem driver code) +Sjeep: For the SjDATA filesystem driver +F|res: For the original DECI2 implementation +libmpeg2: For the mpeg2 decoding routines +Aumatt: For applying fixes to pcsx2 +Microsoft: For VC.Net 2003 (and now 2005) (really faster than vc6) :P +NASM team: For nasm +CKemu: Logos/design + +and probably to a few more.. + +Special Shadow's thanks go to... + +My friends: Dimitris, James, Thodoris, Thanasis and probably to a few more..and of course to a lady somewhere out there.... + + +Created for v0.9.4 by bositman. + + + + + + + +The PCSX2 Coding and Beta testing team + diff --git a/pcsx2/Docs/readme Playground.txt b/pcsx2/Docs/readme Playground.txt new file mode 100644 index 0000000000..b50fea69ff --- /dev/null +++ b/pcsx2/Docs/readme Playground.txt @@ -0,0 +1,70 @@ +About PCSX2 Playground: + +pcsx2-playground is originally based off the c/c++ official pcsx2 trunk code. It has since been modified heavily in an on-going attempt to clean up the code, fix bugs, and improve overall emulation ability. + + +Devs (alphabetically) - Comments/Specialty + +arcum42 - Linux compatibility and porting. +cottonvibes - FPU and VU recompilers, general coding. +drkIIRaziel - Memory management, emulation theory/principals, recompiler design. +gigaherz - General coding, spu2ghz, cdvdGigaherz, works on stuff until he gets 'bored' :p +Jake.Stine - MTGS, counters, timing/syncing, general coding. +ramapcsx2 - pcsx2 tweaker, always finds those magic values that make the game work. :p + +Beta Testers (alphabetically): + +Bositman - Setup our Playground forums and helps moderate them. Also beta tests for us and actively contributes in Playground discussion. +Crushtest - Helps with Playground beta testing and discussion. +Krakatos - Project Management and Playground forums moderation. Devotes several hours of his day to playground beta-testing and discussion. + +Additional Credits: + +Special thanks to the official pcsx2 team for making ps2 emulation a reality! +Also a big special-thanks to the pcsx2 team for hosting our Playground forums and builds! +More info about the official pcsx2 team can be found in "readme 0.9.4.txt" + + +Added Features and Improvements: + + * Improved Frameskip/VU-skip + * Special Game Fixes Section + * Advanced Options Section for custom tweaking VU/FPU behavior. + * Rewritten Multithreaded GS (MTGS) mode, with 15% speedup and fixes many instabilities. + * Improved VU/FPU Flags and Clamping support (helps fix odd behaviors and SPS in some games) + * Improved EE/IOP synchronization (fixes many freeze-ups and vmhacks). + * Improved CDVD support. + + +Performance: + +Emulation speeds when using Playground's default settings will be slower than the official 0.9.4 or 0.9.5 builds because pcsx2-playground is doing more work to provide your games with a more correct emulated environment. Properly emulating all of the VU clamping and flags is very time-consuming in particular. So various speedhacks were made so people can disable some of these slower emulation features for games that do not need them. When set-up properly, pcsx2-playground is usually faster than the official build. + +The speed hacks offered are still a work-in-progress, and so for now there isn't much documentation on how to use them. Every game is different and so you'll just have to play around and see what works best. You can also try visiting the Pcsx2 forums, where members of the Playground team and other knowledgeable contributors should be able to help you get the most out of the emulator. + + +The Future of Pcsx2 Playground + +There is still alot of room for improvement, but we're trying hard to fix stuff as best we can. Our todo/wish lists are extensive but we're optimistic that the emulator can continue to improve. :) + +Visit our forums at Pcsx2.net, or come to the #pcsx2 IRC channel on efnet if you want to contribute! + + +System Requirements: + +Minimum + + * Windows/Linux OS + * CPU that supports SSE2 (Pentium 4 and up, Athlon64 and up) + * GPU that supports Pixel Shaders 2.0 + * 512mb RAM + +Recommended + + * Windows Vista 32bit/64bit + * CPU: Intel Core 2 Duo @ 3.2ghz or better + * GPU: 8600gt or better + * 1gb RAM (2gb if on Vista) + +Note: pcsx2 only takes advantage of 2 cores so far; so a quad-core processor will not help with speed. + diff --git a/pcsx2/Docs/specs.tex b/pcsx2/Docs/specs.tex new file mode 100644 index 0000000000..aca2581945 --- /dev/null +++ b/pcsx2/Docs/specs.tex @@ -0,0 +1,159 @@ +\documentclass[10pt]{article} + +\begin{document} +\section{...stuff...} +-order the plugins are started\\ +-platform dependent stuff, calling convention\\ + +\section{Generic functions} +The following three functions will be present in all the plugin libraries, + in order to be recognized as valid PS2E plugins. They will help + the emulator to find a plugin capabilities. + +\subsection{PS2EgetLibType} +\begin{quote}\texttt{unsigned int} PS2EgetLibType(\texttt{void});\end{quote} + +\begin{description} +\item[PS2EgetLibType] returns the type of the plugin. + In fact it indicates the APIs supported by the dynamic library. + The returned value can be one of: +\begin{itemize} +\item PS2E\_LT\_GS 0x01 +\item PS2E\_LT\_PAD 0x02 +\item PS2E\_LT\_SPU2 0x04 +\item PS2E\_LT\_CDVD 0x08 +\item PS2E\_LT\_DEV9 0x10 +\end{itemize} +Currently, these are the only plugin types supported. Note that the values + can be ORed. +\end{description} + + + +\subsection{PS2EgetLibVersion2} +\begin{quote}\texttt{unsigned int} PS2EgetLibVersion2(\texttt{unsigned int} + type);\end{quote} + +\begin{description} +\item[PS2EgetLibVersion2] returns a combination of version numbers. +Parameter \emph{type} is used to select the functions set for which + the emulator requests version information. See \texttt{PS2EgetLibType} + for the values of this parameter. + +The 5 APIs and their corresponding specs have changed over time. + In order to prevent crashes and incompatibilities, a spec version have + been introduced as the highest 16 bits of the returned value. +\begin{itemize} +\item PS2E\_GS\_VERSION 0x0002 +\item PS2E\_PAD\_VERSION 0x0002 +\item PS2E\_SPU2\_VERSION 0x0002 +\item PS2E\_CDVD\_VERSION 0x0003 +\item PS2E\_DEV9\_VERSION 0x0001 +\end{itemize} +Notice that when the specs do change \texttt{and} the compatibility is broken, + this version number is increased. The emulator loading code will support + only one version for a certain library type at a time. If the internal + version and plugin API version does not match, the plugin + will not be loaded nor used. + +The low half of the returned value reflects the version of the plugin itself. + A major.minor versioning scheme is used on the two bytes like this: +\begin{verbatim} +...//code +return (PS2E_CDVD_VERSION<<16) | (0<<8) | (67); //version 0.67 +\end{verbatim} +\end{description} + + + +\subsection{PS2EgetLibName} +\begin{quote}\texttt{char*} PS2EgetLibName(\texttt{void});\end{quote} + +\begin{description} +\item[PS2EgetLibName] returns a string that contains a short\footnote{ +less then 30 chars in one line} name. The string is stored + in the plugin and will be used to represent the plugin in a config dialog. +\end{description} + + + + + +\section{CDVD functions} +This section describes the functions that corresponds to CDVD\footnote{short for CD/DVD} +API - type PS2E\_LT\_CDVD(0x08). + These specs are for PS2E\_CDVD\_VERSION(0x0003). + +\subsection{CDVDinit} +\begin{quote}\texttt{int} CDVDinit(\texttt{void});\end{quote} + +\begin{description} +\item[CDVDinit] does the initialization of the CDVD interface. + It is the first function called; so, it can be used to do all the + init stuff such as reading saved configuration, one-time hardware init and + preparing the internal structures, tables, etc\ldots + If an error is found the function will return -1, otherwise 0. +\end{description} + + + +\subsection{CDVDshutdown} +\begin{quote}\texttt{void} CDVDshutdown(\texttt{void});\end{quote} + +\begin{description} +\item[CDVDshutdown] is called when the emulator is closed. Do now the freeing + operations. DO NOT FORGET TO FREE the resources used. The OS will probably + free the garbage left, but some pieces of hardware might need a + ``deinitialization'' procedure in order to work next time the emulator + is run. Imagine that the user will choose another plugin to run with + next time instead of yours, do not cause troubles. +\end{description} + + + +\subsection{CDVDopen} +\begin{quote}\texttt{int} CDVDopen(\texttt{void});\end{quote} + +\begin{description} +\item[CDVDopen] is called when the emulation starts. + It is recommended that functions called from now on (until + \texttt{CDVDclose} is met) to spend few processing time. Avoid calling + blocking functions and if you do, the user should be notified visualy. + Report errors by return value and warrings using a log. + If an error is found the function will return -1 and the emulation stops, + otherwise 0. + +Do not report errors using message boxes while the emu runs, the GS plugin + might use a display mode that can cause troubles to windowing system + in showing your message. +\end{description} + + + +\subsection{CDVDclose} +\begin{quote}\texttt{void} CDVDclose(\texttt{void});\end{quote} + +\begin{description} +\item[CDVDclose] is called when the emulation is stopped. Some of the + resources that you aquired with \texttt{CDVDstart} have to be released now + in order that other programs to use them. If you locked the CD/DVD tray, + unlock it so the user can change the disc. +\end{description} + + + +\subsection{CDVDreadTrack} +\begin{quote}\texttt{int} CDVDreadTrack(\texttt{unsigned int} + lsn, \texttt{int} mode);\end{quote} + +\begin{description} +\item[CDVDreadTrack] is the function that performs the read of \texttt{a} + sector from the CD/DVD. Parameter \emph{lsn} specifies the absolute value + of the sector number in linear addressing mode without \emph{lead-in}\footnote{i.e.\ +without leading 150 sectors == 2 seconds}. Usualy, the plugin will read + a full sector of 2352 bytes in its internal buffer. + The second parameter tells what port of ... +\end{description} + +\end{document} + diff --git a/pcsx2/EEregs.h b/pcsx2/EEregs.h new file mode 100644 index 0000000000..2af2517334 --- /dev/null +++ b/pcsx2/EEregs.h @@ -0,0 +1,40 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __EEREGS_H__ +#define __EEREGS_H__ + +#define at cpuRegs.GPR.n.at +#define k0 cpuRegs.GPR.n.k0 +#define k1 cpuRegs.GPR.n.k1 +#define v0 cpuRegs.GPR.n.v0 +#define v1 cpuRegs.GPR.n.v1 +#define a0 cpuRegs.GPR.n.a0 +#define a1 cpuRegs.GPR.n.a1 +#define a2 cpuRegs.GPR.n.a2 +#define a3 cpuRegs.GPR.n.a3 +#define t0 cpuRegs.GPR.n.t0 +#define s0 cpuRegs.GPR.n.s0 +#define gp cpuRegs.GPR.n.gp +#define fp cpuRegs.GPR.n.s8 +#define sp cpuRegs.GPR.n.sp +#define ra cpuRegs.GPR.n.ra + +#define pc0 cpuRegs.pc + +#endif /* __EEREGS_H__ */ diff --git a/pcsx2/Elfheader.cpp b/pcsx2/Elfheader.cpp new file mode 100644 index 0000000000..7e718e9868 --- /dev/null +++ b/pcsx2/Elfheader.cpp @@ -0,0 +1,662 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "CDVDisodrv.h" + +using namespace std; + +#ifdef _MSC_VER +#pragma warning(disable:4996) //ignore the stricmp deprecated warning +#endif + +u32 ElfCRC; + +struct ELF_HEADER { + u8 e_ident[16]; //0x7f,"ELF" (ELF file identifier) + u16 e_type; //ELF type: 0=NONE, 1=REL, 2=EXEC, 3=SHARED, 4=CORE + u16 e_machine; //Processor: 8=MIPS R3000 + u32 e_version; //Version: 1=current + u32 e_entry; //Entry point address + u32 e_phoff; //Start of program headers (offset from file start) + u32 e_shoff; //Start of section headers (offset from file start) + u32 e_flags; //Processor specific flags = 0x20924001 noreorder, mips + u16 e_ehsize; //ELF header size (0x34 = 52 bytes) + u16 e_phentsize; //Program headers entry size + u16 e_phnum; //Number of program headers + u16 e_shentsize; //Section headers entry size + u16 e_shnum; //Number of section headers + u16 e_shstrndx; //Section header stringtable index +}; + +struct ELF_PHR { + u32 p_type; //see notes1 + u32 p_offset; //Offset from file start to program segment. + u32 p_vaddr; //Virtual address of the segment + u32 p_paddr; //Physical address of the segment + u32 p_filesz; //Number of bytes in the file image of the segment + u32 p_memsz; //Number of bytes in the memory image of the segment + u32 p_flags; //Flags for segment + u32 p_align; //Alignment. The address of 0x08 and 0x0C must fit this alignment. 0=no alignment +}; + +/* +notes1 +------ +0=Inactive +1=Load the segment into memory, no. of bytes specified by 0x10 and 0x14 +2=Dynamic linking +3=Interpreter. The array element must specify a path name +4=Note. The array element must specify the location and size of aux. info +5=reserved +6=The array element must specify location and size of the program header table. +*/ + +struct ELF_SHR { + u32 sh_name; //No. to the index of the Section header stringtable index + u32 sh_type; //See notes2 + u32 sh_flags; //see notes3 + u32 sh_addr; //Section start address + u32 sh_offset; //Offset from start of file to section + u32 sh_size; //Size of section + u32 sh_link; //Section header table index link + u32 sh_info; //Info + u32 sh_addralign; //Alignment. The adress of 0x0C must fit this alignment. 0=no alignment. + u32 sh_entsize; //Fixed size entries. +}; + +/* +notes 2 +------- +Type: +0=Inactive +1=PROGBITS +2=SYMTAB symbol table +3=STRTAB string table +4=RELA relocation entries +5=HASH hash table +6=DYNAMIC dynamic linking information +7=NOTE +8=NOBITS +9=REL relocation entries +10=SHLIB +0x70000000=LOPROC processor specifc +0x7fffffff=HIPROC +0x80000000=LOUSER lower bound +0xffffffff=HIUSER upper bound + +notes 3 +------- +Section Flags: (1 bit, you may combine them like 3 = alloc & write permission) +1=Write section contains data the is be writeable during execution. +2=Alloc section occupies memory during execution +4=Exec section contains executable instructions +0xf0000000=Mask bits processor-specific +*/ + +struct Elf32_Sym { + u32 st_name; + u32 st_value; + u32 st_size; + u8 st_info; + u8 st_other; + u16 st_shndx; +}; + +#define ELF32_ST_TYPE(i) ((i)&0xf) + +struct Elf32_Rel { + u32 r_offset; + u32 r_info; +}; + +//2002-09-19 (Florin) +char args[256]="ez.m2v"; //to be accessed by other files +uptr args_ptr; //a big value; in fact, it is an address + +//in a0 is passed the address of the command line args, +//i.e. a pointer to an area like this: +//+00 unknown/unused +//+04 argc; number of arguments +//+08 argv[0]; address of the first parameter - program name (char*) - +//+08 argv[1]; address of the second parameter (char*) | +//+0C argv[2]; and so on | +//........ | +//+08+4*argc the program name(first param) <-- +//+08+4*argc+strlen(argv[0]+1) the rest of params; i.e. a copy of 'args' +// see above 'char args[256];' +static uint parseCommandLine( const char *filename ) +{ + if ( ( args_ptr != 0xFFFFFFFF ) && ( args_ptr > 264 ) ) + { // 4 + 4 + 256 + const char * p; + int argc; + int i; + + args_ptr -= 256; + + args[ 255 ] = 0; + memcpy( &PS2MEM_BASE[ args_ptr ], args, 256 ); //params 1, 2, etc copied + memset( &PS2MEM_BASE[ args_ptr + strlen( args ) ], 0, 256 - strlen( args ) ); +#ifdef _WIN32 + p = strrchr( filename, '\\' ); +#else //linux + p = strrchr( filename, '/' ); + if( p == NULL ) + p = strchr(filename, '\\'); +#endif + if ( p ) + { + p++; + } + else + { + p = filename; + } + args_ptr -= strlen( p ) + 1; + /* if ( args_ptr < 0 ) // fixme- This is still impossible. + { + return 0; + }*/ + strcpy( (char*)&PS2MEM_BASE[ args_ptr ], p ); //fill param 0; i.e. name of the program + + for ( i = strlen( p ) + 1 + 256, argc = 0; i > 0; i-- ) + { + while ( i && ( ( PS2MEM_BASE[ args_ptr + i ] == 0 ) || ( PS2MEM_BASE[ args_ptr + i ] == 32 ) ) ) + { + i--; + } + if ( PS2MEM_BASE[ args_ptr + i + 1 ] == ' ' ) + { + PS2MEM_BASE[ args_ptr + i + 1 ] = 0; + } + while ( i && ( PS2MEM_BASE[ args_ptr + i ] != 0 ) && ( PS2MEM_BASE[ args_ptr + i] != 32 ) ) + { + i--; + } + if ( ( PS2MEM_BASE[ args_ptr + i ] != 0 ) && ( PS2MEM_BASE[ args_ptr + i ] != 32 ) ) + { //i==0 + argc++; + if ( args_ptr - 4 - 4 - argc * 4 < 0 ) // fixme - Should this be cast to a signed int? + { + return 0; + } + ((u32*)PS2MEM_BASE)[ args_ptr / 4 - argc ] = args_ptr + i; + } + else + { + if ( ( PS2MEM_BASE[ args_ptr + i + 1 ] != 0 ) && ( PS2MEM_BASE[ args_ptr + i + 1 ] != 32 ) ) + { + argc++; + if ( args_ptr - 4 - 4 - argc * 4 < 0 ) // fixme - Should this be cast to a signed int? + { + return 0; + } + ((u32*)PS2MEM_BASE)[ args_ptr / 4 - argc ] = args_ptr + i + 1; + } + } + } + ((u32*)PS2MEM_BASE)[ args_ptr /4 - argc - 1 ] = argc; //how many args + ((u32*)PS2MEM_BASE)[ args_ptr /4 - argc - 2 ] = ( argc > 0); //have args? //not used, cannot be filled at all + + return ( args_ptr - argc * 4 - 8 ); + } + + return 0; +} +//--------------- + +struct ElfObject +{ + string filename; + MemoryAlloc data; + ELF_HEADER& header; + ELF_PHR* proghead; + ELF_SHR* secthead; + + // Destructor! + // C++ does all the cleanup automagically for us. + ~ElfObject() { } + + ElfObject( const string& srcfile, uint hdrsize ) : + filename( srcfile ) + , data( hdrsize, "ELF headers" ) + , header( *(ELF_HEADER*)data.GetPtr() ) + , proghead( NULL ) + , secthead( NULL ) + { + readFile(); + proghead = (ELF_PHR*)&data[header.e_phoff]; + secthead = (ELF_SHR*)&data[header.e_shoff]; + + if ( ( header.e_shnum > 0 ) && ( header.e_shentsize != sizeof(ELF_SHR) ) ) + { + Console::Error( "ElfLoader Warning > Size of section headers is not standard" ); + } + + if ( ( header.e_phnum > 0 ) && ( header.e_phentsize != sizeof(ELF_PHR) ) ) + { + Console::Error( "ElfLoader Warning > Size of program headers is not standard" ); + } + + ELF_LOG( "type: " ); + switch( header.e_type ) + { + default: + ELF_LOG( "unknown %x", header.e_type ); + break; + + case 0x0: + ELF_LOG( "no file type" ); + break; + + case 0x1: + ELF_LOG( "relocatable" ); + break; + + case 0x2: + ELF_LOG( "executable" ); + break; + } + ELF_LOG( "\n" ); + ELF_LOG( "machine: " ); + + switch ( header.e_machine ) + { + default: + ELF_LOG( "unknown" ); + break; + + case 0x8: + ELF_LOG( "mips_rs3000" ); + break; + } + + ELF_LOG("\n"); + ELF_LOG("version: %d\n",header.e_version); + ELF_LOG("entry: %08x\n",header.e_entry); + ELF_LOG("flags: %08x\n",header.e_flags); + ELF_LOG("eh size: %08x\n",header.e_ehsize); + ELF_LOG("ph off: %08x\n",header.e_phoff); + ELF_LOG("ph entsiz: %08x\n",header.e_phentsize); + ELF_LOG("ph num: %08x\n",header.e_phnum); + ELF_LOG("sh off: %08x\n",header.e_shoff); + ELF_LOG("sh entsiz: %08x\n",header.e_shentsize); + ELF_LOG("sh num: %08x\n",header.e_shnum); + ELF_LOG("sh strndx: %08x\n",header.e_shstrndx); + + ELF_LOG("\n"); + } + + void readFile() + { + int rsize = 0; + if ((strnicmp( filename.c_str(), "cdrom0:", strlen("cdromN:")) == 0) || + (strnicmp( filename.c_str(), "cdrom1:", strlen("cdromN:")) == 0)) + { + int fi; + fi = CDVDFS_open(filename.c_str() + strlen("cdromN:"), 1);//RDONLY + if (fi < 0) + throw Exception::FileNotFound( filename ); + CDVDFS_lseek( fi, 0, SEEK_SET ); + rsize = CDVDFS_read( fi, (char*)data.GetPtr(), data.GetSizeInBytes() ); + CDVDFS_close( fi ); + } + else + { + FILE *f; + + f = fopen( filename.c_str(), "rb" ); + if( f == NULL ) + Exception::FileNotFound( filename ); + fseek( f, 0, SEEK_SET ); + rsize = fread( data.GetPtr(), 1, data.GetSizeInBytes(), f ); + fclose( f ); + } + + if( rsize < data.GetSizeInBytes() ) + throw Exception::EndOfStream( filename ); + } + + u32 GetCRC() const + { + u32 CRC = 0; + + const u32* srcdata = (u32*)data.GetPtr(); + for(u32 i=data.GetSizeInBytes()/4; i; --i, ++srcdata) + CRC ^= *srcdata; + + return CRC; + } + + + void loadProgramHeaders() + { + if ( header.e_phnum == 0 ) + return; + + for( int i = 0 ; i < header.e_phnum ; i++ ) + { + ELF_LOG( "Elf32 Program Header\n" ); + ELF_LOG( "type: " ); + + switch ( proghead[ i ].p_type ) { + default: + ELF_LOG( "unknown %x", (int)proghead[ i ].p_type ); + break; + + case 0x1: + { + ELF_LOG("load"); + const uint elfsize = data.GetLength(); + + if (proghead[ i ].p_offset < elfsize) { + int size; + + if ((proghead[ i ].p_filesz + proghead[ i ].p_offset) > elfsize) + size = elfsize - proghead[ i ].p_offset; + else + size = proghead[ i ].p_filesz; + + if( proghead[ i ].p_vaddr != proghead[ i ].p_paddr ) + Console::Notice( "ElfProgram different load addrs: paddr=0x%8.8x, vaddr=0x%8.8x", params + proghead[ i ].p_paddr, proghead[ i ].p_vaddr); + + // used to be paddr + memcpy( + &PS2MEM_BASE[proghead[ i ].p_vaddr & 0x1ffffff], + data.GetPtr(proghead[ i ].p_offset), size + ); + + ELF_LOG("\t*LOADED*"); + } + } + break; + } + + ELF_LOG("\n"); + ELF_LOG("offset: %08x\n",(int)proghead[i].p_offset); + ELF_LOG("vaddr: %08x\n",(int)proghead[i].p_vaddr); + ELF_LOG("paddr: %08x\n",proghead[i].p_paddr); + ELF_LOG("file size: %08x\n",proghead[i].p_filesz); + ELF_LOG("mem size: %08x\n",proghead[i].p_memsz); + ELF_LOG("flags: %08x\n",proghead[i].p_flags); + ELF_LOG("palign: %08x\n",proghead[i].p_align); + ELF_LOG("\n"); + + } + } + + + void loadSectionHeaders() + { + if( header.e_shnum == 0 || header.e_shoff > (u32)data.GetLength() ) + return; + + const u8* sections_names = data.GetPtr( secthead[ header.e_shstrndx ].sh_offset ); + + int i_st = -1; + int i_dt = -1; + + for( int i = 0 ; i < header.e_shnum ; i++ ) + { + ELF_LOG( "Elf32 Section Header [%x] %s", i, §ions_names[ secthead[ i ].sh_name ] ); + + if ( secthead[i].sh_flags & 0x2 ) + args_ptr = min( args_ptr, secthead[ i ].sh_addr & 0x1ffffff ); + +#ifdef PCSX2_DEVBULD + ELF_LOG("\n"); + ELF_LOG("type: "); + + switch ( secthead[ i ].sh_type ) + { + default: + ELF_LOG("unknown %08x",secthead[i].sh_type); + break; + + case 0x0: + ELF_LOG("null"); + break; + + case 0x1: + ELF_LOG("progbits"); + break; + + case 0x2: + ELF_LOG("symtab"); + break; + + case 0x3: + ELF_LOG("strtab"); + break; + + case 0x4: + ELF_LOG("rela"); + break; + + case 0x8: + ELF_LOG("no bits"); + break; + + case 0x9: + ELF_LOG("rel"); + break; + } + + ELF_LOG("\n"); + ELF_LOG("flags: %08x\n", secthead[i].sh_flags); + ELF_LOG("addr: %08x\n", secthead[i].sh_addr); + ELF_LOG("offset: %08x\n", secthead[i].sh_offset); + ELF_LOG("size: %08x\n", secthead[i].sh_size); + ELF_LOG("link: %08x\n", secthead[i].sh_link); + ELF_LOG("info: %08x\n", secthead[i].sh_info); + ELF_LOG("addralign: %08x\n", secthead[i].sh_addralign); + ELF_LOG("entsize: %08x\n", secthead[i].sh_entsize); + // dump symbol table + + if( secthead[ i ].sh_type == 0x02 ) + { + i_st = i; + i_dt = secthead[i].sh_link; + } +#endif + } + + if( ( i_st >= 0 ) && ( i_dt >= 0 ) ) + { + const char * SymNames; + Elf32_Sym * eS; + + SymNames = (char*)data.GetPtr( secthead[ i_dt ].sh_offset ); + eS = (Elf32_Sym*)data.GetPtr( secthead[ i_st ].sh_offset ); + Console::WriteLn("found %d symbols", params secthead[ i_st ].sh_size / sizeof( Elf32_Sym )); + + for( uint i = 1; i < ( secthead[ i_st ].sh_size / sizeof( Elf32_Sym ) ); i++ ) { + if ( ( eS[ i ].st_value != 0 ) && ( ELF32_ST_TYPE( eS[ i ].st_info ) == 2 ) ) { + R5900::disR5900AddSym( eS[i].st_value, &SymNames[ eS[ i ].st_name ] ); + } + } + } + } +}; + +void ElfApplyPatches() +{ + string filename; + ssprintf( filename, "%8.8x", ElfCRC ); + + // if patches found the following status msg will be overwritten + Console::SetTitle( fmt_string( "Game running [CRC=%hs]", &filename ) ); + + if( !Config.Patch ) return; + + if(LoadPatch( filename ) != 0) + { + Console::WriteLn("XML Loader returned an error. Trying to load a pnach..."); + inifile_read( filename.c_str() ); + } + else + Console::WriteLn("XML Loading success. Will not load from pnach..."); + + applypatch( 0 ); +} + +// Fetches the CRC of the game bound to the CDVD plugin. +u32 loadElfCRC( const char* filename ) +{ + TocEntry toc; + + CDVDFS_init( ); + if ( CDVD_findfile( filename + strlen( "cdromN:" ), &toc ) == -1 ) + return 0; + + DevCon::Status( "loadElfFile: %d bytes", params toc.fileSize ); + u32 crcval = ElfObject( filename, toc.fileSize ).GetCRC(); + Console::Status( "loadElfFile: %s; CRC = %8.8X", params filename, crcval ); + + return crcval; +} + +int loadElfFile(const char *filename) +{ + // Reset all recompilers prior to initiating a BIOS or new ELF. The cleaner the + // slate, the happier the recompiler! + + SysResetExecutionState(); + + if( filename == NULL || filename[0] == 0 ) + { + Console::Notice( "Running the PS2 BIOS..." ); + return -1; + } + + // We still need to run the BIOS stub, so that all the EE hardware gets initialized correctly. + cpuExecuteBios(); + + int elfsize; + + Console::Status("loadElfFile: %s", params filename); + if (strnicmp( filename, "cdrom0:", strlen( "cdromN:" ) ) && + strnicmp( filename, "cdrom1:", strlen( "cdromN:" ) ) ) + { + // Loading from a file (or non-cd image) + struct stat sbuf; + if ( stat( filename, &sbuf ) != 0 ) + return -1; + elfsize = sbuf.st_size; + } + else + { + // Loading from a CD rom or CD image. + TocEntry toc; + CDVDFS_init( ); + if ( CDVD_findfile( filename + strlen( "cdromN:" ), &toc ) == -1 ) + return -1; + elfsize = toc.fileSize; + } + + Console::Status( "loadElfFile: %d", params elfsize); + ElfObject elfobj( filename, elfsize ); + + //2002-09-19 (Florin) + args_ptr = 0xFFFFFFFF; //big value, searching for minimum + + elfobj.loadProgramHeaders(); + elfobj.loadSectionHeaders(); + + cpuRegs.pc = elfobj.header.e_entry; //set pc to proper place + ELF_LOG( "PC set to: %8.8lx\n", cpuRegs.pc ); + + cpuRegs.GPR.n.sp.UL[0] = 0x81f00000; + cpuRegs.GPR.n.gp.UL[0] = 0x81f80000; // might not be 100% ok + cpuRegs.GPR.n.a0.UL[0] = parseCommandLine( filename ); + + for ( uint i = 0; i < 0x100000; i++ ) { + if ( strcmp( "rom0:OSDSYS", (char*)PSM( i ) ) == 0 ) { + strcpy( (char*)PSM( i ), filename ); + DevCon::Status( "addr %x \"%s\" -> \"%s\"", params i, "rom0:OSDSYS", filename ); + } + } + + ElfCRC = elfobj.GetCRC(); + Console::Status( "loadElfFile: %s; CRC = %8.8X\n", params filename, ElfCRC); + + ElfApplyPatches(); + LoadGameSpecificSettings(); + return 0; +} + +#include "VU.h" +extern int g_FFXHack; +extern int path3hack; +int g_VUGameFixes = 0; + +// fixme - this should be moved to patches or eliminated +void LoadGameSpecificSettings() +{ + // default + g_VUGameFixes = 0; + g_FFXHack = 0; + + switch(ElfCRC) { + // The code involving VUFIX_SIGNEDZERO & VUFIX_EXTRAFLAGS + // is no longer in pcsx2. + + //case 0x0c414549: // spacefisherman, missing gfx + // g_VUGameFixes |= VUFIX_SIGNEDZERO; + // break; + //case 0x4C9EE7DF: // crazy taxi (u) + //case 0xC9C145BF: // crazy taxi, missing gfx + // g_VUGameFixes |= VUFIX_EXTRAFLAGS; + // break; + + case 0xb99379b7: // erementar gerad (discolored chars) + g_VUGameFixes |= VUFIX_XGKICKDELAY2; // Tested - still needed - arcum42 + break; + case 0xa08c4057: //Sprint Cars (SLUS) + case 0x8b0725d5: //Flinstones Bedrock Racing (SLES) + path3hack = 1; // We can move this to patch files right now + break; + + case 0x6a4efe60: // ffx(j) + case 0xA39517AB: // ffx(e) + case 0xBB3D833A: // ffx(u) + case 0x941bb7d9: // ffx(g) + case 0xD9FC6310: // ffx int(j) + case 0xa39517ae: // ffx(f) + case 0xa39517a9: // ffx(i) + case 0x658597e2: // ffx int + case 0x941BB7DE: // ffx(s) + case 0x3866CA7E: // ffx(asia) + case 0x48FE0C71: // ffx2 (u) + case 0x9aac530d: // ffx2 (g) + case 0x9AAC5309: // ffx2 (e) + case 0x8A6D7F14: // ffx2 (j) + case 0x9AAC530B: // ffx2 (i) + case 0x9AAC530A: // ffx2 (f) + case 0xe1fd9a2d: // ffx2 last mission (?) + case 0x93f9b89a: // ffx2 demo (g) + case 0x304C115C: // harvest moon - awl + case 0xF0A6D880: // harvest moon - sth + g_FFXHack = 1; + break; + } +} diff --git a/pcsx2/Elfheader.h b/pcsx2/Elfheader.h new file mode 100644 index 0000000000..a3514fa333 --- /dev/null +++ b/pcsx2/Elfheader.h @@ -0,0 +1,34 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __ELF_H__ +#define __ELF_H__ + +//2002-09-20 (Florin) +extern char args[256]; //to be filled by GUI +extern unsigned int args_ptr; + +//------------------- +int loadElfFile(const char *filename); +u32 loadElfCRC(const char *filename); +void LoadGameSpecificSettings(); +void ElfApplyPatches(); + +extern u32 ElfCRC; + +#endif diff --git a/pcsx2/Exceptions.h b/pcsx2/Exceptions.h new file mode 100644 index 0000000000..0d92db9ce0 --- /dev/null +++ b/pcsx2/Exceptions.h @@ -0,0 +1,303 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef _PCSX2_EXCEPTIONS_H_ +#define _PCSX2_EXCEPTIONS_H_ + +#include +#include "StringUtils.h" + +// This class provides an easy and clean method for ensuring objects are not copyable. +class NoncopyableObject +{ +protected: + NoncopyableObject() {} + ~NoncopyableObject() {} + +// Programmer's note: +// No need to provide implementations for these methods since they should +// never be referenced anyway. No references? No Linker Errors! Noncopyable! +private: + // Copy me? I think not! + explicit NoncopyableObject( const NoncopyableObject& ); + // Assign me? I think not! + const NoncopyableObject& operator=( const NoncopyableObject& ); +}; + + +// Base class used to implement type-safe sealed classes. +// This class should never be used directly. Use the Sealed +// macro instead, which ensures all sealed classes derive from a unique BaseSealed +// (preventing them from accidentally cirumventing sealing by inheriting from +// multiple sealed classes. +template < int T > +class __BaseSealed +{ +protected: + __BaseSealed() + { + } +}; + +// Use this macro/class as a base to seal a class from being derrived from. +// This macro works by providing a unique base class with a protected constructor +// for every class that derives from it. +#define Sealed private virtual __BaseSealed<__COUNTER__> + +namespace Exception +{ + // std::exception sucks, so I made a replacement. + // Note, this class is "abstract" which means you shouldn't use it directly like, ever. + // Use Exception::RuntimeError or Exception::LogicError instead. + class BaseException + { + protected: + const std::string m_message; // a "detailed" message of what disasterous thing has occured! + + public: + virtual ~BaseException() throw()=0; // the =0; syntax forces this class into "abstract" mode. + explicit BaseException( const std::string& msg="Unhandled exception." ) : + m_message( msg ) + {} + + const std::string& Message() const { return m_message; } + const char* cMessage() const { return m_message.c_str(); } + }; + + class RuntimeError : public BaseException + { + public: + virtual ~RuntimeError() throw() {} + explicit RuntimeError( const std::string& msg="An unhandled runtime error has occured, somewhere in the depths of Pcsx2's cluttered brain-matter." ) : + BaseException( msg ) + {} + }; + + class LogicError : public BaseException + { + public: + virtual ~LogicError() throw() {} + explicit LogicError( const std::string& msg="An unhandled logic error has occured." ) : + BaseException( msg ) + {} + }; + + class OutOfMemory : public RuntimeError + { + public: + explicit OutOfMemory( const std::string& msg="Out of memory!" ) : + RuntimeError( msg ) {} + }; + + // This exception thrown any time an operation is attempted when an object + // is in an uninitialized state. + class InvalidOperation : public LogicError + { + public: + virtual ~InvalidOperation() throw() {} + explicit InvalidOperation( const std::string& msg="Attempted method call is invalid for the current object or program state." ) : + LogicError( msg ) {} + }; + + // Keep those array indexers in bounds when using the SafeArray type, or you'll be + // seeing these. + class IndexBoundsFault : public LogicError + { + public: + virtual ~IndexBoundsFault() throw() {} + explicit IndexBoundsFault( const std::string& msg="Array index is outsides the bounds of an array." ) : + LogicError( msg ) {} + }; + + class HardwareDeficiency : public RuntimeError + { + public: + explicit HardwareDeficiency( const std::string& msg="Your machine's hardware is incapable of running Pcsx2. Sorry dood." ) : + RuntimeError( msg ) {} + }; + + // This exception is thrown by the PS2 emulation (R5900, etc) when bad things happen + // that force the emulation state to terminate. The GUI should handle them by returning + // the user to the GUI. + class CpuStateShutdown : public RuntimeError + { + public: + virtual ~CpuStateShutdown() throw() {} + explicit CpuStateShutdown( const std::string& msg="The PS2 emulated state was shut down unexpectedly." ) : + RuntimeError( msg ) {} + }; + + // Exception thrown by SaveState class when a critical plugin or gzread + class FreezePluginFailure : public RuntimeError + { + public: + std::string plugin_name; // name of the plugin + std::string freeze_action; + + virtual ~FreezePluginFailure() throw() {} + explicit FreezePluginFailure( const std::string& plugin, const std::string& action ) : + RuntimeError( plugin + " plugin returned an error while " + action + " the state." ) + , plugin_name( plugin ) + , freeze_action( action ){} + }; + + // The savestate code throws Recoverable errors when it fails prior to actually modifying + // the current emulation state. Recoverable errors are always thrown from the SaveState + // object construction (and never from Freeze methods). + class StateLoadError_Recoverable : public RuntimeError + { + public: + virtual ~StateLoadError_Recoverable() throw() {} + explicit StateLoadError_Recoverable( const std::string& msg="Recoverable error while loading savestate (existing emulation state is still intact)." ) : + RuntimeError( msg ) {} + }; + + // A recoverable exception thrown when the savestate being loaded isn't supported. + class UnsupportedStateVersion : public StateLoadError_Recoverable + { + public: + u32 Version; // version number of the unsupported state. + + public: + virtual ~UnsupportedStateVersion() throw() {} + explicit UnsupportedStateVersion( int version ) : + StateLoadError_Recoverable( fmt_string( "Unknown or unsupported savestate version: 0x%x", version ) ) + {} + + explicit UnsupportedStateVersion( int version, const std::string& msg ) : + StateLoadError_Recoverable( msg ) {} + }; + + // A recoverable exception thrown when the CRC of the savestate does not match the + // CRC returned by the Cdvd driver. + class StateCrcMismatch : public StateLoadError_Recoverable + { + public: + u32 Crc_Savestate; + u32 Crc_Cdvd; + + public: + virtual ~StateCrcMismatch() throw() {} + explicit StateCrcMismatch( u32 crc_save, u32 crc_cdvd ) + : StateLoadError_Recoverable( fmt_string( + "Game/CDVD does not match the savestate CRC.\n" + "\tCdvd CRC: 0x%X\n\tGame CRC: 0x%X\n", params crc_save, crc_cdvd + ) ) + , Crc_Savestate( crc_save ) + , Crc_Cdvd( crc_cdvd ) + {} + + explicit StateCrcMismatch( u32 crc_save, u32 crc_cdvd, const std::string& msg ) + : StateLoadError_Recoverable( msg ) + , Crc_Savestate( crc_save ) + , Crc_Cdvd( crc_cdvd ) + {} + }; + + class PluginFailure : public RuntimeError + { + public: + std::string plugin_name; // name of the plugin + + virtual ~PluginFailure() throw() {} + explicit PluginFailure( const std::string& plugin, const std::string& msg = "A plugin encountered a critical error." ) : + RuntimeError( msg ) + , plugin_name( plugin ) {} + }; + + class ThreadCreationError : public RuntimeError + { + public: + virtual ~ThreadCreationError() throw() {} + explicit ThreadCreationError( const std::string& msg="Thread could not be created." ) : + RuntimeError( msg ) {} + }; + + // This is a "special" exception that's primarily included for safe functioning in the + // Win32's ASCII API (ie, the non-Unicode one). Many of the old Win32 APIs don't support + // paths over 256 characters. + class PathTooLong : public RuntimeError + { + public: + virtual ~PathTooLong() throw() {} + explicit PathTooLong( const std::string& msg= + "A Pcsx2 pathname was too long for the system. Please move or reinstall Pcsx2 to\n" + "a location on your hard drive that has a shorter path." ) : + RuntimeError( msg ) {} + }; + + /////////////////////////////////////////////////////////////////////// + // BEGIN STREAMING EXCEPTIONS + + // Generic stream error. Contains the name of the stream and a message. + // This exception is usually thrown via derrived classes, except in the (rare) case of a generic / unknown error. + class Stream : public RuntimeError + { + public: + std::string stream_name; // name of the stream (if applicable) + + virtual ~Stream() throw() {} + + // copy construct! + Stream( const Stream& src ) : + RuntimeError( src.Message() ) + , stream_name( src.stream_name ) {} + + explicit Stream( + const std::string& objname=std::string(), + const std::string& msg="Invalid stream object" ) : + RuntimeError( msg + ": " + objname ) + , stream_name( objname ) {} + }; + + // Exception thrown when a corrupted or truncated savestate is encountered. + class BadSavedState : public Stream + { + public: + virtual ~BadSavedState() throw() {} + explicit BadSavedState( + const std::string& objname=std::string(), + const std::string& msg="Corrupted data or end of file encountered while loading savestate" ) : + Stream( objname, msg ) {} + }; + + // Exception thrown when an attempt to open a non-existant file is made. + // (this exception can also mean file permissions are invalid) + class FileNotFound : public Stream + { + public: + virtual ~FileNotFound() throw() {} + explicit FileNotFound( + const std::string& objname=std::string(), + const std::string& msg="File not found or permission denied" ) : + Stream( objname, msg ) {} + }; + + // Generic End of Stream exception (sometimes an error, and sometimes just used as a + // shortcut for manual feof checks). + class EndOfStream : public Stream + { + public: + virtual ~EndOfStream() throw() {} + explicit EndOfStream( const std::string& objname=std::string(), const std::string& msg="End of stream was encountered" ) : + Stream( objname, msg ) {} + }; + +} + +#endif diff --git a/pcsx2/FPU.cpp b/pcsx2/FPU.cpp new file mode 100644 index 0000000000..cc92316ec4 --- /dev/null +++ b/pcsx2/FPU.cpp @@ -0,0 +1,388 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include +#include "Common.h" +#include "R5900.h" +#include "R5900OpcodeTables.h" + +using namespace std; // for min / max + +// Helper Macros +//**************************************************************** + +// IEEE 754 Values +#define PosInfinity 0x7f800000 +#define NegInfinity 0xff800000 +#define posFmax 0x7F7FFFFF +#define negFmax 0xFF7FFFFF + + +/* Used in compare function to compensate for differences between IEEE 754 and the FPU. + Setting it to ~0x00000000 = Compares Exact Value. (comment out this macro for faster Exact Compare method) + Setting it to ~0x00000001 = Discards the least significant bit when comparing. + Setting it to ~0x00000003 = Discards the least 2 significant bits when comparing... etc.. */ +//#define comparePrecision ~0x00000001 + +// Operands +#define _Ft_ ( ( cpuRegs.code >> 16 ) & 0x1F ) +#define _Fs_ ( ( cpuRegs.code >> 11 ) & 0x1F ) +#define _Fd_ ( ( cpuRegs.code >> 6 ) & 0x1F ) + +// Floats +#define _FtValf_ fpuRegs.fpr[ _Ft_ ].f +#define _FsValf_ fpuRegs.fpr[ _Fs_ ].f +#define _FdValf_ fpuRegs.fpr[ _Fd_ ].f +#define _FAValf_ fpuRegs.ACC.f + +// U32's +#define _FtValUl_ fpuRegs.fpr[ _Ft_ ].UL +#define _FsValUl_ fpuRegs.fpr[ _Fs_ ].UL +#define _FdValUl_ fpuRegs.fpr[ _Fd_ ].UL +#define _FAValUl_ fpuRegs.ACC.UL + +// FPU Control Reg (FCR31) +#define _ContVal_ fpuRegs.fprc[ 31 ] + +// FCR31 Flags +#define FPUflagC 0X00800000 +#define FPUflagI 0X00020000 +#define FPUflagD 0X00010000 +#define FPUflagO 0X00008000 +#define FPUflagU 0X00004000 +#define FPUflagSI 0X00000040 +#define FPUflagSD 0X00000020 +#define FPUflagSO 0X00000010 +#define FPUflagSU 0X00000008 + +//**************************************************************** + +// If we have an infinity value, then Overflow has occured. +#define checkOverflow(xReg, cFlagsToSet, shouldReturn) { \ + if ( ( xReg & ~0x80000000 ) == PosInfinity ) { \ + /*SysPrintf( "FPU OVERFLOW!: Changing to +/-Fmax!!!!!!!!!!!!\n" );*/ \ + xReg = ( xReg & 0x80000000 ) | posFmax; \ + _ContVal_ |= cFlagsToSet; \ + if ( shouldReturn ) { return; } \ + } \ +} + +// If we have a denormal value, then Underflow has occured. +#define checkUnderflow(xReg, cFlagsToSet, shouldReturn) { \ + if ( ( ( xReg & 0x7F800000 ) == 0 ) && ( ( xReg & 0x007FFFFF ) != 0 ) ) { \ + /*SysPrintf( "FPU UNDERFLOW!: Changing to +/-0!!!!!!!!!!!!\n" );*/ \ + xReg &= 0x80000000; \ + _ContVal_ |= cFlagsToSet; \ + if ( shouldReturn ) { return; } \ + } \ +} + +/* Checks if Divide by Zero will occur. (z/y = x) + cFlagsToSet1 = Flags to set if (z != 0) + cFlagsToSet2 = Flags to set if (z == 0) + ( Denormals are counted as "0" ) +*/ +#define checkDivideByZero(xReg, yDivisorReg, zDividendReg, cFlagsToSet1, cFlagsToSet2, shouldReturn) { \ + if ( ( yDivisorReg & 0x7F800000 ) == 0 ) { \ + _ContVal_ |= ( ( zDividendReg & 0x7F800000 ) == 0 ) ? cFlagsToSet2 : cFlagsToSet1; \ + xReg = ( ( yDivisorReg ^ zDividendReg ) & 0x80000000 ) | posFmax; \ + if ( shouldReturn ) { return; } \ + } \ +} + +/* Clears the "Cause Flags" of the Control/Status Reg + The "EE Core Users Manual" implies that all the Cause flags are cleared every instruction... + But, the "EE Core Instruction Set Manual" says that only certain Cause Flags are cleared + for specific instructions... I'm just setting them to clear when the Instruction Set Manual + says to... (cottonvibes) +*/ +#define clearFPUFlags(cFlags) { \ + _ContVal_ &= ~( cFlags ) ; \ +} + +#ifdef comparePrecision +// This compare discards the least-significant bit(s) in order to solve some rounding issues. + #define C_cond_S(cond) { \ + FPRreg tempA, tempB; \ + tempA.UL = _FsValUl_ & comparePrecision; \ + tempB.UL = _FtValUl_ & comparePrecision; \ + _ContVal_ = ( ( tempA.f ) cond ( tempB.f ) ) ? \ + ( _ContVal_ | FPUflagC ) : \ + ( _ContVal_ & ~FPUflagC ); \ + } +#else +// Used for Comparing; This compares if the floats are exactly the same. + #define C_cond_S(cond) { \ + _ContVal_ = ( _FsValf_ cond _FtValf_ ) ? \ + ( _ContVal_ | FPUflagC ) : \ + ( _ContVal_ & ~FPUflagC ); \ + } +#endif + +// Conditional Branch +#define BC1(cond) \ + if ( ( _ContVal_ & FPUflagC ) cond 0 ) { \ + intDoBranch( _BranchTarget_ ); \ + } + +// Conditional Branch +#define BC1L(cond) \ + if ( ( _ContVal_ & FPUflagC ) cond 0 ) { \ + intDoBranch( _BranchTarget_ ); \ + } else cpuRegs.pc += 4; + +namespace R5900 { +namespace Interpreter { +namespace OpcodeImpl { +namespace COP1 { + +//**************************************************************** +// FPU Opcodes +//**************************************************************** + +float fpuDouble(u32 f) +{ + switch(f & 0x7f800000){ + case 0x0: + f &= 0x80000000; + return *(float*)&f; + break; + case 0x7f800000: + f = (f & 0x80000000)|0x7f7fffff; + return *(float*)&f; + break; + default: + return *(float*)&f; + break; + } +} + +void ABS_S() { + _FdValUl_ = _FsValUl_ & 0x7fffffff; + clearFPUFlags( FPUflagO | FPUflagU ); +} + +void ADD_S() { + _FdValf_ = fpuDouble( _FsValUl_ ) + fpuDouble( _FtValUl_ ); + checkOverflow( _FdValUl_, FPUflagO | FPUflagSO, 1 ); + checkUnderflow( _FdValUl_, FPUflagU | FPUflagSU, 1 ); +} + +void ADDA_S() { + _FAValf_ = fpuDouble( _FsValUl_ ) + fpuDouble( _FtValUl_ ); + checkOverflow( _FAValUl_, FPUflagO | FPUflagSO, 1 ); + checkUnderflow( _FAValUl_, FPUflagU | FPUflagSU, 1 ); +} + +void BC1F() { + BC1(==); +} + +void BC1FL() { + BC1L(==); // Equal to 0 +} + +void BC1T() { + BC1(!=); +} + +void BC1TL() { + BC1L(!=); // different from 0 +} + +void C_EQ() { + C_cond_S(==); +} + +void C_F() { + clearFPUFlags( FPUflagC ); //clears C regardless +} + +void C_LE() { + C_cond_S(<=); +} + +void C_LT() { + C_cond_S(<); +} + +void CFC1() { + if ( !_Rt_ || ( (_Fs_ != 0) && (_Fs_ != 31) ) ) return; + cpuRegs.GPR.r[_Rt_].SD[0] = (s64)fpuRegs.fprc[_Fs_]; +} + +void CTC1() { + if ( _Fs_ != 31 ) return; + fpuRegs.fprc[_Fs_] = cpuRegs.GPR.r[_Rt_].UL[0]; +} + +void CVT_S() { + _FdValf_ = (float)(*(s32*)&_FsValUl_); + _FdValf_ = fpuDouble( _FdValUl_ ); +} + +void CVT_W() { + if ( ( _FsValUl_ & 0x7F800000 ) <= 0x4E800000 ) { _FdValUl_ = (s32)_FsValf_; } + else if ( ( _FsValUl_ & 0x80000000 ) == 0 ) { _FdValUl_ = 0x7fffffff; } + else { _FdValUl_ = 0x80000000; } +} + +void DIV_S() { + checkDivideByZero( _FdValUl_, _FtValUl_, _FsValUl_, FPUflagD | FPUflagSD, FPUflagI | FPUflagSI, 1 ); + _FdValf_ = fpuDouble( _FsValUl_ ) / fpuDouble( _FtValUl_ ); + checkOverflow( _FdValUl_, 0, 1); + checkUnderflow( _FdValUl_, 0, 1 ); +} + +/* The Instruction Set manual has an overly complicated way of + determining the flags that are set. Hopefully this shorter + method provides a similar outcome and is faster. (cottonvibes) +*/ +void MADD_S() { + FPRreg temp; + temp.f = fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ ); + _FdValf_ = fpuDouble( _FAValUl_ ) + fpuDouble( temp.UL ); + checkOverflow( _FdValUl_, FPUflagO | FPUflagSO, 1 ); + checkUnderflow( _FdValUl_, FPUflagU | FPUflagSU, 1 ); +} + +void MADDA_S() { + _FAValf_ += fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ ); + checkOverflow( _FAValUl_, FPUflagO | FPUflagSO, 1 ); + checkUnderflow( _FAValUl_, FPUflagU | FPUflagSU, 1 ); +} + +void MAX_S() { + _FdValf_ = max( _FsValf_, _FtValf_ ); + clearFPUFlags( FPUflagO | FPUflagU ); +} + +void MFC1() { + if ( !_Rt_ ) return; + cpuRegs.GPR.r[_Rt_].SD[0] = (s64)_FsValUl_; +} + +void MIN_S() { + _FdValf_ = min( _FsValf_, _FtValf_ ); + clearFPUFlags( FPUflagO | FPUflagU ); +} + +void MOV_S() { + _FdValUl_ = _FsValUl_; +} + +void MSUB_S() { + FPRreg temp; + temp.f = fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ ); + _FdValf_ = fpuDouble( _FAValUl_ ) - fpuDouble( temp.UL ); + checkOverflow( _FdValUl_, FPUflagO | FPUflagSO, 1 ); + checkUnderflow( _FdValUl_, FPUflagU | FPUflagSU, 1 ); +} + +void MSUBA_S() { + _FAValf_ -= fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ ); + checkOverflow( _FAValUl_, FPUflagO | FPUflagSO, 1 ); + checkUnderflow( _FAValUl_, FPUflagU | FPUflagSU, 1 ); +} + +void MTC1() { + _FsValUl_ = cpuRegs.GPR.r[_Rt_].UL[0]; +} + +void MUL_S() { + _FdValf_ = fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ ); + checkOverflow( _FdValUl_, FPUflagO | FPUflagSO, 1 ); + checkUnderflow( _FdValUl_, FPUflagU | FPUflagSU, 1 ); +} + +void MULA_S() { + _FAValf_ = fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ ); + checkOverflow( _FAValUl_, FPUflagO | FPUflagSO, 1 ); + checkUnderflow( _FAValUl_, FPUflagU | FPUflagSU, 1 ); +} + +void NEG_S() { + _FdValUl_ = (_FsValUl_ ^ 0x80000000); + clearFPUFlags( FPUflagO | FPUflagU ); +} + +void RSQRT_S() { + FPRreg temp; + if ( ( _FtValUl_ & 0x7F800000 ) == 0 ) { // Ft is zero (Denormals are Zero) + _ContVal_ |= FPUflagD | FPUflagSD; + _FdValUl_ = ( ( _FsValUl_ ^ _FtValUl_ ) & 0x80000000 ) | posFmax; + return; + } + else if ( _FtValUl_ & 0x80000000 ) { // Ft is negative + _ContVal_ |= FPUflagI | FPUflagSI; + temp.f = sqrt( fabs( fpuDouble( _FtValUl_ ) ) ); + _FdValf_ = fpuDouble( _FsValUl_ ) / fpuDouble( temp.UL ); + } + else { _FdValf_ = fpuDouble( _FsValUl_ ) / sqrt( fpuDouble( _FtValUl_ ) ); } // Ft is positive and not zero + + checkOverflow( _FdValUl_, 0, 1 ); + checkUnderflow( _FdValUl_, 0, 1 ); +} + +void SQRT_S() { + if ( ( _FtValUl_ & 0xFF800000 ) == 0x80000000 ) { _FdValUl_ = 0x80000000; } // If Ft = -0 + else if ( _FtValUl_ & 0x80000000 ) { // If Ft is Negative + _ContVal_ |= FPUflagI | FPUflagSI; + _FdValf_ = sqrt( fabs( fpuDouble( _FtValUl_ ) ) ); + } + else { _FdValf_ = sqrt( fpuDouble( _FtValUl_ ) ); } // If Ft is Positive + clearFPUFlags( FPUflagD ); +} + +void SUB_S() { + _FdValf_ = fpuDouble( _FsValUl_ ) - fpuDouble( _FtValUl_ ); + checkOverflow( _FdValUl_, FPUflagO | FPUflagSO, 1 ); + checkUnderflow( _FdValUl_, FPUflagU | FPUflagSU, 1 ); +} + +void SUBA_S() { + _FAValf_ = fpuDouble( _FsValUl_ ) - fpuDouble( _FtValUl_ ); + checkOverflow( _FAValUl_, FPUflagO | FPUflagSO, 1 ); + checkUnderflow( _FAValUl_, FPUflagU | FPUflagSU, 1 ); +} + +} // End Namespace COP1 + +///////////////////////////////////////////////////////////////////// +// COP1 (FPU) Load/Store Instructions + +// These are actually EE opcodes but since they're related to FPU registers and such they +// seem more appropriately located here. + +void LWC1() { + u32 addr; + addr = cpuRegs.GPR.r[_Rs_].UL[0] + (s32)(s16)(cpuRegs.code & 0xffff); + if (addr & 0x00000003) { Console::Error( "FPU (LWC1 Opcode): Invalid Memory Address" ); return; } // Should signal an exception? + memRead32(addr, &fpuRegs.fpr[_Rt_].UL); +} + +void SWC1() { + u32 addr; + addr = cpuRegs.GPR.r[_Rs_].UL[0] + (s32)(s16)(cpuRegs.code & 0xffff); + if (addr & 0x00000003) { Console::Error( "FPU (SWC1 Opcode): Invalid Memory Address" ); return; } // Should signal an exception? + memWrite32(addr, fpuRegs.fpr[_Rt_].UL); +} + +} } } diff --git a/pcsx2/FPU2.cpp b/pcsx2/FPU2.cpp new file mode 100644 index 0000000000..3ff56385a9 --- /dev/null +++ b/pcsx2/FPU2.cpp @@ -0,0 +1,42 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + +// sqrtf only defined in C++ +extern "C" { + +float (*fpusqrtf)(float fval) = 0; +float (*fpufabsf)(float fval) = 0; +float (*fpusinf)(float fval) = 0; +float (*fpucosf)(float fval) = 0; +float (*fpuexpf)(float fval) = 0; +float (*fpuatanf)(float fval) = 0; +float (*fpuatan2f)(float fvalx, float fvaly) = 0; + +void InitFPUOps() +{ + fpusqrtf = sqrtf; + fpufabsf = fabsf; + fpusinf = sinf; + fpucosf = cosf; + fpuexpf = expf; + fpuatanf = atanf; + fpuatan2f = atan2f; +} + +} diff --git a/pcsx2/FiFo.cpp b/pcsx2/FiFo.cpp new file mode 100644 index 0000000000..a2a4baf255 --- /dev/null +++ b/pcsx2/FiFo.cpp @@ -0,0 +1,152 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "Hw.h" +#include "GS.h" + +#include "Vif.h" +#include "VifDma.h" + +////////////////////////////////////////////////////////////////////////// +/////////////////////////// Quick & dirty FIFO :D //////////////////////// +////////////////////////////////////////////////////////////////////////// +extern int FIFOto_write(u32* pMem, int size); +extern void FIFOfrom_readsingle(void *value); + +extern int g_nIPU0Data; +extern u8* g_pIPU0Pointer; +extern int FOreadpos; +// NOTE: cannot use XMM/MMX regs +void ReadFIFO(u32 mem, u64 *out) { + if ((mem >= 0x10004000) && (mem < 0x10005000)) { + VIF_LOG("ReadFIFO VIF0 0x%08X\n", mem); + out[0] = psHu64(mem ); + out[1] = psHu64(mem+8); + return; + } else + if ((mem >= 0x10005000) && (mem < 0x10006000)) { + +#ifdef PCSX2_DEVBUILD + VIF_LOG("ReadFIFO VIF1 0x%08X\n", mem); + + if( vif1Regs->stat & (VIF1_STAT_INT|VIF1_STAT_VSS|VIF1_STAT_VIS|VIF1_STAT_VFS) ) { + SysPrintf("reading from vif1 fifo when stalled\n"); + } +#endif + + if (vif1Regs->stat & 0x800000) { + if (--psHu32(D1_QWC) == 0) { + vif1Regs->stat&= ~0x1f000000; + } else { + } + } + out[0] = psHu64(mem ); + out[1] = psHu64(mem+8); + return; + } else if( (mem&0xfffff010) == 0x10007000) { + + if( g_nIPU0Data > 0 ) { + out[0] = *(u64*)(g_pIPU0Pointer); + out[1] = *(u64*)(g_pIPU0Pointer+8); + FOreadpos = (FOreadpos + 4) & 31; + g_nIPU0Data--; + g_pIPU0Pointer += 16; + } + return; + }else if ( (mem&0xfffff010) == 0x10007010) { + FIFOfrom_readsingle((void*)out); + return; + } + SysPrintf("ReadFIFO Unknown %x\n", mem); +} + +void ConstReadFIFO(u32 mem) +{ + // not done +} + +void WriteFIFO(u32 mem, const u64 *value) { + int ret; + + if ((mem >= 0x10004000) && (mem < 0x10005000)) { + VIF_LOG("WriteFIFO VIF0 0x%08X\n", mem); + + psHu64(mem ) = value[0]; + psHu64(mem+8) = value[1]; + vif0ch->qwc += 1; + ret = VIF0transfer((u32*)value, 4, 0); + assert(ret == 0 ); // vif stall code not implemented + } + else if ((mem >= 0x10005000) && (mem < 0x10006000)) { + VIF_LOG("WriteFIFO VIF1 0x%08X\n", mem); + + psHu64(mem ) = value[0]; + psHu64(mem+8) = value[1]; + +#ifdef PCSX2_DEVBUILD + if(vif1Regs->stat & VIF1_STAT_FDR) + SysPrintf("writing to fifo when fdr is set!\n"); + if( vif1Regs->stat & (VIF1_STAT_INT|VIF1_STAT_VSS|VIF1_STAT_VIS|VIF1_STAT_VFS) ) { + SysPrintf("writing to vif1 fifo when stalled\n"); + } +#endif + vif1ch->qwc += 1; + ret = VIF1transfer((u32*)value, 4, 0); + assert(ret == 0 ); // vif stall code not implemented + } + else if ((mem >= 0x10006000) && (mem < 0x10007000)) { + GIF_LOG("WriteFIFO GIF 0x%08X\n", mem); + + psHu64(mem ) = value[0]; + psHu64(mem+8) = value[1]; + + if( mtgsThread != NULL ) + { + const uint count = mtgsThread->PrepDataPacket( GIF_PATH_3, value, 1 ); + jASSUME( count == 1 ); + u64* data = (u64*)mtgsThread->GetDataPacketPtr(); + data[0] = value[0]; + data[1] = value[1]; + mtgsThread->SendDataPacket(); + } + else + { + FreezeXMMRegs(1); + GSGIFTRANSFER3((u32*)value, 1); + FreezeXMMRegs(0); + } + + } else + if ((mem&0xfffff010) == 0x10007010) { + IPU_LOG("WriteFIFO IPU_in[%d] <- %8.8X_%8.8X_%8.8X_%8.8X\n", (mem - 0x10007010)/8, ((u32*)value)[3], ((u32*)value)[2], ((u32*)value)[1], ((u32*)value)[0]); + + //commiting every 16 bytes + while( FIFOto_write((u32*)value, 1) == 0 ) { + Console::WriteLn("IPU sleeping"); + Threading::Timeslice(); + } + } else { + Console::Notice("WriteFIFO Unknown %x", params mem); + } +} + +void ConstWriteFIFO(u32 mem) { +} diff --git a/pcsx2/GS.cpp b/pcsx2/GS.cpp new file mode 100644 index 0000000000..0c7d6a2360 --- /dev/null +++ b/pcsx2/GS.cpp @@ -0,0 +1,793 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include + +#include "Common.h" +#include "GS.h" +#include "iR5900.h" +#include "Counters.h" + +#include "VifDma.h" + +using namespace Threading; +using namespace std; + +using namespace R5900; + +#ifdef DEBUG +#define MTGS_LOG SysPrintf +#else +#define MTGS_LOG 0&& +#endif + +static bool m_gsOpened = false; + +int g_FFXHack=0; + +#ifdef PCSX2_DEVBUILD + +// GS Playback +int g_SaveGSStream = 0; // save GS stream; 1 - prepare, 2 - save +int g_nLeftGSFrames = 0; // when saving, number of frames left +gzSavingState* g_fGSSave; + +void GSGIFTRANSFER1(u32 *pMem, u32 addr) { + if( g_SaveGSStream == 2) { + u32 type = GSRUN_TRANS1; + u32 size = (0x4000-(addr))/16; + g_fGSSave->Freeze( type ); + g_fGSSave->Freeze( size ); + g_fGSSave->FreezeMem( ((u8*)pMem)+(addr), size*16 ); + } + GSgifTransfer1(pMem, addr); +} + +void GSGIFTRANSFER2(u32 *pMem, u32 size) { + if( g_SaveGSStream == 2) { + u32 type = GSRUN_TRANS2; + u32 _size = size; + g_fGSSave->Freeze( type ); + g_fGSSave->Freeze( size ); + g_fGSSave->FreezeMem( pMem, _size*16 ); + } + GSgifTransfer2(pMem, size); +} + +void GSGIFTRANSFER3(u32 *pMem, u32 size) { + if( g_SaveGSStream == 2 ) { + u32 type = GSRUN_TRANS3; + u32 _size = size; + g_fGSSave->Freeze( type ); + g_fGSSave->Freeze( size ); + g_fGSSave->FreezeMem( pMem, _size*16 ); + } + GSgifTransfer3(pMem, size); +} + +__forceinline void GSVSYNC(void) { + if( g_SaveGSStream == 2 ) { + u32 type = GSRUN_VSYNC; + g_fGSSave->Freeze( type ); + } +} +#else + +__forceinline void GSGIFTRANSFER1(u32 *pMem, u32 addr) { + GSgifTransfer1(pMem, addr); +} + +__forceinline void GSGIFTRANSFER2(u32 *pMem, u32 size) { + GSgifTransfer2(pMem, size); +} + +__forceinline void GSGIFTRANSFER3(u32 *pMem, u32 size) { + GSgifTransfer3(pMem, size); +} + +__forceinline void GSVSYNC(void) { +} +#endif + +u32 CSRw; + +PCSX2_ALIGNED16( u8 g_RealGSMem[0x2000] ); +#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff)) + +extern int m_nCounters[]; + +// FrameSkipping Stuff +// Yuck, iSlowStart is needed by the MTGS, so can't make it static yet. + +u64 m_iSlowStart=0; +static s64 m_iSlowTicks=0; +static bool m_justSkipped = false; +static bool m_StrictSkipping = false; + +void _gs_ChangeTimings( u32 framerate, u32 iTicks ) +{ + m_iSlowStart = GetCPUTicks(); + + u32 frameSkipThreshold = Config.CustomFrameSkip*50; + if( Config.CustomFrameSkip == 0) + { + // default: load the frameSkipThreshold with a value roughly 90% of our current framerate + frameSkipThreshold = ( framerate * 242 ) / 256; + } + + m_iSlowTicks = ( GetTickFrequency() * 50 ) / frameSkipThreshold; + + // sanity check against users who set a "minimum" frame that's higher + // than the maximum framerate. Also, if framerates are within 1/3300th + // of a second of each other, assume strict skipping (it's too close, + // and could cause excessive skipping). + + if( m_iSlowTicks <= (iTicks + ((s64)GetTickFrequency()/3300)) ) + { + m_iSlowTicks = iTicks; + m_StrictSkipping = true; + } +} + +void gsOnModeChanged( u32 framerate, u32 newTickrate ) +{ + if( mtgsThread != NULL ) + mtgsThread->SendSimplePacket( GS_RINGTYPE_MODECHANGE, framerate, newTickrate, 0 ); + else + _gs_ChangeTimings( framerate, newTickrate ); +} + +void gsSetVideoRegionType( u32 isPal ) +{ + if( isPal ) + { + if( Config.PsxType & 1 ) return; + Console::WriteLn( "PAL Display Mode Initialized." ); + Config.PsxType |= 1; + } + else + { + if( !(Config.PsxType & 1 ) ) return; + Console::WriteLn( "NTSC Display Mode Initialized." ); + Config.PsxType &= ~1; + } + + UpdateVSyncRate(); +} + + +// Make sure framelimiter options are in sync with the plugin's capabilities. +void gsInit() +{ + switch(CHECK_FRAMELIMIT) + { + case PCSX2_FRAMELIMIT_SKIP: + case PCSX2_FRAMELIMIT_VUSKIP: + if( GSsetFrameSkip == NULL ) + { + Config.Options &= ~PCSX2_FRAMELIMIT_MASK; + Console::WriteLn("Notice: Disabling frameskip -- GS plugin does not support it."); + } + break; + } +} + +// Opens the gsRingbuffer thread. +s32 gsOpen() +{ + if( m_gsOpened ) return 0; + + // mtgs overrides these as necessary... + GSsetBaseMem( PS2MEM_GS ); + GSirqCallback( gsIrq ); + + //video + // Only bind the gsIrq if we're not running the MTGS. + // The MTGS simulates its own gsIrq in order to maintain proper sync. + + m_gsOpened = mtgsOpen(); + if( !m_gsOpened ) + { + // MTGS failed to init or is disabled. Try the GS instead! + // ... and set the memptr again just in case (for switching between GS/MTGS on the fly) + + m_gsOpened = !GSopen((void *)&pDsp, "PCSX2", 0); + } + + /*if( m_gsOpened ) + { + gsOnModeChanged( + (Config.PsxType & 1) ? FRAMERATE_PAL : FRAMERATE_NTSC, + UpdateVSyncRate() + ); + }*/ + return !m_gsOpened; +} + +void gsClose() +{ + if( !m_gsOpened ) return; + m_gsOpened = false; + + // Throw an assert if our multigs setting and mtgsThread status + // aren't synched. It shouldn't break the code anyway but it's a + // bad coding habit that we should catch and fix early. + assert( !!CHECK_MULTIGS == (mtgsThread != NULL ) ); + + if( mtgsThread != NULL ) + { + safe_delete( mtgsThread ); + } + else + GSclose(); +} + +void gsReset() +{ + // Sanity check in case the plugin hasn't been initialized... + if( !m_gsOpened ) return; + + if( mtgsThread != NULL ) + mtgsThread->Reset(); + else + { + Console::Notice( "GIF reset" ); + GSreset(); + GSsetFrameSkip(0); + } + + gsOnModeChanged( + (Config.PsxType & 1) ? FRAMERATE_PAL : FRAMERATE_NTSC, + UpdateVSyncRate() + ); + + memzero_obj(g_RealGSMem); + + Path3transfer = 0; + + GSCSRr = 0x551B400F; // Set the FINISH bit to 1 for now + GSIMR = 0x7f00; + psHu32(GIF_STAT) = 0; + psHu32(GIF_CTRL) = 0; + psHu32(GIF_MODE) = 0; +} + +bool gsGIFSoftReset( int mask ) +{ + if( GSgifSoftReset == NULL ) + { + static bool warned = false; + if( !warned ) + { + Console::Notice( "GIF Warning > Soft reset requested, but the GS plugin doesn't support it!" ); + //warned = true; + } + return false; + } + + if( mtgsThread != NULL ) + mtgsThread->GIFSoftReset( mask ); + else + GSgifSoftReset( mask ); + + return true; +} + +void gsGIFReset() +{ + // fixme - should this be here? (air) + //memzero_obj(g_RealGSMem); + + // perform a soft reset (but do not do a full reset if the soft reset API is unavailable) + gsGIFSoftReset( 7 ); + + GSCSRr = 0x551B400F; // Set the FINISH bit to 1 for now + GSIMR = 0x7f00; + psHu32(GIF_STAT) = 0; + psHu32(GIF_CTRL) = 0; + psHu32(GIF_MODE) = 0; +} + +void gsCSRwrite(u32 value) +{ + CSRw |= value & ~0x60; + + if( mtgsThread != NULL ) + mtgsThread->SendSimplePacket( GS_RINGTYPE_WRITECSR, CSRw, 0, 0 ); + else + GSwriteCSR(CSRw); + + GSCSRr = ((GSCSRr&~value)&0x1f)|(GSCSRr&~0x1f); + + // Our emulated GS has no FIFO... + /*if( value & 0x100 ) { // FLUSH + //SysPrintf("GS_CSR FLUSH GS fifo: %x (CSRr=%x)\n", value, GSCSRr); + }*/ + + if (value & 0x200) { // resetGS + + // perform a soft reset -- and fall back to doing a full reset if the plugin doesn't + // support soft resets. + + if( !gsGIFSoftReset( 7 ) ) + { + if( mtgsThread != NULL ) + mtgsThread->SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 ); + else + GSreset(); + } + + GSCSRr = 0x551B400F; // Set the FINISH bit to 1 - GS is always at a finish state as we don't have a FIFO(saqib) + GSIMR = 0x7F00; //This is bits 14-8 thats all that should be 1 + } +} + +static void IMRwrite(u32 value) { + GSIMR = (value & 0x1f00)|0x6000; + + // don't update mtgs mem +} + +__forceinline void gsWrite8(u32 mem, u8 value) { + switch (mem) { + case 0x12001000: // GS_CSR + gsCSRwrite((CSRw & ~0x000000ff) | value); break; + case 0x12001001: // GS_CSR + gsCSRwrite((CSRw & ~0x0000ff00) | (value << 8)); break; + case 0x12001002: // GS_CSR + gsCSRwrite((CSRw & ~0x00ff0000) | (value << 16)); break; + case 0x12001003: // GS_CSR + gsCSRwrite((CSRw & ~0xff000000) | (value << 24)); break; + default: + *PS2GS_BASE(mem) = value; + + if( mtgsThread != NULL ) + mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE8, mem&0x13ff, value, 0); + } + GIF_LOG("GS write 8 at %8.8lx with data %8.8lx\n", mem, value); +} + +__forceinline void gsWrite16(u32 mem, u16 value) { + + GIF_LOG("GS write 16 at %8.8lx with data %8.8lx\n", mem, value); + + switch (mem) { + case 0x12000010: // GS_SMODE1 + gsSetVideoRegionType( (value & 0x6000) == 0x6000 ); + break; + + case 0x12000020: // GS_SMODE2 + if(value & 0x1) Config.PsxType |= 2; // Interlaced + else Config.PsxType &= ~2; // Non-Interlaced + break; + + case 0x12001000: // GS_CSR + gsCSRwrite( (CSRw&0xffff0000) | value); + return; // do not write to MTGS memory + case 0x12001002: // GS_CSR + gsCSRwrite( (CSRw&0xffff) | ((u32)value<<16)); + return; // do not write to MTGS memory + case 0x12001010: // GS_IMR + //SysPrintf("writing to IMR 16\n"); + IMRwrite(value); + return; // do not write to MTGS memory + } + + *(u16*)PS2GS_BASE(mem) = value; + + if( mtgsThread != NULL ) + mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE16, mem&0x13ff, value, 0); +} + +__forceinline void gsWrite32(u32 mem, u32 value) +{ + assert( !(mem&3)); + GIF_LOG("GS write 32 at %8.8lx with data %8.8lx\n", mem, value); + + switch (mem) { + case 0x12000010: // GS_SMODE1 + gsSetVideoRegionType( (value & 0x6000) == 0x6000 ); + break; + + case 0x12000020: // GS_SMODE2 + if(value & 0x1) Config.PsxType |= 2; // Interlaced + else Config.PsxType &= ~2; // Non-Interlaced + break; + + case 0x12001000: // GS_CSR + gsCSRwrite(value); + return; + + case 0x12001010: // GS_IMR + IMRwrite(value); + return; + } + + *(u32*)PS2GS_BASE(mem) = value; + + if( mtgsThread != NULL ) + mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE32, mem&0x13ff, value, 0); +} + +__forceinline void gsWrite64(u32 mem, u64 value) { + + GIF_LOG("GS write 64 at %8.8lx with data %8.8lx_%8.8lx\n", mem, ((u32*)&value)[1], (u32)value); + + switch (mem) { + case 0x12000010: // GS_SMODE1 + gsSetVideoRegionType( (value & 0x6000) == 0x6000 ); + break; + + case 0x12000020: // GS_SMODE2 + if(value & 0x1) Config.PsxType |= 2; // Interlaced + else Config.PsxType &= ~2; // Non-Interlaced + break; + + case 0x12001000: // GS_CSR + gsCSRwrite((u32)value); + return; + + case 0x12001010: // GS_IMR + IMRwrite((u32)value); + return; + } + + *(u64*)PS2GS_BASE(mem) = value; + + if( mtgsThread != NULL ) + mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE64, mem&0x13ff, (u32)value, (u32)(value>>32)); +} + +__forceinline u8 gsRead8(u32 mem) +{ + GIF_LOG("GS read 8 from %8.8lx value: %8.8lx\n", mem, *(u8*)PS2GS_BASE(mem)); + return *(u8*)PS2GS_BASE(mem); +} + +__forceinline u16 gsRead16(u32 mem) +{ + GIF_LOG("GS read 16 from %8.8lx value: %8.8lx\n", mem, *(u16*)PS2GS_BASE(mem)); + return *(u16*)PS2GS_BASE(mem); +} + +__forceinline u32 gsRead32(u32 mem) +{ + GIF_LOG("GS read 32 from %8.8lx value: %8.8lx\n", mem, *(u32*)PS2GS_BASE(mem)); + return *(u32*)PS2GS_BASE(mem); +} + +__forceinline u64 gsRead64(u32 mem) +{ + GIF_LOG("GS read 64 from %8.8lx value: %8.8lx_%8.8lx\n", mem, *(u32*)PS2GS_BASE(mem+4), *(u32*)PS2GS_BASE(mem) ); + return *(u64*)PS2GS_BASE(mem); +} + +void gsIrq() { + hwIntcIrq(0); +} + +void gsSyncLimiterLostTime( s32 deltaTime ) +{ + // This sync issue applies only to configs that are trying to maintain + // a perfect "specific" framerate (where both min and max fps are the same) + // any other config will eventually equalize out. + + if( !m_StrictSkipping ) return; + + //SysPrintf("LostTime on the EE!\n"); + + if( mtgsThread != NULL ) + { + mtgsThread->SendSimplePacket( + GS_RINGTYPE_STARTTIME, + deltaTime, + 0, + 0 + ); + } + else + { + m_iSlowStart += deltaTime; + //m_justSkipped = false; + } +} + +// FrameSkipper - Measures delta time between calls and issues frameskips +// it the time is too long. Also regulates the status of the EE's framelimiter. + +// This function does not regulate frame limiting, meaning it does no stalling. +// Stalling functions are performed by the EE: If the MTGS were throtted and not +// the EE, the EE would fill the ringbuffer while the MTGS regulated frames -- +// fine for most situations but could result in literally dozens of frames queued +// up in the ringbuffer durimg some game menu screens; which in turn would result +// in a half-second lag of keystroke actions becoming visible to the user (bad!). + +// Alternative: Instead of this, I could have everything regulated here, and then +// put a framecount limit on the MTGS ringbuffer. But that seems no less complicated +// and would also mean that aforementioned menus would still be laggy by whatever +// frame count threshold. This method is more responsive. + +__forceinline void gsFrameSkip( bool forceskip ) +{ + static u8 FramesToRender = 0; + static u8 FramesToSkip = 0; + + if( CHECK_FRAMELIMIT != PCSX2_FRAMELIMIT_SKIP && + CHECK_FRAMELIMIT != PCSX2_FRAMELIMIT_VUSKIP ) return; + + // FrameSkip and VU-Skip Magic! + // Skips a sequence of consecutive frames after a sequence of rendered frames + + // This is the least number of consecutive frames we will render w/o skipping + const int noSkipFrames = ((Config.CustomConsecutiveFrames>0) ? Config.CustomConsecutiveFrames : 1); + // This is the number of consecutive frames we will skip + const int yesSkipFrames = ((Config.CustomConsecutiveSkip>0) ? Config.CustomConsecutiveSkip : 1); + + const u64 iEnd = GetCPUTicks(); + const s64 uSlowExpectedEnd = m_iSlowStart + m_iSlowTicks; + const s64 sSlowDeltaTime = iEnd - uSlowExpectedEnd; + + m_iSlowStart = uSlowExpectedEnd; + + if( forceskip ) + { + if( !FramesToSkip ) + { + //Console::Status( "- Skipping some VUs!" ); + + GSsetFrameSkip( 1 ); + FramesToRender = noSkipFrames; + FramesToSkip = 1; // just set to 1 + + // We're already skipping, so FramesToSkip==1 will just restore the gsFrameSkip + // setting and reset our delta times as needed. + } + return; + } + + // if we've already given the EE a skipcount assignment then don't do anything more. + // Otherwise we could start compounding the issue and skips would be too long. + if( g_vu1SkipCount > 0 ) + { + //Console::Status("- Already Assigned a Skipcount.. %d", params g_vu1SkipCount ); + return; + } + + if( FramesToRender == 0 ) + { + // -- Standard operation section -- + // Means neither skipping frames nor force-rendering consecutive frames. + + if( sSlowDeltaTime > 0 ) + { + // The game is running below the minimum framerate. + // But don't start skipping yet! That would be too sensitive. + // So the skipping code is only engaged if the SlowDeltaTime falls behind by + // a full frame, or if we're already skipping (in which case we don't care + // to avoid errant skips). + + // Note: The MTGS can go out of phase from the EE, which means that the + // variance for a "nominal" framerate can range from 0 to m_iSlowTicks. + // We also check for that here. + + if( (m_justSkipped && (sSlowDeltaTime > m_iSlowTicks)) || + (sSlowDeltaTime > m_iSlowTicks*2) ) + { + //Console::Status( "Frameskip Initiated! Lateness: %d", params (int)( (sSlowDeltaTime*100) / m_iSlowTicks ) ); + + if( CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_VUSKIP ) + { + // For best results we have to wait for the EE to + // tell us when to skip, so that VU skips are synched with GS skips. + AtomicExchangeAdd( g_vu1SkipCount, yesSkipFrames+1 ); + } + else + { + GSsetFrameSkip(1); + FramesToRender = noSkipFrames+1; + FramesToSkip = yesSkipFrames; + } + } + } + else + { + // Running at or above full speed, so reset the StartTime since the Limiter + // will muck things up. (special case: if skip and limit fps are equal then + // we don't reset starttime since it would cause desyncing. We let the EE + // regulate it via calls to gsSyncLimiterStartTime). + + if( !m_StrictSkipping ) + m_iSlowStart = iEnd; + } + m_justSkipped = false; + return; + } + else if( FramesToSkip > 0 ) + { + // -- Frames-a-Skippin' Section -- + + FramesToSkip--; + + if( FramesToSkip == 0 ) + { + // Skipped our last frame, so restore non-skip behavior + + GSsetFrameSkip(0); + + // Note: If we lag behind by 250ms then it's time to give up on the idea + // of catching up. Force the game to slow down by resetting iStart to + // something closer to iEnd. + + if( sSlowDeltaTime > (m_iSlowTicks + ((s64)GetTickFrequency() / 4)) ) + { + //Console::Status( "Frameskip couldn't skip enough -- had to lose some time!" ); + m_iSlowStart = iEnd - m_iSlowTicks; + } + + m_justSkipped = true; + } + else + return; + } + + //SysPrintf( "Consecutive Frames -- Lateness: %d\n", (int)( sSlowDeltaTime / m_iSlowTicks ) ); + + // -- Consecutive frames section -- + // Force-render consecutive frames without skipping. + + FramesToRender--; + + if( sSlowDeltaTime < 0 ) + { + m_iSlowStart = iEnd; + } +} + +// updategs - if FALSE the gs will skip the frame. +void gsPostVsyncEnd( bool updategs ) +{ + *(u32*)(PS2MEM_GS+0x1000) ^= 0x2000; // swap the vsync field + + if( mtgsThread != NULL ) + { + mtgsThread->SendSimplePacket( GS_RINGTYPE_VSYNC, + (*(u32*)(PS2MEM_GS+0x1000)&0x2000), updategs, 0); + + // No need to freeze MMX/XMM registers here since this + // code is always called from the context of a BranchTest. + mtgsThread->SetEvent(); + } + else + { + GSvsync((*(u32*)(PS2MEM_GS+0x1000)&0x2000)); + + // update here on single thread mode *OBSOLETE* + if( PAD1update != NULL ) PAD1update(0); + if( PAD2update != NULL ) PAD2update(1); + + gsFrameSkip( !updategs ); + } +} + +void _gs_ResetFrameskip() +{ + g_vu1SkipCount = 0; // set to 0 so that EE will re-enable the VU at the next vblank. + GSsetFrameSkip( 0 ); +} + +// Disables the GS Frameskip at runtime without any racy mess... +void gsResetFrameSkip() +{ + if( mtgsThread != NULL ) + mtgsThread->SendSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0); + else + _gs_ResetFrameskip(); +} + +void gsDynamicSkipEnable() +{ + if( !m_StrictSkipping ) return; + + mtgsWaitGS(); + m_iSlowStart = GetCPUTicks(); + frameLimitReset(); +} + +void SaveState::gsFreeze() +{ + FreezeMem(PS2MEM_GS, 0x2000); + Freeze(CSRw); + mtgsFreeze(); +} + +#ifdef PCSX2_DEVBUILD + +struct GSStatePacket +{ + u32 type; + vector mem; +}; + +// runs the GS +void RunGSState( gzLoadingState& f ) +{ + u32 newfield; + list< GSStatePacket > packets; + + while( !f.Finished() ) + { + int type, size; + f.Freeze( type ); + + if( type != GSRUN_VSYNC ) f.Freeze( size ); + + packets.push_back(GSStatePacket()); + GSStatePacket& p = packets.back(); + + p.type = type; + + if( type != GSRUN_VSYNC ) { + p.mem.resize(size*16); + f.FreezeMem( &p.mem[0], size*16 ); + } + } + + list::iterator it = packets.begin(); + g_SaveGSStream = 3; + + int skipfirst = 1; + + // first extract the data + while(1) { + + switch(it->type) { + case GSRUN_TRANS1: + GSgifTransfer1((u32*)&it->mem[0], 0); + break; + case GSRUN_TRANS2: + GSgifTransfer2((u32*)&it->mem[0], it->mem.size()/16); + break; + case GSRUN_TRANS3: + GSgifTransfer3((u32*)&it->mem[0], it->mem.size()/16); + break; + case GSRUN_VSYNC: + // flip + newfield = (*(u32*)(PS2MEM_GS+0x1000)&0x2000) ? 0 : 0x2000; + *(u32*)(PS2MEM_GS+0x1000) = (*(u32*)(PS2MEM_GS+0x1000) & ~(1<<13)) | newfield; + + GSvsync(newfield); + SysUpdate(); + + if( g_SaveGSStream != 3 ) + return; + break; + + jNO_DEFAULT + } + + ++it; + if( it == packets.end() ) + it = packets.begin(); + } +} + +#endif + +#undef GIFchain diff --git a/pcsx2/GS.h b/pcsx2/GS.h new file mode 100644 index 0000000000..4570ad44e4 --- /dev/null +++ b/pcsx2/GS.h @@ -0,0 +1,334 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __GS_H__ +#define __GS_H__ + +// GCC needs these includes +#include +#include +#include + +#include "Common.h" +#include "Threading.h" +#include "zlib.h" + +#define GSPATH3FIX + +PCSX2_ALIGNED16( extern u8 g_RealGSMem[0x2000] ); +#define GSCSRr *((u64*)(g_RealGSMem+0x1000)) +#define GSIMR *((u32*)(g_RealGSMem+0x1010)) +#define GSSIGLBLID ((GSRegSIGBLID*)(g_RealGSMem+0x1080)) + +///////////////////////////////////////////////////////////////////////////// +// MTGS GIFtag Parser - Declaration +// +// The MTGS needs a dummy "GS plugin" for processing SIGNAL, FINISH, and LABEL +// commands. These commands trigger gsIRQs, which need to be handled accurately +// in synch with the EE (which can be running several frames ahead of the MTGS) +// +// Yeah, it's a lot of work, but the performance gains are huge, even on HT cpus. + +struct GSRegSIGBLID +{ + u32 SIGID; + u32 LBLID; +}; + +enum GIF_FLG +{ + GIF_FLG_PACKED = 0, + GIF_FLG_REGLIST = 1, + GIF_FLG_IMAGE = 2, + GIF_FLG_IMAGE2 = 3 +}; + +enum GIF_REG +{ + GIF_REG_PRIM = 0x00, + GIF_REG_RGBA = 0x01, + GIF_REG_STQ = 0x02, + GIF_REG_UV = 0x03, + GIF_REG_XYZF2 = 0x04, + GIF_REG_XYZ2 = 0x05, + GIF_REG_TEX0_1 = 0x06, + GIF_REG_TEX0_2 = 0x07, + GIF_REG_CLAMP_1 = 0x08, + GIF_REG_CLAMP_2 = 0x09, + GIF_REG_FOG = 0x0a, + GIF_REG_XYZF3 = 0x0c, + GIF_REG_XYZ3 = 0x0d, + GIF_REG_A_D = 0x0e, + GIF_REG_NOP = 0x0f, +}; + +struct GIFTAG +{ + u32 nloop : 15; + u32 eop : 1; + u32 dummy0 : 16; + u32 dummy1 : 14; + u32 pre : 1; + u32 prim : 11; + u32 flg : 2; + u32 nreg : 4; + u32 regs[2]; +}; + +struct GIFPath +{ + GIFTAG tag; + u32 curreg; + u32 _pad[3]; + u8 regs[16]; + + __forceinline void PrepRegs(); + void SetTag(const void* mem); + u32 GetReg(); +}; + + +///////////////////////////////////////////////////////////////////////////// +// MTGS Threaded Class Declaration + +// Uncomment this to enable the MTGS debug stack, which tracks to ensure reads +// and writes stay synchronized. Warning: the debug stack is VERY slow. +//#define RINGBUF_DEBUG_STACK + +enum GIF_PATH +{ + GIF_PATH_1 = 0, + GIF_PATH_2, + GIF_PATH_3, +}; + + +enum GS_RINGTYPE +{ + GS_RINGTYPE_RESTART = 0 +, GS_RINGTYPE_P1 +, GS_RINGTYPE_P2 +, GS_RINGTYPE_P3 +, GS_RINGTYPE_VSYNC +, GS_RINGTYPE_FRAMESKIP +, GS_RINGTYPE_MEMWRITE8 +, GS_RINGTYPE_MEMWRITE16 +, GS_RINGTYPE_MEMWRITE32 +, GS_RINGTYPE_MEMWRITE64 +, GS_RINGTYPE_FREEZE +, GS_RINGTYPE_RECORD +, GS_RINGTYPE_RESET // issues a GSreset() command. +, GS_RINGTYPE_SOFTRESET // issues a soft reset for the GIF +, GS_RINGTYPE_WRITECSR +, GS_RINGTYPE_MODECHANGE // for issued mode changes. +, GS_RINGTYPE_STARTTIME // special case for min==max fps frameskip settings +}; + +class mtgsThreadObject : public Threading::Thread +{ + friend class SaveState; + +protected: + // Size of the ringbuffer as a power of 2 -- size is a multiple of simd128s. + // (actual size is 1< m_RingBuffer; + + // mtgs needs its own memory space separate from the PS2. The PS2 memory is in + // synch with the EE while this stays in sync with the GS (ie, it lags behind) + u8* const m_gsMem; + +public: + mtgsThreadObject(); + virtual ~mtgsThreadObject(); + + void Reset(); + void GIFSoftReset( int mask ); + + // Waits for the GS to empty out the entire ring buffer contents. + // Used primarily for plugin startup/shutdown. + void WaitGS(); + + int PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size ); + int PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 size ); + int PrepDataPacket( GIF_PATH pathidx, const u64* srcdata, u32 size ); + void SendDataPacket(); + + void SendSimplePacket( GS_RINGTYPE type, int data0, int data1, int data2 ); + void SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 ); + + u8* GetDataPacketPtr() const; + void Freeze( SaveState& state ); + void SetEvent(); + + uptr FnPtr_SimplePacket() const + { +#ifndef __LINUX__ + __asm mov eax, SendSimplePacket +#else + __asm ("mov %eax, SendSimplePacket"); +#endif + //return (uptr)&SendSimplePacket; + } + +protected: + // Saves MMX/XMM regs, posts an event to the mtgsThread flag and releases a timeslice. + // For use in surrounding loops that wait on the mtgs. + void PrepEventWait(); + + // Restores MMX/XMM regs. For use in surrounding loops that wait on the mtgs. + void PostEventWait() const; + + // Processes a GIFtag & packet, and throws out some gsIRQs as needed. + // Used to keep interrupts in sync with the EE, while the GS itself + // runs potentially several frames behind. + u32 _gifTransferDummy( GIF_PATH pathidx, const u8 *pMem, u32 size ); + + // Used internally by SendSimplePacket type functions + uint _PrepForSimplePacket(); + void _FinishSimplePacket( uint future_writepos ); + + int Callback(); +}; + +extern mtgsThreadObject* mtgsThread; + +void mtgsWaitGS(); +bool mtgsOpen(); +void mtgsRingBufSimplePacket( s32 command, u32 data0, u32 data1, u32 data2 ); + +///////////////////////////////////////////////////////////////////////////// +// Generalized GS Functions and Stuff + +extern void gsInit(); +extern s32 gsOpen(); +extern void gsClose(); +extern void gsReset(); +extern void gsOnModeChanged( u32 framerate, u32 newTickrate ); +extern void gsSetVideoRegionType( u32 isPal ); +extern void gsResetFrameSkip(); +extern void gsSyncLimiterLostTime( s32 deltaTime ); +extern void gsDynamicSkipEnable(); +extern void gsPostVsyncEnd( bool updategs ); +extern void gsFrameSkip( bool forceskip ); + +// Some functions shared by both the GS and MTGS +extern void _gs_ResetFrameskip(); +extern void _gs_ChangeTimings( u32 framerate, u32 iTicks ); + + +// used for resetting GIF fifo +bool gsGIFSoftReset( int mask ); +void gsGIFReset(); +void gsCSRwrite(u32 value); + +extern void gsWrite8(u32 mem, u8 value); +extern void gsWrite16(u32 mem, u16 value); +extern void gsWrite32(u32 mem, u32 value); +extern void gsWrite64(u32 mem, u64 value); + +extern u8 gsRead8(u32 mem); +extern u16 gsRead16(u32 mem); +extern u32 gsRead32(u32 mem); +extern u64 gsRead64(u32 mem); + +void gsConstWrite8(u32 mem, int mmreg); +void gsConstWrite16(u32 mem, int mmreg); +void gsConstWrite32(u32 mem, int mmreg); +void gsConstWrite64(u32 mem, int mmreg); +void gsConstWrite128(u32 mem, int mmreg); + +int gsConstRead8(u32 x86reg, u32 mem, u32 sign); +int gsConstRead16(u32 x86reg, u32 mem, u32 sign); +int gsConstRead32(u32 x86reg, u32 mem); +void gsConstRead64(u32 mem, int mmreg); +void gsConstRead128(u32 mem, int xmmreg); + +void gsIrq(); +extern void gsInterrupt(); +void dmaGIF(); +void GIFdma(); +void mfifoGIFtransfer(int qwc); +int _GIFchain(); +void gifMFIFOInterrupt(); + +extern u32 g_vu1SkipCount; +extern u32 CSRw; +extern u64 m_iSlowStart; + +// GS Playback +#define GSRUN_TRANS1 1 +#define GSRUN_TRANS2 2 +#define GSRUN_TRANS3 3 +#define GSRUN_VSYNC 4 + +#ifdef PCSX2_DEVBUILD + +extern int g_SaveGSStream; +extern int g_nLeftGSFrames; +extern gzSavingState* g_fGSSave; + +#endif + +void RunGSState(gzLoadingState& f); + +extern void GSGIFTRANSFER1(u32 *pMem, u32 addr); +extern void GSGIFTRANSFER2(u32 *pMem, u32 addr); +extern void GSGIFTRANSFER3(u32 *pMem, u32 addr); +extern void GSVSYNC(); + +#endif diff --git a/pcsx2/Gif.cpp b/pcsx2/Gif.cpp new file mode 100644 index 0000000000..82230f5f0b --- /dev/null +++ b/pcsx2/Gif.cpp @@ -0,0 +1,533 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "VU.h" +#include "GS.h" +#include "iR5900.h" +#include "Counters.h" + +#include "VifDma.h" + +using std::min; + + +#define gif ((DMACh*)&psH[0xA000]) + +static u64 s_gstag=0; // used for querying the last tag +static int gspath3done=0; +static int gscycles = 0; + +__forceinline void gsInterrupt() { + GIF_LOG("gsInterrupt: %8.8x\n", cpuRegs.cycle); + + if((gif->chcr & 0x100) == 0){ + //SysPrintf("Eh? why are you still interrupting! chcr %x, qwc %x, done = %x\n", gif->chcr, gif->qwc, done); + return; + } + if(gif->qwc > 0 || gspath3done == 0) { + if( !(psHu32(DMAC_CTRL) & 0x1) ) { + Console::Notice("gs dma masked, re-scheduling..."); + // re-raise the int shortly in the future + CPU_INT( 2, 64 ); + return; + } + + GIFdma(); +#ifdef GSPATH3FIX + // re-reaise the IRQ as part of the mysterious Path3fix. + // fixme - this hack *should* have the gs_irq raised from the VIF, I think. It would be + // more efficient and more correct. (air) + if (!(vif1Regs->mskpath3 && (vif1ch->chcr & 0x100)) || (psHu32(GIF_MODE) & 0x1)) + CPU_INT( 2, 64 ); +#endif + return; + } + + gspath3done = 0; + gscycles = 0; + Path3transfer = 0; + gif->chcr &= ~0x100; + GSCSRr &= ~0xC000; //Clear FIFO stuff + GSCSRr |= 0x4000; //FIFO empty + //psHu32(GIF_MODE)&= ~0x4; + psHu32(GIF_STAT)&= ~0xE00; // OPH=0 | APATH=0 + psHu32(GIF_STAT)&= ~0x1F000000; // QFC=0 + hwDmacIrq(DMAC_GIF); + +} + +static void WRITERING_DMA(u32 *pMem, u32 qwc) +{ + psHu32(GIF_STAT) |= 0xE00; + + // Path3 transfer will be set to zero by the GIFtag handler. + Path3transfer = 1; + + if( mtgsThread != NULL ) + { + int sizetoread = (qwc)<<4; + sizetoread = mtgsThread->PrepDataPacket( GIF_PATH_3, pMem, qwc ); + u8* pgsmem = mtgsThread->GetDataPacketPtr(); + + /* check if page of endmem is valid (dark cloud2) */ + // fixme: this hack makes no sense, because the giftagDummy will + // process the full length of bytes regardess of how much we copy. + // So you'd think if we're truncating the copy to prevent DEPs, we + // should truncate the gif packet size too.. (air) + + // fixed? PrepDataPacket now returns the actual size of the packet. + // VIF handles scratchpad wrapping also, so this code shouldn't be needed anymore. + + memcpy_aligned(pgsmem, pMem, sizetoread<<4); + + mtgsThread->SendDataPacket(); + } + else + { + GSGIFTRANSFER3(pMem, qwc); + if( GSgetLastTag != NULL ) + { + GSgetLastTag(&s_gstag); + if( s_gstag == 1 ) + Path3transfer = 0; /* fixes SRS and others */ + } + } +} + +int _GIFchain() { +#ifdef GSPATH3FIX + u32 qwc = (psHu32(GIF_MODE) & 0x4 && vif1Regs->mskpath3) ? min(8, (int)gif->qwc) : gif->qwc; +#else + u32 qwc = gif->qwc; +#endif + u32 *pMem; + + //if (gif->qwc == 0) return 0; + + pMem = (u32*)dmaGetAddr(gif->madr); + if (pMem == NULL) { + // reset path3, fixes dark cloud 2 + + gsGIFSoftReset(4); + + //must increment madr and clear qwc, else it loops + gif->madr+= gif->qwc*16; + gif->qwc = 0; + Console::Notice( "Hackfix - NULL GIFchain" ); + return -1; + } + WRITERING_DMA(pMem, qwc); + + //if((psHu32(GIF_MODE) & 0x4)) amount -= qwc; + gif->madr+= qwc*16; + gif->qwc -= qwc; + return (qwc)*2; +} + +#define GIFchain() \ + if (gif->qwc) { \ + gscycles+= _GIFchain(); /* guessing */ \ + } + +int gscount = 0; +static int prevcycles = 0; +static u32* prevtag = NULL; + +void GIFdma() +{ + u32 *ptag; + u32 id; + + gscycles= prevcycles ? prevcycles: gscycles; + + if( (psHu32(GIF_CTRL) & 8) ) { // temporarily stop + SysPrintf("Gif dma temp paused?\n"); + return; + } + + GIF_LOG("dmaGIFstart chcr = %lx, madr = %lx, qwc = %lx\n tadr = %lx, asr0 = %lx, asr1 = %lx\n", gif->chcr, gif->madr, gif->qwc, gif->tadr, gif->asr0, gif->asr1); + +#ifndef GSPATH3FIX + if ( !(psHu32(GIF_MODE) & 0x4) ) { + if (vif1Regs->mskpath3 || psHu32(GIF_MODE) & 0x1) { + gif->chcr &= ~0x100; + psHu32(GIF_STAT)&= ~0xE00; // OPH=0 | APATH=0 + hwDmacIrq(2); + return; + } + } +#endif + + if ((psHu32(DMAC_CTRL) & 0xC0) == 0x80 && prevcycles != 0) { // STD == GIF + SysPrintf("GS Stall Control Source = %x, Drain = %x\n MADR = %x, STADR = %x", (psHu32(0xe000) >> 4) & 0x3, (psHu32(0xe000) >> 6) & 0x3, gif->madr, psHu32(DMAC_STADR)); + + if( gif->madr + (gif->qwc * 16) > psHu32(DMAC_STADR) ) { + CPU_INT(2, gscycles); + gscycles = 0; + return; + } + prevcycles = 0; + gif->qwc = 0; + } + + GSCSRr &= ~0xC000; //Clear FIFO stuff + GSCSRr |= 0x8000; //FIFO full + //psHu32(GIF_STAT)|= 0xE00; // OPH=1 | APATH=3 + psHu32(GIF_STAT)|= 0x10000000; // FQC=31, hack ;) + +#ifdef GSPATH3FIX + if (vif1Regs->mskpath3 || psHu32(GIF_MODE) & 0x1) { + if(gif->qwc == 0) { + if((gif->chcr & 0x10e) == 0x104) { + ptag = (u32*)dmaGetAddr(gif->tadr); //Set memory pointer to TADR + + if (ptag == NULL) { //Is ptag empty? + psHu32(DMAC_STAT)|= 1<<15; //If yes, set BEIS (BUSERR) in DMAC_STAT register + return; + } + gscycles += 2; + gif->chcr = ( gif->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + id = (ptag[0] >> 28) & 0x7; //ID for DmaChain copied from bit 28 of the tag + gif->qwc = (u16)ptag[0]; //QWC set to lower 16bits of the tag + gif->madr = ptag[1]; //MADR = ADDR field + gspath3done = hwDmacSrcChainWithStack(gif, id); + GIF_LOG("PTH3 MASK gifdmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx\n", ptag[1], ptag[0], gif->qwc, id, gif->madr); + + if ((gif->chcr & 0x80) && ptag[0] >> 31) { //Check TIE bit of CHCR and IRQ bit of tag + GIF_LOG("PATH3 MSK dmaIrq Set\n"); + SysPrintf("GIF TIE\n"); + gspath3done |= 1; + } + } + } + // When MTGS is enabled, Gifchain calls WRITERING_DMA, which calls GSRINGBUF_DONECOPY, which freezes + // the registers inside of the FreezeXMMRegs calls here and in the other two below.. + // I'm not really sure that is intentional. --arcum42 + FreezeXMMRegs(1); + FreezeMMXRegs(1); + GIFchain(); + FreezeXMMRegs(0); // Theres a comment below that says not to unfreeze the xmm regs, so not sure about this. + FreezeMMXRegs(0); + + if((gspath3done == 1 || (gif->chcr & 0xc) == 0) && gif->qwc == 0){ + if(gif->qwc > 0) SysPrintf("Horray\n"); + gspath3done = 0; + gif->chcr &= ~0x100; + //psHu32(GIF_MODE)&= ~0x4; + GSCSRr &= ~0xC000; + GSCSRr |= 0x4000; + Path3transfer = 0; + psHu32(GIF_STAT)&= ~0x1F000E00; // OPH=0 | APATH=0 | QFC=0 + hwDmacIrq(DMAC_GIF); + } + //Dont unfreeze xmm regs here, Masked PATH3 can only be called by VIF, which is already handling it. + return; + } +#endif + //gscycles = 0; + // Transfer Dn_QWC from Dn_MADR to GIF + if ((gif->chcr & 0xc) == 0 || gif->qwc > 0) { // Normal Mode + //gscount++; + if ((psHu32(DMAC_CTRL) & 0xC0) == 0x80 && (gif->chcr & 0xc) == 0) { + SysPrintf("DMA Stall Control on GIF normal\n"); + } + FreezeXMMRegs(1); + FreezeMMXRegs(1); + GIFchain(); //Transfers the data set by the switch + FreezeXMMRegs(0); + FreezeMMXRegs(0); + if(gif->qwc == 0 && (gif->chcr & 0xc) == 0) gspath3done = 1; + } + else { + // Chain Mode + while (gspath3done == 0 && gif->qwc == 0) { //Loop if the transfers aren't intermittent + ptag = (u32*)dmaGetAddr(gif->tadr); //Set memory pointer to TADR + if (ptag == NULL) { //Is ptag empty? + psHu32(DMAC_STAT)|= 1<<15; //If yes, set BEIS (BUSERR) in DMAC_STAT register + return; + } + gscycles+=2; // Add 1 cycles from the QW read for the tag + + // Transfer dma tag if tte is set + if (gif->chcr & 0x40) { + //u32 temptag[4] = {0}; + //SysPrintf("GIF TTE: %x_%x\n", ptag[3], ptag[2]); + + //temptag[0] = ptag[2]; + //temptag[1] = ptag[3]; + //GSGIFTRANSFER3(ptag, 1); + } + + gif->chcr = ( gif->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + + id = (ptag[0] >> 28) & 0x7; //ID for DmaChain copied from bit 28 of the tag + gif->qwc = (u16)ptag[0]; //QWC set to lower 16bits of the tag + gif->madr = ptag[1]; //MADR = ADDR field + + gspath3done = hwDmacSrcChainWithStack(gif, id); + GIF_LOG("gifdmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx\n", ptag[1], ptag[0], gif->qwc, id, gif->madr); + + if ((psHu32(DMAC_CTRL) & 0xC0) == 0x80) { // STD == GIF + // there are still bugs, need to also check if gif->madr +16*qwc >= stadr, if not, stall + if(!gspath3done && gif->madr + (gif->qwc * 16) > psHu32(DMAC_STADR) && id == 4) { + // stalled + SysPrintf("GS Stall Control Source = %x, Drain = %x\n MADR = %x, STADR = %x", (psHu32(0xe000) >> 4) & 0x3, (psHu32(0xe000) >> 6) & 0x3,gif->madr, psHu32(DMAC_STADR)); + prevcycles = gscycles; + gif->tadr -= 16; + hwDmacIrq(13); + CPU_INT(2, gscycles); + gscycles = 0; + return; + } + } + + FreezeXMMRegs(1); + FreezeMMXRegs(1); + GIFchain(); //Transfers the data set by the switch + FreezeXMMRegs(0); + FreezeMMXRegs(0); + + if ((gif->chcr & 0x80) && ptag[0] >> 31) { //Check TIE bit of CHCR and IRQ bit of tag + GIF_LOG("dmaIrq Set\n"); + gspath3done = 1; + //gif->qwc = 0; + } + } + } + prevtag = NULL; + prevcycles = 0; + if (!(vif1Regs->mskpath3 || (psHu32(GIF_MODE) & 0x1))) { + CPU_INT(2, gscycles); + gscycles = 0; + } +} + +void dmaGIF() { + //if(vif1Regs->mskpath3 || (psHu32(GIF_MODE) & 0x1)){ + // CPU_INT(2, 48); //Wait time for the buffer to fill, fixes some timing problems in path 3 masking + //} //It takes the time of 24 QW for the BUS to become ready - The Punisher, And1 Streetball + //else + gspath3done = 0; // For some reason this doesnt clear? So when the system starts the thread, we will clear it :) + + if(gif->qwc > 0 && (gif->chcr & 0x4) == 0x4) + gspath3done = 1; //Halflife sets a QWC amount in chain mode, no tadr set. + + if ((psHu32(DMAC_CTRL) & 0xC) == 0xC ) { // GIF MFIFO + //SysPrintf("GIF MFIFO\n"); + gifMFIFOInterrupt(); + return; + } + + GIFdma(); +} + +#define spr0 ((DMACh*)&PS2MEM_HW[0xD000]) + +static unsigned int mfifocycles; +static unsigned int gifqwc = 0; +static unsigned int gifdone = 0; + +// called from only one location, so forceinline it: +static __forceinline int mfifoGIFrbTransfer() { + u32 qwc = (psHu32(GIF_MODE) & 0x4 && vif1Regs->mskpath3) ? min(8, (int)gif->qwc) : gif->qwc; + int mfifoqwc = min(gifqwc, qwc); + u32 *src; + + + /* Check if the transfer should wrap around the ring buffer */ + if ((gif->madr+mfifoqwc*16) > (psHu32(DMAC_RBOR) + psHu32(DMAC_RBSR)+16)) { + int s1 = ((psHu32(DMAC_RBOR) + psHu32(DMAC_RBSR)+16) - gif->madr) >> 4; + + // fixme - I don't think these should use WRITERING_DMA, since our source + // isn't the DmaGetAddr(gif->madr) address that WRITERING_DMA expects. + + /* it does, so first copy 's1' bytes from 'addr' to 'data' */ + src = (u32*)PSM(gif->madr); + if (src == NULL) return -1; + WRITERING_DMA(src, s1); + + /* and second copy 's2' bytes from 'maddr' to '&data[s1]' */ + src = (u32*)PSM(psHu32(DMAC_RBOR)); + if (src == NULL) return -1; + WRITERING_DMA(src, (mfifoqwc - s1)); + + } else { + /* it doesn't, so just transfer 'qwc*16' words + from 'gif->madr' to GS */ + src = (u32*)PSM(gif->madr); + if (src == NULL) return -1; + + WRITERING_DMA(src, mfifoqwc); + gif->madr = psHu32(DMAC_RBOR) + (gif->madr & psHu32(DMAC_RBSR)); + } + + gifqwc -= mfifoqwc; + gif->qwc -= mfifoqwc; + gif->madr+= mfifoqwc*16; + mfifocycles+= (mfifoqwc) * 2; /* guessing */ + + + return 0; +} + +// called from only one location, so forceinline it: +static __forceinline int mfifoGIFchain() { + /* Is QWC = 0? if so there is nothing to transfer */ + + if (gif->qwc == 0) return 0; + + if (gif->madr >= psHu32(DMAC_RBOR) && + gif->madr <= (psHu32(DMAC_RBOR)+psHu32(DMAC_RBSR))) { + if (mfifoGIFrbTransfer() == -1) return -1; + } else { + int mfifoqwc = (psHu32(GIF_MODE) & 0x4 && vif1Regs->mskpath3) ? min(8, (int)gif->qwc) : gif->qwc; + u32 *pMem = (u32*)dmaGetAddr(gif->madr); + if (pMem == NULL) return -1; + + WRITERING_DMA(pMem, mfifoqwc); + gif->madr+= mfifoqwc*16; + gif->qwc -= mfifoqwc; + mfifocycles+= (mfifoqwc) * 2; /* guessing */ + } + + return 0; +} + + +void mfifoGIFtransfer(int qwc) { + u32 *ptag; + int id; + u32 temp = 0; + mfifocycles = 0; + + if(qwc > 0 ) { + gifqwc += qwc; + if(!(gif->chcr & 0x100))return; + } + SPR_LOG("mfifoGIFtransfer %x madr %x, tadr %x\n", gif->chcr, gif->madr, gif->tadr); + + if(gif->qwc == 0){ + if(gif->tadr == spr0->madr) { + #ifdef PCSX2_DEVBUILD + /*if( gifqwc > 1 ) + SysPrintf("gif mfifo tadr==madr but qwc = %d\n", gifqwc);*/ + #endif + //hwDmacIrq(14); + + return; + } + gif->tadr = psHu32(DMAC_RBOR) + (gif->tadr & psHu32(DMAC_RBSR)); + ptag = (u32*)dmaGetAddr(gif->tadr); + + id = (ptag[0] >> 28) & 0x7; + gif->qwc = (ptag[0] & 0xffff); + gif->madr = ptag[1]; + mfifocycles += 2; + + gif->chcr = ( gif->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); + SPR_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx mfifo qwc = %x spr0 madr = %x\n", + ptag[1], ptag[0], gif->qwc, id, gif->madr, gif->tadr, gifqwc, spr0->madr); + + gifqwc--; + switch (id) { + case 0: // Refe - Transfer Packet According to ADDR field + gif->tadr = psHu32(DMAC_RBOR) + ((gif->tadr + 16) & psHu32(DMAC_RBSR)); + gifdone = 2; //End Transfer + break; + + case 1: // CNT - Transfer QWC following the tag. + gif->madr = psHu32(DMAC_RBOR) + ((gif->tadr + 16) & psHu32(DMAC_RBSR)); //Set MADR to QW after Tag + gif->tadr = psHu32(DMAC_RBOR) + ((gif->madr + (gif->qwc << 4)) & psHu32(DMAC_RBSR)); //Set TADR to QW following the data + gifdone = 0; + break; + + case 2: // Next - Transfer QWC following tag. TADR = ADDR + temp = gif->madr; //Temporarily Store ADDR + gif->madr = psHu32(DMAC_RBOR) + ((gif->tadr + 16) & psHu32(DMAC_RBSR)); //Set MADR to QW following the tag + gif->tadr = temp; //Copy temporarily stored ADDR to Tag + gifdone = 0; + break; + + case 3: // Ref - Transfer QWC from ADDR field + case 4: // Refs - Transfer QWC from ADDR field (Stall Control) + gif->tadr = psHu32(DMAC_RBOR) + ((gif->tadr + 16) & psHu32(DMAC_RBSR)); //Set TADR to next tag + gifdone = 0; + break; + + case 7: // End - Transfer QWC following the tag + gif->madr = psHu32(DMAC_RBOR) + ((gif->tadr + 16) & psHu32(DMAC_RBSR)); //Set MADR to data following the tag + gif->tadr = psHu32(DMAC_RBOR) + ((gif->madr + (gif->qwc << 4)) & psHu32(DMAC_RBSR)); //Set TADR to QW following the data + gifdone = 2; //End Transfer + break; + } + if ((gif->chcr & 0x80) && (ptag[0] >> 31)) { + SPR_LOG("dmaIrq Set\n"); + gifdone = 2; + } + } + FreezeXMMRegs(1); + FreezeMMXRegs(1); + if (mfifoGIFchain() == -1) { + SysPrintf("GIF dmaChain error size=%d, madr=%lx, tadr=%lx\n", + gif->qwc, gif->madr, gif->tadr); + gifdone = 1; + } + FreezeXMMRegs(0); + FreezeMMXRegs(0); + + if(gif->qwc == 0 && gifdone == 2) gifdone = 1; + CPU_INT(11,mfifocycles); + + SPR_LOG("mfifoGIFtransfer end %x madr %x, tadr %x\n", gif->chcr, gif->madr, gif->tadr); +} + +void gifMFIFOInterrupt() +{ + if(!(gif->chcr & 0x100)) { SysPrintf("WTF GIFMFIFO\n");cpuRegs.interrupt &= ~(1 << 11); return ; } + + if(gifdone != 1) { + if(gifqwc <= 0) { + //SysPrintf("Empty\n"); + psHu32(GIF_STAT)&= ~0xE00; // OPH=0 | APATH=0 + hwDmacIrq(14); + return; + } + mfifoGIFtransfer(0); + return; + } +#ifdef PCSX2_DEVBUILD + if(gifdone == 0 || gif->qwc > 0) { + Console::Error("gifMFIFO Panic > Shouldnt go here!"); + return; + } +#endif + //if(gifqwc > 0)SysPrintf("GIF MFIFO ending with stuff in it %x\n", gifqwc); + gifqwc = 0; + gifdone = 0; + gif->chcr &= ~0x100; + hwDmacIrq(DMAC_GIF); + GSCSRr &= ~0xC000; //Clear FIFO stuff + GSCSRr |= 0x4000; //FIFO empty + //psHu32(GIF_MODE)&= ~0x4; + psHu32(GIF_STAT)&= ~0xE00; // OPH=0 | APATH=0 + psHu32(GIF_STAT)&= ~0x1F000000; // QFC=0 +} + diff --git a/pcsx2/Hw.cpp b/pcsx2/Hw.cpp new file mode 100644 index 0000000000..0cea5ff0ee --- /dev/null +++ b/pcsx2/Hw.cpp @@ -0,0 +1,1818 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "iR5900.h" +#include "VUmicro.h" +#include "IopMem.h" + +// The full suite of hardware APIs: +#include "IPU/IPU.h" +#include "GS.h" +#include "Counters.h" +#include "Vif.h" +#include "VifDma.h" +#include "SPR.h" +#include "Sif.h" + +using namespace R5900; + +u8 *psH; // hw mem + +int rdram_devices = 2; // put 8 for TOOL and 2 for PS2 and PSX +int rdram_sdevid = 0; + +void hwInit() +{ + gsInit(); + vif0Init(); + vif1Init(); + vifDmaInit(); + sifInit(); + sprInit(); + ipuInit(); +} + +void hwShutdown() { + ipuShutdown(); +} + +void hwReset() +{ + hwInit(); + + memzero_ptr( PS2MEM_HW ); + //memset(PS2MEM_HW+0x2000, 0, 0x0000e000); + + psHu32(0xf520) = 0x1201; + psHu32(0xf260) = 0x1D000060; + // i guess this is kinda a version, it's used by some bioses + psHu32(0xf590) = 0x1201; + + gsReset(); + ipuReset(); +} + +__forceinline u8 hwRead8(u32 mem) +{ + u8 ret; + + if( mem >= 0x10002000 && mem < 0x10008000 ) + DevCon::Notice("hwRead8 to %x", params mem); + + SPR_LOG("Hardware read 8bit at %lx, ret %lx\n", mem, psHu8(mem)); + + switch (mem) + { + case 0x10000000: ret = (u8)rcntRcount(0); break; + case 0x10000010: ret = (u8)counters[0].modeval; break; + case 0x10000020: ret = (u8)counters[0].target; break; + case 0x10000030: ret = (u8)counters[0].hold; break; + case 0x10000001: ret = (u8)(rcntRcount(0)>>8); break; + case 0x10000011: ret = (u8)(counters[0].modeval>>8); break; + case 0x10000021: ret = (u8)(counters[0].target>>8); break; + case 0x10000031: ret = (u8)(counters[0].hold>>8); break; + + case 0x10000800: ret = (u8)rcntRcount(1); break; + case 0x10000810: ret = (u8)counters[1].modeval; break; + case 0x10000820: ret = (u8)counters[1].target; break; + case 0x10000830: ret = (u8)counters[1].hold; break; + case 0x10000801: ret = (u8)(rcntRcount(1)>>8); break; + case 0x10000811: ret = (u8)(counters[1].modeval>>8); break; + case 0x10000821: ret = (u8)(counters[1].target>>8); break; + case 0x10000831: ret = (u8)(counters[1].hold>>8); break; + + case 0x10001000: ret = (u8)rcntRcount(2); break; + case 0x10001010: ret = (u8)counters[2].modeval; break; + case 0x10001020: ret = (u8)counters[2].target; break; + case 0x10001001: ret = (u8)(rcntRcount(2)>>8); break; + case 0x10001011: ret = (u8)(counters[2].modeval>>8); break; + case 0x10001021: ret = (u8)(counters[2].target>>8); break; + + case 0x10001800: ret = (u8)rcntRcount(3); break; + case 0x10001810: ret = (u8)counters[3].modeval; break; + case 0x10001820: ret = (u8)counters[3].target; break; + case 0x10001801: ret = (u8)(rcntRcount(3)>>8); break; + case 0x10001811: ret = (u8)(counters[3].modeval>>8); break; + case 0x10001821: ret = (u8)(counters[3].target>>8); break; + + default: + if ((mem & 0xffffff0f) == 0x1000f200) + { + if(mem == 0x1000f260) ret = 0; + else if(mem == 0x1000F240) { + ret = psHu32(mem); + //psHu32(mem) &= ~0x4000; + } + else ret = psHu32(mem); + return (u8)ret; + } + + ret = psHu8(mem); + HW_LOG("Unknown Hardware Read 8 at %x\n",mem); + break; + } + + return ret; +} + +__forceinline u16 hwRead16(u32 mem) +{ + u16 ret; + + if( mem >= 0x10002000 && mem < 0x10008000 ) + Console::Notice("hwRead16 to %x", params mem); + + switch (mem) { + case 0x10000000: ret = (u16)rcntRcount(0); break; + case 0x10000010: ret = (u16)counters[0].modeval; break; + case 0x10000020: ret = (u16)counters[0].target; break; + case 0x10000030: ret = (u16)counters[0].hold; break; + + case 0x10000800: ret = (u16)rcntRcount(1); break; + case 0x10000810: ret = (u16)counters[1].modeval; break; + case 0x10000820: ret = (u16)counters[1].target; break; + case 0x10000830: ret = (u16)counters[1].hold; break; + + case 0x10001000: ret = (u16)rcntRcount(2); break; + case 0x10001010: ret = (u16)counters[2].modeval; break; + case 0x10001020: ret = (u16)counters[2].target; break; + + case 0x10001800: ret = (u16)rcntRcount(3); break; + case 0x10001810: ret = (u16)counters[3].modeval; break; + case 0x10001820: ret = (u16)counters[3].target; break; + + default: + if ((mem & 0xffffff0f) == 0x1000f200) { + if(mem == 0x1000f260) ret = 0; + else if(mem == 0x1000F240) { + ret = psHu16(mem) | 0x0102; + psHu32(mem) &= ~0x4000; + } + else ret = psHu32(mem); + return (u16)ret; + } + ret = psHu16(mem); + HW_LOG("Hardware Read16 at 0x%x, value= 0x%x\n", ret, mem); + break; + } + return ret; +} + +// Reads hardware registers for page 0 (counters 0 and 1) +mem32_t __fastcall hwRead32_page_00(u32 mem) +{ + mem &= 0xffff; + switch( mem ) + { + case 0x00: return (u16)rcntRcount(0); + case 0x10: return (u16)counters[0].modeval; + case 0x20: return (u16)counters[0].target; + case 0x30: return (u16)counters[0].hold; + + case 0x800: return (u16)rcntRcount(1); + case 0x810: return (u16)counters[1].modeval; + case 0x820: return (u16)counters[1].target; + case 0x830: return (u16)counters[1].hold; + } + + return *((u32*)&PS2MEM_HW[mem]); +} + +// Reads hardware registers for page 1 (counters 2 and 3) +mem32_t __fastcall hwRead32_page_01(u32 mem) +{ + mem &= 0xffff; + switch( mem ) + { + case 0x1000: return (u16)rcntRcount(2); + case 0x1010: return (u16)counters[2].modeval; + case 0x1020: return (u16)counters[2].target; + + case 0x1800: return (u16)rcntRcount(3); + case 0x1810: return (u16)counters[3].modeval; + case 0x1820: return (u16)counters[3].target; + } + + return *((u32*)&PS2MEM_HW[mem]); +} + +// Reads hardware registers for page 15 (0x0F). +mem32_t __fastcall hwRead32_page_0F(u32 mem) +{ + // *Performance Warning* This function is called -A-LOT. Be weary when making changes. It + // could impact FPS significantly. + + // Optimization Note: + // Shortcut for the INTC_STAT register, which is checked *very* frequently as part of the EE's + // vsynch timers. INTC_STAT has the disadvantage of being in the 0x1000f000 case, which has + // a lot of additional registers in it, and combined with it's call frequency is a bad thing. + + mem &= 0xffff; + + /*if(mem == (INTC_STAT & 0xffff) ) + { + // This one is checked alot, so leave it commented out unless you love 600 meg logfiles. + //HW_LOG("DMAC_STAT Read 32bit %x\n", psHu32(0xe010)); + return psHu32(INTC_STAT); + }*/ + + switch( mem ) + { + case 0xf000: + // This one is checked alot, so leave it commented out unless you love 600 meg logfiles. + //HW_LOG("DMAC_STAT Read 32bit %x\n", psHu32(0xe010)); + break; + + case 0xf010: + HW_LOG("INTC_MASK Read32, value=0x%x", psHu32(INTC_MASK)); + break; + + case 0xf130: // 0x1000f130 + case 0xf260: // 0x1000f260 SBUS? + case 0xf410: // 0x1000f410 + case 0xf430: // MCH_RICM + return 0; + + case 0xf240: // 0x1000f240: SBUS + return psHu32(0xf240) | 0xF0000102; + + case 0xf440: // 0x1000f440: MCH_DRD + + if( !((psHu32(0xf430) >> 6) & 0xF) ) + { + switch ((psHu32(0xf430)>>16) & 0xFFF) + { + //MCH_RICM: x:4|SA:12|x:5|SDEV:1|SOP:4|SBC:1|SDEV:5 + + case 0x21://INIT + if(rdram_sdevid < rdram_devices) + { + rdram_sdevid++; + return 0x1F; + } + return 0; + + case 0x23://CNFGA + return 0x0D0D; //PVER=3 | MVER=16 | DBL=1 | REFBIT=5 + + case 0x24://CNFGB + //0x0110 for PSX SVER=0 | CORG=8(5x9x7) | SPT=1 | DEVTYP=0 | BYTE=0 + return 0x0090; //SVER=0 | CORG=4(5x9x6) | SPT=1 | DEVTYP=0 | BYTE=0 + + case 0x40://DEVID + return psHu32(0xf430) & 0x1F; // =SDEV + } + } + return 0; + } + return *((u32*)&PS2MEM_HW[mem]); +} + +mem32_t __fastcall hwRead32_page_02(u32 mem) +{ + return ipuRead32( mem ); +} + +// Used for all pages not explicitly specified above. +mem32_t __fastcall hwRead32_page_other(u32 mem) +{ + const u16 masked_mem = mem & 0xffff; + + switch( masked_mem>>12 ) // switch out as according to the 4k page of the access. + { + /////////////////////////////////////////////////////// + // Most of the following case handlers are for developer builds only (logging). + // It'll all optimize to ziltch in public release builds. + + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + { + const char* regName = "Unknown"; + + switch( mem ) + { + case D2_CHCR: regName = "DMA2_CHCR"; break; + case D2_MADR: regName = "DMA2_MADR"; break; + case D2_QWC: regName = "DMA2_QWC"; break; + case D2_TADR: regName = "DMA2_TADDR"; break; + case D2_ASR0: regName = "DMA2_ASR0"; break; + case D2_ASR1: regName = "DMA2_ASR1"; break; + case D2_SADR: regName = "DMA2_SADDR"; break; + } + + HW_LOG( "Hardware Read32 at 0x%x (%s), value=0x%x\n", mem, regName, psHu32(mem) ); + } + break; + + case 0x0b: + if( mem == D4_CHCR ) + HW_LOG("Hardware Read32 at 0x%x (IPU1:DMA4_CHCR), value=0x%x\n", mem, psHu32(mem)); + break; + + case 0x0c: + case 0x0d: + case 0x0e: + if( mem == DMAC_STAT ) + HW_LOG("DMAC_STAT Read32, value=0x%x\n", psHu32(DMAC_STAT)); + break; + + jNO_DEFAULT; + } + + return *((u32*)&PS2MEM_HW[masked_mem]); +} + +__forceinline u64 hwRead64(u32 mem) { + u64 ret; + + if ((mem>=0x10002000) && (mem<0x10003000)) { + return ipuRead64(mem); + } + +// switch (mem) { +// default: + if (mem < 0x10010000) { + ret = psHu64(mem); + } + else ret = 0; + HW_LOG("Unknown Hardware Read 64 at %x\n",mem); +// break; +// } + + return ret; +} + +__forceinline void hwRead128(u32 mem, u64 *out) { + if (mem >= 0x10004000 && mem < 0x10008000) { + ReadFIFO(mem, out); return; + } + + if (mem < 0x10010000) { + out[0] = psHu64(mem); + out[1] = psHu64(mem+8); + } + + HW_LOG("Unknown Hardware Read 128 at %x\n",mem); +} + +// dark cloud2 uses 8 bit DMAs register writes +static __forceinline void DmaExec8( void (*func)(), u32 mem, u8 value ) +{ + psHu8(mem) = (u8)value; + if ((psHu8(mem) & 0x1) && (psHu32(DMAC_CTRL) & 0x1)) + { + /*SysPrintf("Running DMA 8 %x\n", psHu32(mem & ~0x1));*/ + func(); + } +} + +static __forceinline void DmaExec16( void (*func)(), u32 mem, u16 value ) +{ + psHu16(mem) = (u16)value; + if ((psHu16(mem) & 0x100) && (psHu32(DMAC_CTRL) & 0x1)) + { + //SysPrintf("16bit DMA Start\n"); + func(); + } +} + +static void DmaExec( void (*func)(), u32 mem, u32 value ) +{ + /* Keep the old tag if in chain mode and hw doesnt set it*/ + if( (value & 0xc) == 0x4 && (value & 0xffff0000) == 0) + psHu32(mem) = (psHu32(mem) & 0xFFFF0000) | (u16)value; + else /* Else (including Normal mode etc) write whatever the hardware sends*/ + psHu32(mem) = (u32)value; + + if ((psHu32(mem) & 0x100) && (psHu32(DMAC_CTRL) & 0x1)) + func(); +} + +char sio_buffer[1024]; +int sio_count; + +void hwWrite8(u32 mem, u8 value) { + +#ifdef PCSX2_DEVBUILD + if( mem >= 0x10002000 && mem < 0x10008000 ) + SysPrintf("hwWrite8 to %x\n", mem); +#endif + + switch (mem) { + case 0x10000000: rcntWcount(0, value); break; + case 0x10000010: rcntWmode(0, (counters[0].modeval & 0xff00) | value); break; + case 0x10000011: rcntWmode(0, (counters[0].modeval & 0xff) | value << 8); break; + case 0x10000020: rcntWtarget(0, value); break; + case 0x10000030: rcntWhold(0, value); break; + + case 0x10000800: rcntWcount(1, value); break; + case 0x10000810: rcntWmode(1, (counters[1].modeval & 0xff00) | value); break; + case 0x10000811: rcntWmode(1, (counters[1].modeval & 0xff) | value << 8); break; + case 0x10000820: rcntWtarget(1, value); break; + case 0x10000830: rcntWhold(1, value); break; + + case 0x10001000: rcntWcount(2, value); break; + case 0x10001010: rcntWmode(2, (counters[2].modeval & 0xff00) | value); break; + case 0x10001011: rcntWmode(2, (counters[2].modeval & 0xff) | value << 8); break; + case 0x10001020: rcntWtarget(2, value); break; + + case 0x10001800: rcntWcount(3, value); break; + case 0x10001810: rcntWmode(3, (counters[3].modeval & 0xff00) | value); break; + case 0x10001811: rcntWmode(3, (counters[3].modeval & 0xff) | value << 8); break; + case 0x10001820: rcntWtarget(3, value); break; + + case 0x1000f180: + if (value == '\n') { + sio_buffer[sio_count] = 0; + Console::WriteLn( Color_Cyan, sio_buffer ); + sio_count = 0; + } else { + if (sio_count < 1023) { + sio_buffer[sio_count++] = value; + } + } +// SysPrintf("%c", value); + break; + + case 0x10003c02: //Tony Hawks Project 8 uses this + vif1Write32(mem & ~0x2, value << 16); + break; + case 0x10008001: // dma0 - vif0 + DMA_LOG("VIF0dma %lx\n", value); + DmaExec8(dmaVIF0, mem, value); + break; + + case 0x10009001: // dma1 - vif1 + DMA_LOG("VIF1dma %lx\n", value); + DmaExec8(dmaVIF1, mem, value); + break; + + case 0x1000a001: // dma2 - gif + DMA_LOG("0x%8.8x hwWrite8: GSdma %lx 0x%lx\n", cpuRegs.cycle, value); + DmaExec8(dmaGIF, mem, value); + break; + + case 0x1000b001: // dma3 - fromIPU + DMA_LOG("IPU0dma %lx\n", value); + DmaExec8(dmaIPU0, mem, value); + break; + + case 0x1000b401: // dma4 - toIPU + DMA_LOG("IPU1dma %lx\n", value); + DmaExec8(dmaIPU1, mem, value); + break; + + case 0x1000c001: // dma5 - sif0 + DMA_LOG("SIF0dma %lx\n", value); +// if (value == 0) psxSu32(0x30) = 0x40000; + DmaExec8(dmaSIF0, mem, value); + break; + + case 0x1000c401: // dma6 - sif1 + DMA_LOG("SIF1dma %lx\n", value); + DmaExec8(dmaSIF1, mem, value); + break; + + case 0x1000c801: // dma7 - sif2 + DMA_LOG("SIF2dma %lx\n", value); + DmaExec8(dmaSIF2, mem, value); + break; + + case 0x1000d001: // dma8 - fromSPR + DMA_LOG("fromSPRdma8 %lx\n", value); + DmaExec8(dmaSPR0, mem, value); + break; + + case 0x1000d401: // dma9 - toSPR + DMA_LOG("toSPRdma8 %lx\n", value); + DmaExec8(dmaSPR1, mem, value); + break; + + case 0x1000f592: // DMAC_ENABLEW + psHu8(0xf592) = value; + psHu8(0xf522) = value; + break; + + case 0x1000f200: // SIF(?) + psHu8(mem) = value; + break; + + case 0x1000f240:// SIF(?) + if(!(value & 0x100)) + psHu32(mem) &= ~0x100; + break; + + default: + assert( (mem&0xff0f) != 0xf200 ); + + switch(mem&~3) { + case 0x1000f130: + case 0x1000f410: + case 0x1000f430: + break; + default: + psHu8(mem) = value; + } + HW_LOG("Unknown Hardware write 8 at %x with value %x\n", mem, value); + break; + } +} + +__forceinline void hwWrite16(u32 mem, u16 value) +{ +#ifdef PCSX2_DEVBUILD + if( mem >= 0x10002000 && mem < 0x10008000 ) + SysPrintf("hwWrite16 to %x\n", mem); +#endif + + switch(mem) + { + case 0x10000000: rcntWcount(0, value); break; + case 0x10000010: rcntWmode(0, value); break; + case 0x10000020: rcntWtarget(0, value); break; + case 0x10000030: rcntWhold(0, value); break; + + case 0x10000800: rcntWcount(1, value); break; + case 0x10000810: rcntWmode(1, value); break; + case 0x10000820: rcntWtarget(1, value); break; + case 0x10000830: rcntWhold(1, value); break; + + case 0x10001000: rcntWcount(2, value); break; + case 0x10001010: rcntWmode(2, value); break; + case 0x10001020: rcntWtarget(2, value); break; + + case 0x10001800: rcntWcount(3, value); break; + case 0x10001810: rcntWmode(3, value); break; + case 0x10001820: rcntWtarget(3, value); break; + + case 0x10008000: // dma0 - vif0 + DMA_LOG("VIF0dma %lx\n", value); + DmaExec16(dmaVIF0, mem, value); + break; + + case 0x10009000: // dma1 - vif1 - chcr + DMA_LOG("VIF1dma CHCR %lx\n", value); + DmaExec16(dmaVIF1, mem, value); + break; + +#ifdef PCSX2_DEVBUILD + case 0x10009010: // dma1 - vif1 - madr + HW_LOG("VIF1dma Madr %lx\n", value); + psHu16(mem) = value;//dma1 madr + break; + case 0x10009020: // dma1 - vif1 - qwc + HW_LOG("VIF1dma QWC %lx\n", value); + psHu16(mem) = value;//dma1 qwc + break; + case 0x10009030: // dma1 - vif1 - tadr + HW_LOG("VIF1dma TADR %lx\n", value); + psHu16(mem) = value;//dma1 tadr + break; + case 0x10009040: // dma1 - vif1 - asr0 + HW_LOG("VIF1dma ASR0 %lx\n", value); + psHu16(mem) = value;//dma1 asr0 + break; + case 0x10009050: // dma1 - vif1 - asr1 + HW_LOG("VIF1dma ASR1 %lx\n", value); + psHu16(mem) = value;//dma1 asr1 + break; + case 0x10009080: // dma1 - vif1 - sadr + HW_LOG("VIF1dma SADR %lx\n", value); + psHu16(mem) = value;//dma1 sadr + break; +#endif +// --------------------------------------------------- + + case 0x1000a000: // dma2 - gif + DMA_LOG("0x%8.8x hwWrite32: GSdma %lx\n", cpuRegs.cycle, value); + DmaExec16(dmaGIF, mem, value); + break; + +#ifdef PCSX2_DEVBUILD + case 0x1000a010: + psHu16(mem) = value;//dma2 madr + HW_LOG("Hardware write DMA2_MADR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000a020: + psHu16(mem) = value;//dma2 qwc + HW_LOG("Hardware write DMA2_QWC 32bit at %x with value %x\n",mem,value); + break; + case 0x1000a030: + psHu16(mem) = value;//dma2 taddr + HW_LOG("Hardware write DMA2_TADDR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000a040: + psHu16(mem) = value;//dma2 asr0 + HW_LOG("Hardware write DMA2_ASR0 32bit at %x with value %x\n",mem,value); + break; + case 0x1000a050: + psHu16(mem) = value;//dma2 asr1 + HW_LOG("Hardware write DMA2_ASR1 32bit at %x with value %x\n",mem,value); + break; + case 0x1000a080: + psHu16(mem) = value;//dma2 saddr + HW_LOG("Hardware write DMA2_SADDR 32bit at %x with value %x\n",mem,value); + break; +#endif + + case 0x1000b000: // dma3 - fromIPU + DMA_LOG("IPU0dma %lx\n", value); + DmaExec16(dmaIPU0, mem, value); + break; + +#ifdef PCSX2_DEVBUILD + case 0x1000b010: + psHu16(mem) = value;//dma2 madr + HW_LOG("Hardware write IPU0DMA_MADR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b020: + psHu16(mem) = value;//dma2 madr + HW_LOG("Hardware write IPU0DMA_QWC 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b030: + psHu16(mem) = value;//dma2 tadr + HW_LOG("Hardware write IPU0DMA_TADR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b080: + psHu16(mem) = value;//dma2 saddr + HW_LOG("Hardware write IPU0DMA_SADDR 32bit at %x with value %x\n",mem,value); + break; +#endif + + case 0x1000b400: // dma4 - toIPU + DMA_LOG("IPU1dma %lx\n", value); + DmaExec16(dmaIPU1, mem, value); + break; + +#ifdef PCSX2_DEVBUILD + case 0x1000b410: + psHu16(mem) = value;//dma2 madr + HW_LOG("Hardware write IPU1DMA_MADR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b420: + psHu16(mem) = value;//dma2 madr + HW_LOG("Hardware write IPU1DMA_QWC 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b430: + psHu16(mem) = value;//dma2 tadr + HW_LOG("Hardware write IPU1DMA_TADR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b480: + psHu16(mem) = value;//dma2 saddr + HW_LOG("Hardware write IPU1DMA_SADDR 32bit at %x with value %x\n",mem,value); + break; +#endif + case 0x1000c000: // dma5 - sif0 + DMA_LOG("SIF0dma %lx\n", value); +// if (value == 0) psxSu32(0x30) = 0x40000; + DmaExec16(dmaSIF0, mem, value); + break; + + case 0x1000c002: + //? + break; + case 0x1000c400: // dma6 - sif1 + DMA_LOG("SIF1dma %lx\n", value); + DmaExec16(dmaSIF1, mem, value); + break; + +#ifdef PCSX2_DEVBUILD + case 0x1000c420: // dma6 - sif1 - qwc + HW_LOG("SIF1dma QWC = %lx\n", value); + psHu16(mem) = value; + break; + + case 0x1000c430: // dma6 - sif1 - tadr + HW_LOG("SIF1dma TADR = %lx\n", value); + psHu16(mem) = value; + break; +#endif + + case 0x1000c800: // dma7 - sif2 + DMA_LOG("SIF2dma %lx\n", value); + DmaExec16(dmaSIF2, mem, value); + break; + case 0x1000c802: + //? + break; + case 0x1000d000: // dma8 - fromSPR + DMA_LOG("fromSPRdma %lx\n", value); + DmaExec16(dmaSPR0, mem, value); + break; + + case 0x1000d400: // dma9 - toSPR + DMA_LOG("toSPRdma %lx\n", value); + DmaExec16(dmaSPR1, mem, value); + break; + case 0x1000f592: // DMAC_ENABLEW + psHu16(0xf592) = value; + psHu16(0xf522) = value; + break; + case 0x1000f130: + case 0x1000f132: + case 0x1000f410: + case 0x1000f412: + case 0x1000f430: + case 0x1000f432: + break; + + case 0x1000f200: + psHu16(mem) = value; + break; + + case 0x1000f220: + psHu16(mem) |= value; + break; + case 0x1000f230: + psHu16(mem) &= ~value; + break; + case 0x1000f240: + if(!(value & 0x100)) + psHu16(mem) &= ~0x100; + else + psHu16(mem) |= 0x100; + break; + case 0x1000f260: + psHu16(mem) = 0; + break; + + default: + psHu16(mem) = value; + HW_LOG("Unknown Hardware write 16 at %x with value %x\n",mem,value); + } +} + +// Page 0 of HW mwmory houses registers for Counters 0 and 1 +void __fastcall hwWrite32_page_00( u32 mem, u32 value ) +{ + mem &= 0xffff; + switch (mem) + { + case 0x000: rcntWcount(0, value); return; + case 0x010: rcntWmode(0, value); return; + case 0x020: rcntWtarget(0, value); return; + case 0x030: rcntWhold(0, value); return; + + case 0x800: rcntWcount(1, value); return; + case 0x810: rcntWmode(1, value); return; + case 0x820: rcntWtarget(1, value); return; + case 0x830: rcntWhold(1, value); return; + } + + *((u32*)&PS2MEM_HW[mem]) = value; +} + +// Page 1 of HW mwmory houses registers for Counters 2 and 3 +void __fastcall hwWrite32_page_01( u32 mem, u32 value ) +{ + mem &= 0xffff; + switch (mem) + { + case 0x1000: rcntWcount(2, value); return; + case 0x1010: rcntWmode(2, value); return; + case 0x1020: rcntWtarget(2, value); return; + + case 0x1800: rcntWcount(3, value); return; + case 0x1810: rcntWmode(3, value); return; + case 0x1820: rcntWtarget(3, value); return; + } + + *((u32*)&PS2MEM_HW[mem]) = value; +} + +// page 2 is the IPU register space! +void __fastcall hwWrite32_page_02( u32 mem, u32 value ) +{ + ipuWrite32(mem, value); +} + +// Page 3 contains writes to vif0 and vif1 registers, plus some GIF stuff! +void __fastcall hwWrite32_page_03( u32 mem, u32 value ) +{ + if(mem>=0x10003800) + { + if(mem<0x10003c00) + vif0Write32(mem, value); + else + vif1Write32(mem, value); + return; + } + + switch (mem) + { + case GIF_CTRL: + //SysPrintf("GIF_CTRL write %x\n", value); + psHu32(mem) = value & 0x8; + if (value & 0x1) gsGIFReset(); + else if( value & 8 ) psHu32(GIF_STAT) |= 8; + else psHu32(GIF_STAT) &= ~8; + break; + + case GIF_MODE: + // need to set GIF_MODE (hamster ball) + psHu32(GIF_MODE) = value; + if (value & 0x1) psHu32(GIF_STAT)|= 0x1; + else psHu32(GIF_STAT)&= ~0x1; + if (value & 0x4) psHu32(GIF_STAT)|= 0x4; + else psHu32(GIF_STAT)&= ~0x4; + break; + + case GIF_STAT: // stat is readonly + DevCon::Notice("*PCSX2* Gifstat write value = 0x%x\n", params value); + break; + + default: + psHu32(mem) = value; + } +} + +void __fastcall hwWrite32_page_0B( u32 mem, u32 value ) +{ + // Used for developer logging -- optimized away in Public Release. + const char* regName = "Unknown"; + + switch( mem ) + { + case D3_CHCR: // dma3 - fromIPU + DMA_LOG("IPU0dma EXECUTE, value=0x%x\n", value); + DmaExec(dmaIPU0, mem, value); + return; + + case D3_MADR: regName = "IPU0DMA_MADR"; break; + case D3_QWC: regName = "IPU0DMA_QWC"; break; + case D3_TADR: regName = "IPU0DMA_TADR"; break; + case D3_SADR: regName = "IPU0DMA_SADDR"; break; + + //------------------------------------------------------------------ + + case D4_CHCR: // dma4 - toIPU + DMA_LOG("IPU1dma EXECUTE, value=0x%x\n", value); + DmaExec(dmaIPU1, mem, value); + return; + + case D4_MADR: regName = "IPU1DMA_MADR"; break; + case D4_QWC: regName = "IPU1DMA_QWC"; break; + case D4_TADR: regName = "IPU1DMA_TADR"; break; + case D4_SADR: regName = "IPU1DMA_SADDR"; break; + } + + HW_LOG( "Hardware Write32 at 0x%x (%s), value=0x%x\n", mem, regName, value ); + psHu32(mem) = value; +} + +void __fastcall hwWrite32_page_0E( u32 mem, u32 value ) +{ + if( mem == DMAC_CTRL ) + { + HW_LOG("DMAC_CTRL Write 32bit %x\n", value); + } + else if( mem == DMAC_STAT ) + { + HW_LOG("DMAC_STAT Write 32bit %x\n", value); + psHu16(0xe010)&= ~(value & 0xffff); // clear on 1 + psHu16(0xe012) ^= (u16)(value >> 16); + + cpuTestDMACInts(); + return; + } + + psHu32(mem) = value; +} + +void __fastcall hwWrite32_page_0F( u32 mem, u32 value ) +{ + // Shift the middle 8 bits (bits 4-12) into the lower 8 bits. + // This helps the compiler optimize the switch statement into a lookup table. :) + +#define HELPSWITCH(m) (((m)>>4) & 0xff) + + switch( HELPSWITCH(mem) ) + { + case HELPSWITCH(INTC_STAT): + HW_LOG("INTC_STAT Write 32bit %x\n", value); + psHu32(INTC_STAT) &= ~value; + //cpuTestINTCInts(); + break; + + case HELPSWITCH(INTC_MASK): + HW_LOG("INTC_MASK Write 32bit %x\n", value); + psHu32(INTC_MASK) ^= (u16)value; + cpuTestINTCInts(); + break; + + //------------------------------------------------------------------ + case HELPSWITCH(0x1000f430)://MCH_RICM: x:4|SA:12|x:5|SDEV:1|SOP:4|SBC:1|SDEV:5 + if ((((value >> 16) & 0xFFF) == 0x21) && (((value >> 6) & 0xF) == 1) && (((psHu32(0xf440) >> 7) & 1) == 0))//INIT & SRP=0 + rdram_sdevid = 0; // if SIO repeater is cleared, reset sdevid + psHu32(mem) = value & ~0x80000000; //kill the busy bit + break; + + case HELPSWITCH(0x1000f440)://MCH_DRD: + psHu32(mem) = value; + break; + //------------------------------------------------------------------ + case HELPSWITCH(0x1000f590): // DMAC_ENABLEW + HW_LOG("DMAC_ENABLEW Write 32bit %lx\n", value); + psHu32(0xf590) = value; + psHu32(0xf520) = value; + break; + //------------------------------------------------------------------ + case HELPSWITCH(0x1000f200): + psHu32(mem) = value; + break; + case HELPSWITCH(0x1000f220): + psHu32(mem) |= value; + break; + case HELPSWITCH(0x1000f230): + psHu32(mem) &= ~value; + break; + case HELPSWITCH(0x1000f240): + if(!(value & 0x100)) + psHu32(mem) &= ~0x100; + else + psHu32(mem) |= 0x100; + break; + case HELPSWITCH(0x1000f260): + psHu32(mem) = 0; + break; + //------------------------------------------------------------------ + case HELPSWITCH(0x1000f130): + case HELPSWITCH(0x1000f410): + HW_LOG("Unknown Hardware write 32 at %x with value %x (%x)\n", mem, value, cpuRegs.CP0.n.Status.val); + break; + + default: + psHu32(mem) = value; + } +} + +void __fastcall hwWrite32_page_other( u32 mem, u32 value ) +{ + // Used for developer logging -- optimized away in Public Release. + const char* regName = "Unknown"; + + switch (mem) + { + case D0_CHCR: // dma0 - vif0 + DMA_LOG("VIF0dma EXECUTE, value=0x%x\n", value); + DmaExec(dmaVIF0, mem, value); + return; + +//------------------------------------------------------------------ + case D1_CHCR: // dma1 - vif1 - chcr + DMA_LOG("VIF1dma EXECUTE, value=0x%x\n", value); + DmaExec(dmaVIF1, mem, value); + return; + + case D1_MADR: regName = "VIF1dma MADR"; break; + case D1_QWC: regName = "VIF1dma QWC"; break; + case D1_TADR: regName = "VIF1dma TADR"; break; + case D1_ASR0: regName = "VIF1dma ASR0"; break; + case D1_ASR1: regName = "VIF1dma ASR1"; break; + case D1_SADR: regName = "VIF1dma SADR"; break; + +//------------------------------------------------------------------ + case D2_CHCR: // dma2 - gif + DMA_LOG("GIFdma EXECUTE, value=0x%x", value); + DmaExec(dmaGIF, mem, value); + return; + + case D2_MADR: regName = "GIFdma MADR"; break; + case D2_QWC: regName = "GIFdma QWC"; break; + case D2_TADR: regName = "GIFdma TADDR"; break; + case D2_ASR0: regName = "GIFdma ASR0"; break; + case D2_ASR1: regName = "GIFdma ASR1"; break; + case D2_SADR: regName = "GIFdma SADDR"; break; + +//------------------------------------------------------------------ + case 0x1000c000: // dma5 - sif0 + DMA_LOG("SIF0dma EXECUTE, value=0x%x\n", value); + //if (value == 0) psxSu32(0x30) = 0x40000; + DmaExec(dmaSIF0, mem, value); + return; +//------------------------------------------------------------------ + case 0x1000c400: // dma6 - sif1 + DMA_LOG("SIF1dma EXECUTE, value=0x%x\n", value); + DmaExec(dmaSIF1, mem, value); + return; + + case 0x1000c420: regName = "SIF1dma QWC"; break; + case 0x1000c430: regName = "SIF1dma TADR"; break; + +//------------------------------------------------------------------ + case 0x1000c800: // dma7 - sif2 + DMA_LOG("SIF2dma EXECUTE, value=0x%x\n", value); + DmaExec(dmaSIF2, mem, value); + return; +//------------------------------------------------------------------ + case 0x1000d000: // dma8 - fromSPR + DMA_LOG("SPR0dma EXECUTE (fromSPR), value=0x%x\n", value); + DmaExec(dmaSPR0, mem, value); + return; +//------------------------------------------------------------------ + case 0x1000d400: // dma9 - toSPR + DMA_LOG("SPR0dma EXECUTE (toSPR), value=0x%x\n", value); + DmaExec(dmaSPR1, mem, value); + return; + } + HW_LOG( "Hardware Write32 at 0x%x (%s), value=0x%x\n", mem, regName, value ); + psHu32(mem) = value; +} + +__forceinline void hwWrite64(u32 mem, u64 value) { + u32 val32; + int i; + + if ((mem>=0x10002000) && (mem<=0x10002030)) { + ipuWrite64(mem, value); + return; + } + + if ((mem>=0x10003800) && (mem<0x10003c00)) { + vif0Write32(mem, value); return; + } + if ((mem>=0x10003c00) && (mem<0x10004000)) { + vif1Write32(mem, value); return; + } + + switch (mem) { + case GIF_CTRL: + DevCon::Status("GIF_CTRL write 64", params value); + psHu32(mem) = value & 0x8; + if(value & 0x1) { + gsGIFReset(); + //gsReset(); + } + else { + if( value & 8 ) psHu32(GIF_STAT) |= 8; + else psHu32(GIF_STAT) &= ~8; + } + + return; + + case GIF_MODE: +#ifdef GSPATH3FIX + Console::Status("GIFMODE64 %x\n", params value); +#endif + psHu64(GIF_MODE) = value; + if (value & 0x1) psHu32(GIF_STAT)|= 0x1; + else psHu32(GIF_STAT)&= ~0x1; + if (value & 0x4) psHu32(GIF_STAT)|= 0x4; + else psHu32(GIF_STAT)&= ~0x4; + break; + + case GIF_STAT: // stat is readonly + return; + + case 0x1000a000: // dma2 - gif + DMA_LOG("0x%8.8x hwWrite64: GSdma %lx\n", cpuRegs.cycle, value); + DmaExec(dmaGIF, mem, value); + break; + + case 0x1000e000: // DMAC_CTRL + HW_LOG("DMAC_CTRL Write 64bit %x\n", value); + psHu64(mem) = value; + break; + + case 0x1000e010: // DMAC_STAT + HW_LOG("DMAC_STAT Write 64bit %x\n", value); + val32 = (u32)value; + psHu16(0xe010)&= ~(val32 & 0xffff); // clear on 1 + val32 = val32 >> 16; + for (i=0; i<16; i++) { // reverse on 1 + if (val32 & (1<= 0x10004000 && mem < 0x10008000) { + WriteFIFO(mem, value); return; + } + + switch (mem) { + case 0x1000f590: // DMAC_ENABLEW + psHu32(0xf590) = *(u32*)value; + psHu32(0xf520) = *(u32*)value; + break; + case 0x1000f130: + case 0x1000f410: + case 0x1000f430: + break; + + default: + + psHu64(mem ) = value[0]; + psHu64(mem+8) = value[1]; + + HW_LOG("Unknown Hardware write 128 at %x with value %x_%x (status=%x)\n", mem, value[1], value[0], cpuRegs.CP0.n.Status.val); + break; + } +} + +__forceinline void intcInterrupt() { + if ((cpuRegs.CP0.n.Status.val & 0x400) != 0x400) return; + + if ((psHu32(INTC_STAT)) == 0) { + DevCon::Notice("*PCSX2*: intcInterrupt already cleared"); + return; + } + if ((psHu32(INTC_STAT) & psHu32(INTC_MASK)) == 0) return; + + HW_LOG("intcInterrupt %x\n", psHu32(INTC_STAT) & psHu32(INTC_MASK)); + if(psHu32(INTC_STAT) & 0x2){ + counters[0].hold = rcntRcount(0); + counters[1].hold = rcntRcount(1); + } + + cpuException(0x400, cpuRegs.branch); +} + +__forceinline void dmacInterrupt() +{ + if ((cpuRegs.CP0.n.Status.val & 0x10807) != 0x10801) return; + + if( ((psHu16(0xe012) & psHu16(0xe010)) == 0 ) && + ( psHu16(0xe010) & 0x8000) == 0 ) return; + + if((psHu32(DMAC_CTRL) & 0x1) == 0) return; + + HW_LOG("dmacInterrupt %x\n", (psHu16(0xe012) & psHu16(0xe010) || + psHu16(0xe010) & 0x8000)); + + cpuException(0x800, cpuRegs.branch); +} + +void hwIntcIrq(int n) { + psHu32(INTC_STAT)|= 1<= msize) { + int s1 = msize - addr; + int s2 = size - s1; + + /* it does, so first copy 's1' bytes from 'data' to 'addr' */ + dst = (u8*)PSM(addr); + if (dst == NULL) return -1; + Cpu->Clear(addr, s1/4); + memcpy_fast(dst, data, s1); + + /* and second copy 's2' bytes from '&data[s1]' to 'maddr' */ + dst = (u8*)PSM(psHu32(DMAC_RBOR)); + if (dst == NULL) return -1; + Cpu->Clear(psHu32(DMAC_RBOR), s2/4); + memcpy_fast(dst, &data[s1], s2); + } else { + //u32 * tempptr, * tempptr2; + + /* it doesn't, so just copy 'size' bytes from 'data' to 'addr' */ + dst = (u8*)PSM(addr); + if (dst == NULL) return -1; + Cpu->Clear(addr, size/4); + memcpy_fast(dst, data, size); + } + + + return 0; +} + + +int hwDmacSrcChainWithStack(DMACh *dma, int id) { + u32 temp; + + switch (id) { + case 0: // Refe - Transfer Packet According to ADDR field + //dma->tadr += 16; + return 1; //End Transfer + + case 1: // CNT - Transfer QWC following the tag. + dma->madr = dma->tadr + 16; //Set MADR to QW after Tag + dma->tadr = dma->madr + (dma->qwc << 4); //Set TADR to QW following the data + return 0; + + case 2: // Next - Transfer QWC following tag. TADR = ADDR + temp = dma->madr; //Temporarily Store ADDR + dma->madr = dma->tadr + 16; //Set MADR to QW following the tag + dma->tadr = temp; //Copy temporarily stored ADDR to Tag + return 0; + + case 3: // Ref - Transfer QWC from ADDR field + case 4: // Refs - Transfer QWC from ADDR field (Stall Control) + dma->tadr += 16; //Set TADR to next tag + return 0; + + case 5: // Call - Transfer QWC following the tag, save succeeding tag + temp = dma->madr; //Temporarily Store ADDR + + dma->madr = dma->tadr + 16; //Set MADR to data following the tag + + if ((dma->chcr & 0x30) == 0x0) { //Check if ASR0 is empty + dma->asr0 = dma->madr + (dma->qwc << 4); //If yes store Succeeding tag + dma->chcr = (dma->chcr & 0xffffffcf) | 0x10; //1 Address in call stack + }else if((dma->chcr & 0x30) == 0x10){ + dma->chcr = (dma->chcr & 0xffffffcf) | 0x20; //2 Addresses in call stack + dma->asr1 = dma->madr + (dma->qwc << 4); //If no store Succeeding tag in ASR1 + }else { + SysPrintf("Call Stack Overflow (report if it fixes/breaks anything)\n"); + return 1; //Return done + } + dma->tadr = temp; //Set TADR to temporarily stored ADDR + + return 0; + + case 6: // Ret - Transfer QWC following the tag, load next tag + dma->madr = dma->tadr + 16; //Set MADR to data following the tag + + if ((dma->chcr & 0x30) == 0x20) { //If ASR1 is NOT equal to 0 (Contains address) + dma->chcr = (dma->chcr & 0xffffffcf) | 0x10; //1 Address left in call stack + dma->tadr = dma->asr1; //Read ASR1 as next tag + dma->asr1 = 0; //Clear ASR1 + } else { //If ASR1 is empty (No address held) + if((dma->chcr & 0x30) == 0x10) { //Check if ASR0 is NOT equal to 0 (Contains address) + dma->chcr = (dma->chcr & 0xffffffcf); //No addresses left in call stack + dma->tadr = dma->asr0; //Read ASR0 as next tag + dma->asr0 = 0; //Clear ASR0 + } else { //Else if ASR1 and ASR0 are empty + //dma->tadr += 16; //Clear tag address - Kills Klonoa 2 + return 1; //End Transfer + } + } + return 0; + + case 7: // End - Transfer QWC following the tag + dma->madr = dma->tadr + 16; //Set MADR to data following the tag + //comment out tadr fixes lemans + //dma->tadr = dma->madr + (dma->qwc << 4); //Dont Increment tag, breaks Soul Calibur II and III + return 1; //End Transfer + } + + return -1; +} + +int hwDmacSrcChain(DMACh *dma, int id) { + u32 temp; + + switch (id) { + case 0: // Refe - Transfer Packet According to ADDR field + //dma->tadr += 16; + return 1; //End Transfer + + case 1: // CNT - Transfer QWC following the tag. + dma->madr = dma->tadr + 16; //Set MADR to QW after Tag + dma->tadr = dma->madr + (dma->qwc << 4); //Set TADR to QW following the data + return 0; + + case 2: // Next - Transfer QWC following tag. TADR = ADDR + temp = dma->madr; //Temporarily Store ADDR + dma->madr = dma->tadr + 16; //Set MADR to QW following the tag + dma->tadr = temp; //Copy temporarily stored ADDR to Tag + return 0; + + case 3: // Ref - Transfer QWC from ADDR field + case 4: // Refs - Transfer QWC from ADDR field (Stall Control) + dma->tadr += 16; //Set TADR to next tag + return 0; + + case 7: // End - Transfer QWC following the tag + dma->madr = dma->tadr + 16; //Set MADR to data following the tag + //dma->tadr = dma->madr + (dma->qwc << 4); //Dont Increment tag, breaks Soul Calibur II and III + return 1; //End Transfer + } + + return -1; +} + +// Original hwRead/Write32 functions .. left in for now, for troubleshooting purposes. +#if 0 +mem32_t __fastcall hwRead32(u32 mem) +{ + // *Performance Warning* This function is called -A-LOT. Be weary when making changes. It + // could impact FPS significantly. + + // Optimization Note: + // Shortcut for the INTC_STAT register, which is checked *very* frequently as part of the EE's + // vsynch timers. INTC_STAT has the disadvantage of being in the 0x1000f000 case, which has + // a lot of additional registers in it, and combined with it's call frequency is a bad thing. + + if(mem == INTC_STAT) + { + // This one is checked alot, so leave it commented out unless you love 600 meg logfiles. + //HW_LOG("DMAC_STAT Read 32bit %x\n", psHu32(0xe010)); + return psHu32(INTC_STAT); + } + + const u16 masked_mem = mem & 0xffff; + + // We optimize the hw register reads by breaking them into manageable 4k chunks (for a total of + // 16 cases spanning the 64k PS2 hw register memory map). It helps also that the EE is, for + // the most part, designed so that various classes of registers are sectioned off into these + // 4k segments. + + // Notes: Breaks from the switch statement will return a standard hw memory read. + // Special case handling of reads should use "return" directly. + + switch( masked_mem>>12 ) // switch out as according to the 4k page of the access. + { + // Counters Registers + // This code uses some optimized trickery to produce more compact output. + // See below for the "reference" block to get a better idea what this code does. :) + + case 0x0: // counters 0 and 1 + case 0x1: // counters 2 and 3 + { + const uint cntidx = masked_mem >> 11; // neat trick to scale the counter HW address into 0-3 range. + switch( (masked_mem>>4) & 0xf ) + { + case 0x0: return (u16)rcntRcount(cntidx); + case 0x1: return (u16)counters[cntidx].modeval; + case 0x2: return (u16)counters[cntidx].target; + case 0x3: return (u16)counters[cntidx].hold; + } + } + +#if 0 // Counters Reference Block (original case setup) + case 0x10000000: return (u16)rcntRcount(0); + case 0x10000010: return (u16)counters[0].modeval; + case 0x10000020: return (u16)counters[0].target; + case 0x10000030: return (u16)counters[0].hold; + + case 0x10000800: return (u16)rcntRcount(1); + case 0x10000810: return (u16)counters[1].modeval; + case 0x10000820: return (u16)counters[1].target; + case 0x10000830: return (u16)counters[1].hold; + + case 0x10001000: return (u16)rcntRcount(2); + case 0x10001010: return (u16)counters[2].modeval; + case 0x10001020: return (u16)counters[2].target; + + case 0x10001800: return (u16)rcntRcount(3); + case 0x10001810: return (u16)counters[3].modeval; + case 0x10001820: return (u16)counters[3].target; +#endif + + break; + + case 0x2: return ipuRead32( mem ); + + case 0xf: + switch( (masked_mem >> 4) & 0xff ) + { + case 0x01: + HW_LOG("INTC_MASK Read32, value=0x%x", psHu32(INTC_MASK)); + break; + + case 0x13: // 0x1000f130 + case 0x26: // 0x1000f260 SBUS? + case 0x41: // 0x1000f410 + case 0x43: // MCH_RICM + return 0; + + case 0x24: // 0x1000f240: SBUS + return psHu32(0xf240) | 0xF0000102; + + case 0x44: // 0x1000f440: MCH_DRD + + if( !((psHu32(0xf430) >> 6) & 0xF) ) + { + switch ((psHu32(0xf430)>>16) & 0xFFF) + { + //MCH_RICM: x:4|SA:12|x:5|SDEV:1|SOP:4|SBC:1|SDEV:5 + + case 0x21://INIT + if(rdram_sdevid < rdram_devices) + { + rdram_sdevid++; + return 0x1F; + } + return 0; + + case 0x23://CNFGA + return 0x0D0D; //PVER=3 | MVER=16 | DBL=1 | REFBIT=5 + + case 0x24://CNFGB + //0x0110 for PSX SVER=0 | CORG=8(5x9x7) | SPT=1 | DEVTYP=0 | BYTE=0 + return 0x0090; //SVER=0 | CORG=4(5x9x6) | SPT=1 | DEVTYP=0 | BYTE=0 + + case 0x40://DEVID + return psHu32(0xf430) & 0x1F; // =SDEV + } + } + return 0; + } + break; + + /////////////////////////////////////////////////////// + // Most of the following case handlers are for developer builds only (logging). + // It'll all optimize to ziltch in public release builds. + + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + { + const char* regName = "Unknown"; + + switch( mem ) + { + case D2_CHCR: regName = "DMA2_CHCR"; break; + case D2_MADR: regName = "DMA2_MADR"; break; + case D2_QWC: regName = "DMA2_QWC"; break; + case D2_TADR: regName = "DMA2_TADDR"; break; + case D2_ASR0: regName = "DMA2_ASR0"; break; + case D2_ASR1: regName = "DMA2_ASR1"; break; + case D2_SADR: regName = "DMA2_SADDR"; break; + } + + HW_LOG( "Hardware Read32 at 0x%x (%s), value=0x%x\n", mem, regName, psHu32(mem) ); + } + break; + + case 0x0b: + if( mem == D4_CHCR ) + HW_LOG("Hardware Read32 at 0x%x (IPU1:DMA4_CHCR), value=0x%x\n", mem, psHu32(mem)); + break; + + case 0x0c: + case 0x0d: + case 0x0e: + if( mem == DMAC_STAT ) + HW_LOG("DMAC_STAT Read32, value=0x%x\n", psHu32(DMAC_STAT)); + break; + + jNO_DEFAULT; + } + + // Optimization note: We masked 'mem' earlier, so it's safe to access PS2MEM_HW directly. + // (checked disasm, and MSVC 2008 fails to optimize it on its own) + + //return psHu32(mem); + return *((u32*)&PS2MEM_HW[masked_mem]); +} + + +__forceinline void __fastcall hwWrite32(u32 mem, u32 value) +{ + + if ((mem>=0x10002000) && (mem<0x10003000)) { //IPU regs + ipuWrite32(mem,value); + return; + } + if ((mem>=0x10003800) && (mem<0x10003c00)) { + vif0Write32(mem, value); + return; + } + if ((mem>=0x10003c00) && (mem<0x10004000)) { + vif1Write32(mem, value); + return; + } + + switch (mem) { + case 0x10000000: rcntWcount(0, value); break; + case 0x10000010: rcntWmode(0, value); break; + case 0x10000020: rcntWtarget(0, value); break; + case 0x10000030: rcntWhold(0, value); break; + + case 0x10000800: rcntWcount(1, value); break; + case 0x10000810: rcntWmode(1, value); break; + case 0x10000820: rcntWtarget(1, value); break; + case 0x10000830: rcntWhold(1, value); break; + + case 0x10001000: rcntWcount(2, value); break; + case 0x10001010: rcntWmode(2, value); break; + case 0x10001020: rcntWtarget(2, value); break; + + case 0x10001800: rcntWcount(3, value); break; + case 0x10001810: rcntWmode(3, value); break; + case 0x10001820: rcntWtarget(3, value); break; + + case GIF_CTRL: + //SysPrintf("GIF_CTRL write %x\n", value); + psHu32(mem) = value & 0x8; + if (value & 0x1) gsGIFReset(); + else if( value & 8 ) psHu32(GIF_STAT) |= 8; + else psHu32(GIF_STAT) &= ~8; + return; + + case GIF_MODE: + // need to set GIF_MODE (hamster ball) + psHu32(GIF_MODE) = value; + if (value & 0x1) psHu32(GIF_STAT)|= 0x1; + else psHu32(GIF_STAT)&= ~0x1; + if (value & 0x4) psHu32(GIF_STAT)|= 0x4; + else psHu32(GIF_STAT)&= ~0x4; + break; + + case GIF_STAT: // stat is readonly + SysPrintf("Gifstat write value = %x\n", value); + return; + + case 0x10008000: // dma0 - vif0 + DMA_LOG("VIF0dma %lx\n", value); + DmaExec(dmaVIF0, mem, value); + break; +//------------------------------------------------------------------ + case 0x10009000: // dma1 - vif1 - chcr + DMA_LOG("VIF1dma CHCR %lx\n", value); + DmaExec(dmaVIF1, mem, value); + break; +#ifdef PCSX2_DEVBUILD + case 0x10009010: // dma1 - vif1 - madr + HW_LOG("VIF1dma Madr %lx\n", value); + psHu32(mem) = value;//dma1 madr + break; + case 0x10009020: // dma1 - vif1 - qwc + HW_LOG("VIF1dma QWC %lx\n", value); + psHu32(mem) = value;//dma1 qwc + break; + case 0x10009030: // dma1 - vif1 - tadr + HW_LOG("VIF1dma TADR %lx\n", value); + psHu32(mem) = value;//dma1 tadr + break; + case 0x10009040: // dma1 - vif1 - asr0 + HW_LOG("VIF1dma ASR0 %lx\n", value); + psHu32(mem) = value;//dma1 asr0 + break; + case 0x10009050: // dma1 - vif1 - asr1 + HW_LOG("VIF1dma ASR1 %lx\n", value); + psHu32(mem) = value;//dma1 asr1 + break; + case 0x10009080: // dma1 - vif1 - sadr + HW_LOG("VIF1dma SADR %lx\n", value); + psHu32(mem) = value;//dma1 sadr + break; +#endif +//------------------------------------------------------------------ + case 0x1000a000: // dma2 - gif + DMA_LOG("0x%8.8x hwWrite32: GSdma %lx\n", cpuRegs.cycle, value); + DmaExec(dmaGIF, mem, value); + break; +#ifdef PCSX2_DEVBUILD + case 0x1000a010: + psHu32(mem) = value;//dma2 madr + HW_LOG("Hardware write DMA2_MADR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000a020: + psHu32(mem) = value;//dma2 qwc + HW_LOG("Hardware write DMA2_QWC 32bit at %x with value %x\n",mem,value); + break; + case 0x1000a030: + psHu32(mem) = value;//dma2 taddr + HW_LOG("Hardware write DMA2_TADDR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000a040: + psHu32(mem) = value;//dma2 asr0 + HW_LOG("Hardware write DMA2_ASR0 32bit at %x with value %x\n",mem,value); + break; + case 0x1000a050: + psHu32(mem) = value;//dma2 asr1 + HW_LOG("Hardware write DMA2_ASR1 32bit at %x with value %x\n",mem,value); + break; + case 0x1000a080: + psHu32(mem) = value;//dma2 saddr + HW_LOG("Hardware write DMA2_SADDR 32bit at %x with value %x\n",mem,value); + break; +#endif +//------------------------------------------------------------------ + case 0x1000b000: // dma3 - fromIPU + DMA_LOG("IPU0dma %lx\n", value); + DmaExec(dmaIPU0, mem, value); + break; +//------------------------------------------------------------------ +#ifdef PCSX2_DEVBUILD + case 0x1000b010: + psHu32(mem) = value;//dma2 madr + HW_LOG("Hardware write IPU0DMA_MADR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b020: + psHu32(mem) = value;//dma2 madr + HW_LOG("Hardware write IPU0DMA_QWC 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b030: + psHu32(mem) = value;//dma2 tadr + HW_LOG("Hardware write IPU0DMA_TADR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b080: + psHu32(mem) = value;//dma2 saddr + HW_LOG("Hardware write IPU0DMA_SADDR 32bit at %x with value %x\n",mem,value); + break; +#endif +//------------------------------------------------------------------ + case 0x1000b400: // dma4 - toIPU + DMA_LOG("IPU1dma %lx\n", value); + DmaExec(dmaIPU1, mem, value); + break; +//------------------------------------------------------------------ +#ifdef PCSX2_DEVBUILD + case 0x1000b410: + psHu32(mem) = value;//dma2 madr + HW_LOG("Hardware write IPU1DMA_MADR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b420: + psHu32(mem) = value;//dma2 madr + HW_LOG("Hardware write IPU1DMA_QWC 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b430: + psHu32(mem) = value;//dma2 tadr + HW_LOG("Hardware write IPU1DMA_TADR 32bit at %x with value %x\n",mem,value); + break; + case 0x1000b480: + psHu32(mem) = value;//dma2 saddr + HW_LOG("Hardware write IPU1DMA_SADDR 32bit at %x with value %x\n",mem,value); + break; +#endif +//------------------------------------------------------------------ + case 0x1000c000: // dma5 - sif0 + DMA_LOG("SIF0dma %lx\n", value); + //if (value == 0) psxSu32(0x30) = 0x40000; + DmaExec(dmaSIF0, mem, value); + break; +//------------------------------------------------------------------ + case 0x1000c400: // dma6 - sif1 + DMA_LOG("SIF1dma %lx\n", value); + DmaExec(dmaSIF1, mem, value); + break; +#ifdef PCSX2_DEVBUILD + case 0x1000c420: // dma6 - sif1 - qwc + HW_LOG("SIF1dma QWC = %lx\n", value); + psHu32(mem) = value; + break; + case 0x1000c430: // dma6 - sif1 - tadr + HW_LOG("SIF1dma TADR = %lx\n", value); + psHu32(mem) = value; + break; +#endif +//------------------------------------------------------------------ + case 0x1000c800: // dma7 - sif2 + DMA_LOG("SIF2dma %lx\n", value); + DmaExec(dmaSIF2, mem, value); + break; +//------------------------------------------------------------------ + case 0x1000d000: // dma8 - fromSPR + DMA_LOG("fromSPRdma %lx\n", value); + DmaExec(dmaSPR0, mem, value); + break; +//------------------------------------------------------------------ + case 0x1000d400: // dma9 - toSPR + DMA_LOG("toSPRdma %lx\n", value); + DmaExec(dmaSPR1, mem, value); + break; +//------------------------------------------------------------------ + case 0x1000e000: // DMAC_CTRL + HW_LOG("DMAC_CTRL Write 32bit %x\n", value); + psHu32(0xe000) = value; + break; + + case 0x1000e010: // DMAC_STAT + HW_LOG("DMAC_STAT Write 32bit %x\n", value); + psHu16(0xe010)&= ~(value & 0xffff); // clear on 1 + psHu16(0xe012) ^= (u16)(value >> 16); + + cpuTestDMACInts(); + break; +//------------------------------------------------------------------ + case 0x1000f000: // INTC_STAT + HW_LOG("INTC_STAT Write 32bit %x\n", value); + psHu32(0xf000)&=~value; + //cpuTestINTCInts(); + break; + + case 0x1000f010: // INTC_MASK + HW_LOG("INTC_MASK Write 32bit %x\n", value); + psHu32(0xf010) ^= (u16)value; + cpuTestINTCInts(); + break; +//------------------------------------------------------------------ + case 0x1000f430://MCH_RICM: x:4|SA:12|x:5|SDEV:1|SOP:4|SBC:1|SDEV:5 + if ((((value >> 16) & 0xFFF) == 0x21) && (((value >> 6) & 0xF) == 1) && (((psHu32(0xf440) >> 7) & 1) == 0))//INIT & SRP=0 + rdram_sdevid = 0; // if SIO repeater is cleared, reset sdevid + psHu32(mem) = value & ~0x80000000; //kill the busy bit + break; + + case 0x1000f440://MCH_DRD: + psHu32(mem) = value; + break; +//------------------------------------------------------------------ + case 0x1000f590: // DMAC_ENABLEW + HW_LOG("DMAC_ENABLEW Write 32bit %lx\n", value); + psHu32(0xf590) = value; + psHu32(0xf520) = value; + return; +//------------------------------------------------------------------ + case 0x1000f200: + psHu32(mem) = value; + break; + case 0x1000f220: + psHu32(mem) |= value; + break; + case 0x1000f230: + psHu32(mem) &= ~value; + break; + case 0x1000f240: + if(!(value & 0x100)) + psHu32(mem) &= ~0x100; + else + psHu32(mem) |= 0x100; + break; + case 0x1000f260: + psHu32(mem) = 0; + break; +//------------------------------------------------------------------ + case 0x1000f130: + case 0x1000f410: + HW_LOG("Unknown Hardware write 32 at %x with value %x (%x)\n", mem, value, cpuRegs.CP0.n.Status.val); + break; +//------------------------------------------------------------------ + default: + psHu32(mem) = value; + HW_LOG("Unknown Hardware write 32 at %x with value %x (%x)\n", mem, value, cpuRegs.CP0.n.Status.val); + break; + } +} +#endif + diff --git a/pcsx2/Hw.h b/pcsx2/Hw.h new file mode 100644 index 0000000000..ee15b2288e --- /dev/null +++ b/pcsx2/Hw.h @@ -0,0 +1,421 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __HW_H__ +#define __HW_H__ + +extern u8 *psH; // hw mem + +#define psHs8(mem) (*(s8 *)&PS2MEM_HW[(mem) & 0xffff]) +#define psHs16(mem) (*(s16*)&PS2MEM_HW[(mem) & 0xffff]) +#define psHs32(mem) (*(s32*)&PS2MEM_HW[(mem) & 0xffff]) +#define psHs64(mem) (*(s64*)&PS2MEM_HW[(mem) & 0xffff]) +#define psHu8(mem) (*(u8 *)&PS2MEM_HW[(mem) & 0xffff]) +#define psHu16(mem) (*(u16*)&PS2MEM_HW[(mem) & 0xffff]) +#define psHu32(mem) (*(u32*)&PS2MEM_HW[(mem) & 0xffff]) +#define psHu64(mem) (*(u64*)&PS2MEM_HW[(mem) & 0xffff]) + +extern void CPU_INT( u32 n, s32 ecycle ); + +// VIF0 -- 0x10004000 -- psH[0x4000] +// VIF1 -- 0x10005000 -- psH[0x5000] +// GIF -- 0x10006000 -- psH[0x6000] +// IPUout -- 0x10007000 -- psH[0x7000] +// IPUin -- 0x10007010 -- psH[0x7010] + +void ReadFIFO(u32 mem, u64 *out); +void ConstReadFIFO(u32 mem); + +void WriteFIFO(u32 mem, const u64 *value); +void ConstWriteFIFO(u32 mem); + + +// +// --- DMA --- +// + +struct DMACh { + u32 chcr; + u32 null0[3]; + u32 madr; + u32 null1[3]; + u16 qwc; u16 pad; + u32 null2[3]; + u32 tadr; + u32 null3[3]; + u32 asr0; + u32 null4[3]; + u32 asr1; + u32 null5[11]; + u32 sadr; +}; + +// HW defines + +#define RCNT0_COUNT 0x10000000 +#define RCNT0_MODE 0x10000010 +#define RCNT0_TARGET 0x10000020 +#define RCNT0_HOLD 0x10000030 + +#define RCNT1_COUNT 0x10000800 +#define RCNT1_MODE 0x10000810 +#define RCNT1_TARGET 0x10000820 +#define RCNT1_HOLD 0x10000830 + +#define RCNT2_COUNT 0x10001000 +#define RCNT2_MODE 0x10001010 +#define RCNT2_TARGET 0x10001020 + +#define RCNT3_COUNT 0x10001800 +#define RCNT3_MODE 0x10001810 +#define RCNT3_TARGET 0x10001820 + +#define IPU_CMD 0x10002000 +#define IPU_CTRL 0x10002010 +#define IPU_BP 0x10002020 +#define IPU_TOP 0x10002030 + +#define GIF_CTRL 0x10003000 +#define GIF_MODE 0x10003010 +#define GIF_STAT 0x10003020 +#define GIF_TAG0 0x10003040 +#define GIF_TAG1 0x10003050 +#define GIF_TAG2 0x10003060 +#define GIF_TAG3 0x10003070 +#define GIF_CNT 0x10003080 +#define GIF_P3CNT 0x10003090 +#define GIF_P3TAG 0x100030A0 + +#define GIF_FIFO 0x10006000 + +#define IPUout_FIFO 0x10007000 +#define IPUin_FIFO 0x10007010 + +//VIF0 +#define D0_CHCR 0x10008000 +#define D0_MADR 0x10008010 +#define D0_QWC 0x10008020 + +//VIF1 +#define D1_CHCR 0x10009000 +#define D1_MADR 0x10009010 +#define D1_QWC 0x10009020 +#define D1_TADR 0x10009030 +#define D1_ASR0 0x10009040 +#define D1_ASR1 0x10009050 +#define D1_SADR 0x10009080 + +//GS +#define D2_CHCR 0x1000A000 +#define D2_MADR 0x1000A010 +#define D2_QWC 0x1000A020 +#define D2_TADR 0x1000A030 +#define D2_ASR0 0x1000A040 +#define D2_ASR1 0x1000A050 +#define D2_SADR 0x1000A080 + +//fromIPU +#define D3_CHCR 0x1000B000 +#define D3_MADR 0x1000B010 +#define D3_QWC 0x1000B020 +#define D3_TADR 0x1000B030 +#define D3_SADR 0x1000B080 + +//toIPU +#define D4_CHCR 0x1000B400 +#define D4_MADR 0x1000B410 +#define D4_QWC 0x1000B420 +#define D4_TADR 0x1000B430 +#define D4_SADR 0x1000B480 + +//SIF0 +#define D5_CHCR 0x1000C000 +#define D5_MADR 0x1000C010 +#define D5_QWC 0x1000C020 + +//SIF1 +#define D6_CHCR 0x1000C400 +#define D6_MADR 0x1000C410 +#define D6_QWC 0x1000C420 + +//SIF2 +#define D7_CHCR 0x1000C800 +#define D7_MADR 0x1000C810 +#define D7_QWC 0x1000C820 + +//fromSPR +#define D8_CHCR 0x1000D000 +#define D8_MADR 0x1000D010 +#define D8_QWC 0x1000D020 +#define D8_SADR 0x1000D080 + + +#define DMAC_CTRL 0x1000E000 +#define DMAC_STAT 0x1000E010 +#define DMAC_PCR 0x1000E020 +#define DMAC_SQWC 0x1000E030 +#define DMAC_RBSR 0x1000E040 +#define DMAC_RBOR 0x1000E050 +#define DMAC_STADR 0x1000E060 + +#define INTC_STAT 0x1000F000 +#define INTC_MASK 0x1000F010 + +#define SBUS_F220 0x1000F220 +#define SBUS_SMFLG 0x1000F230 +#define SBUS_F240 0x1000F240 + +#define DMAC_ENABLER 0x1000F520 +#define DMAC_ENABLEW 0x1000F590 + +#define SBFLG_IOPALIVE 0x10000 +#define SBFLG_IOPSYNC 0x40000 + +#define GS_PMODE 0x12000000 +#define GS_SMODE1 0x12000010 +#define GS_SMODE2 0x12000020 +#define GS_SRFSH 0x12000030 +#define GS_SYNCH1 0x12000040 +#define GS_SYNCH2 0x12000050 +#define GS_SYNCV 0x12000060 +#define GS_DISPFB1 0x12000070 +#define GS_DISPLAY1 0x12000080 +#define GS_DISPFB2 0x12000090 +#define GS_DISPLAY2 0x120000A0 +#define GS_EXTBUF 0x120000B0 +#define GS_EXTDATA 0x120000C0 +#define GS_EXTWRITE 0x120000D0 +#define GS_BGCOLOR 0x120000E0 +#define GS_CSR 0x12001000 +#define GS_IMR 0x12001010 +#define GS_BUSDIR 0x12001040 +#define GS_SIGLBLID 0x12001080 + +#define INTC_GS 0 +#define INTC_SBUS 1 +#define INTC_VBLANK_S 2 +#define INTC_VBLANK_E 3 +#define INTC_VIF0 4 +#define INTC_VIF1 5 +#define INTC_VU0 6 +#define INTC_VU1 7 +#define INTC_IPU 8 +#define INTC_TIM0 9 +#define INTC_TIM1 10 +#define INTC_TIM2 11 +#define INTC_TIM3 12 + +#define DMAC_STAT_SIS (1<<13) // stall condition +#define DMAC_STAT_MEIS (1<<14) // mfifo empty +#define DMAC_STAT_BEIS (1<<15) // bus error +#define DMAC_STAT_SIM (1<<29) // stall mask +#define DMAC_STAT_MEIM (1<<30) // mfifo mask + +#define DMAC_VIF0 0 +#define DMAC_VIF1 1 +#define DMAC_GIF 2 +#define DMAC_FROM_IPU 3 +#define DMAC_TO_IPU 4 +#define DMAC_SIF0 5 +#define DMAC_SIF1 6 +#define DMAC_SIF2 7 +#define DMAC_FROM_SPR 8 +#define DMAC_TO_SPR 9 +#define DMAC_ERROR 15 + +#define VIF0_STAT_VPS_W (1) +#define VIF0_STAT_VPS_D (2) +#define VIF0_STAT_VPS_T (3) +#define VIF0_STAT_VPS (3) +#define VIF0_STAT_VEW (1<<2) +#define VIF0_STAT_MRK (1<<6) +#define VIF0_STAT_DBF (1<<7) +#define VIF0_STAT_VSS (1<<8) +#define VIF0_STAT_VFS (1<<9) +#define VIF0_STAT_VIS (1<<10) +#define VIF0_STAT_INT (1<<11) +#define VIF0_STAT_ER0 (1<<12) +#define VIF0_STAT_ER1 (1<<13) + +#define VIF1_STAT_VPS_W (1) +#define VIF1_STAT_VPS_D (2) +#define VIF1_STAT_VPS_T (3) +#define VIF1_STAT_VPS (3) +#define VIF1_STAT_VEW (1<<2) +#define VIF1_STAT_VGW (1<<3) +#define VIF1_STAT_MRK (1<<6) +#define VIF1_STAT_DBF (1<<7) +#define VIF1_STAT_VSS (1<<8) +#define VIF1_STAT_VFS (1<<9) +#define VIF1_STAT_VIS (1<<10) +#define VIF1_STAT_INT (1<<11) +#define VIF1_STAT_ER0 (1<<12) +#define VIF1_STAT_ER1 (1<<13) +#define VIF1_STAT_FDR (1<<23) + +//DMA interrupts & masks +#define BEISintr (0x8000) +#define VIF0intr (0x10001) +#define VIF1intr (0x20002) +#define GIFintr (0x40004) +#define IPU0intr (0x80008) +#define IPU1intr (0x100010) +#define SIF0intr (0x200020) +#define SIF1intr (0x400040) +#define SIF2intr (0x800080) +#define SPR0intr (0x1000100) +#define SPR1intr (0x2000200) +#define SISintr (0x20002000) +#define MEISintr (0x40004000) + +#define DMAend(dma, num) { \ + dma->chcr &= ~0x100; \ + psHu32(DMAC_STAT)|= 1<>12 ].aPFNs == NULL ) { + SysPrintf("dmaGetAddr: memLUT PFN warning\n"); + return NULL;//p; + } + + pbase = (u8*)memLUT[ (p-PS2MEM_BASE)>>12 ].aVFNs[0]; + if( pbase != NULL ) + p = pbase + ((u32)p&0xfff); +#endif + + return p; +} + +#else + +// Note: Dma addresses are guaranteed to be aligned to 16 bytes (128 bits) +static __forceinline void *dmaGetAddr(u32 addr) { + u8 *ptr; + +// if (addr & 0xf) { DMA_LOG("*PCSX2*: DMA address not 128bit aligned: %8.8x\n", addr); } + + if (addr & 0x80000000) { // teh sux why the f00k 0xE0000000 + return (void*)&psS[addr & 0x3ff0]; + } + + ptr = (u8*)vtlb_GetPhyPtr(addr&0x1FFFFFF0); + if (ptr == NULL) { + Console::Error("*PCSX2*: DMA error: %8.8x", params addr); + return NULL; + } + return ptr; +} + +#endif + +void hwInit(); +void hwReset(); +void hwShutdown(); + +// hw read functions +extern u8 hwRead8 (u32 mem); +extern u16 hwRead16(u32 mem); +extern u64 hwRead64(u32 mem); +extern void hwRead128(u32 mem, u64 *out); + +extern mem32_t __fastcall hwRead32_page_00(u32 mem); +extern mem32_t __fastcall hwRead32_page_01(u32 mem); +extern mem32_t __fastcall hwRead32_page_02(u32 mem); +extern mem32_t __fastcall hwRead32_page_0F(u32 mem); +extern mem32_t __fastcall hwRead32_page_other(u32 mem); + +extern mem32_t __fastcall hwRead32(u32 mem); + +// hw write functions +extern void hwWrite8 (u32 mem, u8 value); +extern void hwWrite16(u32 mem, u16 value); +extern void hwWrite64(u32 mem, u64 value); +extern void hwWrite128(u32 mem, const u64 *value); + +extern void __fastcall hwWrite32_page_00( u32 mem, u32 value ); +extern void __fastcall hwWrite32_page_01( u32 mem, u32 value ); +extern void __fastcall hwWrite32_page_02( u32 mem, u32 value ); +extern void __fastcall hwWrite32_page_03( u32 mem, u32 value ); +extern void __fastcall hwWrite32_page_0B( u32 mem, u32 value ); +extern void __fastcall hwWrite32_page_0E( u32 mem, u32 value ); +extern void __fastcall hwWrite32_page_0F( u32 mem, u32 value ); +extern void __fastcall hwWrite32_page_other( u32 mem, u32 value ); + +extern void __fastcall hwWrite32(u32 mem, u32 value); + +void hwIntcIrq(int n); +void hwDmacIrq(int n); + +int hwMFIFORead(u32 addr, u8 *data, u32 size); +int hwMFIFOWrite(u32 addr, u8 *data, u32 size); + +int hwDmacSrcChainWithStack(DMACh *dma, int id); +int hwDmacSrcChain(DMACh *dma, int id); + +int hwConstRead8 (u32 x86reg, u32 mem, u32 sign); +int hwConstRead16(u32 x86reg, u32 mem, u32 sign); +int hwConstRead32(u32 x86reg, u32 mem); +void hwConstRead64(u32 mem, int mmreg); +void hwConstRead128(u32 mem, int xmmreg); + +void hwConstWrite8 (u32 mem, int mmreg); +void hwConstWrite16(u32 mem, int mmreg); +void hwConstWrite32(u32 mem, int mmreg); +void hwConstWrite64(u32 mem, int mmreg); +void hwConstWrite128(u32 mem, int xmmreg); + +extern void intcInterrupt(); +extern void dmacInterrupt(); + +extern int rdram_devices, rdram_sdevid; + +#endif /* __HW_H__ */ diff --git a/pcsx2/IPU/IPU.cpp b/pcsx2/IPU/IPU.cpp new file mode 100644 index 0000000000..1e9b8aeca1 --- /dev/null +++ b/pcsx2/IPU/IPU.cpp @@ -0,0 +1,1743 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" + +#include "IPU.h" +#include "mpeg2lib/Mpeg.h" +#include "yuv2rgb.h" +#include "coroutine.h" + +#include "Vif.h" + +using namespace std; // for min / max + +// Zero cycle IRQ schedules aren't really good, but the IPU uses them. +// Better to throw the IRQ inline: + +#define IPU_INT0_FROM() ipu0Interrupt() +//#define IPU_INT0_FROM() CPU_INT( DMAC_FROM_IPU, 0 ) + +// IPU Inline'd IRQs : Calls the IPU interrupt handlers directly instead of +// feeding them through the EE's branch test. (see IPU.H for details) + +#ifdef IPU_INLINE_IRQS +# define IPU_INT_TO( cycles ) ipu1Interrupt() +# define IPU_INT_FROM( cycles ) ipu0Interrupt() +# define IPU_FORCEINLINE +#else +# define IPU_INT_TO( cycles ) CPU_INT( DMAC_TO_IPU, cycles ) +# define IPU_INT_FROM( cycles ) CPU_INT( DMAC_FROM_IPU, cycles ) +# define IPU_FORCEINLINE __forceinline +#endif + +//IPUregisters g_ipuRegsReal; + +#define ipu0dma ((DMACh *)&PS2MEM_HW[0xb000]) +#define ipu1dma ((DMACh *)&PS2MEM_HW[0xb400]) + +#define IPU_DMA_GIFSTALL 1 +#define IPU_DMA_TIE0 2 +#define IPU_DMA_TIE1 4 +#define IPU_DMA_ACTV1 8 +#define IPU_DMA_DOTIE1 16 +#define IPU_DMA_FIREINT0 32 +#define IPU_DMA_FIREINT1 64 +#define IPU_DMA_VIFSTALL 128 + +static int g_nDMATransfer = 0; +int g_nIPU0Data = 0; // data left to transfer +u8* g_pIPU0Pointer = NULL; +int g_nCmdPos[2] = {0}, g_nCmdIndex = 0; +int ipuCurCmd = 0xffffffff; + + +int FOreadpos = 0, FOwritepos = 0; +static int FIreadpos = 0, FIwritepos = 0; +PCSX2_ALIGNED16(u32 fifo_input[32]); +PCSX2_ALIGNED16(u32 fifo_output[32]); + +void ReorderBitstream(); + +// the BP doesn't advance and returns -1 if there is no data to be read +tIPU_BP g_BP; +static coroutine_t s_routine; // used for executing BDEC/IDEC +static int s_RoutineDone = 0; +static u32 s_tempstack[0x4000]; // 64k + +void IPUCMD_WRITE(u32 val); +void IPUWorker(); +int IPU0dma(); +int IPU1dma(); + +// Color conversion stuff, the memory layout is a total hack +// convert_data_buffer is a pointer to the internal rgb struct (the first param in convert_init_t) +//char convert_data_buffer[sizeof(convert_rgb_t)]; +char convert_data_buffer[0x1C]; + +convert_init_t convert_init={convert_data_buffer, sizeof(convert_data_buffer)}; +convert_t *convert; + +// Quantization matrix +static u8 niq[64], //non-intraquant matrix + iq[64]; //intraquant matrix +u16 vqclut[16]; //clut conversion table +static u8 s_thresh[2]; //thresholds for color conversions +int coded_block_pattern=0; +PCSX2_ALIGNED16(macroblock_8 mb8); +PCSX2_ALIGNED16(macroblock_16 mb16); +PCSX2_ALIGNED16(macroblock_rgb32 rgb32); +PCSX2_ALIGNED16(macroblock_rgb16 rgb16); + +u8 indx4[16*16/2]; +u32 mpeg2_inited; //mpeg2_idct_init() must be called only once +u8 PCT[]={'r', 'I', 'P', 'B', 'D', '-', '-', '-'}; +decoder_t g_decoder; //static, only to place it in bss +decoder_t tempdec; + +extern "C" +{ + extern u8 mpeg2_scan_norm[64]; + extern u8 mpeg2_scan_alt[64]; +} + +PCSX2_ALIGNED16(u8 _readbits[80]); //local buffer (ring buffer) +u8* readbits = _readbits; // always can decrement by one 1qw + +#define SATURATE_4BITS(val) ((val)>15 ? 15 : (val)) + +void IPUProcessInterrupt() +{ + if( ipuRegs->ctrl.BUSY ) { + IPUWorker(); + } +} + +///////////////////////////////////////////////////////// +// Register accesses (run on EE thread) +int ipuInit() +{ + memzero_obj(*ipuRegs); + memzero_obj(g_BP); + + //other stuff + g_decoder.intra_quantizer_matrix =(u8*)iq; + g_decoder.non_intra_quantizer_matrix =(u8*)niq; + g_decoder.picture_structure = FRAME_PICTURE; //default: progressive...my guess:P + g_decoder.mb8 =&mb8; + g_decoder.mb16=&mb16; + g_decoder.rgb32=&rgb32; + g_decoder.rgb16=&rgb16; + g_decoder.stride=16; + + return 0; +} + +void ipuReset() +{ + memzero_obj(*ipuRegs); + g_nDMATransfer = 0; +} + +void ipuShutdown() +{ +} + +// fixme - ipuFreeze looks fairly broken. Should probably take a closer look at some point. + +void SaveState::ipuFreeze() { + IPUProcessInterrupt(); + + FreezeMem(ipuRegs, sizeof(IPUregisters)); + Freeze(g_nDMATransfer); + Freeze(FIreadpos); + Freeze(FIwritepos); + Freeze(fifo_input); + Freeze(FOreadpos); + Freeze(FOwritepos); + Freeze(fifo_output); + Freeze(g_BP); + Freeze(niq); + Freeze(iq); + Freeze(vqclut); + Freeze(s_thresh); + Freeze(coded_block_pattern); + Freeze(g_decoder); + Freeze(mpeg2_scan_norm); + Freeze(mpeg2_scan_alt); + Freeze(g_nCmdPos); + Freeze(g_nCmdIndex); + Freeze(ipuCurCmd); + + Freeze(_readbits); + + int temp = readbits-_readbits; + Freeze(temp); + + if( IsLoading() ) + { + readbits = _readbits; + + //other stuff + g_decoder.intra_quantizer_matrix =(u8*)iq; + g_decoder.non_intra_quantizer_matrix =(u8*)niq; + g_decoder.picture_structure = FRAME_PICTURE; //default: progressive...my guess:P + g_decoder.mb8 =&mb8; + g_decoder.mb16=&mb16; + g_decoder.rgb32=&rgb32; + g_decoder.rgb16=&rgb16; + g_decoder.stride=16; + + if (!mpeg2_inited){ + mpeg2_idct_init(); + convert=convert_rgb (CONVERT_RGB, 32); + convert(16, 16, 0, NULL, &convert_init); + memzero_obj(mb8.Y); + memzero_obj(mb8.Cb); + memzero_obj(mb8.Cr); + memzero_obj(mb16.Y); + memzero_obj(mb16.Cb); + memzero_obj(mb16.Cr); + mpeg2_inited=1; + } + } +} + +bool ipuCanFreeze() +{ + return ipuCurCmd == 0xffffffff; +} + +__forceinline u32 ipuRead32(u32 mem) +{ + IPUProcessInterrupt(); + + switch (mem){ + + case 0x10002010: // IPU_CTRL + ipuRegs->ctrl.IFC = g_BP.IFC; + //ipuRegs->ctrl.OFC = min(g_nIPU0Data, 8); // check if transfering to ipu0 + ipuRegs->ctrl.CBP = coded_block_pattern; + + if( !ipuRegs->ctrl.BUSY ) + IPU_LOG("Ipu read32: IPU_CTRL=0x%08X %x\n", ipuRegs->ctrl._u32, cpuRegs.pc); + + return ipuRegs->ctrl._u32; + + case 0x10002020: // IPU_BP + + ipuRegs->ipubp = g_BP.BP & 0x7f; + ipuRegs->ipubp |= g_BP.IFC<<8; + ipuRegs->ipubp |= (g_BP.FP+g_BP.bufferhasnew) << 16; + + IPU_LOG("Ipu read32: IPU_BP=0x%08X\n", *(u32*)&g_BP); + return ipuRegs->ipubp; + } + + return *(u32*)(((u8*)ipuRegs)+(mem&0xff)); // ipu repeats every 0x100 +} + +__forceinline u64 ipuRead64(u32 mem) +{ + IPUProcessInterrupt(); + +#ifdef PCSX2_DEVBUILD + if( mem == 0x10002010 ) { + SysPrintf("reading 64bit IPU ctrl\n"); + } + if( mem == 0x10002020 ) { + SysPrintf("reading 64bit IPU top\n"); + } +#endif + + switch (mem){ + case 0x10002000: // IPU_CMD + + //if(!ipuRegs->cmd.BUSY){ + if( ipuRegs->cmd.DATA&0xffffff ) + IPU_LOG("Ipu read64: IPU_CMD=BUSY=%x, DATA=%08X\n", ipuRegs->cmd.BUSY?1:0,ipuRegs->cmd.DATA); + //return *(u64*)&ipuRegs->cmd; + break; + + case 0x10002030: // IPU_TOP + IPU_LOG("Ipu read64: IPU_TOP=%x, bp = %d\n",ipuRegs->top,g_BP.BP); + + //return *(u64*)&ipuRegs->top; + break; + + default: + IPU_LOG("Ipu read64: Unknown=%x\n", mem); + break; + + } + return *(u64*)(((u8*)ipuRegs)+(mem&0xff)); +} + +void ipuSoftReset() +{ + if (!mpeg2_inited){ + mpeg2_idct_init(); + convert=convert_rgb (CONVERT_RGB, 32); + convert(16, 16, 0, NULL, &convert_init); + memzero_obj(mb8.Y); + memzero_obj(mb8.Cb); + memzero_obj(mb8.Cr); + memzero_obj(mb16.Y); + memzero_obj(mb16.Cb); + memzero_obj(mb16.Cr); + mpeg2_inited=1; + } + + FIFOto_clear(); + memzero_obj(fifo_output); + FOwritepos = 0; + FOreadpos = 0; + coded_block_pattern = 0; + + //g_nDMATransfer = 0; + + ipuRegs->ctrl._u32 = 0; + g_BP.BP = 0; + g_BP.IFC = 0; + g_BP.FP = 0; + g_BP.bufferhasnew = 0; + ipuRegs->top = 0; + g_nCmdIndex = 0; + ipuCurCmd = 0xffffffff; + g_nCmdPos[0] = 0; g_nCmdPos[1] = 0; +} + +__forceinline void ipuWrite32(u32 mem,u32 value) +{ + IPUProcessInterrupt(); + + switch (mem){ + case 0x10002000: // IPU_CMD + IPU_LOG("Ipu write32: IPU_CMD=0x%08X\n",value); + IPUCMD_WRITE(value); + break; + case 0x10002010: // IPU_CTRL + ipuRegs->ctrl._u32 = (value&0x47f30000)|(ipuRegs->ctrl._u32&0x8000ffff); + if( ipuRegs->ctrl.IDP == 3 ) { + SysPrintf("IPU Invalid Intra DC Precision, switching to 9 bits\n"); + ipuRegs->ctrl.IDP = 1; + } + if (ipuRegs->ctrl.RST & 0x1) { // RESET + ipuSoftReset(); + } + + IPU_LOG("Ipu write32: IPU_CTRL=0x%08X\n",value); + + break; + default: + IPU_LOG("Ipu write32: Unknown=%x\n", mem); + *(u32*)((u8*)ipuRegs + (mem&0xfff)) = value; + break; + } +} + +__forceinline void ipuWrite64(u32 mem, u64 value) +{ + IPUProcessInterrupt(); + + switch (mem){ + case 0x10002000: + IPU_LOG("Ipu write64: IPU_CMD=0x%08X\n",value); + IPUCMD_WRITE((u32)value); + break; + + default: + IPU_LOG("Ipu write64: Unknown=%x\n", mem); + *(u64*)((u8*)ipuRegs + (mem&0xfff)) = value; + break; + } +} + + +////////////////////////////////////////////////////// +// IPU Commands (exec on worker thread only) + +static void ipuBCLR(u32 val) { + FIFOto_clear(); + g_BP.BP = val & 0x7F; + g_BP.FP = 0; + g_BP.bufferhasnew = 0; + g_BP.IFC = 0; + ipuRegs->ctrl.BUSY = 0; + ipuRegs->cmd.BUSY = 0; + memzero_ptr<80>(readbits); + IPU_LOG("Clear IPU input FIFO. Set Bit offset=0x%X\n", g_BP.BP); +} + +static __forceinline BOOL ipuIDEC(u32 val) +{ + tIPU_CMD_IDEC idec( val ); + + + IPU_LOG("IPU IDEC command.\n"); + if (idec.FB){ IPU_LOG(" Skip %d bits.",idec.FB);} + IPU_LOG(" Quantizer step code=0x%X.",idec.QSC); + if (idec.DTD==0){ IPU_LOG(" Does not decode DT."); + }else{ IPU_LOG(" Decodes DT.");} + if (idec.SGN==0){ IPU_LOG(" No bias."); + }else{ IPU_LOG(" Bias=128.");} + if (idec.DTE==1){ IPU_LOG(" Dither Enabled.");} + if (idec.OFM==0){ IPU_LOG(" Output format is RGB32."); + }else{ IPU_LOG(" Output format is RGB16.");} + IPU_LOG("\n"); + + g_BP.BP+= idec.FB;//skip FB bits + //from IPU_CTRL + ipuRegs->ctrl.PCT = I_TYPE; //Intra DECoding;) + g_decoder.coding_type =ipuRegs->ctrl.PCT; + g_decoder.mpeg1 =ipuRegs->ctrl.MP1; + g_decoder.q_scale_type =ipuRegs->ctrl.QST; + g_decoder.intra_vlc_format=ipuRegs->ctrl.IVF; + g_decoder.scan =ipuRegs->ctrl.AS ? mpeg2_scan_alt: mpeg2_scan_norm; + g_decoder.intra_dc_precision=ipuRegs->ctrl.IDP; + //from IDEC value + g_decoder.quantizer_scale =idec.QSC; + g_decoder.frame_pred_frame_dct=!idec.DTD; + g_decoder.sgn =idec.SGN; + g_decoder.dte =idec.DTE; + g_decoder.ofm =idec.OFM; + //other stuff + g_decoder.dcr =1;//resets DC prediction value + + s_routine = so_create(mpeg2sliceIDEC, &s_RoutineDone, s_tempstack, sizeof(s_tempstack)); + assert( s_routine != NULL ); + so_call(s_routine); + if(s_RoutineDone) + s_routine = NULL; + + return s_RoutineDone; +} + +#ifdef _DEBUG +static int s_bdec=0; +#else +#define s_bdec 0 +#endif + +static __forceinline BOOL ipuBDEC(u32 val) +{ + tIPU_CMD_BDEC bdec( val ); + + IPU_LOG("IPU BDEC(macroblock decode) command %x, num: 0x%x\n",cpuRegs.pc, s_bdec); + if (bdec.FB){ IPU_LOG(" Skip 0x%X bits.", bdec.FB);} + if (bdec.MBI){ IPU_LOG(" Intra MB.");} + else{ IPU_LOG(" Non-intra MB.");} + if (bdec.DCR){ IPU_LOG(" Resets DC prediction value.");} + else{ IPU_LOG(" Doesn't reset DC prediction value.");} + if (bdec.DT){ IPU_LOG(" Use field DCT.");} + else{ IPU_LOG(" Use frame DCT.");} + IPU_LOG(" Quantizer step=0x%X\n",bdec.QSC); +#ifdef _DEBUG + s_bdec++; +#endif + + g_BP.BP+= bdec.FB;//skip FB bits + g_decoder.coding_type = I_TYPE; + g_decoder.mpeg1 =ipuRegs->ctrl.MP1; + g_decoder.q_scale_type =ipuRegs->ctrl.QST; + g_decoder.intra_vlc_format=ipuRegs->ctrl.IVF; + g_decoder.scan =ipuRegs->ctrl.AS ? mpeg2_scan_alt: mpeg2_scan_norm; + g_decoder.intra_dc_precision=ipuRegs->ctrl.IDP; + //from BDEC value + /* JayteeMaster: the quantizer (linear/non linear) depends on the q_scale_type */ + g_decoder.quantizer_scale =g_decoder.q_scale_type?non_linear_quantizer_scale [bdec.QSC]:bdec.QSC<<1; + g_decoder.macroblock_modes =bdec.DT ? DCT_TYPE_INTERLACED : 0; + g_decoder.dcr =bdec.DCR; + g_decoder.macroblock_modes|=bdec.MBI ? MACROBLOCK_INTRA : MACROBLOCK_PATTERN; + + memzero_obj(mb8); + memzero_obj(mb16); + + s_routine = so_create(mpeg2_slice, &s_RoutineDone, s_tempstack, sizeof(s_tempstack)); + assert( s_routine != NULL ); + so_call(s_routine); + if(s_RoutineDone) + s_routine = NULL; + return s_RoutineDone; +} + +static BOOL __fastcall ipuVDEC(u32 val) { + + switch( g_nCmdPos[0] ) { + case 0: + ipuRegs->cmd.DATA = 0; + if( !getBits32((u8*)&g_decoder.bitstream_buf, 0) ) + return FALSE; + + g_decoder.bitstream_bits = -16; + BigEndian(g_decoder.bitstream_buf, g_decoder.bitstream_buf); + + switch((val >> 26) & 3){ + case 0://Macroblock Address Increment + g_decoder.mpeg1 =ipuRegs->ctrl.MP1; + ipuRegs->cmd.DATA = get_macroblock_address_increment(&g_decoder); + break; + case 1://Macroblock Type //known issues: no error detected + g_decoder.frame_pred_frame_dct=1;//prevent DCT_TYPE_INTERLACED + g_decoder.coding_type =ipuRegs->ctrl.PCT; + ipuRegs->cmd.DATA=get_macroblock_modes(&g_decoder); + break; + case 2://Motion Code //known issues: no error detected + ipuRegs->cmd.DATA=get_motion_delta(&g_decoder,0); + break; + case 3://DMVector + ipuRegs->cmd.DATA=get_dmv(&g_decoder); + break; + } + + g_BP.BP+=(g_decoder.bitstream_bits+16); + if((int)g_BP.BP < 0) { + g_BP.BP += 128; + ReorderBitstream(); + } + + FillInternalBuffer(&g_BP.BP,1,0); + + ipuRegs->cmd.DATA = (ipuRegs->cmd.DATA & 0xFFFF) | ((g_decoder.bitstream_bits+16) << 16); + ipuRegs->ctrl.ECD = (ipuRegs->cmd.DATA==0); + + case 1: + if( !getBits32((u8*)&ipuRegs->top, 0) ) { + g_nCmdPos[0] = 1; + return FALSE; + } + + BigEndian(ipuRegs->top, ipuRegs->top); + + IPU_LOG("IPU VDEC command data 0x%x(0x%x). Skip 0x%X bits/Table=%d (%s), pct %d\n", + ipuRegs->cmd.DATA,ipuRegs->cmd.DATA >> 16,val & 0x3f, (val >> 26) & 3, (val >> 26) & 1 ? + ((val >> 26) & 2 ? "DMV" : "MBT") : (((val >> 26) & 2 ? "MC" : "MBAI")),ipuRegs->ctrl.PCT); + + return TRUE; + + jNO_DEFAULT + } + + return FALSE; +} + +static BOOL ipuFDEC(u32 val) +{ + if( !getBits32((u8*)&ipuRegs->cmd.DATA, 0) ) + return FALSE; + + BigEndian(ipuRegs->cmd.DATA, ipuRegs->cmd.DATA); + ipuRegs->top = ipuRegs->cmd.DATA; + + IPU_LOG("FDEC read: 0x%8.8x\n", ipuRegs->top); + + return TRUE; +} + +static __forceinline BOOL ipuSETIQ(u32 val) +{ + int i; + + if ((val >> 27) & 1){ + g_nCmdPos[0] += getBits((u8*)niq + g_nCmdPos[0], 512-8*g_nCmdPos[0], 1); // 8*8*8 + + IPU_LOG("Read non-intra quantization matrix from IPU FIFO.\n"); + for (i=0; i<8; i++){ + IPU_LOG("%02X %02X %02X %02X %02X %02X %02X %02X\n", + niq[i*8+0], niq[i*8+1], niq[i*8+2], niq[i*8+3], + niq[i*8+4], niq[i*8+5], niq[i*8+6], niq[i*8+7]); + } + }else{ + g_nCmdPos[0] += getBits((u8*)iq+8*g_nCmdPos[0], 512-8*g_nCmdPos[0], 1); + IPU_LOG("Read intra quantization matrix from IPU FIFO.\n"); + for (i=0; i<8; i++){ + IPU_LOG("%02X %02X %02X %02X %02X %02X %02X %02X\n", + iq[i*8+0], iq[i*8+1], iq[i*8+2], iq[i*8+3], + iq[i*8+4], iq[i*8+5], iq[i*8+6], iq[i*8+7]); + } + } + + return g_nCmdPos[0] == 64; +} + +static __forceinline BOOL ipuSETVQ(u32 val) +{ + g_nCmdPos[0] += getBits((u8*)vqclut+g_nCmdPos[0], 256-8*g_nCmdPos[0], 1); // 16*2*8 + + if( g_nCmdPos[0] == 32 ) + { + IPU_LOG("IPU SETVQ command.\nRead VQCLUT table from IPU FIFO.\n"); + IPU_LOG( + "%02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d " + "%02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d\n" + "%02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d " + "%02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d\n", + vqclut[0] >> 10, (vqclut[0] >> 5) & 0x1F, vqclut[0] & 0x1F, + vqclut[1] >> 10, (vqclut[1] >> 5) & 0x1F, vqclut[1] & 0x1F, + vqclut[2] >> 10, (vqclut[2] >> 5) & 0x1F, vqclut[2] & 0x1F, + vqclut[3] >> 10, (vqclut[3] >> 5) & 0x1F, vqclut[3] & 0x1F, + vqclut[4] >> 10, (vqclut[4] >> 5) & 0x1F, vqclut[4] & 0x1F, + vqclut[5] >> 10, (vqclut[5] >> 5) & 0x1F, vqclut[5] & 0x1F, + vqclut[6] >> 10, (vqclut[6] >> 5) & 0x1F, vqclut[6] & 0x1F, + vqclut[7] >> 10, (vqclut[7] >> 5) & 0x1F, vqclut[7] & 0x1F, + vqclut[8] >> 10, (vqclut[8] >> 5) & 0x1F, vqclut[8] & 0x1F, + vqclut[9] >> 10, (vqclut[9] >> 5) & 0x1F, vqclut[9] & 0x1F, + vqclut[10] >> 10, (vqclut[10] >> 5) & 0x1F, vqclut[10] & 0x1F, + vqclut[11] >> 10, (vqclut[11] >> 5) & 0x1F, vqclut[11] & 0x1F, + vqclut[12] >> 10, (vqclut[12] >> 5) & 0x1F, vqclut[12] & 0x1F, + vqclut[13] >> 10, (vqclut[13] >> 5) & 0x1F, vqclut[13] & 0x1F, + vqclut[14] >> 10, (vqclut[14] >> 5) & 0x1F, vqclut[14] & 0x1F, + vqclut[15] >> 10, (vqclut[15] >> 5) & 0x1F, vqclut[15] & 0x1F); + } + + return g_nCmdPos[0] == 32; +} + +// IPU Transfers are split into 8Qwords so we need to send ALL the data +static BOOL __fastcall ipuCSC(u32 val) +{ + tIPU_CMD_CSC csc( val ); + + IPU_LOG("IPU CSC(Colorspace conversion from YCbCr) command (%d).\n",csc.MBC); + if (csc.OFM){ IPU_LOG("Output format is RGB16. ");} + else{ IPU_LOG("Output format is RGB32. ");} + if (csc.DTE){ IPU_LOG("Dithering enabled."); } + + //SysPrintf("CSC\n"); + for (;g_nCmdIndex<(int)csc.MBC; g_nCmdIndex++){ + + if( g_nCmdPos[0] < 3072/8 ) { + g_nCmdPos[0] += getBits((u8*)&mb8+g_nCmdPos[0], 3072-8*g_nCmdPos[0], 1); + + if( g_nCmdPos[0] < 3072/8 ) + return FALSE; + + ipu_csc(&mb8, &rgb32, 0); + if (csc.OFM){ + ipu_dither2(&rgb32, &rgb16, csc.DTE); + } + } + + if (csc.OFM){ + while(g_nCmdPos[1] < 32) + { + g_nCmdPos[1] += FIFOfrom_write(((u32*)&rgb16)+4*g_nCmdPos[1], 32-g_nCmdPos[1]); + + if( g_nCmdPos[1] <= 0 ) + return FALSE; + } + } + else { + while(g_nCmdPos[1] < 64) + { + g_nCmdPos[1] += FIFOfrom_write(((u32*)&rgb32)+4*g_nCmdPos[1], 64-g_nCmdPos[1]); + + if( g_nCmdPos[1] <= 0 ) + return FALSE; + } + } + + g_nCmdPos[0] = 0; + g_nCmdPos[1] = 0; + } + + return TRUE; +} + +// Todo - Need to add the same stop and start code as CSC +static BOOL ipuPACK(u32 val) +{ + tIPU_CMD_CSC csc( val ); + + IPU_LOG("IPU PACK (Colorspace conversion from RGB32) command.\n"); + if (csc.OFM){ IPU_LOG("Output format is RGB16. ");} + else{ IPU_LOG("Output format is INDX4. ");} + if (csc.DTE){ IPU_LOG("Dithering enabled."); } + IPU_LOG("Number of macroblocks to be converted: %d\n", csc.MBC); + + for (;g_nCmdIndex<(int)csc.MBC; g_nCmdIndex++){ + + if( g_nCmdPos[0] < 512 ) { + g_nCmdPos[0] += getBits((u8*)&mb8+g_nCmdPos[0], 512-8*g_nCmdPos[0], 1); + + if( g_nCmdPos[0] < 64 ) + return FALSE; + + ipu_csc(&mb8, &rgb32, 0); + ipu_dither2(&rgb32, &rgb16, csc.DTE); + if (csc.OFM){ + ipu_vq(&rgb16, indx4); + } + } + + if (csc.OFM) { + g_nCmdPos[1] += FIFOfrom_write(((u32*)&rgb16)+4*g_nCmdPos[1], 32-g_nCmdPos[1]); + + if( g_nCmdPos[1] < 32 ) + return FALSE; + } + else { + g_nCmdPos[1] += FIFOfrom_write(((u32*)indx4)+4*g_nCmdPos[1], 8-g_nCmdPos[1]); + + if( g_nCmdPos[1] < 8 ) + return FALSE; + } + + g_nCmdPos[0] = 0; + g_nCmdPos[1] = 0; + } + + return TRUE; +} + +static void ipuSETTH(u32 val) { + s_thresh[0] = (val & 0xff); + s_thresh[1] = ((val>>16) & 0xff); + IPU_LOG("IPU SETTH (Set threshold value)command %x.\n", val&0xff00ff); +} + +/////////////////////// +// IPU Worker Thread // +/////////////////////// +#define IPU_INTERRUPT(dma) { \ + hwIntcIrq(INTC_IPU); \ +} + +void IPUCMD_WRITE(u32 val) { + + // don't process anything if currently busy + if( ipuRegs->ctrl.BUSY ) { + // wait for thread + SysPrintf("IPU BUSY!\n"); + } + + ipuRegs->ctrl.ECD = 0; + ipuRegs->ctrl.SCD = 0; //clear ECD/SCD + ipuRegs->cmd.DATA = val; + g_nCmdPos[0] = 0; + + switch (ipuRegs->cmd.CMD) { + case SCE_IPU_BCLR: + ipuBCLR(val); + IPU_INTERRUPT(DMAC_TO_IPU); + return; + + case SCE_IPU_VDEC: + + g_BP.BP+= val & 0x3F; + + // check if enough data in queue + if( ipuVDEC(val) ) { + return; + } + + ipuRegs->cmd.BUSY = 0x80000000; + ipuRegs->topbusy = 0x80000000; + + break; + + case SCE_IPU_FDEC: + IPU_LOG("IPU FDEC command. Skip 0x%X bits, FIFO 0x%X qwords, BP 0x%X, FP %d, CHCR 0x%x, %x\n", + val & 0x3f,g_BP.IFC,(int)g_BP.BP,g_BP.FP,((DMACh*)&PS2MEM_HW[0xb400])->chcr,cpuRegs.pc); + + g_BP.BP+= val & 0x3F; + + if( ipuFDEC(val) ) { + return; + } + + ipuRegs->cmd.BUSY = 0x80000000; + ipuRegs->topbusy = 0x80000000; + + break; + + case SCE_IPU_SETTH: + ipuSETTH(val); + hwIntcIrq(INTC_IPU); + return; + + case SCE_IPU_SETIQ: + IPU_LOG("IPU SETIQ command.\n"); + + if (val & 0x3f) + IPU_LOG("Skip %d bits.\n", val & 0x3f); + + g_BP.BP+= val & 0x3F; + + if( ipuSETIQ(ipuRegs->cmd.DATA) ) { + return; + } + + break; + case SCE_IPU_SETVQ: + if( ipuSETVQ(ipuRegs->cmd.DATA) ) { + return; + } + + break; + case SCE_IPU_CSC: + g_nCmdPos[1] = 0; + g_nCmdIndex = 0; + + if( ipuCSC(ipuRegs->cmd.DATA) ) { + if(ipu0dma->qwc > 0 && (ipu0dma->chcr & 0x100)) + IPU_INT0_FROM(); + return; + } + + break; + case SCE_IPU_PACK: + + g_nCmdPos[1] = 0; + g_nCmdIndex = 0; + + if( ipuPACK(ipuRegs->cmd.DATA) ) { + return; + } + + break; + + case SCE_IPU_IDEC: + if( ipuIDEC(val) ) { + // idec done, ipu0 done too + if(ipu0dma->qwc > 0 && (ipu0dma->chcr & 0x100)) + IPU_INT0_FROM(); + return; + } + + ipuRegs->topbusy = 0x80000000; + // have to resort to the thread + ipuCurCmd = val>>28; + ipuRegs->ctrl.BUSY = 1; + + return; + + case SCE_IPU_BDEC: + if( ipuBDEC(val)) { + if(ipu0dma->qwc > 0 && (ipu0dma->chcr & 0x100)) + IPU_INT0_FROM(); + if (ipuRegs->ctrl.SCD || ipuRegs->ctrl.ECD) + hwIntcIrq(INTC_IPU); + + return; + } + + ipuRegs->topbusy = 0x80000000; + ipuCurCmd = val>>28; + ipuRegs->ctrl.BUSY = 1; + + return; + } + + // have to resort to the thread + ipuCurCmd = val>>28; + ipuRegs->ctrl.BUSY = 1; + hwIntcIrq(INTC_IPU); +} + +void IPUWorker() +{ + assert( ipuRegs->ctrl.BUSY ); + + switch (ipuCurCmd) { + case SCE_IPU_VDEC: + if( !ipuVDEC(ipuRegs->cmd.DATA) ) + { + hwIntcIrq(INTC_IPU); + return; + } + + ipuRegs->cmd.BUSY = 0; + ipuRegs->topbusy = 0; + + break; + + case SCE_IPU_FDEC: + if( !ipuFDEC(ipuRegs->cmd.DATA) ) + { + hwIntcIrq(INTC_IPU); + return; + } + + ipuRegs->cmd.BUSY = 0; + ipuRegs->topbusy = 0; + + break; + + case SCE_IPU_SETIQ: + if( !ipuSETIQ(ipuRegs->cmd.DATA) ) + { + hwIntcIrq(INTC_IPU); + return; + } + + break; + case SCE_IPU_SETVQ: + if( !ipuSETVQ(ipuRegs->cmd.DATA) ) + { + hwIntcIrq(INTC_IPU); + return; + } + + break; + case SCE_IPU_CSC: + if( !ipuCSC(ipuRegs->cmd.DATA) ) + { + hwIntcIrq(INTC_IPU); + return; + } + + if(ipu0dma->qwc > 0 && (ipu0dma->chcr & 0x100)) + IPU_INT0_FROM(); + break; + case SCE_IPU_PACK: + if( !ipuPACK(ipuRegs->cmd.DATA) ) + { + hwIntcIrq(INTC_IPU); + return; + } + + break; + + case SCE_IPU_IDEC: + so_call(s_routine); + if( !s_RoutineDone ) { + hwIntcIrq(INTC_IPU); + return; + } + + ipuRegs->ctrl.OFC = 0; + ipuRegs->ctrl.BUSY = 0; + ipuRegs->topbusy = 0; + ipuRegs->cmd.BUSY = 0; + ipuCurCmd = 0xffffffff; + // CHECK!: IPU0dma remains when IDEC is done, so we need to clear it + if(ipu0dma->qwc > 0 && (ipu0dma->chcr & 0x100)) + IPU_INT0_FROM(); + + s_routine = NULL; + break; + case SCE_IPU_BDEC: + so_call(s_routine); + if(!s_RoutineDone) + { + hwIntcIrq(INTC_IPU); + return; + } + + ipuRegs->ctrl.BUSY = 0; + ipuRegs->topbusy = 0; + ipuRegs->cmd.BUSY = 0; + ipuCurCmd = 0xffffffff; + if(ipu0dma->qwc > 0 && (ipu0dma->chcr & 0x100)) + IPU_INT0_FROM(); + s_routine = NULL; + if (ipuRegs->ctrl.SCD || ipuRegs->ctrl.ECD) + hwIntcIrq(INTC_IPU); + return; + + default: + SysPrintf("Unknown IPU command: %x\n", ipuRegs->cmd.CMD); + break; + } + + // success + ipuRegs->ctrl.BUSY = 0; + ipuCurCmd = 0xffffffff; +} + +///////////////// +// Buffer reader + +// move the readbits queue +__forceinline void inc_readbits() +{ + readbits += 16; + if( readbits >= _readbits+64 ) { + + // move back + *(u64*)(_readbits) = *(u64*)(_readbits+64); + *(u64*)(_readbits+8) = *(u64*)(_readbits+72); + readbits = _readbits; + } +} + +// returns the pointer of readbits moved by 1 qword +__forceinline u8* next_readbits() +{ + return readbits + 16; +} + +// returns the pointer of readbits moved by 1 qword +u8* prev_readbits() +{ + if( readbits < _readbits+16 ) { + return _readbits+48-(readbits-_readbits); + } + + return readbits-16; +} + +void ReorderBitstream() +{ + readbits = prev_readbits(); + g_BP.FP = 2; +} + + +// IPU has a 2qword internal buffer whose status is pointed by FP. +// If FP is 1, there's 1 qword in buffer. Second qword is only loaded +// incase there are less than 32bits available in the first qword. +// \return Number of bits available (clamps at 16 bits) +u16 __fastcall FillInternalBuffer(u32 * pointer, u32 advance, u32 size) +{ + if(g_BP.FP == 0) + { + if( FIFOto_read(next_readbits()) == 0 ) + return 0; + + inc_readbits(); + g_BP.FP = 1; + } + else if(g_BP.FP < 2 && (*(int*)pointer+size) >= 128) + { + if( FIFOto_read(next_readbits()) ) + { + g_BP.FP += 1; + } + } + + if(*(int*)pointer >= 128) + { + assert( g_BP.FP >= 1); + + if(g_BP.FP > 1) + { + inc_readbits(); + } + + if(advance) + { + g_BP.FP--; + *pointer &= 127; + } + } + + return g_BP.FP >= 1 ? g_BP.FP*128-(*(int*)pointer) : 0; +} + +// whenever reading fractions of bytes. The low bits always come from the next byte +// while the high bits come from the current byte +u8 __fastcall getBits32(u8 *address, u32 advance) +{ + register u32 mask, shift=0; + u8* readpos; + + // Check if the current BP has exceeded or reached the limit of 128 + if( FillInternalBuffer(&g_BP.BP,1,32) < 32 ) + return 0; + + readpos = readbits+(int)g_BP.BP/8; + + if (g_BP.BP & 7) { + + shift = g_BP.BP&7; + mask = (0xff>>shift); + mask = mask|(mask<<8)|(mask<<16)|(mask<<24); + + *(u32*)address = ((~mask&*(u32*)(readpos+1))>>(8-shift)) | (((mask)&*(u32*)readpos)<>shift); + mask = mask|(mask<<8); + + *(u16*)address = ((~mask&*(u16*)(readpos+1))>>(8-shift)) | (((mask)&*(u16*)readpos)<>shift); + + *(u8*)address = (((~mask)&readpos[1])>>(8-shift)) | (((mask)&*readpos)<> (pointer&7)) << + (8-howmuch-(pointer&7))) & 0xFF; + mask &= readbits[((pointer)>>3)]; + mask >>= 8-howmuch-(pointer&7); + pointer += howmuch; + size -= howmuch; + shift -= howmuch; + *address |= mask << shift; + } + + ++address; + } + else + { + u8* readmem; + while (size) + { + if( FillInternalBuffer(&pointer,advance,8) < 8 ) + { + if(advance) + { + g_BP.BP = pointer; + } + return address-oldaddr; + } + + howmuch = min(128-pointer, size); + size -= howmuch; + + readmem = readbits + (pointer>>3); + pointer += howmuch; + howmuch >>= 3; + + while(howmuch >= 4) { + *(u32*)address = *(u32*)readmem; + howmuch -= 4; + address += 4; + readmem += 4; + } + + switch(howmuch) { + case 3: address[2] = readmem[2]; + case 2: address[1] = readmem[1]; + case 1: address[0] = readmem[0]; + case 0: + break; + + jNO_DEFAULT + } + + address += howmuch; + } + } + + // If not advance then reset the Reading buffer value + if(advance) + { + g_BP.BP = pointer; + + } + else readbits = oldbits; // restore the last pointer + + return address-oldaddr; +} + +///////////////////// CORE FUNCTIONS ///////////////// +void Skl_YUV_To_RGB32_MMX(u8 *RGB, const int Dst_BpS, const u8 *Y, const u8 *U, const u8 *V, + const int Src_BpS, const int Width, const int Height); + +void __fastcall ipu_csc(macroblock_8 *mb8, macroblock_rgb32 *rgb32, int sgn){ + int i; + u8* p = (u8*)rgb32; + + convert_init.start(convert_init.id, (u8*)rgb32, CONVERT_FRAME); + convert_init.copy(convert_init.id, (u8*)mb8->Y, (u8*)mb8->Cr, (u8*)mb8->Cb, 0); + + if( s_thresh[0] > 0 ) { + for(i = 0; i < 64*4; i++, p += 4) { + if( p[0] < s_thresh[0] && p[1] < s_thresh[0] && p[2] < s_thresh[0] ) + *(u32*)p = 0; + else + p[3] = (p[1] < s_thresh[1] && p[2] < s_thresh[1] && p[3] < s_thresh[1]) ? 0x40 : 0x80; + } + } + else if( s_thresh[1] > 0 ) { + for(i = 0; i < 64*4; i++, p += 4) { + p[3] = (p[1] < s_thresh[1] && p[2] < s_thresh[1] && p[3] < s_thresh[1]) ? 0x40 : 0x80; + } + } + else { + for(i = 0; i < 64; i++, p += 16) { + p[3] = p[7] = p[11] = p[15] = 0x80; + } + } +} + +void __fastcall ipu_dither2(const macroblock_rgb32* rgb32, macroblock_rgb16 *rgb16, int dte) +{ + int i, j; + for(i = 0; i < 16; ++i) { + for(j = 0; j < 16; ++j) { + rgb16->c[i][j].r = rgb32->c[i][j].r>>3; + rgb16->c[i][j].g = rgb32->c[i][j].g>>3; + rgb16->c[i][j].b = rgb32->c[i][j].b>>3; + rgb16->c[i][j].a = rgb32->c[i][j].a==0x40; + } + } +} + +void __fastcall ipu_dither(macroblock_8 *mb8, macroblock_rgb16 *rgb16, int dte) +{ + //SysPrintf("IPU: Dither not implemented"); +} + +void __fastcall ipu_vq(macroblock_rgb16 *rgb16, u8* indx4){ + Console::Error("IPU: VQ not implemented"); +} + +void __fastcall ipu_copy(const macroblock_8 *mb8, macroblock_16 *mb16) { + const u8 *s=(const u8*)mb8; + s16 *d=(s16*)mb16; + int i; + for (i=0; i< 256; i++) *d++ = *s++; //Y bias - 16 + for (i=0; i< 64; i++) *d++ = *s++; //Cr bias - 128 + for (i=0; i< 64; i++) *d++ = *s++; //Cb bias - 128 + /*for(i = 0; i < 384/(16*6); ++i, s += 16*4, d += 16*4) { + __m128i r0, r1, r2, r3, r4, r5, r6, r7; + + r0 = _mm_load_si128((__m128i*)s); + r2 = _mm_load_si128((__m128i*)s+1); + r4 = _mm_load_si128((__m128i*)s+2); + r6 = _mm_load_si128((__m128i*)s+3); + + // signed shift + r1 = _mm_srai_epi16(_mm_unpackhi_epi8(r0, r0), 8); + r0 = _mm_srai_epi16(_mm_unpacklo_epi8(r0, r0), 8); + r3 = _mm_srai_epi16(_mm_unpackhi_epi8(r2, r2), 8); + r2 = _mm_srai_epi16(_mm_unpacklo_epi8(r2, r2), 8); + r5 = _mm_srai_epi16(_mm_unpackhi_epi8(r4, r4), 8); + r4 = _mm_srai_epi16(_mm_unpacklo_epi8(r4, r4), 8); + r7 = _mm_srai_epi16(_mm_unpackhi_epi8(r6, r6), 8); + r6 = _mm_srai_epi16(_mm_unpacklo_epi8(r6, r6), 8); + + _mm_store_si128((__m128i*)d, r0); + _mm_store_si128((__m128i*)d+1, r1); + _mm_store_si128((__m128i*)d+2, r2); + _mm_store_si128((__m128i*)d+3, r3); + _mm_store_si128((__m128i*)d+4, r4); + _mm_store_si128((__m128i*)d+5, r5); + _mm_store_si128((__m128i*)d+6, r6); + _mm_store_si128((__m128i*)d+7, r7); + }*/ +} + +///////////////////// IPU DMA //////////////////////// +void FIFOto_clear() +{ + //assert( g_BP.IFC == 0 ); + memzero_obj(fifo_input); + g_BP.IFC = 0; + ipuRegs->ctrl.IFC = 0; + FIreadpos = 0; + FIwritepos = 0; +} + +int FIFOto_read(void *value) +{ + // wait until enough data + if( g_BP.IFC == 0 ) { + if( IPU1dma() == 0 ) + return 0; + + assert( g_BP.IFC > 0 ); + } + + // transfer 1 qword, split into two transfers + ((u32*)value)[0] = fifo_input[FIreadpos]; fifo_input[FIreadpos] = 0; + ((u32*)value)[1] = fifo_input[FIreadpos+1]; fifo_input[FIreadpos+1] = 0; + ((u32*)value)[2] = fifo_input[FIreadpos+2]; fifo_input[FIreadpos+2] = 0; + ((u32*)value)[3] = fifo_input[FIreadpos+3]; fifo_input[FIreadpos+3] = 0; + FIreadpos = (FIreadpos + 4) & 31; + g_BP.IFC--; + return 1; +} + +int FIFOto_write(u32* pMem, int size) +{ + int transsize; + int firsttrans = min(size, 8-(int)g_BP.IFC); + + g_BP.IFC+=firsttrans; + transsize = firsttrans; + + while(transsize-- > 0) { + fifo_input[FIwritepos] = pMem[0]; + fifo_input[FIwritepos+1] = pMem[1]; + fifo_input[FIwritepos+2] = pMem[2]; + fifo_input[FIwritepos+3] = pMem[3]; + FIwritepos = (FIwritepos+4)&31; + pMem +=4; + } + + return firsttrans; +} + +#define IPU1chain() { \ + if(ipu1dma->qwc > 0) \ + { \ + int qwc = ipu1dma->qwc; \ + pMem = (u32*)dmaGetAddr(ipu1dma->madr); \ + if (pMem == NULL) { Console::Error("ipu1dma NULL!"); return totalqwc; } \ + qwc = FIFOto_write(pMem, qwc); \ + ipu1dma->madr += qwc<< 4; \ + ipu1dma->qwc -= qwc; \ + totalqwc += qwc; \ + if( ipu1dma->qwc > 0 ) { \ + g_nDMATransfer |= IPU_DMA_ACTV1; \ + return totalqwc; \ + } \ + } \ +} + +int IPU1dma() +{ + u32 *ptag, *pMem; + int done=0; + int ipu1cycles = 0; + int totalqwc = 0; + + assert( !(ipu1dma->chcr&0x40) ); + + if( !(ipu1dma->chcr & 0x100) || (cpuRegs.interrupt & (1<qwc > 0 ) { + IPU1chain(); + + if ((ipu1dma->chcr & 0x80) && (g_nDMATransfer&IPU_DMA_DOTIE1)) { //Check TIE bit of CHCR and IRQ bit of tag + SysPrintf("IPU1 TIE\n"); + + IPU_INT_TO(totalqwc*BIAS); + g_nDMATransfer &= ~(IPU_DMA_ACTV1|IPU_DMA_DOTIE1); + g_nDMATransfer |= IPU_DMA_TIE1; + return totalqwc; + } + + g_nDMATransfer &= ~(IPU_DMA_ACTV1|IPU_DMA_DOTIE1); + + if( (ipu1dma->chcr&0xc) == 0 ) { + IPU_INT_TO(totalqwc*BIAS); + return totalqwc; + } + else { + u32 tag = ipu1dma->chcr; // upper bits describe current tag + + if ((ipu1dma->chcr & 0x80) && (tag&0x80000000)) { + ptag = (u32*)dmaGetAddr(ipu1dma->tadr); + + switch(tag&0x70000000) { + case 0x00000000: ipu1dma->tadr += 16; break; + case 0x70000000: ipu1dma->tadr = ipu1dma->madr; break; + } + + ipu1dma->chcr = (ipu1dma->chcr & 0xFFFF) | ( (*ptag) & 0xFFFF0000 ); + IPU_LOG("IPU dmaIrq Set\n"); + IPU_INT_TO(totalqwc*BIAS); + g_nDMATransfer |= IPU_DMA_TIE1; + return totalqwc; + } + + switch( tag&0x70000000 ) + { + case 0x00000000: + ipu1dma->tadr += 16; + IPU_INT_TO((1+totalqwc)*BIAS); + return totalqwc; + + case 0x70000000: + ipu1dma->tadr = ipu1dma->madr; + IPU_INT_TO((1+totalqwc)*BIAS); + return totalqwc; + } + } + } + + if ((ipu1dma->chcr & 0xc) == 0 && ipu1dma->qwc == 0) { // Normal Mode + //SysPrintf("ipu1 normal empty qwc?\n"); + return totalqwc; + } + + // Transfer Dn_QWC from Dn_MADR to GIF + + if ((ipu1dma->chcr & 0xc) == 0 || ipu1dma->qwc > 0) { // Normal Mode + IPU_LOG("dmaIPU1 Normal size=%d, addr=%lx, fifosize=%x\n", + ipu1dma->qwc, ipu1dma->madr, 8 - g_BP.IFC); + IPU1chain(); + IPU_INT_TO((ipu1cycles+totalqwc)*BIAS); + return totalqwc; + } + else + { + // Chain Mode + //while (done == 0) { // Loop while Dn_CHCR.STR is 1 + ptag = (u32*)dmaGetAddr(ipu1dma->tadr); //Set memory pointer to TADR + if (ptag == NULL) { //Is ptag empty? + SysPrintf("IPU1 BUSERR\n"); + ipu1dma->chcr = ( ipu1dma->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + psHu32(DMAC_STAT)|= 1<<15; //If yes, set BEIS (BUSERR) in DMAC_STAT register + return totalqwc; + } + ipu1cycles+=1; // Add 1 cycles from the QW read for the tag + + ipu1dma->chcr = ( ipu1dma->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + ipu1dma->qwc = (u16)ptag[0]; //QWC set to lower 16bits of the tag + //ipu1dma->madr = ptag[1]; //MADR = ADDR field + + //done = hwDmacSrcChainWithStack(ipu1dma, id); + switch(ptag[0] & 0x70000000) { + case 0x00000000: // refe + // do not change tadr + ipu1dma->madr = ptag[1]; + done = 1; + break; + + case 0x10000000: // cnt + ipu1dma->madr = ipu1dma->tadr + 16; + // Set the taddr to the next tag + ipu1dma->tadr += 16 + (ipu1dma->qwc << 4); + break; + + case 0x20000000: // next + ipu1dma->madr = ipu1dma->tadr + 16; + ipu1dma->tadr = ptag[1]; + break; + + case 0x30000000: // ref + ipu1dma->madr = ptag[1]; + ipu1dma->tadr += 16; + break; + + case 0x70000000: // end + // do not change tadr + ipu1dma->madr = ipu1dma->tadr + 16; + done = 1; + break; + + default: + Console::Error("IPU ERROR: different transfer mode!, Please report to PCSX2 Team\n"); + break; + } + + IPU_LOG("dmaIPU1 dmaChain %8.8x_%8.8x size=%d, addr=%lx, fifosize=%x\n", + ptag[1], ptag[0], ipu1dma->qwc, ipu1dma->madr, 8 - g_BP.IFC); + + if( (ipu1dma->chcr & 0x80) && ptag[0] & 0x80000000 ) + g_nDMATransfer |= IPU_DMA_DOTIE1; + else + g_nDMATransfer &= ~IPU_DMA_DOTIE1; + + //Britney Dance beat does a blank NEXT tag, for some odd reason the fix doesnt work if after IPU1Chain O_o + if(ipu1dma->qwc == 0 && done == 0 && !(g_nDMATransfer & IPU_DMA_DOTIE1)) IPU1dma(); + + IPU1chain(); + + if ((ipu1dma->chcr & 0x80) && (ptag[0]&0x80000000) && ipu1dma->qwc == 0) { //Check TIE bit of CHCR and IRQ bit of tag + SysPrintf("IPU1 TIE\n"); + + if( done ) { + ptag = (u32*)dmaGetAddr(ipu1dma->tadr); + + switch(ptag[0]&0x70000000) { + case 0x00000000: ipu1dma->tadr += 16; break; + case 0x70000000: ipu1dma->tadr = ipu1dma->madr; break; + } + + ipu1dma->chcr = (ipu1dma->chcr & 0xFFFF) | ( (*ptag) & 0xFFFF0000 ); + } + + IPU_INT_TO(ipu1cycles+totalqwc*BIAS); + g_nDMATransfer |= IPU_DMA_TIE1; + return totalqwc; + } + + if(ipu1dma->qwc == 0) + { + switch( ptag[0]&0x70000000 ) + { + case 0x00000000: + ipu1dma->tadr += 16; + break; + + case 0x70000000: + ipu1dma->tadr = ipu1dma->madr; + break; + } + } + } + + IPU_INT_TO((ipu1cycles+totalqwc)*BIAS); + return totalqwc; +} + + +int FIFOfrom_write(u32 *value,int size) +{ + int transsize; + int firsttrans; + + if((int)ipuRegs->ctrl.OFC >= 8) + { + if(IPU0dma() == 0) + { +// ipuRegs->ctrl.OFC = 0; + } + } + + transsize = min(size,8-(int)ipuRegs->ctrl.OFC); + firsttrans = transsize; + + while(transsize-- > 0) { + fifo_output[FOwritepos] = ((u32*)value)[0]; + fifo_output[FOwritepos+1] = ((u32*)value)[1]; + fifo_output[FOwritepos+2] = ((u32*)value)[2]; + fifo_output[FOwritepos+3] = ((u32*)value)[3]; + FOwritepos = (FOwritepos+4)&31; + value += 4; + } + + ipuRegs->ctrl.OFC+=firsttrans; + IPU0dma(); + //SysPrintf("Written %d qwords, %d\n",firsttrans,ipuRegs->ctrl.OFC); + + return firsttrans; +} + +void FIFOfrom_read(void *value,int size) +{ + ipuRegs->ctrl.OFC -= size; + while(size > 0) + { + // transfer 1 qword, split into two transfers + ((u32*)value)[0] = fifo_output[FOreadpos]; fifo_output[FOreadpos] = 0; + ((u32*)value)[1] = fifo_output[FOreadpos+1]; fifo_output[FOreadpos+1] = 0; + ((u32*)value)[2] = fifo_output[FOreadpos+2]; fifo_output[FOreadpos+2] = 0; + ((u32*)value)[3] = fifo_output[FOreadpos+3]; fifo_output[FOreadpos+3] = 0; + value = (u32*)value + 4; + FOreadpos = (FOreadpos + 4) & 31; + size--; + } +} + + +void FIFOfrom_readsingle(void *value) +{ + if(ipuRegs->ctrl.OFC > 0) + { + ipuRegs->ctrl.OFC --; + // transfer 1 qword, split into two transfers + ((u32*)value)[0] = fifo_output[FOreadpos]; fifo_output[FOreadpos] = 0; + ((u32*)value)[1] = fifo_output[FOreadpos+1]; fifo_output[FOreadpos+1] = 0; + ((u32*)value)[2] = fifo_output[FOreadpos+2]; fifo_output[FOreadpos+2] = 0; + ((u32*)value)[3] = fifo_output[FOreadpos+3]; fifo_output[FOreadpos+3] = 0; + FOreadpos = (FOreadpos + 4) & 31; + } +} + +int IPU0dma() +{ + int readsize; + void* pMem; + + //int qwc = ipu0dma->qwc; + //u32 chcr = ipu0dma->chcr; + + if( !(ipu0dma->chcr & 0x100) || (cpuRegs.interrupt & (1<qwc == 0) + return 0; + + assert( !(ipu0dma->chcr&0x40) ); + + IPU_LOG("dmaIPU0 chcr = %lx, madr = %lx, qwc = %lx\n", + ipu0dma->chcr, ipu0dma->madr, ipu0dma->qwc); + + assert((ipu0dma->chcr & 0xC) == 0 ); + pMem = (u32*)dmaGetAddr(ipu0dma->madr); + readsize = min(ipu0dma->qwc, (u16)ipuRegs->ctrl.OFC); + FIFOfrom_read(pMem,readsize); + ipu0dma->madr += readsize<< 4; + ipu0dma->qwc -= readsize; // note: qwc is u16 + if(ipu0dma->qwc == 0) { + if ((psHu32(DMAC_CTRL) & 0x30) == 0x30) { // STS == fromIPU + psHu32(DMAC_STADR) = ipu0dma->madr; + switch (psHu32(DMAC_CTRL) & 0xC0) { + case 0x80: // GIF + g_nDMATransfer |= IPU_DMA_GIFSTALL; + break; + case 0x40: // VIF + g_nDMATransfer |= IPU_DMA_VIFSTALL; + break; + } + } + IPU_INT_FROM( readsize*BIAS ); + } + + return readsize; +} + +void dmaIPU0() // fromIPU +{ + if( ipuRegs->ctrl.BUSY ) + IPUWorker(); +} + +void dmaIPU1() // toIPU +{ + //g_nDMATransfer &= ~(IPU_DMA_ACTV1|IPU_DMA_DOTIE1); + IPU1dma(); + if( ipuRegs->ctrl.BUSY ) + IPUWorker(); +} + +extern void GIFdma(); + +void ipu0Interrupt() { + IPU_LOG("ipu0Interrupt: %x\n", cpuRegs.cycle); + + if( g_nDMATransfer & IPU_DMA_FIREINT0 ) { + hwIntcIrq(INTC_IPU); + g_nDMATransfer &= ~IPU_DMA_FIREINT0; + } + + if( g_nDMATransfer & IPU_DMA_GIFSTALL ) { + // gif + g_nDMATransfer &= ~IPU_DMA_GIFSTALL; + if(((DMACh*)&PS2MEM_HW[0xA000])->chcr & 0x100) GIFdma(); + } + + if( g_nDMATransfer & IPU_DMA_VIFSTALL ) { + // vif + g_nDMATransfer &= ~IPU_DMA_VIFSTALL; + if(((DMACh*)&PS2MEM_HW[0x9000])->chcr & 0x100)dmaVIF1(); + } + + if( g_nDMATransfer & IPU_DMA_TIE0 ) { + g_nDMATransfer &= ~IPU_DMA_TIE0; + } + + ipu0dma->chcr &= ~0x100; + + hwDmacIrq(DMAC_FROM_IPU); +} + +IPU_FORCEINLINE void ipu1Interrupt() { + IPU_LOG("ipu1Interrupt %x:\n", cpuRegs.cycle); + + if( g_nDMATransfer & IPU_DMA_FIREINT1 ) { + hwIntcIrq(INTC_IPU); + g_nDMATransfer &= ~IPU_DMA_FIREINT1; + } + + if( g_nDMATransfer & IPU_DMA_TIE1 ) { + g_nDMATransfer &= ~IPU_DMA_TIE1; + }else + ipu1dma->chcr &= ~0x100; + + hwDmacIrq(DMAC_TO_IPU); +} diff --git a/pcsx2/IPU/IPU.h b/pcsx2/IPU/IPU.h new file mode 100644 index 0000000000..acb0f36c88 --- /dev/null +++ b/pcsx2/IPU/IPU.h @@ -0,0 +1,265 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IPU_H__ +#define __IPU_H__ + +// IPU_INLINE_IRQS +// Scheduling ints into the future is a purist approach to emulation, and +// is mostly cosmetic since the emulator itself performs all actions instantly +// (as far as the emulated CPU is concerned). In some cases this can actually +// cause more sync problems than it supposedly solves, due to accumulated +// delays incurred by the recompiler's low cycle update rate and also Pcsx2 +// failing to properly handle pre-emptive DMA/IRQs or cpu exceptions. + +// Uncomment the following line to enable inline IRQs for the IPU. Tests show +// that it doesn't have any effect on compatibility or audio/video sync, and it +// speeds up movie playback by some 6-8%. But it lacks the purist touch, so it's +// not enabled by default. + +//#define IPU_INLINE_IRQS + + +#ifdef _MSC_VER +#pragma pack(1) +#endif + +// +// Bitfield Structure +// +union tIPU_CMD { + struct { + u32 OPTION : 28; // VDEC decoded value + u32 CMD : 4; // last command + u32 _BUSY; + }; + struct { + u32 DATA; + u32 BUSY; + }; +}; + +#define IPU_CTRL_IFC_M (0x0f<< 0) +#define IPU_CTRL_OFC_M (0x0f<< 4) +#define IPU_CTRL_CBP_M (0x3f<< 8) +#define IPU_CTRL_ECD_M (0x01<<14) +#define IPU_CTRL_SCD_M (0x01<<15) +#define IPU_CTRL_IDP_M (0x03<<16) +#define IPU_CTRL_AS_M (0x01<<20) +#define IPU_CTRL_IVF_M (0x01<<21) +#define IPU_CTRL_QST_M (0x01<<22) +#define IPU_CTRL_MP1_M (0x01<<23) +#define IPU_CTRL_PCT_M (0x07<<24) +#define IPU_CTRL_RST_M (0x01<<30) +#define IPU_CTRL_BUSY_M (0x01<<31) + +#define IPU_CTRL_IFC_O ( 0) +#define IPU_CTRL_OFC_O ( 4) +#define IPU_CTRL_CBP_O ( 8) +#define IPU_CTRL_ECD_O (14) +#define IPU_CTRL_SCD_O (15) +#define IPU_CTRL_IDP_O (16) +#define IPU_CTRL_AS_O (20) +#define IPU_CTRL_IVF_O (21) +#define IPU_CTRL_QST_O (22) +#define IPU_CTRL_MP1_O (23) +#define IPU_CTRL_PCT_O (24) +#define IPU_CTRL_RST_O (30) +#define IPU_CTRL_BUSY_O (31) + + +// +// Bitfield Structure +// +union tIPU_CTRL { + struct { + u32 IFC : 4; // Input FIFO counter + u32 OFC : 4; // Output FIFO counter + u32 CBP : 6; // Coded block pattern + u32 ECD : 1; // Error code pattern + u32 SCD : 1; // Start code detected + u32 IDP : 2; // Intra DC precision + u32 resv0 : 2; + u32 AS : 1; // Alternate scan + u32 IVF : 1; // Intra VLC format + u32 QST : 1; // Q scale step + u32 MP1 : 1; // MPEG1 bit strea + u32 PCT : 3; // Picture Type + u32 resv1 : 3; + u32 RST : 1; // Reset + u32 BUSY : 1; // Busy + }; + u32 _u32; +}; + +#define IPU_BP_BP_M (0x7f<< 0) +#define IPU_BP_IFC_M (0x0f<< 8) +#define IPU_BP_FP_M (0x03<<16) + +#define IPU_BP_BP_O ( 0) +#define IPU_BP_IFC_O ( 8) +#define IPU_BP_FP_O (16) + + +// +// Bitfield Structure +// +struct tIPU_BP { + u32 BP; // Bit stream point + u16 IFC; // Input FIFO counter + u8 FP; // FIFO point + u8 bufferhasnew; +}; + +#ifdef _WIN32 +#pragma pack() +#endif + +union tIPU_CMD_IDEC +{ + struct + { + u32 FB : 6; + u32 UN2 :10; + u32 QSC : 5; + u32 UN1 : 3; + u32 DTD : 1; + u32 SGN : 1; + u32 DTE : 1; + u32 OFM : 1; + u32 cmd : 4; + }; + + u32 value; + + tIPU_CMD_IDEC( u32 val ) : value( val ) + { + } +}; + +union tIPU_CMD_BDEC +{ + struct + { + u32 FB : 6; + u32 UN2 :10; + u32 QSC : 5; + u32 UN1 : 4; + u32 DT : 1; + u32 DCR : 1; + u32 MBI : 1; + u32 cmd : 4; + }; + u32 value; + + tIPU_CMD_BDEC( u32 val ) : value( val ) + { + } +}; + +union tIPU_CMD_CSC +{ + struct + { + u32 MBC :11; + u32 UN2 :15; + u32 DTE : 1; + u32 OFM : 1; + u32 cmd : 4; + }; + u32 value; + + tIPU_CMD_CSC( u32 val ) : value( val ) + { + } +}; + +enum SCE_IPU +{ + SCE_IPU_BCLR = 0x0 +, SCE_IPU_IDEC +, SCE_IPU_BDEC +, SCE_IPU_VDEC +, SCE_IPU_FDEC +, SCE_IPU_SETIQ +, SCE_IPU_SETVQ +, SCE_IPU_CSC +, SCE_IPU_PACK +, SCE_IPU_SETTH +}; + +struct IPUregisters { + tIPU_CMD cmd; + u32 dummy0[2]; + tIPU_CTRL ctrl; + u32 dummy1[3]; + u32 ipubp; + u32 dummy2[3]; + u32 top; + u32 topbusy; + u32 dummy3[2]; +}; + +#define ipuRegs ((IPUregisters*)(PS2MEM_HW+0x2000)) + +extern tIPU_BP g_BP; +extern int coded_block_pattern; +extern int g_nIPU0Data; // or 0x80000000 whenever transferring +extern u8* g_pIPU0Pointer; + + +void dmaIPU0(); +void dmaIPU1(); + +int ipuInit(); +void ipuReset(); +void ipuShutdown(); +int ipuFreeze(gzFile f, int Mode); +bool ipuCanFreeze(); + + +extern u32 ipuRead32(u32 mem); +extern u64 ipuRead64(u32 mem); +extern void ipuWrite32(u32 mem,u32 value); +extern void ipuWrite64(u32 mem,u64 value); + +int ipuConstRead32(u32 x86reg, u32 mem); +void ipuConstRead64(u32 mem, int mmreg); +void ipuConstWrite32(u32 mem, int mmreg); +void ipuConstWrite64(u32 mem, int mmreg); + +extern void IPUCMD_WRITE(u32 val); +extern void ipuSoftReset(); +extern void IPUProcessInterrupt(); +extern void ipu0Interrupt(); +extern void ipu1Interrupt(); + +extern u16 __fastcall FillInternalBuffer(u32 * pointer, u32 advance, u32 size); +extern u8 __fastcall getBits32(u8 *address, u32 advance); +extern u8 __fastcall getBits16(u8 *address, u32 advance); +extern u8 __fastcall getBits8(u8 *address, u32 advance); +extern int __fastcall getBits(u8 *address, u32 size, u32 advance); + +// returns number of qw read +int FIFOfrom_write(u32 * value, int size); +void FIFOfrom_read(void *value,int size); +int FIFOto_read(void *value); +int FIFOto_write(u32* pMem, int size); +void FIFOto_clear(); + +#endif diff --git a/pcsx2/IPU/Makefile.am b/pcsx2/IPU/Makefile.am new file mode 100644 index 0000000000..87ee9d061f --- /dev/null +++ b/pcsx2/IPU/Makefile.am @@ -0,0 +1,10 @@ +INCLUDES = -I@srcdir@/../ -I@srcdir@/../x86 -I@srcdir@/../common/ +noinst_LIBRARIES = libIPU.a + +libIPU_a_SOURCES = IPU.cpp yuv2rgb.cpp coroutine.cpp \ +IPU.h yuv2rgb.h coroutine.h \ +acoroutine.S + +SUBDIRS = mpeg2lib + +#libIPU_a_LIBADD = mpeg2lib/libmpeg2IPU.a \ No newline at end of file diff --git a/pcsx2/IPU/acoroutine.S b/pcsx2/IPU/acoroutine.S new file mode 100644 index 0000000000..d81b0e02dc --- /dev/null +++ b/pcsx2/IPU/acoroutine.S @@ -0,0 +1,181 @@ +.intel_syntax + +.extern g_pCurrentRoutine + +#ifdef __x86_64__ + +#define FUNC_OFFSET 0 +#define STACK_OFFSET 8 +#define RBX_OFFSET 16 +#define RBP_OFFSET 24 +#define R12_OFFSET 32 +#define R13_OFFSET 40 +#define R14_OFFSET 48 +#define R15_OFFSET 56 +#define DATA_OFFSET 64 +#define RESTORE_OFFSET 72 + +.globl so_call +so_call: + test dword ptr [%rdi+RESTORE_OFFSET], 1 + jnz so_call_RestoreRegs + mov [%rdi+RBP_OFFSET], %rbp + mov [%rdi+RBX_OFFSET], %rbx + mov [%rdi+R12_OFFSET], %r12 + mov [%rdi+R13_OFFSET], %r13 + mov [%rdi+R14_OFFSET], %r14 + mov [%rdi+R15_OFFSET], %r15 + mov dword ptr [%rdi+RESTORE_OFFSET], 1 + jmp so_call_CallFn +so_call_RestoreRegs: + // have to load and save at the same time + mov %rax, [%rdi+RBP_OFFSET] + mov %rcx, [%rdi+RBX_OFFSET] + mov %rdx, [%rdi+R12_OFFSET] + mov [%rdi+RBP_OFFSET], %rbp + mov [%rdi+RBX_OFFSET], %rbx + mov [%rdi+R12_OFFSET], %r12 + mov %rbp, %rax + mov %rbx, %rcx + mov %r12, %rdx + mov %rax, [%rdi+R13_OFFSET] + mov %rcx, [%rdi+R14_OFFSET] + mov %rdx, [%rdi+R15_OFFSET] + mov [%rdi+R13_OFFSET], %r13 + mov [%rdi+R14_OFFSET], %r14 + mov [%rdi+R15_OFFSET], %r15 + mov %r13, %rax + mov %r14, %rcx + mov %r15, %rdx + +so_call_CallFn: + mov [g_pCurrentRoutine], %rdi + + // swap the stack + mov %rax, [%rdi+STACK_OFFSET] + mov [%rdi+STACK_OFFSET], %rsp + mov %rsp, %rax + mov %rax, [%rdi+FUNC_OFFSET] + mov %rdi, [%rdi+DATA_OFFSET] + + jmp %rax + +.globl so_resume +so_resume: + mov %rdi, [g_pCurrentRoutine] + mov %rax, [%rdi+RBP_OFFSET] + mov %rcx, [%rdi+RBX_OFFSET] + mov %rdx, [%rdi+R12_OFFSET] + mov [%rdi+RBP_OFFSET], %rbp + mov [%rdi+RBX_OFFSET], %rbx + mov [%rdi+R12_OFFSET], %r12 + mov %rbp, %rax + mov %rbx, %rcx + mov %r12, %rdx + mov %rax, [%rdi+R13_OFFSET] + mov %rcx, [%rdi+R14_OFFSET] + mov %rdx, [%rdi+R15_OFFSET] + mov [%rdi+R13_OFFSET], %r13 + mov [%rdi+R14_OFFSET], %r14 + mov [%rdi+R15_OFFSET], %r15 + mov %r13, %rax + mov %r14, %rcx + mov %r15, %rdx + + // put the return address in pcalladdr + mov %rsi, [%rsp] + mov [%rdi], %rsi + add %rsp, 8 // remove the return address + + // swap stack pointers + mov %rax, [%rdi+STACK_OFFSET] + mov [%rdi+STACK_OFFSET], %rsp + mov %rsp, %rax + + ret + +.globl so_exit +so_exit: + mov %rdi, [g_pCurrentRoutine] + mov %rsp, [%rdi+STACK_OFFSET] + mov %rbp, [%rdi+RBP_OFFSET] + mov %rbx, [%rdi+RBX_OFFSET] + mov %r12, [%rdi+R12_OFFSET] + mov %r13, [%rdi+R13_OFFSET] + mov %r14, [%rdi+R14_OFFSET] + mov %r15, [%rdi+R15_OFFSET] + ret +#else + +.globl so_call +so_call: + mov %eax, dword ptr [%esp+4] + test dword ptr [%eax+24], 1 + jnz RestoreRegs + mov [%eax+8], %ebx + mov [%eax+12], %esi + mov [%eax+16], %edi + mov [%eax+20], %ebp + mov dword ptr [%eax+24], 1 + jmp CallFn +RestoreRegs: + // have to load and save at the same time + mov %ecx, [%eax+8] + mov %edx, [%eax+12] + mov [%eax+8], %ebx + mov [%eax+12], %esi + mov %ebx, %ecx + mov %esi, %edx + mov %ecx, [%eax+16] + mov %edx, [%eax+20] + mov [%eax+16], %edi + mov [%eax+20], %ebp + mov %edi, %ecx + mov %ebp, %edx + +CallFn: + mov [g_pCurrentRoutine], %eax + mov %ecx, %esp + mov %esp, [%eax+4] + mov [%eax+4], %ecx + + jmp dword ptr [%eax] + +.globl so_resume +so_resume: + mov %eax, [g_pCurrentRoutine] + mov %ecx, [%eax+8] + mov %edx, [%eax+12] + mov [%eax+8], %ebx + mov [%eax+12], %esi + mov %ebx, %ecx + mov %esi, %edx + mov %ecx, [%eax+16] + mov %edx, [%eax+20] + mov [%eax+16], %edi + mov [%eax+20], %ebp + mov %edi, %ecx + mov %ebp, %edx + + // put the return address in pcalladdr + mov %ecx, [%esp] + mov [%eax], %ecx + add %esp, 4 // remove the return address + + // swap stack pointers + mov %ecx, [%eax+4] + mov [%eax+4], %esp + mov %esp, %ecx + ret + +.globl so_exit +so_exit: + mov %eax, [g_pCurrentRoutine] + mov %esp, [%eax+4] + mov %ebx, [%eax+8] + mov %esi, [%eax+12] + mov %edi, [%eax+16] + mov %ebp, [%eax+20] + ret + +#endif diff --git a/pcsx2/IPU/acoroutine.asm b/pcsx2/IPU/acoroutine.asm new file mode 100644 index 0000000000..d81a5f12d2 --- /dev/null +++ b/pcsx2/IPU/acoroutine.asm @@ -0,0 +1,140 @@ +; Pcsx2 - Pc Ps2 Emulator +; Copyright (C) 2002-2008 Pcsx2 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 + +;; x86-64 coroutine fucntions +extern g_pCurrentRoutine:ptr + +.code + +so_call proc public + test dword ptr [rcx+88], 1 + jnz so_call_RestoreRegs + mov [rcx+24], rbp + mov [rcx+16], rbx + mov [rcx+32], r12 + mov [rcx+40], r13 + mov [rcx+48], r14 + mov [rcx+56], r15 + mov [rcx+64], rsi + mov [rcx+72], rdi + mov dword ptr [rcx+88], 1 + jmp so_call_CallFn +so_call_RestoreRegs: + ;; have to load and save at the same time + ;; rbp, rbx, r12 + mov rax, [rcx+24] + mov r8, [rcx+16] + mov rdx, [rcx+32] + mov [rcx+24], rbp + mov [rcx+16], rbx + mov [rcx+32], r12 + mov rbp, rax + mov rbx, r8 + mov r12, rdx + ;; r13, r14, r15 + mov rax, [rcx+40] + mov r8, [rcx+48] + mov rdx, [rcx+56] + mov [rcx+40], r13 + mov [rcx+48], r14 + mov [rcx+56], r15 + mov r13, rax + mov r14, r8 + mov r15, rdx + + ;; rsi, rdi + mov rax, [rcx+64] + mov rdx, [rcx+72] + mov [rcx+64], rsi + mov [rcx+72], rdi + mov rsi, rax + mov rdi, rdx + +so_call_CallFn: + mov [g_pCurrentRoutine], rcx + + ;; swap the stack + mov rax, [rcx+8] + mov [rcx+8], rsp + mov rsp, rax + mov rax, [rcx+0] + mov rcx, [rcx+80] + + jmp rax + +so_call endp + +; so_resume +so_resume proc public + ;; rbp, rbx, r12 + mov rcx, [g_pCurrentRoutine] + mov rax, [rcx+24] + mov r8, [rcx+16] + mov rdx, [rcx+32] + mov [rcx+24], rbp + mov [rcx+16], rbx + mov [rcx+32], r12 + mov rbp, rax + mov rbx, r8 + mov r12, rdx + ;; r13, r14, r15 + mov rax, [rcx+40] + mov r8, [rcx+48] + mov rdx, [rcx+56] + mov [rcx+40], r13 + mov [rcx+48], r14 + mov [rcx+56], r15 + mov r13, rax + mov r14, r8 + mov r15, rdx + ;; rsi, rdi + mov rax, [rcx+64] + mov rdx, [rcx+72] + mov [rcx+64], rsi + mov [rcx+72], rdi + mov rsi, rax + mov rdi, rdx + + ;; put the return address in pcalladdr + mov rax, [rsp] + mov [rcx], rax + add rsp, 8 ;; remove the return address + + ;; swap stack pointers + mov rax, [rcx+8] + mov [rcx+8], rsp + mov rsp, rax + + ret + +so_resume endp + +so_exit proc public + mov rcx, [g_pCurrentRoutine] + mov rsp, [rcx+8] + mov rbp, [rcx+24] + mov rbx, [rcx+16] + mov r12, [rcx+32] + mov r13, [rcx+40] + mov r14, [rcx+48] + mov r15, [rcx+56] + mov rsi, [rcx+64] + mov rdi, [rcx+72] + ret +so_exit endp + +end \ No newline at end of file diff --git a/pcsx2/IPU/coroutine.cpp b/pcsx2/IPU/coroutine.cpp new file mode 100644 index 0000000000..aeb290b1ff --- /dev/null +++ b/pcsx2/IPU/coroutine.cpp @@ -0,0 +1,158 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "coroutine.h" + +struct coroutine { + void* pcalladdr; + void *pcurstack; + + uptr storeebx, storeesi, storeedi, storeebp; + + int restore; // if nonzero, restore the registers + int alloc; + //struct s_coroutine *caller; + //struct s_coroutine *restarget; + +}; + +#define CO_STK_ALIGN 256 +#define CO_STK_COROSIZE ((sizeof(coroutine) + CO_STK_ALIGN - 1) & ~(CO_STK_ALIGN - 1)) +#define CO_MIN_SIZE (4 * 1024) + +coroutine* g_pCurrentRoutine; + +coroutine_t so_create(void (*func)(void *), void *data, void *stack, int size) +{ + void* endstack; + int alloc = 0; // r = CO_STK_COROSIZE; + coroutine *co; + + if ((size &= ~(sizeof(long) - 1)) < CO_MIN_SIZE) + return NULL; + if (!stack) { + size = (size + sizeof(coroutine) + CO_STK_ALIGN - 1) & ~(CO_STK_ALIGN - 1); + stack = malloc(size); + if (!stack) + return NULL; + alloc = size; + } + endstack = (char*)stack + size - 64; + co = (coroutine*)stack; + stack = (char *) stack + CO_STK_COROSIZE; + *(void**)endstack = NULL; + *(void**)((char*)endstack+sizeof(void*)) = data; + co->alloc = alloc; + co->pcalladdr = (void*)func; + co->pcurstack = endstack; + return co; +} + +void so_delete(coroutine_t coro) +{ + coroutine *co = (coroutine *) coro; + assert( co != NULL ); + if (co->alloc) + free(co); +} + +// see acoroutines.S and acoroutines.asm for other asm implementations +#if defined(_MSC_VER) + +__declspec(naked) void so_call(coroutine_t coro) +{ + __asm { + mov eax, dword ptr [esp+4] + test dword ptr [eax+24], 1 + jnz RestoreRegs + mov [eax+8], ebx + mov [eax+12], esi + mov [eax+16], edi + mov [eax+20], ebp + mov dword ptr [eax+24], 1 + jmp CallFn +RestoreRegs: + // have to load and save at the same time + mov ecx, [eax+8] + mov edx, [eax+12] + mov [eax+8], ebx + mov [eax+12], esi + mov ebx, ecx + mov esi, edx + mov ecx, [eax+16] + mov edx, [eax+20] + mov [eax+16], edi + mov [eax+20], ebp + mov edi, ecx + mov ebp, edx + +CallFn: + mov [g_pCurrentRoutine], eax + mov ecx, esp + mov esp, [eax+4] + mov [eax+4], ecx + + jmp dword ptr [eax] + } +} + +__declspec(naked) void so_resume(void) +{ + __asm { + mov eax, [g_pCurrentRoutine] + mov ecx, [eax+8] + mov edx, [eax+12] + mov [eax+8], ebx + mov [eax+12], esi + mov ebx, ecx + mov esi, edx + mov ecx, [eax+16] + mov edx, [eax+20] + mov [eax+16], edi + mov [eax+20], ebp + mov edi, ecx + mov ebp, edx + + // put the return address in pcalladdr + mov ecx, [esp] + mov [eax], ecx + add esp, 4 // remove the return address + + // swap stack pointers + mov ecx, [eax+4] + mov [eax+4], esp + mov esp, ecx + ret + } +} + +__declspec(naked) void so_exit(void) +{ + __asm { + mov eax, [g_pCurrentRoutine] + mov esp, [eax+4] + mov ebx, [eax+8] + mov esi, [eax+12] + mov edi, [eax+16] + mov ebp, [eax+20] + ret + } +} +#endif diff --git a/pcsx2/IPU/coroutine.h b/pcsx2/IPU/coroutine.h new file mode 100644 index 0000000000..e9edfd956f --- /dev/null +++ b/pcsx2/IPU/coroutine.h @@ -0,0 +1,37 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef PCSX2_COROUTINE_LIB +#define PCSX2_COROUTINE_LIB + +// low level coroutine library +typedef void *coroutine_t; + +coroutine_t so_create(void (*func)(void *), void *data, void *stack, int size); +void so_delete(coroutine_t coro); + +#ifdef __LINUX__ +extern "C" { +#endif +void so_call(coroutine_t coro); +void so_resume(void); +void so_exit(void); +#ifdef __LINUX__ +} +#endif +#endif diff --git a/pcsx2/IPU/idct_mmx.obj b/pcsx2/IPU/idct_mmx.obj new file mode 100644 index 0000000000000000000000000000000000000000..9f7d3136085bf4a54addc94ef8f77313e62d4963 GIT binary patch literal 11510 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 HNu%)(hlkEg literal 0 HcmV?d00001 diff --git a/pcsx2/IPU/mpeg2lib/Idct.cpp b/pcsx2/IPU/mpeg2lib/Idct.cpp new file mode 100644 index 0000000000..a285e74ad0 --- /dev/null +++ b/pcsx2/IPU/mpeg2lib/Idct.cpp @@ -0,0 +1,307 @@ +/* + * idct.c + * Copyright (C) 2000-2002 Michel Lespinasse + * Copyright (C) 1999-2000 Aaron Holtzman + * Modified by Florin for PCSX2 emu + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec 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. + * + * mpeg2dec 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "IPU.h" + +#define W1 2841 /* 2048*sqrt (2)*cos (1*pi/16) */ +#define W2 2676 /* 2048*sqrt (2)*cos (2*pi/16) */ +#define W3 2408 /* 2048*sqrt (2)*cos (3*pi/16) */ +#define W5 1609 /* 2048*sqrt (2)*cos (5*pi/16) */ +#define W6 1108 /* 2048*sqrt (2)*cos (6*pi/16) */ +#define W7 565 /* 2048*sqrt (2)*cos (7*pi/16) */ +#define clp(val,res) res = (val < 0) ? 0 : ((val > 255) ? 255 : val); +#define clp2(val,res) res = (val < -255) ? -255 : ((val > 255) ? 255 : val); + +/* idct main entry point */ +void (__fastcall *mpeg2_idct_copy) (s16 * block, u8 * dest, int stride); +/* JayteeMaster: changed dest to 16 bit signed */ +void (__fastcall *mpeg2_idct_add) (int last, s16 * block, + /*u8*/s16 * dest, int stride); + +/* + * In legal streams, the IDCT output should be between -384 and +384. + * In corrupted streams, it is possible to force the IDCT output to go + * to +-3826 - this is the worst case for a column IDCT where the + * column inputs are 16-bit values. + */ +static u8 clip_lut[1024]; +#define CLIP(i) ((clip_lut+384)[(i)]) + +#if 0 +#define BUTTERFLY(t0,t1,W0,W1,d0,d1) \ +do { \ + t0 = W0*d0 + W1*d1; \ + t1 = W0*d1 - W1*d0; \ +} while (0) +#else +#define BUTTERFLY(t0,t1,W0,W1,d0,d1) \ +do { \ + int tmp = W0 * (d0 + d1); \ + t0 = tmp + (W1 - W0) * d1; \ + t1 = tmp - (W1 + W0) * d0; \ +} while (0) +#endif + +static __forceinline void idct_row (s16 * const block) +{ + int d0, d1, d2, d3; + int a0, a1, a2, a3, b0, b1, b2, b3; + int t0, t1, t2, t3; + + /* shortcut */ + if (!(block[1] | ((s32 *)block)[1] | ((s32 *)block)[2] | + ((s32 *)block)[3])) { + u32 tmp = (u16) (block[0] << 3); + tmp |= tmp << 16; + ((s32 *)block)[0] = tmp; + ((s32 *)block)[1] = tmp; + ((s32 *)block)[2] = tmp; + ((s32 *)block)[3] = tmp; + return; + } + + d0 = (block[0] << 11) + 128; + d1 = block[1]; + d2 = block[2] << 11; + d3 = block[3]; + t0 = d0 + d2; + t1 = d0 - d2; + BUTTERFLY (t2, t3, W6, W2, d3, d1); + a0 = t0 + t2; + a1 = t1 + t3; + a2 = t1 - t3; + a3 = t0 - t2; + + d0 = block[4]; + d1 = block[5]; + d2 = block[6]; + d3 = block[7]; + BUTTERFLY (t0, t1, W7, W1, d3, d0); + BUTTERFLY (t2, t3, W3, W5, d1, d2); + b0 = t0 + t2; + b3 = t1 + t3; + t0 -= t2; + t1 -= t3; + b1 = ((t0 + t1) * 181) >> 8; + b2 = ((t0 - t1) * 181) >> 8; + + block[0] = (a0 + b0) >> 8; + block[1] = (a1 + b1) >> 8; + block[2] = (a2 + b2) >> 8; + block[3] = (a3 + b3) >> 8; + block[4] = (a3 - b3) >> 8; + block[5] = (a2 - b2) >> 8; + block[6] = (a1 - b1) >> 8; + block[7] = (a0 - b0) >> 8; +} + +static __forceinline void idct_col (s16 * const block) +{ + int d0, d1, d2, d3; + int a0, a1, a2, a3, b0, b1, b2, b3; + int t0, t1, t2, t3; + + d0 = (block[8*0] << 11) + 65536; + d1 = block[8*1]; + d2 = block[8*2] << 11; + d3 = block[8*3]; + t0 = d0 + d2; + t1 = d0 - d2; + BUTTERFLY (t2, t3, W6, W2, d3, d1); + a0 = t0 + t2; + a1 = t1 + t3; + a2 = t1 - t3; + a3 = t0 - t2; + + d0 = block[8*4]; + d1 = block[8*5]; + d2 = block[8*6]; + d3 = block[8*7]; + BUTTERFLY (t0, t1, W7, W1, d3, d0); + BUTTERFLY (t2, t3, W3, W5, d1, d2); + b0 = t0 + t2; + b3 = t1 + t3; + t0 = (t0 - t2) >> 8; + t1 = (t1 - t3) >> 8; + b1 = (t0 + t1) * 181; + b2 = (t0 - t1) * 181; + + block[8*0] = (a0 + b0) >> 17; + block[8*1] = (a1 + b1) >> 17; + block[8*2] = (a2 + b2) >> 17; + block[8*3] = (a3 + b3) >> 17; + block[8*4] = (a3 - b3) >> 17; + block[8*5] = (a2 - b2) >> 17; + block[8*6] = (a1 - b1) >> 17; + block[8*7] = (a0 - b0) >> 17; +} + +static void __fastcall mpeg2_idct_copy_c (s16 * block, u8 * dest, + const int stride) +{ + int i; + + for (i = 0; i < 8; i++) + idct_row (block + 8 * i); + for (i = 0; i < 8; i++) + idct_col (block + i); + do { + dest[0] = CLIP (block[0]); + dest[1] = CLIP (block[1]); + dest[2] = CLIP (block[2]); + dest[3] = CLIP (block[3]); + dest[4] = CLIP (block[4]); + dest[5] = CLIP (block[5]); + dest[6] = CLIP (block[6]); + dest[7] = CLIP (block[7]); + + block[0] = 0; block[1] = 0; block[2] = 0; block[3] = 0; + block[4] = 0; block[5] = 0; block[6] = 0; block[7] = 0; + + dest += stride; + block += 8; + } while (--i); +} + +/* JayteeMaster: changed dest to 16 bit signed */ +static void __fastcall mpeg2_idct_add_c (const int last, s16 * block, + /*u8*/s16 * dest, const int stride) +{ + int i; + + if (last != 129 || (block[0] & 7) == 4) { + for (i = 0; i < 8; i++) + idct_row (block + 8 * i); + for (i = 0; i < 8; i++) + idct_col (block + i); + do { + dest[0] = block[0]; + dest[1] = block[1]; + dest[2] = block[2]; + dest[3] = block[3]; + dest[4] = block[4]; + dest[5] = block[5]; + dest[6] = block[6]; + dest[7] = block[7]; + + block[0] = 0; block[1] = 0; block[2] = 0; block[3] = 0; + block[4] = 0; block[5] = 0; block[6] = 0; block[7] = 0; + + dest += stride; + block += 8; + } while (--i); + } else { + int DC; + + DC = (block[0] + 4) >> 3; + block[0] = block[63] = 0; + i = 8; + do { + dest[0] = DC; + dest[1] = DC; + dest[2] = DC; + dest[3] = DC; + dest[4] = DC; + dest[5] = DC; + dest[6] = DC; + dest[7] = DC; + dest += stride; + } while (--i); + } +} + +extern "C" +{ +u8 mpeg2_scan_norm[64] = { + /* Zig-Zag scan pattern */ + 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 +}; + +u8 mpeg2_scan_alt[64] = { + /* Alternate scan pattern */ + 0, 8, 16, 24, 1, 9, 2, 10, 17, 25, 32, 40, 48, 56, 57, 49, + 41, 33, 26, 18, 3, 11, 4, 12, 19, 27, 34, 42, 50, 58, 35, 43, + 51, 59, 20, 28, 5, 13, 6, 14, 21, 29, 36, 44, 52, 60, 37, 45, + 53, 61, 22, 30, 7, 15, 23, 31, 38, 46, 54, 62, 39, 47, 55, 63 +}; +}; + +/* idct_mmx.c */ +void mpeg2_idct_copy_mmxext (s16 * block, u8 * dest, int stride); +void mpeg2_idct_add_mmxext (int last, s16 * block, + s16 * dest, int stride); +void mpeg2_idct_copy_mmx (s16 * block, u8 * dest, int stride); +void mpeg2_idct_add_mmx (int last, s16 * block, + s16 * dest, int stride); +void mpeg2_idct_mmx_init (void); + +void mpeg2_idct_init() +{ +#if !defined(_MSC_VER) || _MSC_VER < 1400 // ignore vc2005 and beyond + int i, j; + +/* if(hasMultimediaExtensions == 1) + { + mpeg2_idct_copy = mpeg2_idct_copy_mmx; + mpeg2_idct_add = mpeg2_idct_add_mmx; + mpeg2_idct_mmx_init (); + }else if(hasMultimediaExtensionsExt == 1) + { + mpeg2_idct_copy = mpeg2_idct_copy_mmxext; + mpeg2_idct_add = mpeg2_idct_add_mmxext; + mpeg2_idct_mmx_init (); + }else*/ + { + mpeg2_idct_copy = mpeg2_idct_copy_c; + mpeg2_idct_add = mpeg2_idct_add_c; + for (i = -384; i < 640; i++) + clip_lut[i+384] = (i < 0) ? 0 : ((i > 255) ? 255 : i); + for (i = 0; i < 64; i++) { + j = mpeg2_scan_norm[i]; + mpeg2_scan_norm[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); + j = mpeg2_scan_alt[i]; + mpeg2_scan_alt[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); + } + } + +#else //blah vcnet2005 idiocity :D + int i,j; + mpeg2_idct_copy = mpeg2_idct_copy_c; + mpeg2_idct_add = mpeg2_idct_add_c; + for (i = -384; i < 640; i++) + clip_lut[i+384] = (i < 0) ? 0 : ((i > 255) ? 255 : i); + for (i = 0; i < 64; i++) { + j = mpeg2_scan_norm[i]; + mpeg2_scan_norm[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); + j = mpeg2_scan_alt[i]; + mpeg2_scan_alt[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); + } +#endif +} diff --git a/pcsx2/IPU/mpeg2lib/Makefile.am b/pcsx2/IPU/mpeg2lib/Makefile.am new file mode 100644 index 0000000000..dbde829629 --- /dev/null +++ b/pcsx2/IPU/mpeg2lib/Makefile.am @@ -0,0 +1,4 @@ +INCLUDES = -I@srcdir@/../ -I@srcdir@/../../ -I@srcdir@/../../common/ +noinst_LIBRARIES = libmpeg2IPU.a + +libmpeg2IPU_a_SOURCES = Idct.cpp Mpeg.cpp Mpeg.h Vlc.h \ No newline at end of file diff --git a/pcsx2/IPU/mpeg2lib/Mpeg.cpp b/pcsx2/IPU/mpeg2lib/Mpeg.cpp new file mode 100644 index 0000000000..4df1793c70 --- /dev/null +++ b/pcsx2/IPU/mpeg2lib/Mpeg.cpp @@ -0,0 +1,1368 @@ +/* + * Mpeg.c + * Copyright (C) 2000-2002 Michel Lespinasse + * Copyright (C) 1999-2000 Aaron Holtzman + * Modified by Florin for PCSX2 emu + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec 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. + * + * mpeg2dec 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 + */ + +// [Air] Note: many functions in this module are large and only used once, so they +// have been forced to inline since it won't bloat the program and gets rid of +// some call overhead. + +#include "PrecompiledHeader.h" + +#include "Common.h" +#include "IPU.h" +#include "Mpeg.h" +#include "Vlc.h" +#include "coroutine.h" + +int non_linear_quantizer_scale [] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 10, 12, 14, 16, 18, 20, 22, + 24, 28, 32, 36, 40, 44, 48, 52, + 56, 64, 72, 80, 88, 96, 104, 112 +}; + +/* Bitstream and buffer needs to be reallocated in order for successful + reading of the old data. Here the old data stored in the 2nd slot + of the internal buffer is copied to 1st slot, and the new data read + into 1st slot is copied to the 2nd slot. Which will later be copied + back to the 1st slot when 128bits have been read. +*/ +extern void ReorderBitstream(); + +int get_macroblock_modes (decoder_t * const decoder) +{ +#define bit_buf (decoder->bitstream_buf) +#define bits (decoder->bitstream_bits) +#define bit_ptr (decoder->bitstream_ptr) + int macroblock_modes; + const MBtab * tab; + + switch (decoder->coding_type) { + case I_TYPE: + + macroblock_modes = UBITS (bit_buf, 2); + + if( macroblock_modes == 0 ) + return 0; // error + + tab = MB_I + (macroblock_modes>>1); + DUMPBITS (bit_buf, bits, tab->len); + macroblock_modes = tab->modes; + + if ((! (decoder->frame_pred_frame_dct)) && + (decoder->picture_structure == FRAME_PICTURE)) { + macroblock_modes |= UBITS (bit_buf, 1) * DCT_TYPE_INTERLACED; + DUMPBITS (bit_buf, bits, 1); + } + + return macroblock_modes; + + case P_TYPE: + + macroblock_modes = UBITS (bit_buf, 6); + + if( macroblock_modes == 0 ) + return 0; // error + + tab = MB_P + (macroblock_modes>>1); + DUMPBITS (bit_buf, bits, tab->len); + macroblock_modes = tab->modes; + + if (decoder->picture_structure != FRAME_PICTURE) { + if (macroblock_modes & MACROBLOCK_MOTION_FORWARD) { + macroblock_modes |= UBITS (bit_buf, 2) * MOTION_TYPE_BASE; + DUMPBITS (bit_buf, bits, 2); + } + return macroblock_modes; + } else if (decoder->frame_pred_frame_dct) { + if (macroblock_modes & MACROBLOCK_MOTION_FORWARD) + macroblock_modes |= MC_FRAME; + return macroblock_modes; + } else { + if (macroblock_modes & MACROBLOCK_MOTION_FORWARD) { + macroblock_modes |= UBITS (bit_buf, 2) * MOTION_TYPE_BASE; + DUMPBITS (bit_buf, bits, 2); + } + if (macroblock_modes & (MACROBLOCK_INTRA | MACROBLOCK_PATTERN)) { + macroblock_modes |= UBITS (bit_buf, 1) * DCT_TYPE_INTERLACED; + DUMPBITS (bit_buf, bits, 1); + } + return macroblock_modes; + } + + case B_TYPE: + + macroblock_modes = UBITS (bit_buf, 6); + + if( macroblock_modes == 0 ) + return 0; // error + tab = MB_B + macroblock_modes; + DUMPBITS (bit_buf, bits, tab->len); + macroblock_modes = tab->modes; + + if (decoder->picture_structure != FRAME_PICTURE) { + if (! (macroblock_modes & MACROBLOCK_INTRA)) { + macroblock_modes |= UBITS (bit_buf, 2) * MOTION_TYPE_BASE; + DUMPBITS (bit_buf, bits, 2); + } + return macroblock_modes; + } else if (decoder->frame_pred_frame_dct) { + /* if (! (macroblock_modes & MACROBLOCK_INTRA)) */ + macroblock_modes |= MC_FRAME; + return macroblock_modes; + } else { + if (macroblock_modes & MACROBLOCK_INTRA) + goto intra; + macroblock_modes |= UBITS (bit_buf, 2) * MOTION_TYPE_BASE; + DUMPBITS (bit_buf, bits, 2); + if (macroblock_modes & (MACROBLOCK_INTRA | MACROBLOCK_PATTERN)) { + intra: + macroblock_modes |= UBITS (bit_buf, 1) * DCT_TYPE_INTERLACED; + DUMPBITS (bit_buf, bits, 1); + } + return macroblock_modes; + } + + case D_TYPE: + + macroblock_modes = UBITS (bit_buf, 1); + if( macroblock_modes == 0 ) + return 0; // error + DUMPBITS (bit_buf, bits, 1); + return MACROBLOCK_INTRA; + + default: + return 0; + } +#undef bit_buf +#undef bits +#undef bit_ptr +} + +static __forceinline int get_quantizer_scale (decoder_t * const decoder) +{ + int quantizer_scale_code; + + quantizer_scale_code = UBITS (decoder->bitstream_buf, 5); + DUMPBITS (decoder->bitstream_buf, decoder->bitstream_bits, 5); + + if (decoder->q_scale_type) return non_linear_quantizer_scale [quantizer_scale_code]; + else return quantizer_scale_code << 1; +} + +static __forceinline int get_coded_block_pattern (decoder_t * const decoder) +{ + const CBPtab * tab; + + NEEDBITS (decoder->bitstream_buf, decoder->bitstream_bits, decoder->bitstream_ptr); + + if (decoder->bitstream_buf >= 0x20000000) { + tab = CBP_7 + (UBITS (decoder->bitstream_buf, 7) - 16); + DUMPBITS (decoder->bitstream_buf, decoder->bitstream_bits, tab->len); + return tab->cbp; + } + + tab = CBP_9 + UBITS (decoder->bitstream_buf, 9); + DUMPBITS (decoder->bitstream_buf, decoder->bitstream_bits, tab->len); + return tab->cbp; +} + +static __forceinline int get_luma_dc_dct_diff (decoder_t * const decoder) +{ +#define bit_buf (decoder->bitstream_buf) +#define bits (decoder->bitstream_bits) +#define bit_ptr (decoder->bitstream_ptr) + + const DCtab * tab; + int size; + int dc_diff; + + if (bit_buf < 0xf8000000) { + tab = DC_lum_5 + UBITS (bit_buf, 5); + size = tab->size; + if (size) { + DUMPBITS (bit_buf, bits, tab->len); + bits += size; + dc_diff = + UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size); + bit_buf <<= size; + return dc_diff; + } else { + DUMPBITS (bit_buf, bits, 3); + return 0; + } + } + + tab = DC_long + (UBITS (bit_buf, 9) - 0x1e0);//0x1e0); + size = tab->size; + DUMPBITS (bit_buf, bits, tab->len); + NEEDBITS (bit_buf, bits, bit_ptr); + dc_diff = UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size); + DUMPBITS (bit_buf, bits, size); + return dc_diff; +#undef bit_buf +#undef bits +#undef bit_ptr +} + +static __forceinline int get_chroma_dc_dct_diff (decoder_t * const decoder) +{ +#define bit_buf (decoder->bitstream_buf) +#define bits (decoder->bitstream_bits) +#define bit_ptr (decoder->bitstream_ptr) + + const DCtab * tab; + int size; + int dc_diff; + + if (bit_buf < 0xf8000000) { + tab = DC_chrom_5 + UBITS (bit_buf, 5); + size = tab->size; + if (size) { + DUMPBITS (bit_buf, bits, tab->len); + bits += size; + dc_diff = + UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size); + bit_buf <<= size; + return dc_diff; + } else { + DUMPBITS (bit_buf, bits, 2); + return 0; + } + } + + tab = DC_long + (UBITS (bit_buf, 10) - 0x3e0); + size = tab->size; + DUMPBITS (bit_buf, bits, tab->len + 1); + NEEDBITS (bit_buf, bits, bit_ptr); + dc_diff = UBITS (bit_buf, size) - UBITS (SBITS (~bit_buf, 1), size); + DUMPBITS (bit_buf, bits, size); + return dc_diff; +#undef bit_buf +#undef bits +#undef bit_ptr +} + +#define SATURATE(val) \ +do { \ + if (((u32)(val + 2048) > 4095)) \ + val = SBITS (val, 1) ^ 2047; \ +} while (0) + +static __forceinline void get_intra_block_B14 (decoder_t * const decoder) +{ + int i; + int j; + int val; + const u8 * scan = decoder->scan; + const u8 * quant_matrix = decoder->intra_quantizer_matrix; + int quantizer_scale = decoder->quantizer_scale; + int mismatch; + const DCTtab * tab; + u32 bit_buf; + u8 * bit_ptr; + int bits; + s16 * dest; + + dest = decoder->DCTblock; + i = 0; + mismatch = ~dest[0]; + + bit_buf = decoder->bitstream_buf; + bits = decoder->bitstream_bits; + bit_ptr = decoder->bitstream_ptr; + + NEEDBITS (bit_buf, bits, bit_ptr); + + while (1) { + if (bit_buf >= 0x28000000) { + + tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5); + + i += tab->run; + if (i >= 64) break; /* end of block */ + + normal_code: + j = scan[i]; + bit_buf <<= tab->len; + bits += tab->len + 1; + /* JayteeMaster: 10 points! Replaced quant_matrix[j] by quant_matrix[i] as should be */ + val = (tab->level * quantizer_scale * quant_matrix[i]) >> 4; + + /* if (bitstream_get (1)) val = -val; */ + val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1); + + SATURATE (val); + dest[j] = val; + mismatch ^= val; + + bit_buf <<= 1; + NEEDBITS (bit_buf, bits, bit_ptr); + + continue; + + } else if (bit_buf >= 0x04000000) { + + tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4); + + i += tab->run; + if (i < 64) goto normal_code; + + /* escape code */ + + i += UBITS (bit_buf << 6, 6) - 64; + if (i >= 64) break; /* illegal, check needed to avoid buffer overflow */ + + j = scan[i]; + + DUMPBITS (bit_buf, bits, 12); + NEEDBITS (bit_buf, bits, bit_ptr); + /* JayteeMaster: 10 points! Replaced quant_matrix[j] by quant_matrix[i] as should be */ + val = (SBITS (bit_buf, 12) * quantizer_scale * quant_matrix[i]) / 16; + + SATURATE (val); + dest[j] = val; + mismatch ^= val; + + DUMPBITS (bit_buf, bits, 12); + NEEDBITS (bit_buf, bits, bit_ptr); + + continue; + + } else if (bit_buf >= 0x02000000) { + tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8); + i += tab->run; + if (i < 64) goto normal_code; + } else if (bit_buf >= 0x00800000) { + tab = DCT_13 + (UBITS (bit_buf, 13) - 16); + i += tab->run; + if (i < 64) goto normal_code; + } else if (bit_buf >= 0x00200000) { + tab = DCT_15 + (UBITS (bit_buf, 15) - 16); + i += tab->run; + if (i < 64) goto normal_code; + } else { + tab = DCT_16 + UBITS (bit_buf, 16); + bit_buf <<= 16; + GETWORD(&bit_buf,bits+16); + i += tab->run; + if (i < 64) goto normal_code; + } + break; /* illegal, check needed to avoid buffer overflow */ + } + dest[63] ^= mismatch & 1; + if( (bit_buf>>30) != 0x2 ) + ipuRegs->ctrl.ECD = 1; + DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */ + decoder->bitstream_buf = bit_buf; + decoder->bitstream_bits = bits; +} + +static __forceinline void get_intra_block_B15 (decoder_t * const decoder) +{ + int i; + int j; + int val; + const u8 * scan = decoder->scan; + const u8 * quant_matrix = decoder->intra_quantizer_matrix; + int quantizer_scale = decoder->quantizer_scale; + int mismatch; + const DCTtab * tab; + u32 bit_buf; + u8 * bit_ptr; + int bits; + s16 * dest; + + dest = decoder->DCTblock; + i = 0; + mismatch = ~dest[0]; + + bit_buf = decoder->bitstream_buf; + bits = decoder->bitstream_bits; + bit_ptr = decoder->bitstream_ptr; + + NEEDBITS (bit_buf, bits, bit_ptr); + + while (1) { + if (bit_buf >= 0x04000000) { + + tab = DCT_B15_8 + (UBITS (bit_buf, 8) - 4); + + i += tab->run; + if (i < 64) { + normal_code: + j = scan[i]; + bit_buf <<= tab->len; + bits += tab->len + 1; + /* JayteeMaster: 10 points! Replaced quant_matrix[j] by quant_matrix[i] as should be */ + val = (tab->level * quantizer_scale * quant_matrix[i]) >> 4; + + /* if (bitstream_get (1)) val = -val; */ + val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1); + + SATURATE (val); + dest[j] = val; + mismatch ^= val; + + bit_buf <<= 1; + NEEDBITS (bit_buf, bits, bit_ptr); + + continue; + + } else { + /* end of block. I commented out this code because if we */ + /* dont exit here we will still exit at the later test :) */ + //if (i >= 128) break; /* end of block */ + /* escape code */ + + i += UBITS (bit_buf << 6, 6) - 64; + if (i >= 64) break; /* illegal, check against buffer overflow */ + + j = scan[i]; + + DUMPBITS (bit_buf, bits, 12); + NEEDBITS (bit_buf, bits, bit_ptr); + /* JayteeMaster: 10 points! Replaced quant_matrix[j] by quant_matrix[i] as should be */ + val = (SBITS (bit_buf, 12) * quantizer_scale * quant_matrix[i]) / 16; + + SATURATE (val); + dest[j] = val; + mismatch ^= val; + + DUMPBITS (bit_buf, bits, 12); + NEEDBITS (bit_buf, bits, bit_ptr); + + continue; + } + } else if (bit_buf >= 0x02000000) { + tab = DCT_B15_10 + (UBITS (bit_buf, 10) - 8); + i += tab->run; + if (i < 64) goto normal_code; + } else if (bit_buf >= 0x00800000) { + tab = DCT_13 + (UBITS (bit_buf, 13) - 16); + i += tab->run; + if (i < 64) goto normal_code; + } else if (bit_buf >= 0x00200000) { + tab = DCT_15 + (UBITS (bit_buf, 15) - 16); + i += tab->run; + if (i < 64) goto normal_code; + } else { + tab = DCT_16 + UBITS (bit_buf, 16); + bit_buf <<= 16; + GETWORD(&bit_buf,bits+16); + i += tab->run; + if (i < 64) goto normal_code; + } + break; /* illegal, check needed to avoid buffer overflow */ + } + dest[63] ^= mismatch & 1; + if( (bit_buf>>28) != 0x6 ) + ipuRegs->ctrl.ECD = 1; + DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */ + decoder->bitstream_buf = bit_buf; + decoder->bitstream_bits = bits; +} + +static __forceinline int get_non_intra_block (decoder_t * const decoder) +{ +#define bit_buf (decoder->bitstream_buf) +#define bits (decoder->bitstream_bits) +#define bit_ptr (decoder->bitstream_ptr) + int i; + int j; + int val; + const u8 * scan = decoder->scan; + const u8 * quant_matrix = decoder->non_intra_quantizer_matrix; + int quantizer_scale = decoder->quantizer_scale; + int mismatch; + const DCTtab * tab; + s16 * dest; + + i = -1; + mismatch = -1; + dest = decoder->DCTblock; + + + NEEDBITS (bit_buf, bits, bit_ptr); + if (bit_buf >= 0x28000000) { + tab = DCT_B14DC_5 + (UBITS (bit_buf, 5) - 5); + goto entry_1; + } else + goto entry_2; + + while (1) { + if (bit_buf >= 0x28000000) { + + tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5); + + entry_1: + i += tab->run; + if (i >= 64) + break; /* end of block */ + + normal_code: + j = scan[i]; + bit_buf <<= tab->len; + bits += tab->len + 1; + /* JayteeMaster: 10 points! Replaced quant_matrix[j] by quant_matrix[i] as should be */ + val = ((2*tab->level+1) * quantizer_scale * quant_matrix[i]) >> 5; + + /* if (bitstream_get (1)) val = -val; */ + val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1); + + SATURATE (val); + dest[j] = val; + mismatch ^= val; + + bit_buf <<= 1; + NEEDBITS (bit_buf, bits, bit_ptr); + + continue; + + } + + entry_2: + if (bit_buf >= 0x04000000) { + + tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4); + + i += tab->run; + if (i < 64) + goto normal_code; + + /* escape code */ + + i += UBITS (bit_buf << 6, 6) - 64; + if (i >= 64) + break; /* illegal, check needed to avoid buffer overflow */ + + j = scan[i]; + + DUMPBITS (bit_buf, bits, 12); + NEEDBITS (bit_buf, bits, bit_ptr); + val = 2 * (SBITS (bit_buf, 12) + SBITS (bit_buf, 1)) + 1; + /* JayteeMaster: 10 points! Replaced quant_matrix[j] by quant_matrix[i] as should be */ + val = (val * quantizer_scale * quant_matrix[i]) / 32; + + SATURATE (val); + dest[j] = val; + mismatch ^= val; + + DUMPBITS (bit_buf, bits, 12); + NEEDBITS (bit_buf, bits, bit_ptr); + + continue; + + } else if (bit_buf >= 0x02000000) { + tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8); + i += tab->run; + if (i < 64) + goto normal_code; + } else if (bit_buf >= 0x00800000) { + tab = DCT_13 + (UBITS (bit_buf, 13) - 16); + i += tab->run; + if (i < 64) + goto normal_code; + } else if (bit_buf >= 0x00200000) { + tab = DCT_15 + (UBITS (bit_buf, 15) - 16); + i += tab->run; + if (i < 64) + goto normal_code; + } else { + tab = DCT_16 + UBITS (bit_buf, 16); + bit_buf <<= 16; + GETWORD(&bit_buf,bits+16); + i += tab->run; + if (i < 64) + goto normal_code; + } + break; /* illegal, check needed to avoid buffer overflow */ + } + dest[63] ^= mismatch & 1; + if( (bit_buf>>30) != 0x2 ) + ipuRegs->ctrl.ECD = 1; + + DUMPBITS (bit_buf, bits, tab->len); /* dump end of block code */ + decoder->bitstream_buf = bit_buf; + decoder->bitstream_bits = bits; + return i; +#undef bit_buf +#undef bits +#undef bit_ptr +} + +static __forceinline void get_mpeg1_intra_block (decoder_t * const decoder) +{ + int i; + int j; + int val; + const u8 * scan = decoder->scan; + const u8 * quant_matrix = decoder->intra_quantizer_matrix; + int quantizer_scale = decoder->quantizer_scale; + const DCTtab * tab; + u32 bit_buf; + int bits; + u8 * bit_ptr; + s16 * dest; + + i = 0; + dest = decoder->DCTblock; + + bit_buf = decoder->bitstream_buf; + bits = decoder->bitstream_bits; + bit_ptr = decoder->bitstream_ptr; + + NEEDBITS (bit_buf, bits, bit_ptr); + + while (1) { + if (bit_buf >= 0x28000000) { + + tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5); + + i += tab->run; + if (i >= 64) + break; /* end of block */ + + normal_code: + j = scan[i]; + bit_buf <<= tab->len; + bits += tab->len + 1; + /* JayteeMaster: 10 points! Replaced quant_matrix[j] by quant_matrix[i] as should be */ + val = (tab->level * quantizer_scale * quant_matrix[i]) >> 4; + + /* oddification */ + val = (val - 1) | 1; + + /* if (bitstream_get (1)) val = -val; */ + val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1); + + SATURATE (val); + dest[j] = val; + + bit_buf <<= 1; + NEEDBITS (bit_buf, bits, bit_ptr); + + continue; + + } else if (bit_buf >= 0x04000000) { + + tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4); + + i += tab->run; + if (i < 64) + goto normal_code; + + /* escape code */ + + i += UBITS (bit_buf << 6, 6) - 64; + if (i >= 64) + break; /* illegal, check needed to avoid buffer overflow */ + + j = scan[i]; + + DUMPBITS (bit_buf, bits, 12); + NEEDBITS (bit_buf, bits, bit_ptr); + val = SBITS (bit_buf, 8); + if (! (val & 0x7f)) { + DUMPBITS (bit_buf, bits, 8); + val = UBITS (bit_buf, 8) + 2 * val; + } + /* JayteeMaster: 10 points! Replaced quant_matrix[j] by quant_matrix[i] as should be */ + val = (val * quantizer_scale * quant_matrix[i]) >> 4; + + /* oddification */ + val = (val + ~SBITS (val, 1)) | 1; + + SATURATE (val); + dest[j] = val; + + DUMPBITS (bit_buf, bits, 8); + NEEDBITS (bit_buf, bits, bit_ptr); + + continue; + + } else if (bit_buf >= 0x02000000) { + tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8); + i += tab->run; + if (i < 64) + goto normal_code; + } else if (bit_buf >= 0x00800000) { + tab = DCT_13 + (UBITS (bit_buf, 13) - 16); + i += tab->run; + if (i < 64) + goto normal_code; + } else if (bit_buf >= 0x00200000) { + tab = DCT_15 + (UBITS (bit_buf, 15) - 16); + i += tab->run; + if (i < 64) + goto normal_code; + } else { + tab = DCT_16 + UBITS (bit_buf, 16); + bit_buf <<= 16; + GETWORD(&bit_buf,bits+16); + i += tab->run; + if (i < 64) + goto normal_code; + } + break; /* illegal, check needed to avoid buffer overflow */ + } + if( (bit_buf>>30) != 0x2 ) + ipuRegs->ctrl.ECD = 1; + + DUMPBITS (bit_buf, bits, 2); /* dump end of block code */ + decoder->bitstream_buf = bit_buf; + decoder->bitstream_bits = bits; +} + +static __forceinline int get_mpeg1_non_intra_block (decoder_t * const decoder) +{ + int i; + int j; + int val; + const u8 * scan = decoder->scan; + const u8 * quant_matrix = decoder->non_intra_quantizer_matrix; + int quantizer_scale = decoder->quantizer_scale; + const DCTtab * tab; + u32 bit_buf; + int bits; + u8 * bit_ptr; + s16 * dest; + + i = -1; + dest = decoder->DCTblock; + + bit_buf = decoder->bitstream_buf; + bits = decoder->bitstream_bits; + bit_ptr = decoder->bitstream_ptr; + + NEEDBITS (bit_buf, bits, bit_ptr); + if (bit_buf >= 0x28000000) { + tab = DCT_B14DC_5 + (UBITS (bit_buf, 5) - 5); + goto entry_1; + } else + goto entry_2; + + while (1) { + if (bit_buf >= 0x28000000) { + + tab = DCT_B14AC_5 + (UBITS (bit_buf, 5) - 5); + + entry_1: + i += tab->run; + if (i >= 64) + break; /* end of block */ + + normal_code: + j = scan[i]; + bit_buf <<= tab->len; + bits += tab->len + 1; + /* JayteeMaster: 10 points! Replaced quant_matrix[j] by quant_matrix[i] as should be */ + val = ((2*tab->level+1) * quantizer_scale * quant_matrix[i]) >> 5; + + /* oddification */ + val = (val - 1) | 1; + + /* if (bitstream_get (1)) val = -val; */ + val = (val ^ SBITS (bit_buf, 1)) - SBITS (bit_buf, 1); + + SATURATE (val); + dest[j] = val; + + bit_buf <<= 1; + NEEDBITS (bit_buf, bits, bit_ptr); + + continue; + + } + + entry_2: + if (bit_buf >= 0x04000000) { + + tab = DCT_B14_8 + (UBITS (bit_buf, 8) - 4); + + i += tab->run; + if (i < 64) + goto normal_code; + + /* escape code */ + + i += UBITS (bit_buf << 6, 6) - 64; + if (i >= 64) + break; /* illegal, check needed to avoid buffer overflow */ + + j = scan[i]; + + DUMPBITS (bit_buf, bits, 12); + NEEDBITS (bit_buf, bits, bit_ptr); + val = SBITS (bit_buf, 8); + if (! (val & 0x7f)) { + DUMPBITS (bit_buf, bits, 8); + val = UBITS (bit_buf, 8) + 2 * val; + } + val = 2 * (val + SBITS (val, 1)) + 1; + /* JayteeMaster: 10 points! Replaced quant_matrix[j] by quant_matrix[i] as should be */ + val = (val * quantizer_scale * quant_matrix[i]) / 32; + + /* oddification */ + val = (val + ~SBITS (val, 1)) | 1; + + SATURATE (val); + dest[j] = val; + + DUMPBITS (bit_buf, bits, 8); + NEEDBITS (bit_buf, bits, bit_ptr); + + continue; + + } else if (bit_buf >= 0x02000000) { + tab = DCT_B14_10 + (UBITS (bit_buf, 10) - 8); + i += tab->run; + if (i < 64) + goto normal_code; + } else if (bit_buf >= 0x00800000) { + tab = DCT_13 + (UBITS (bit_buf, 13) - 16); + i += tab->run; + if (i < 64) + goto normal_code; + } else if (bit_buf >= 0x00200000) { + tab = DCT_15 + (UBITS (bit_buf, 15) - 16); + i += tab->run; + if (i < 64) + goto normal_code; + } else { + tab = DCT_16 + UBITS (bit_buf, 16); + bit_buf <<= 16; + GETWORD(&bit_buf,bits+16); + i += tab->run; + if (i < 64) + goto normal_code; + } + break; /* illegal, check needed to avoid buffer overflow */ + } + if( (bit_buf>>30) != 0x2 ) + ipuRegs->ctrl.ECD = 1; + + DUMPBITS (bit_buf, bits, 2); /* dump end of block code */ + decoder->bitstream_buf = bit_buf; + decoder->bitstream_bits = bits; + return i; +} + +static void __fastcall slice_intra_DCT (decoder_t * const decoder, const int cc, + u8 * const dest, const int stride) +{ + NEEDBITS (decoder->bitstream_buf, decoder->bitstream_bits, decoder->bitstream_ptr); + /* Get the intra DC coefficient and inverse quantize it */ + if (cc == 0) decoder->dc_dct_pred[0] += get_luma_dc_dct_diff (decoder); + else decoder->dc_dct_pred[cc] += get_chroma_dc_dct_diff (decoder); + + decoder->DCTblock[0] = decoder->dc_dct_pred[cc] << (3 - decoder->intra_dc_precision); + + if (decoder->mpeg1) get_mpeg1_intra_block (decoder); + else if (decoder->intra_vlc_format){ + get_intra_block_B15 (decoder); + }else{ + get_intra_block_B14 (decoder); + } + + mpeg2_idct_copy (decoder->DCTblock, dest, stride); +} + +/* JayteeMaster: changed dest to 16 bit signed */ +static void __fastcall slice_non_intra_DCT (decoder_t * const decoder, + /*u8*/s16 * const dest, const int stride){ + int last; + memzero_obj(decoder->DCTblock); + if (decoder->mpeg1) last = get_mpeg1_non_intra_block (decoder); + else last = get_non_intra_block (decoder); + + mpeg2_idct_add (last, decoder->DCTblock, dest, stride); +} + +#if defined(_MSC_VER) +#pragma pack(push, 1) +#endif +struct TGA_HEADER +{ + u8 identsize; // size of ID field that follows 18 u8 header (0 usually) + u8 colourmaptype; // type of colour map 0=none, 1=has palette + u8 imagetype; // type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed + + s16 colourmapstart; // first colour map entry in palette + s16 colourmaplength; // number of colours in palette + u8 colourmapbits; // number of bits per palette entry 15,16,24,32 + + s16 xstart; // image x origin + s16 ystart; // image y origin + s16 width; // image width in pixels + s16 height; // image height in pixels + u8 bits; // image bits per pixel 8,16,24,32 + u8 descriptor; // image descriptor bits (vh flip bits) + + // pixel data follows header +#if defined(_MSC_VER) +}; +#pragma pack(pop) +#else +} __attribute__((packed)); +#endif + +void SaveTGA(const char* filename, int width, int height, void* pdata) +{ + TGA_HEADER hdr; + FILE* f = fopen(filename, "wb"); + if( f == NULL ) + return; + + assert( sizeof(TGA_HEADER) == 18 && sizeof(hdr) == 18 ); + + memzero_obj(hdr); + hdr.imagetype = 2; + hdr.bits = 32; + hdr.width = width; + hdr.height = height; + hdr.descriptor |= 8|(1<<5); // 8bit alpha, flip vertical + + fwrite(&hdr, sizeof(hdr), 1, f); + fwrite(pdata, width*height*4, 1, f); + fclose(f); +} +static int s_index = 0; //, s_frame = 0; + +void SaveRGB32(u8* ptr) +{ + char filename[255]; + sprintf(filename, "frames/frame%.4d.tga", s_index++); + SaveTGA(filename, 16, 16, ptr); +} + +void waitForSCD() +{ + u8 bit8 = 1; + + while(!getBits8((u8*)&bit8, 0)) + so_resume(); + + if (bit8==0) + { + if (g_BP.BP & 7) + g_BP.BP += 8 - (g_BP.BP&7); + + ipuRegs->ctrl.SCD = 1; + } + + while(!getBits32((u8*)&ipuRegs->top, 0)) + { + so_resume(); + } + + BigEndian(ipuRegs->top, ipuRegs->top); + + /*if(ipuRegs->ctrl.SCD) + { + switch(ipuRegs->top & 0xFFFFFFF0) + { + case 0x100: + case 0x1A0: + break; + case 0x1B0: + ipuRegs->ctrl.SCD = 0; + if(ipuRegs->top == 0x1b4) ipuRegs->ctrl.ECD = 1; + //else + //{ + // do + // { + // while(!getBits32((u8*)&ipuRegs->top, 1)) + // { + // so_resume(); + // } + + // BigEndian(ipuRegs->top, ipuRegs->top); + // } + // while((ipuRegs->top & 0xfffffff0) != 0x100); + //} + break; + default: + ipuRegs->ctrl.SCD = 0; + break; + } + }*/ +} + +void mpeg2sliceIDEC(void* pdone) +{ + u32 read; + + decoder_t * decoder = &g_decoder; + + *(int*)pdone = 0; + bitstream_init(decoder); + + decoder->dc_dct_pred[0] = + decoder->dc_dct_pred[1] = + decoder->dc_dct_pred[2] = 128 << decoder->intra_dc_precision; + + decoder->mbc=0; + ipuRegs->ctrl.ECD = 0; + + if (UBITS (decoder->bitstream_buf, 2) == 0) + { + ipuRegs->ctrl.SCD = 0; + }else{ + while (1) { + int DCT_offset, DCT_stride; + int mba_inc; + const MBAtab * mba; + + NEEDBITS (decoder->bitstream_buf, decoder->bitstream_bits, decoder->bitstream_ptr); + + decoder->macroblock_modes = get_macroblock_modes (decoder); + + /* maybe integrate MACROBLOCK_QUANT test into get_macroblock_modes ? */ + if (decoder->macroblock_modes & MACROBLOCK_QUANT)//only IDEC + decoder->quantizer_scale = get_quantizer_scale (decoder); + + if (decoder->macroblock_modes & DCT_TYPE_INTERLACED) { + DCT_offset = decoder->stride; + DCT_stride = decoder->stride * 2; + } else { + DCT_offset = decoder->stride * 8; + DCT_stride = decoder->stride; + } + + if (decoder->macroblock_modes & MACROBLOCK_INTRA) { + decoder->coded_block_pattern = 0x3F;//all 6 blocks + //ipuRegs->ctrl.CBP = 0x3f; + + memzero_obj(*decoder->mb8); + memzero_obj(*decoder->rgb32); + + slice_intra_DCT (decoder, 0, (u8*)decoder->mb8->Y, DCT_stride); + slice_intra_DCT (decoder, 0, (u8*)decoder->mb8->Y + 8, DCT_stride); + slice_intra_DCT (decoder, 0, (u8*)decoder->mb8->Y + DCT_offset, DCT_stride); + slice_intra_DCT (decoder, 0, (u8*)decoder->mb8->Y + DCT_offset + 8, DCT_stride); + slice_intra_DCT (decoder, 1, (u8*)decoder->mb8->Cb, decoder->stride>>1); + slice_intra_DCT (decoder, 2, (u8*)decoder->mb8->Cr, decoder->stride>>1); + + // Send The MacroBlock via DmaIpuFrom + if (decoder->ofm==0){ + ipu_csc(decoder->mb8, decoder->rgb32, decoder->sgn); + + g_nIPU0Data = 64; + g_pIPU0Pointer = (u8*)decoder->rgb32; + //if( s_frame >= 39 ) SaveRGB32(g_pIPU0Pointer); + while(g_nIPU0Data > 0) { + read = FIFOfrom_write((u32*)g_pIPU0Pointer,g_nIPU0Data); + if( read == 0 ) + { + so_resume(); + } + else { + g_pIPU0Pointer += read*16; + g_nIPU0Data -= read; + } + } + } + else{ + //ipu_dither(decoder->mb8, decoder->rgb16, decoder->dte); + ipu_csc(decoder->mb8, decoder->rgb32, decoder->dte); + ipu_dither2(decoder->rgb32, decoder->rgb16, decoder->dte); + + g_nIPU0Data = 32; + g_pIPU0Pointer = (u8*)decoder->rgb16; + //if( s_frame >= 39 ) SaveRGB32(g_pIPU0Pointer); + while(g_nIPU0Data > 0) { + read = FIFOfrom_write((u32*)g_pIPU0Pointer,g_nIPU0Data); + if( read == 0 ){ + so_resume(); + } + else { + g_pIPU0Pointer += read*16; + g_nIPU0Data -= read; + } + } + } + decoder->mbc++; + } + + NEEDBITS (decoder->bitstream_buf, decoder->bitstream_bits, decoder->bitstream_ptr); + + mba_inc = 0; + while (1) { + if (decoder->bitstream_buf >= 0x10000000) { + mba = MBA_5 + (UBITS (decoder->bitstream_buf, 5) - 2); + break; + } else if (decoder->bitstream_buf >= 0x03000000) { + mba = MBA_11 + (UBITS (decoder->bitstream_buf, 11) - 24); + break; + } else switch (UBITS (decoder->bitstream_buf, 11)) { + case 8: /* macroblock_escape */ + mba_inc += 33; + /* pass through */ + case 15: /* macroblock_stuffing (MPEG1 only) */ + DUMPBITS (decoder->bitstream_buf, decoder->bitstream_bits, 11); + NEEDBITS (decoder->bitstream_buf, decoder->bitstream_bits, decoder->bitstream_ptr); + continue; + default: /* end of slice/frame, or error? */ + { + ipuRegs->ctrl.SCD = 0; + coded_block_pattern=decoder->coded_block_pattern; + + g_BP.BP+=decoder->bitstream_bits-16; + + if((int)g_BP.BP < 0) { + g_BP.BP = 128 + (int)g_BP.BP; + + // After BP is positioned correctly, we need to reload the old buffer + // so that reading may continue properly + ReorderBitstream(); + } + + FillInternalBuffer(&g_BP.BP,1,0); + + waitForSCD(); + + *(int*)pdone = 1; + so_exit(); + } + } + } + DUMPBITS (decoder->bitstream_buf, decoder->bitstream_bits, mba->len); + mba_inc += mba->mba; + + if (mba_inc) { + decoder->dc_dct_pred[0] = decoder->dc_dct_pred[1] = + decoder->dc_dct_pred[2] = 128 << decoder->intra_dc_precision; + do { + decoder->mbc++; + } while (--mba_inc); + } + } + } + + ipuRegs->ctrl.SCD = 0; + + coded_block_pattern=decoder->coded_block_pattern; + + g_BP.BP+=decoder->bitstream_bits-16; + + if((int)g_BP.BP < 0) { + g_BP.BP = 128 + (int)g_BP.BP; + + // After BP is positioned correctly, we need to reload the old buffer + // so that reading may continue properly + ReorderBitstream(); + } + + FillInternalBuffer(&g_BP.BP,1,0); + + waitForSCD(); + + *(int*)pdone = 1; + so_exit(); +} + +void mpeg2_slice(void* pdone) +{ + int DCT_offset, DCT_stride; + //u8 bit8=0; + //u32 fp = g_BP.FP; + u32 bp; + decoder_t * decoder = &g_decoder; + u32 size = 0; + + *(int*)pdone = 0; + ipuRegs->ctrl.ECD = 0; + + memzero_obj(*decoder->mb8); + memzero_obj(*decoder->mb16); + + bitstream_init (decoder); + + if (decoder->dcr) + decoder->dc_dct_pred[0] = decoder->dc_dct_pred[1] = + decoder->dc_dct_pred[2] = 128 << decoder->intra_dc_precision; + + if (decoder->macroblock_modes & DCT_TYPE_INTERLACED) { + DCT_offset = decoder->stride; + DCT_stride = decoder->stride * 2; + } else { + DCT_offset = decoder->stride * 8; + DCT_stride = decoder->stride; + } + if (decoder->macroblock_modes & MACROBLOCK_INTRA) { + decoder->coded_block_pattern = 0x3F;//all 6 blocks + slice_intra_DCT (decoder, 0, (u8*)decoder->mb8->Y, DCT_stride); + slice_intra_DCT (decoder, 0, (u8*)decoder->mb8->Y + 8, DCT_stride); + slice_intra_DCT (decoder, 0, (u8*)decoder->mb8->Y + DCT_offset, DCT_stride); + slice_intra_DCT (decoder, 0, (u8*)decoder->mb8->Y + DCT_offset + 8, DCT_stride); + slice_intra_DCT (decoder, 1, (u8*)decoder->mb8->Cb, decoder->stride>>1); + slice_intra_DCT (decoder, 2, (u8*)decoder->mb8->Cr, decoder->stride>>1); + ipu_copy(decoder->mb8,decoder->mb16); + } else { + if (decoder->macroblock_modes & MACROBLOCK_PATTERN) { + decoder->coded_block_pattern = get_coded_block_pattern (decoder); + /* JayteeMaster: changed from mb8 to mb16 and from u8 to s16 */ + if (decoder->coded_block_pattern & 0x20) slice_non_intra_DCT (decoder, (s16*)decoder->mb16->Y, DCT_stride); + if (decoder->coded_block_pattern & 0x10) slice_non_intra_DCT (decoder, (s16*)decoder->mb16->Y + 8, DCT_stride); + if (decoder->coded_block_pattern & 0x08) slice_non_intra_DCT (decoder, (s16*)decoder->mb16->Y + DCT_offset, DCT_stride); + if (decoder->coded_block_pattern & 0x04) slice_non_intra_DCT (decoder, (s16*)decoder->mb16->Y + DCT_offset + 8, DCT_stride); + if (decoder->coded_block_pattern & 0x2) slice_non_intra_DCT (decoder, (s16*)decoder->mb16->Cb, decoder->stride>>1); + if (decoder->coded_block_pattern & 0x1) slice_non_intra_DCT (decoder, (s16*)decoder->mb16->Cr, decoder->stride>>1); + + } + } + + //Send The MacroBlock via DmaIpuFrom + size = 0; // Reset + + ipuRegs->ctrl.SCD=0; + coded_block_pattern=decoder->coded_block_pattern; + + bp = g_BP.BP; + g_BP.BP+=((int)decoder->bitstream_bits-16); + // BP goes from 0 to 128, so negative values mean to read old buffer + // so we minus from 128 to get the correct BP + if((int)g_BP.BP < 0) { + g_BP.BP = 128 + (int)g_BP.BP; + + // After BP is positioned correctly, we need to reload the old buffer + // so that reading may continue properly + ReorderBitstream(); + } + + FillInternalBuffer(&g_BP.BP,1,0); + + decoder->mbc = 1; + g_nIPU0Data = 48; + g_pIPU0Pointer = (u8*)decoder->mb16; + while(g_nIPU0Data > 0) { + size = FIFOfrom_write((u32*)g_pIPU0Pointer,g_nIPU0Data); + if( size == 0 ) + so_resume(); + else { + g_pIPU0Pointer += size*16; + g_nIPU0Data -= size; + } + } + + waitForSCD(); + + decoder->bitstream_bits = 0; + *(int*)pdone = 1; + so_exit(); +} + +int __forceinline get_motion_delta (decoder_t * const decoder, + const int f_code) +{ +#define bit_buf (decoder->bitstream_buf) +#define bits (decoder->bitstream_bits) +#define bit_ptr (decoder->bitstream_ptr) + + int delta; + int sign; + const MVtab * tab; + + if ( (bit_buf & 0x80000000) ) { + DUMPBITS (bit_buf, bits, 1); + return 0x00010000; + } else if ( (bit_buf & 0xf0000000) || ((bit_buf & 0xfc000000)==0x0c000000) ) { + + tab = MV_4 + UBITS (bit_buf, 4); + delta = (tab->delta << f_code) + 1; + bits += tab->len + f_code + 1; + bit_buf <<= tab->len; + + sign = SBITS (bit_buf, 1); + bit_buf <<= 1; + + if (f_code) + delta += UBITS (bit_buf, f_code); + bit_buf <<= f_code; + + return (delta ^ sign) - sign; + + } else { + + tab = MV_10 + UBITS (bit_buf, 10); + delta = (tab->delta << f_code) + 1; + bits += tab->len + 1; + bit_buf <<= tab->len; + + sign = SBITS (bit_buf, 1); + bit_buf <<= 1; + + if (f_code) { + NEEDBITS (bit_buf, bits, bit_ptr); + delta += UBITS (bit_buf, f_code); + DUMPBITS (bit_buf, bits, f_code); + } + + return (delta ^ sign) - sign; + + } +#undef bit_buf +#undef bits +#undef bit_ptr +} + +int __forceinline get_dmv (decoder_t * const decoder) +{ +#define bit_buf (decoder->bitstream_buf) +#define bits (decoder->bitstream_bits) +#define bit_ptr (decoder->bitstream_ptr) + + const DMVtab * tab; + + tab = DMV_2 + UBITS (bit_buf, 2); + DUMPBITS (bit_buf, bits, tab->len); + return tab->dmv; +#undef bit_buf +#undef bits +#undef bit_ptr +} + +int get_macroblock_address_increment(decoder_t * const decoder){ + const MBAtab *mba; + + if (decoder->bitstream_buf >= 0x10000000) + mba = MBA_5 + (UBITS (decoder->bitstream_buf, 5) - 2); + else if (decoder->bitstream_buf >= 0x03000000) + mba = MBA_11 + (UBITS (decoder->bitstream_buf, 11) - 24); + else switch (UBITS (decoder->bitstream_buf, 11)) { + case 8: /* macroblock_escape */ + DUMPBITS (decoder->bitstream_buf, decoder->bitstream_bits, 11); + return 0x23; + case 15: /* macroblock_stuffing (MPEG1 only) */ + if (decoder->mpeg1){ + DUMPBITS (decoder->bitstream_buf, decoder->bitstream_bits, 11); + return 0x22; + } + default: + return 0;//error + } + + DUMPBITS (decoder->bitstream_buf, decoder->bitstream_bits, mba->len); + return mba->mba + 1; +} diff --git a/pcsx2/IPU/mpeg2lib/Mpeg.h b/pcsx2/IPU/mpeg2lib/Mpeg.h new file mode 100644 index 0000000000..8c21b78525 --- /dev/null +++ b/pcsx2/IPU/mpeg2lib/Mpeg.h @@ -0,0 +1,203 @@ +/* + * Mpeg.h + * Copyright (C) 2000-2002 Michel Lespinasse + * Copyright (C) 1999-2000 Aaron Holtzman + * Modified by Florin for PCSX2 emu + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec 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. + * + * mpeg2dec 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 + */ + +#ifndef __MPEG_H__ +#define __MPEG_H__ + +/* macroblock modes */ +#define MACROBLOCK_INTRA 1 +#define MACROBLOCK_PATTERN 2 +#define MACROBLOCK_MOTION_BACKWARD 4 +#define MACROBLOCK_MOTION_FORWARD 8 +#define MACROBLOCK_QUANT 16 +#define DCT_TYPE_INTERLACED 32 +/* motion_type */ +#define MOTION_TYPE_SHIFT 6 +#define MOTION_TYPE_MASK (3*64) +#define MOTION_TYPE_BASE 64 +#define MC_FIELD (1*64) +#define MC_FRAME (2*64) +#define MC_16X8 (2*64) +#define MC_DMV (3*64) + +/* picture structure */ +#define TOP_FIELD 1 +#define BOTTOM_FIELD 2 +#define FRAME_PICTURE 3 + +/* picture coding type */ +#define I_TYPE 1 +#define P_TYPE 2 +#define B_TYPE 3 +#define D_TYPE 4 + +struct macroblock_8{ + unsigned char Y[16][16]; //0 + unsigned char Cb[8][8]; //1 + unsigned char Cr[8][8]; //2 +}; + +struct macroblock_16{ + short Y[16][16]; //0 + short Cb[8][8]; //1 + short Cr[8][8]; //2 +}; + +struct rgb32{ + unsigned char r, g, b, a; +}; + +struct macroblock_rgb32{ + struct rgb32 c[16][16]; +}; + +struct rgb16{ + unsigned short r:5, g:5, b:5, a:1; +}; + +struct macroblock_rgb16{ + struct rgb16 c[16][16]; +}; + +struct decoder_t { + /* first, state that carries information from one macroblock to the */ + /* next inside a slice, and is never used outside of mpeg2_slice() */ + + /* DCT coefficients - should be kept aligned ! */ + s16 DCTblock[64]; + + /* bit parsing stuff */ + u32 bitstream_buf; /* current 32 bit working set */ + int bitstream_bits; /* used bits in working set */ + u8 * bitstream_ptr; /* buffer with stream data; 128 bits buffer */ + + struct macroblock_8 *mb8; + struct macroblock_16 *mb16; + struct macroblock_rgb32 *rgb32; + struct macroblock_rgb16 *rgb16; + + int stride; + + /* predictor for DC coefficients in intra blocks */ + s16 dc_dct_pred[3]; + + int quantizer_scale; /* remove */ + int dmv_offset; /* remove */ + + /* now non-slice-specific information */ + + /* sequence header stuff */ + u8 *intra_quantizer_matrix; + u8 *non_intra_quantizer_matrix; + + /* picture header stuff */ + + /* what type of picture this is (I, P, B, D) */ + int coding_type; + + /* picture coding extension stuff */ + + /* quantization factor for intra dc coefficients */ + int intra_dc_precision; + /* top/bottom/both fields */ + int picture_structure; + /* bool to indicate all predictions are frame based */ + int frame_pred_frame_dct; + /* bool to indicate whether intra blocks have motion vectors */ + /* (for concealment) */ + int concealment_motion_vectors; + /* bit to indicate which quantization table to use */ + int q_scale_type; + /* bool to use different vlc tables */ + int intra_vlc_format; + /* used for DMV MC */ + int top_field_first; + // Pseudo Sign Offset + int sgn; + // Dither Enable + int dte; + // Output Format + int ofm; + // Macroblock count + int mbc; + // Macroblock type + int macroblock_modes; + // DC Reset + int dcr; + // Coded block pattern + int coded_block_pattern; + + /* stuff derived from bitstream */ + + /* pointer to the zigzag scan we're supposed to be using */ + const u8 * scan; + + int second_field; + + int mpeg1; +}; + +extern void (__fastcall *mpeg2_idct_copy) (s16 * block, u8* dest, int stride); +extern void (__fastcall *mpeg2_idct_add) (int last, s16 * block, + /*u8*/s16* dest, int stride); + +#define IDEC 0 +#define BDEC 1 + +void mpeg2sliceIDEC(void* pdone); +void mpeg2_slice(void* pdone); +int get_macroblock_address_increment(decoder_t * const decoder); +int get_macroblock_modes (decoder_t * const decoder); + +extern int get_motion_delta (decoder_t * const decoder, const int f_code); +extern int get_dmv (decoder_t * const decoder); + +extern int non_linear_quantizer_scale[]; +extern decoder_t g_decoder; + +void __fastcall ipu_csc(macroblock_8 *mb8, macroblock_rgb32 *rgb32, int sgn); +void __fastcall ipu_dither(macroblock_8 *mb8, macroblock_rgb16 *rgb16, int dte); +void __fastcall ipu_dither2(const macroblock_rgb32* rgb32, macroblock_rgb16 *rgb16, int dte); +void __fastcall ipu_vq(macroblock_rgb16 *rgb16, u8* indx4); +void __fastcall ipu_copy(const macroblock_8 *mb8, macroblock_16 *mb16); + +int slice (decoder_t * const decoder, u8 * buffer); +/* idct.c */ +void mpeg2_idct_init (); + +#ifdef _MSC_VER +#define BigEndian(out, in) out = _byteswap_ulong(in) +#else +//#define BigEndian(out, in) \ +// __asm__(".intel_syntax\n" \ +// "bswap %0\n" \ +// ".att_syntax\n" : "=r"(out) : "0"(in) ) + +#define BigEndian(out, in) \ + out = (((((in) >> 24) & 0xFF) << 0) + ((((in) >> 16) & 0xFF) << 8) + \ + ((((in) >> 8) & 0xFF) << 16) + ((((in) >> 0) & 0xFF) << 24)); + +#endif + +#endif//__MPEG_H__ diff --git a/pcsx2/IPU/mpeg2lib/Vlc.h b/pcsx2/IPU/mpeg2lib/Vlc.h new file mode 100644 index 0000000000..495d9ce9bd --- /dev/null +++ b/pcsx2/IPU/mpeg2lib/Vlc.h @@ -0,0 +1,446 @@ +/* + * vlc.h + * Copyright (C) 2000-2002 Michel Lespinasse + * Copyright (C) 1999-2000 Aaron Holtzman + * Modified by Florin for PCSX2 emu + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec 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. + * + * mpeg2dec 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 + */ + +#ifndef __VLC_H__ +#define __VLC_H__ + +#include "coroutine.h" + +static u8 data[2]; +static u8 dword[4]; +extern tIPU_BP g_BP; +extern decoder_t g_decoder; +extern void ReorderBitstream(); + +static __forceinline void GETWORD(u32 * bit_buf,int bits) +{ + while(!getBits16(data,1)) + { + so_resume(); + } + *bit_buf |= ((data[0] << 8) | data[1]) << (bits); +} + +static __forceinline void bitstream_init (decoder_t * decoder){ + decoder->bitstream_bits = -16; + + while( !getBits32(dword, 1) ) + so_resume(); + + decoder->bitstream_buf = (dword[0] << 24) | (dword[1] << 16) | + (dword[2] << 8) |dword[3]; +} + +/* make sure that there are at least 16 valid bits in bit_buf */ +#define NEEDBITS(bit_buf,bits,bit_ptr) \ +do { \ + if (bits > 0) { \ + GETWORD(&bit_buf,bits); \ + bits -= 16; \ + } \ +} while (0) + +/* remove num valid bits from bit_buf */ +#define DUMPBITS(bit_buf,bits,num) \ +do { \ + /*IPU_LOG("DUMPBITS %d\n",num);*/ \ + bit_buf <<= (num); \ + bits += (num); \ +} while (0) + +/* take num bits from the high part of bit_buf and zero extend them */ +#define UBITS(bit_buf,num) (((u32)(bit_buf)) >> (32 - (num))) + +/* take num bits from the high part of bit_buf and sign extend them */ +#define SBITS(bit_buf,num) (((s32)(bit_buf)) >> (32 - (num))) + +struct MBtab { + u8 modes; + u8 len; +}; + +struct MVtab { + u8 delta; + u8 len; +}; + +struct DMVtab { + s8 dmv; + u8 len; +}; + +struct CBPtab { + u8 cbp; + u8 len; +}; + +struct DCtab { + u8 size; + u8 len; +}; + +struct DCTtab { + u8 run; + u8 level; + u8 len; +}; + +struct MBAtab { + u8 mba; + u8 len; +}; + + +#define INTRA MACROBLOCK_INTRA +#define QUANT MACROBLOCK_QUANT + +static const MBtab MB_I [] = { + {INTRA|QUANT, 2}, {INTRA, 1} +}; + +#define MC MACROBLOCK_MOTION_FORWARD +#define CODED MACROBLOCK_PATTERN + +static const MBtab MB_P [] = { + {INTRA|QUANT, 6}, {CODED|QUANT, 5}, {MC|CODED|QUANT, 5}, {INTRA, 5}, + {MC, 3}, {MC, 3}, {MC, 3}, {MC, 3}, + {CODED, 2}, {CODED, 2}, {CODED, 2}, {CODED, 2}, + {CODED, 2}, {CODED, 2}, {CODED, 2}, {CODED, 2}, + {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, + {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, + {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, + {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1}, {MC|CODED, 1} +}; + +#define FWD MACROBLOCK_MOTION_FORWARD +#define BWD MACROBLOCK_MOTION_BACKWARD +#define INTER MACROBLOCK_MOTION_FORWARD|MACROBLOCK_MOTION_BACKWARD + +static const MBtab MB_B [] = { + {0, 0}, {INTRA|QUANT, 6}, + {BWD|CODED|QUANT, 6}, {FWD|CODED|QUANT, 6}, + {INTER|CODED|QUANT, 5}, {INTER|CODED|QUANT, 5}, + {INTRA, 5}, {INTRA, 5}, + {FWD, 4}, {FWD, 4}, {FWD, 4}, {FWD, 4}, + {FWD|CODED, 4}, {FWD|CODED, 4}, {FWD|CODED, 4}, {FWD|CODED, 4}, + {BWD, 3}, {BWD, 3}, {BWD, 3}, {BWD, 3}, + {BWD, 3}, {BWD, 3}, {BWD, 3}, {BWD, 3}, + {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3}, + {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3}, {BWD|CODED, 3}, + {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2}, + {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2}, + {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2}, + {INTER, 2}, {INTER, 2}, {INTER, 2}, {INTER, 2}, + {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, + {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, + {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, + {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2}, {INTER|CODED, 2} +}; + +#undef INTRA +#undef QUANT +#undef MC +#undef CODED +#undef FWD +#undef BWD +#undef INTER + + +static const MVtab MV_4 [] = { + { 3, 6}, { 2, 4}, { 1, 3}, { 1, 3}, { 0, 2}, { 0, 2}, { 0, 2}, { 0, 2} +}; + +static const MVtab MV_10 [] = { + { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10}, { 0,10}, + { 0,10}, { 0,10}, { 0,10}, { 0,10}, {15,10}, {14,10}, {13,10}, {12,10}, + {11,10}, {10,10}, { 9, 9}, { 9, 9}, { 8, 9}, { 8, 9}, { 7, 9}, { 7, 9}, + { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, { 6, 7}, + { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, { 5, 7}, + { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7}, { 4, 7} +}; + + +static const DMVtab DMV_2 [] = { + { 0, 1}, { 0, 1}, { 1, 2}, {-1, 2} +}; + + +static const CBPtab CBP_7 [] = { + {0x22, 7}, {0x12, 7}, {0x0a, 7}, {0x06, 7}, + {0x21, 7}, {0x11, 7}, {0x09, 7}, {0x05, 7}, + {0x3f, 6}, {0x3f, 6}, {0x03, 6}, {0x03, 6}, + {0x24, 6}, {0x24, 6}, {0x18, 6}, {0x18, 6}, + {0x3e, 5}, {0x3e, 5}, {0x3e, 5}, {0x3e, 5}, + {0x02, 5}, {0x02, 5}, {0x02, 5}, {0x02, 5}, + {0x3d, 5}, {0x3d, 5}, {0x3d, 5}, {0x3d, 5}, + {0x01, 5}, {0x01, 5}, {0x01, 5}, {0x01, 5}, + {0x38, 5}, {0x38, 5}, {0x38, 5}, {0x38, 5}, + {0x34, 5}, {0x34, 5}, {0x34, 5}, {0x34, 5}, + {0x2c, 5}, {0x2c, 5}, {0x2c, 5}, {0x2c, 5}, + {0x1c, 5}, {0x1c, 5}, {0x1c, 5}, {0x1c, 5}, + {0x28, 5}, {0x28, 5}, {0x28, 5}, {0x28, 5}, + {0x14, 5}, {0x14, 5}, {0x14, 5}, {0x14, 5}, + {0x30, 5}, {0x30, 5}, {0x30, 5}, {0x30, 5}, + {0x0c, 5}, {0x0c, 5}, {0x0c, 5}, {0x0c, 5}, + {0x20, 4}, {0x20, 4}, {0x20, 4}, {0x20, 4}, + {0x20, 4}, {0x20, 4}, {0x20, 4}, {0x20, 4}, + {0x10, 4}, {0x10, 4}, {0x10, 4}, {0x10, 4}, + {0x10, 4}, {0x10, 4}, {0x10, 4}, {0x10, 4}, + {0x08, 4}, {0x08, 4}, {0x08, 4}, {0x08, 4}, + {0x08, 4}, {0x08, 4}, {0x08, 4}, {0x08, 4}, + {0x04, 4}, {0x04, 4}, {0x04, 4}, {0x04, 4}, + {0x04, 4}, {0x04, 4}, {0x04, 4}, {0x04, 4}, + {0x3c, 3}, {0x3c, 3}, {0x3c, 3}, {0x3c, 3}, + {0x3c, 3}, {0x3c, 3}, {0x3c, 3}, {0x3c, 3}, + {0x3c, 3}, {0x3c, 3}, {0x3c, 3}, {0x3c, 3}, + {0x3c, 3}, {0x3c, 3}, {0x3c, 3}, {0x3c, 3} +}; + +static const CBPtab CBP_9 [] = { + {0, 0}, {0x00, 9}, {0x27, 9}, {0x1b, 9}, + {0x3b, 9}, {0x37, 9}, {0x2f, 9}, {0x1f, 9}, + {0x3a, 8}, {0x3a, 8}, {0x36, 8}, {0x36, 8}, + {0x2e, 8}, {0x2e, 8}, {0x1e, 8}, {0x1e, 8}, + {0x39, 8}, {0x39, 8}, {0x35, 8}, {0x35, 8}, + {0x2d, 8}, {0x2d, 8}, {0x1d, 8}, {0x1d, 8}, + {0x26, 8}, {0x26, 8}, {0x1a, 8}, {0x1a, 8}, + {0x25, 8}, {0x25, 8}, {0x19, 8}, {0x19, 8}, + {0x2b, 8}, {0x2b, 8}, {0x17, 8}, {0x17, 8}, + {0x33, 8}, {0x33, 8}, {0x0f, 8}, {0x0f, 8}, + {0x2a, 8}, {0x2a, 8}, {0x16, 8}, {0x16, 8}, + {0x32, 8}, {0x32, 8}, {0x0e, 8}, {0x0e, 8}, + {0x29, 8}, {0x29, 8}, {0x15, 8}, {0x15, 8}, + {0x31, 8}, {0x31, 8}, {0x0d, 8}, {0x0d, 8}, + {0x23, 8}, {0x23, 8}, {0x13, 8}, {0x13, 8}, + {0x0b, 8}, {0x0b, 8}, {0x07, 8}, {0x07, 8} +}; + +static const DCtab DC_lum_5 [] = { + {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, + {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, + {0, 3}, {0, 3}, {0, 3}, {0, 3}, {3, 3}, {3, 3}, {3, 3}, {3, 3}, + {4, 3}, {4, 3}, {4, 3}, {4, 3}, {5, 4}, {5, 4}, {6, 5} +}; + +static const DCtab DC_chrom_5 [] = { + {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, + {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, + {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, + {3, 3}, {3, 3}, {3, 3}, {3, 3}, {4, 4}, {4, 4}, {5, 5} +}; + +static const DCtab DC_long [] = { + {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, { 6, 5}, { 6, 5}, + {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, {6, 5}, { 6, 5}, { 6, 5}, + {7, 6}, {7, 6}, {7, 6}, {7, 6}, {7, 6}, {7, 6}, { 7, 6}, { 7, 6}, + {8, 7}, {8, 7}, {8, 7}, {8, 7}, {9, 8}, {9, 8}, {10, 9}, {11, 9} +}; + + +static const DCTtab DCT_16 [] = { + {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, + {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, + {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, + {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, {129, 0, 0}, + { 2,18, 0}, { 2,17, 0}, { 2,16, 0}, { 2,15, 0}, + { 7, 3, 0}, { 17, 2, 0}, { 16, 2, 0}, { 15, 2, 0}, + { 14, 2, 0}, { 13, 2, 0}, { 12, 2, 0}, { 32, 1, 0}, + { 31, 1, 0}, { 30, 1, 0}, { 29, 1, 0}, { 28, 1, 0} +}; + +static const DCTtab DCT_15 [] = { + { 1,40,15}, { 1,39,15}, { 1,38,15}, { 1,37,15}, + { 1,36,15}, { 1,35,15}, { 1,34,15}, { 1,33,15}, + { 1,32,15}, { 2,14,15}, { 2,13,15}, { 2,12,15}, + { 2,11,15}, { 2,10,15}, { 2, 9,15}, { 2, 8,15}, + { 1,31,14}, { 1,31,14}, { 1,30,14}, { 1,30,14}, + { 1,29,14}, { 1,29,14}, { 1,28,14}, { 1,28,14}, + { 1,27,14}, { 1,27,14}, { 1,26,14}, { 1,26,14}, + { 1,25,14}, { 1,25,14}, { 1,24,14}, { 1,24,14}, + { 1,23,14}, { 1,23,14}, { 1,22,14}, { 1,22,14}, + { 1,21,14}, { 1,21,14}, { 1,20,14}, { 1,20,14}, + { 1,19,14}, { 1,19,14}, { 1,18,14}, { 1,18,14}, + { 1,17,14}, { 1,17,14}, { 1,16,14}, { 1,16,14} +}; + +static const DCTtab DCT_13 [] = { + { 11, 2,13}, { 10, 2,13}, { 6, 3,13}, { 4, 4,13}, + { 3, 5,13}, { 2, 7,13}, { 2, 6,13}, { 1,15,13}, + { 1,14,13}, { 1,13,13}, { 1,12,13}, { 27, 1,13}, + { 26, 1,13}, { 25, 1,13}, { 24, 1,13}, { 23, 1,13}, + { 1,11,12}, { 1,11,12}, { 9, 2,12}, { 9, 2,12}, + { 5, 3,12}, { 5, 3,12}, { 1,10,12}, { 1,10,12}, + { 3, 4,12}, { 3, 4,12}, { 8, 2,12}, { 8, 2,12}, + { 22, 1,12}, { 22, 1,12}, { 21, 1,12}, { 21, 1,12}, + { 1, 9,12}, { 1, 9,12}, { 20, 1,12}, { 20, 1,12}, + { 19, 1,12}, { 19, 1,12}, { 2, 5,12}, { 2, 5,12}, + { 4, 3,12}, { 4, 3,12}, { 1, 8,12}, { 1, 8,12}, + { 7, 2,12}, { 7, 2,12}, { 18, 1,12}, { 18, 1,12} +}; + +static const DCTtab DCT_B14_10 [] = { + { 17, 1,10}, { 6, 2,10}, { 1, 7,10}, { 3, 3,10}, + { 2, 4,10}, { 16, 1,10}, { 15, 1,10}, { 5, 2,10} +}; + +static const DCTtab DCT_B14_8 [] = { + { 65, 0, 6}, { 65, 0, 6}, { 65, 0, 6}, { 65, 0, 6}, + { 3, 2, 7}, { 3, 2, 7}, { 10, 1, 7}, { 10, 1, 7}, + { 1, 4, 7}, { 1, 4, 7}, { 9, 1, 7}, { 9, 1, 7}, + { 8, 1, 6}, { 8, 1, 6}, { 8, 1, 6}, { 8, 1, 6}, + { 7, 1, 6}, { 7, 1, 6}, { 7, 1, 6}, { 7, 1, 6}, + { 2, 2, 6}, { 2, 2, 6}, { 2, 2, 6}, { 2, 2, 6}, + { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6}, + { 14, 1, 8}, { 1, 6, 8}, { 13, 1, 8}, { 12, 1, 8}, + { 4, 2, 8}, { 2, 3, 8}, { 1, 5, 8}, { 11, 1, 8} +}; + +static const DCTtab DCT_B14AC_5 [] = { + { 1, 3, 5}, { 5, 1, 5}, { 4, 1, 5}, + { 1, 2, 4}, { 1, 2, 4}, { 3, 1, 4}, { 3, 1, 4}, + { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, + {129, 0, 2}, {129, 0, 2}, {129, 0, 2}, {129, 0, 2}, + {129, 0, 2}, {129, 0, 2}, {129, 0, 2}, {129, 0, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2} +}; + +static const DCTtab DCT_B14DC_5 [] = { + { 1, 3, 5}, { 5, 1, 5}, { 4, 1, 5}, + { 1, 2, 4}, { 1, 2, 4}, { 3, 1, 4}, { 3, 1, 4}, + { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, + { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, + { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, + { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, + { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1}, { 1, 1, 1} +}; + +static const DCTtab DCT_B15_10 [] = { + { 6, 2, 9}, { 6, 2, 9}, { 15, 1, 9}, { 15, 1, 9}, + { 3, 4,10}, { 17, 1,10}, { 16, 1, 9}, { 16, 1, 9} +}; + +static const DCTtab DCT_B15_8 [] = { + { 65, 0, 6}, { 65, 0, 6}, { 65, 0, 6}, { 65, 0, 6}, + { 8, 1, 7}, { 8, 1, 7}, { 9, 1, 7}, { 9, 1, 7}, + { 7, 1, 7}, { 7, 1, 7}, { 3, 2, 7}, { 3, 2, 7}, + { 1, 7, 6}, { 1, 7, 6}, { 1, 7, 6}, { 1, 7, 6}, + { 1, 6, 6}, { 1, 6, 6}, { 1, 6, 6}, { 1, 6, 6}, + { 5, 1, 6}, { 5, 1, 6}, { 5, 1, 6}, { 5, 1, 6}, + { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6}, { 6, 1, 6}, + { 2, 5, 8}, { 12, 1, 8}, { 1,11, 8}, { 1,10, 8}, + { 14, 1, 8}, { 13, 1, 8}, { 4, 2, 8}, { 2, 4, 8}, + { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5}, + { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5}, { 3, 1, 5}, + { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5}, + { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5}, { 2, 2, 5}, + { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5}, + { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5}, { 4, 1, 5}, + { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, + { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, + { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, + { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, + { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, + { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, + { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, + { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, { 2, 1, 3}, + {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, + {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, + {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, + {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, {129, 0, 4}, + { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, + { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, + { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, + { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, { 1, 3, 4}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, { 1, 1, 2}, + { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, + { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, + { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, + { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, + { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, + { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, + { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, + { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, { 1, 2, 3}, + { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5}, + { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5}, { 1, 4, 5}, + { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5}, + { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5}, { 1, 5, 5}, + { 10, 1, 7}, { 10, 1, 7}, { 2, 3, 7}, { 2, 3, 7}, + { 11, 1, 7}, { 11, 1, 7}, { 1, 8, 7}, { 1, 8, 7}, + { 1, 9, 7}, { 1, 9, 7}, { 1,12, 8}, { 1,13, 8}, + { 3, 3, 8}, { 5, 2, 8}, { 1,14, 8}, { 1,15, 8} +}; + + +static const MBAtab MBA_5 [] = { + {6, 5}, {5, 5}, {4, 4}, {4, 4}, {3, 4}, {3, 4}, + {2, 3}, {2, 3}, {2, 3}, {2, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1} +}; + +static const MBAtab MBA_11 [] = { + {32, 11}, {31, 11}, {30, 11}, {29, 11}, + {28, 11}, {27, 11}, {26, 11}, {25, 11}, + {24, 11}, {23, 11}, {22, 11}, {21, 11}, + {20, 10}, {20, 10}, {19, 10}, {19, 10}, + {18, 10}, {18, 10}, {17, 10}, {17, 10}, + {16, 10}, {16, 10}, {15, 10}, {15, 10}, + {14, 8}, {14, 8}, {14, 8}, {14, 8}, + {14, 8}, {14, 8}, {14, 8}, {14, 8}, + {13, 8}, {13, 8}, {13, 8}, {13, 8}, + {13, 8}, {13, 8}, {13, 8}, {13, 8}, + {12, 8}, {12, 8}, {12, 8}, {12, 8}, + {12, 8}, {12, 8}, {12, 8}, {12, 8}, + {11, 8}, {11, 8}, {11, 8}, {11, 8}, + {11, 8}, {11, 8}, {11, 8}, {11, 8}, + {10, 8}, {10, 8}, {10, 8}, {10, 8}, + {10, 8}, {10, 8}, {10, 8}, {10, 8}, + { 9, 8}, { 9, 8}, { 9, 8}, { 9, 8}, + { 9, 8}, { 9, 8}, { 9, 8}, { 9, 8}, + { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7}, + { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7}, + { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7}, + { 8, 7}, { 8, 7}, { 8, 7}, { 8, 7}, + { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7}, + { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7}, + { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7}, + { 7, 7}, { 7, 7}, { 7, 7}, { 7, 7} +}; +#endif//__VLC_H__ diff --git a/pcsx2/IPU/yuv2rgb.asm b/pcsx2/IPU/yuv2rgb.asm new file mode 100644 index 0000000000..c5714205ea --- /dev/null +++ b/pcsx2/IPU/yuv2rgb.asm @@ -0,0 +1,242 @@ +;/******************************************************** +; * Some code. Copyright (C) 2003 by Pascal Massimino. * +; * All Rights Reserved. (http://skal.planet-d.net) * +; * For Educational/Academic use ONLY. See 'LICENSE.TXT'.* +; ********************************************************/ +;////////////////////////////////////////////////////////// +;// NASM macros +;////////////////////////////////////////////////////////// + +%ifdef LINUX + +;////////////////////////////////////////////////////////// +; LINUX / egcs / macros +;////////////////////////////////////////////////////////// + +%macro extrn 1 + extern %1 + %define %1 %1 +%endmacro +%macro globl 1 + global %1 + %define %1 %1 +%endmacro + +%macro DATA 0 +[section data align=16 write alloc USE32] +%endmacro +%macro TEXT 0 +[section text align=16 nowrite alloc exec USE32] +%endmacro + +%endif ; LINUX + +;////////////////////////////////////////////////////////// + +%ifdef WIN32 + +%macro extrn 1 + extern _%1 + %define %1 _%1 +%endmacro + +%macro globl 1 + global _%1 + %define %1 _%1 +%endmacro + +%macro DATA 0 +[section .data align=16 write alloc USE32] +%endmacro +%macro TEXT 0 +[section .text align=16 nowrite alloc exec USE32] +%endmacro + +%endif ; WIN32 + +;////////////////////////////////////////////////////////// +; +; MACRO for timing. NASM. +; Total additional code size is 0xb0. +; this keep code alignment right. + +extrn Skl_Cur_Count_ +extrn Skl_Print_Tics + +%macro SKL_USE_RDSTC 0 +extrn SKL_RDTSC_0_ASM +extrn SKL_RDTSC_1_ASM +extrn SKL_RDTSC_2_ASM +%endmacro +%define SKL_RDTSC_OFFSET 15 ; check value with skl_rdtsc.h... + +%macro SKL_RDTSC_IN 0 + SKL_USE_RDSTC + call SKL_RDTSC_0_ASM +.Skl_RDTSC_Loop_: + call SKL_RDTSC_1_ASM +%endmacro + +%macro SKL_RDTSC_OUT 0 + call SKL_RDTSC_2_ASM + dec dword [Skl_Cur_Count_] + jge near .Skl_RDTSC_Loop_ + push dword 53 + call Skl_Print_Tics +%endmacro + +;////////////////////////////////////////////////////////// + +globl Skl_YUV_To_RGB32_MMX + +;////////////////////////////////////////////////////////////////////// + + ; eax: *U + ; ebx: *V + ; esi: *Y + ; edx: Src_BpS + ; edi: *Dst + ; ebx: Dst_BpS + ; ecx: Counter + +%define RGBp esp+20 +%define Yp esp+16 +%define Up esp+12 +%define Vp esp+8 +%define xCnt esp+4 +%define yCnt esp+0 + +Skl_YUV_To_RGB32_MMX: + + push ebx + push esi + push edi + push ebp + + mov edi, [esp+4 +16] ; RGB + mov ebp, [esp+12 +16] ; Y + mov eax, [esp+16 +16] ; U + mov ebx, [esp+20 +16] ; V + mov edx, [esp+24 +16] ; Src_BpS + mov ecx, [esp+28 +16] ; Width + + lea edi, [edi+4*ecx] ; RGB += Width*sizeof(32b) + lea ebp, [ebp+ecx] ; ebp: Y1 = Y + Width + add edx, ebp ; edx: Y2 = Y1+ BpS + push edi ; [RGBp] + push ebp ; [Yp] + shr ecx, 1 ; Width/=2 + lea eax, [eax+ecx] ; U += W/2 + lea ebx, [ebx+ecx] ; V += W/2 + push eax ; [Up] + push ebx ; [Vp] + + neg ecx ; ecx = -Width/2 + push ecx ; save [xCnt] + push eax ; fake ([yCnt]) + + mov ecx, [esp+32 +40] ; Height + shr ecx, 1 ; /2 + + mov esi, [Up] + mov edi, [Vp] + + jmp .Go + +align 16 +.Loop_y + dec ecx + jg .Add + + add esp, 24 ; rid of all tmp + pop ebp + pop edi + pop esi + pop ebx + + ret + +align 16 +.Add + mov edi, [esp+8 +40] ; Dst_BpS + mov esi, [esp+24 +40] ; Src_BpS + mov edx, [RGBp] + mov ebp, [Yp] + lea edx, [edx+2*edi] ; RGB += 2*Dst_BpS + lea ebp, [ebp+2*esi] ; Y += 2*Src_BpS + mov [RGBp], edx + mov edi, [Vp] + mov [Yp], ebp ; Y1 + lea edx, [ebp+esi] ; Y2 + + lea edi, [edi+esi] ; V += Src_BpS + add esi, [Up] ; U += Src_BpS + mov [Vp], edi + mov [Up], esi + +.Go + mov [yCnt], ecx + mov ecx, [xCnt] + + ; 5210c@640x480 + +.Loop_x ; edi,esi: U,V; ebp,edx: Y1, Y2; ecx: xCnt + + ; R = Y + a.U + ; G = Y + c.V + b.U + ; B = Y + d.V + + movzx eax, byte [edi+ecx+0] + movzx ebx, byte [esi+ecx+0] + movq mm0, [Skl_YUV_Tab32_MMX+0*2048 + eax*8] + movzx eax, byte [edi+ecx+1] + paddw mm0, [Skl_YUV_Tab32_MMX+1*2048 + ebx*8] + movzx ebx, byte [esi+ecx+1] + movq mm4, [Skl_YUV_Tab32_MMX+0*2048 + eax*8] + movzx eax, byte [ebp + 2*ecx+0] + paddw mm4, [Skl_YUV_Tab32_MMX+1*2048 + ebx*8] + movzx ebx, byte [ebp + 2*ecx+1] + + movq mm1, mm0 + movq mm2, mm0 + movq mm3, mm0 + movq mm5, mm4 + movq mm6, mm4 + movq mm7, mm4 + + paddw mm0, [Skl_YUV_Tab32_MMX+2*2048 + eax*8] + movzx eax, byte [ebp + 2*ecx+2] + paddw mm1, [Skl_YUV_Tab32_MMX+2*2048 + ebx*8] + movzx ebx, byte [ebp + 2*ecx+3] + packuswb mm0, mm1 + paddw mm4, [Skl_YUV_Tab32_MMX+2*2048 + eax*8] + movzx eax, byte [edx + 2*ecx+0] + paddw mm5, [Skl_YUV_Tab32_MMX+2*2048 + ebx*8] + + packuswb mm4, mm5 + mov esi, [RGBp] + movzx ebx, byte [edx + 2*ecx+1] + movq [esi+8*ecx+0], mm0 ; 2x32b + movq [esi+8*ecx+8], mm4 ; 2x32b + + paddw mm2, [Skl_YUV_Tab32_MMX+2*2048 + eax*8] + movzx eax, byte [edx + 2*ecx+2] + paddw mm3, [Skl_YUV_Tab32_MMX+2*2048 + ebx*8] + movzx ebx, byte [edx + 2*ecx+3] + packuswb mm2, mm3 + paddw mm6, [Skl_YUV_Tab32_MMX+2*2048 + eax*8] + add esi, [esp+8 +40] + paddw mm7, [Skl_YUV_Tab32_MMX+2*2048 + ebx*8] + + mov edi, [Vp] + packuswb mm6, mm7 + movq [esi+8*ecx+0], mm2 ; 2x32b + movq [esi+8*ecx+8], mm6 ; 2x32b + + add ecx, 2 + mov esi, [Up] + + jl near .Loop_x + + mov ecx, [yCnt] + jmp .Loop_y diff --git a/pcsx2/IPU/yuv2rgb.cpp b/pcsx2/IPU/yuv2rgb.cpp new file mode 100644 index 0000000000..edc73dc03d --- /dev/null +++ b/pcsx2/IPU/yuv2rgb.cpp @@ -0,0 +1,514 @@ +/* + * yuv2rgb.c + * Copyright (C) 2000-2002 Michel Lespinasse + * Copyright (C) 1999-2000 Aaron Holtzman + * Modified by Florin for PCSX2 emu + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec 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. + * + * mpeg2dec 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 "PrecompiledHeader.h" + +#include "System.h" +#include "mpeg2lib/Mpeg.h" +#include "yuv2rgb.h" + +//#include "convert_internal.h" //START +struct convert_rgb_t { + u8 * rgb_ptr; + int width; + int uv_stride, uv_stride_frame; + int rgb_stride, rgb_stride_frame; + void (__fastcall * yuv2rgb) (u8 *, u8 *, u8 *, u8 *, + void *, void *, int); +}; + +typedef void __fastcall yuv2rgb_copy (void * id, u8 * const * src, + unsigned int v_offset); + +yuv2rgb_copy __fastcall * yuv2rgb_init_mmxext (int bpp, int mode); +yuv2rgb_copy __fastcall * yuv2rgb_init_mmx (int bpp, int mode); +yuv2rgb_copy __fastcall * yuv2rgb_init_mlib (int bpp, int mode); +//#include "convert_internal.h" //END + +static u32 matrix_coefficients = 6; + +const s32 Inverse_Table_6_9[8][4] = { + {117504, 138453, 13954, 34903}, /*0 no sequence_display_extension */ + {117504, 138453, 13954, 34903}, /*1 ITU-R Rec. 709 (1990) */ + {104597, 132201, 25675, 53279}, /*2 unspecified */ + {104597, 132201, 25675, 53279}, /*3 reserved */ + {104448, 132798, 24759, 53109}, /*4 FCC */ + {104597, 132201, 25675, 53279}, /*5 ITU-R Rec. 624-4 System B, G */ + {104597, 132201, 25675, 53279}, /*6 SMPTE 170M */ + {117579, 136230, 16907, 35559} /*7 SMPTE 240M (1987) */ +}; + +typedef void __fastcall yuv2rgb_c_internal (u8 *, u8 *, u8 *, u8 *, + void *, void *, int); + +void * table_rV[256]; +void * table_gU[256]; +int table_gV[256]; +void * table_bU[256]; + +#define _RGB(type,i) \ + U = pu[i]; \ + V = pv[i]; \ + r = (type *) table_rV[V]; \ + g = (type *) (((u8 *)table_gU[U]) + table_gV[V]); \ + b = (type *) table_bU[U]; + +#define DST(py,dst,i) \ + Y = py[2*i]; \ + dst[2*i] = r[Y] + g[Y] + b[Y]; \ + Y = py[2*i+1]; \ + dst[2*i+1] = r[Y] + g[Y] + b[Y]; + +#define DSTRGB(py,dst,i) \ + Y = py[2*i]; \ + dst[6*i] = r[Y]; dst[6*i+1] = g[Y]; dst[6*i+2] = b[Y]; \ + Y = py[2*i+1]; \ + dst[6*i+3] = r[Y]; dst[6*i+4] = g[Y]; dst[6*i+5] = b[Y]; + +#define DSTBGR(py,dst,i) \ + Y = py[2*i]; \ + dst[6*i] = b[Y]; dst[6*i+1] = g[Y]; dst[6*i+2] = r[Y]; \ + Y = py[2*i+1]; \ + dst[6*i+3] = b[Y]; dst[6*i+4] = g[Y]; dst[6*i+5] = r[Y]; + +static void __fastcall yuv2rgb_c_32 (u8 * py_1, u8 * py_2, + u8 * pu, u8 * pv, + void * _dst_1, void * _dst_2, int width) +{ + int U, V, Y; + u32 * r, * g, * b; + u32 * dst_1, * dst_2; + + width >>= 3; + dst_1 = (u32 *) _dst_1; + dst_2 = (u32 *) _dst_2; + + do { + _RGB (u32, 0); + DST (py_1, dst_1, 0); + DST (py_2, dst_2, 0); + + _RGB (u32, 1); + DST (py_2, dst_2, 1); + DST (py_1, dst_1, 1); + + _RGB (u32, 2); + DST (py_1, dst_1, 2); + DST (py_2, dst_2, 2); + + _RGB (u32, 3); + DST (py_2, dst_2, 3); + DST (py_1, dst_1, 3); + + pu += 4; + pv += 4; + py_1 += 8; + py_2 += 8; + dst_1 += 8; + dst_2 += 8; + } while (--width); +} + +/* This is very near from the yuv2rgb_c_32 code */ +static void __fastcall yuv2rgb_c_24_rgb (u8 * py_1, u8 * py_2, + u8 * pu, u8 * pv, + void * _dst_1, void * _dst_2, int width) +{ + int U, V, Y; + u8 * r, * g, * b; + u8 * dst_1, * dst_2; + + width >>= 3; + dst_1 = (u8 *) _dst_1; + dst_2 = (u8 *) _dst_2; + + do { + _RGB (u8, 0); + DSTRGB (py_1, dst_1, 0); + DSTRGB (py_2, dst_2, 0); + + _RGB (u8, 1); + DSTRGB (py_2, dst_2, 1); + DSTRGB (py_1, dst_1, 1); + + _RGB (u8, 2); + DSTRGB (py_1, dst_1, 2); + DSTRGB (py_2, dst_2, 2); + + _RGB (u8, 3); + DSTRGB (py_2, dst_2, 3); + DSTRGB (py_1, dst_1, 3); + + pu += 4; + pv += 4; + py_1 += 8; + py_2 += 8; + dst_1 += 24; + dst_2 += 24; + } while (--width); +} + +/* only trivial mods from yuv2rgb_c_24_rgb */ +static void __fastcall yuv2rgb_c_24_bgr (u8 * py_1, u8 * py_2, + u8 * pu, u8 * pv, + void * _dst_1, void * _dst_2, int width) +{ + int U, V, Y; + u8 * r, * g, * b; + u8 * dst_1, * dst_2; + + width >>= 3; + dst_1 = (u8 *) _dst_1; + dst_2 = (u8 *) _dst_2; + + do { + _RGB (u8, 0); + DSTBGR (py_1, dst_1, 0); + DSTBGR (py_2, dst_2, 0); + + _RGB (u8, 1); + DSTBGR (py_2, dst_2, 1); + DSTBGR (py_1, dst_1, 1); + + _RGB (u8, 2); + DSTBGR (py_1, dst_1, 2); + DSTBGR (py_2, dst_2, 2); + + _RGB (u8, 3); + DSTBGR (py_2, dst_2, 3); + DSTBGR (py_1, dst_1, 3); + + pu += 4; + pv += 4; + py_1 += 8; + py_2 += 8; + dst_1 += 24; + dst_2 += 24; + } while (--width); +} + +/* This is exactly the same code as yuv2rgb_c_32 except for the types of */ +/* r, g, b, dst_1, dst_2 */ +static void __fastcall yuv2rgb_c_16 (u8 * py_1, u8 * py_2, + u8 * pu, u8 * pv, + void * _dst_1, void * _dst_2, int width) +{ + int U, V, Y; + u16 * r, * g, * b; + u16 * dst_1, * dst_2; + + width >>= 3; + dst_1 = (u16 *) _dst_1; + dst_2 = (u16 *) _dst_2; + + do { + _RGB (u16, 0); + DST (py_1, dst_1, 0); + DST (py_2, dst_2, 0); + + _RGB (u16, 1); + DST (py_2, dst_2, 1); + DST (py_1, dst_1, 1); + + _RGB (u16, 2); + DST (py_1, dst_1, 2); + DST (py_2, dst_2, 2); + + _RGB (u16, 3); + DST (py_2, dst_2, 3); + DST (py_1, dst_1, 3); + + pu += 4; + pv += 4; + py_1 += 8; + py_2 += 8; + dst_1 += 8; + dst_2 += 8; + } while (--width); +} + +static int div_round (int dividend, int divisor) +{ + if (dividend > 0) + return (dividend + (divisor>>1)) / divisor; + else + return -((-dividend + (divisor>>1)) / divisor); +} + +static yuv2rgb_c_internal __fastcall * yuv2rgb_c_init (int order, int bpp) +{ + int i; + u8 table_Y[1024]; + u32 * table_32 = 0; + u16 * table_16 = 0; + u8 * table_8 = 0; + int entry_size = 0; + void * table_r = 0; + void * table_g = 0; + void * table_b = 0; + yuv2rgb_c_internal * yuv2rgb; + + int crv = Inverse_Table_6_9[matrix_coefficients][0]; + int cbu = Inverse_Table_6_9[matrix_coefficients][1]; + int cgu = -Inverse_Table_6_9[matrix_coefficients][2]; + int cgv = -Inverse_Table_6_9[matrix_coefficients][3]; + + for (i = 0; i < 1024; i++) + { + int j; + + j = (76309 * (i - 384 - 16) + 32768) >> 16; + j = (j < 0) ? 0 : ((j > 255) ? 255 : j); + table_Y[i] = j; + } + + switch (bpp) + { + case 32: + yuv2rgb = yuv2rgb_c_32; + + table_32 = (u32 *) malloc ((197 + 2*682 + 256 + 132) * + sizeof (u32)); + + entry_size = sizeof (u32); + table_r = table_32 + 197; + table_b = table_32 + 197 + 685; + table_g = table_32 + 197 + 2*682; + + for (i = -197; i < 256+197; i++) + ((u32 *) table_r)[i] = + table_Y[i+384] << ((order == CONVERT_RGB) ? 16 : 0); + for (i = -132; i < 256+132; i++) + ((u32 *) table_g)[i] = table_Y[i+384] << 8; + for (i = -232; i < 256+232; i++) + ((u32 *) table_b)[i] = + table_Y[i+384] << ((order == CONVERT_RGB) ? 0 : 16); + break; + + case 24: + yuv2rgb = (order == CONVERT_RGB) ? yuv2rgb_c_24_rgb : yuv2rgb_c_24_bgr; + + table_8 = (u8 *) malloc ((256 + 2*232) * sizeof (u8)); + + entry_size = sizeof (u8); + table_r = table_g = table_b = table_8 + 232; + + for (i = -232; i < 256+232; i++) + ((u8 * )table_b)[i] = table_Y[i+384]; + break; + + case 15: + case 16: + yuv2rgb = yuv2rgb_c_16; + + table_16 = (u16 *) malloc ((197 + 2*682 + 256 + 132) * + sizeof (u16)); + + entry_size = sizeof (u16); + table_r = table_16 + 197; + table_b = table_16 + 197 + 685; + table_g = table_16 + 197 + 2*682; + + for (i = -197; i < 256+197; i++) { + int j = table_Y[i+384] >> 3; + + if (order == CONVERT_RGB) + j <<= ((bpp==16) ? 11 : 10); + + ((u16 *)table_r)[i] = j; + } + for (i = -132; i < 256+132; i++) { + int j = table_Y[i+384] >> ((bpp==16) ? 2 : 3); + + ((u16 *)table_g)[i] = j << 5; + } + for (i = -232; i < 256+232; i++) { + int j = table_Y[i+384] >> 3; + + if (order == CONVERT_RGB) + j <<= ((bpp==16) ? 11 : 10); + + ((u16 *)table_b)[i] = j; + } + break; + +#ifdef PCSX2_DEVBUILD + default: + DevCon::Error( "IPU Panic! %ibpp not supported by yuv2rgb", params bpp ); +#else + jNO_DEFAULT +#endif + } + + for (i = 0; i < 256; i++) { + table_rV[i] = (((u8 *)table_r) + + entry_size * div_round (crv * (i-128), 76309)); + table_gU[i] = (((u8 *)table_g) + + entry_size * div_round (cgu * (i-128), 76309)); + table_gV[i] = entry_size * div_round (cgv * (i-128), 76309); + table_bU[i] = (((u8 *)table_b) + + entry_size * div_round (cbu * (i-128), 76309)); + } + + return yuv2rgb; +} + +static void __fastcall convert_yuv2rgb_c (void * _id, u8 * Y, u8 * Cr, u8 * Cb, + unsigned int v_offset) +{ + convert_rgb_t * id = (convert_rgb_t *) _id; + u8 * dst; + u8 * py; + u8 * pu; + u8 * pv; + int loop; + + dst = id->rgb_ptr + id->rgb_stride * v_offset; + py = Y; pu = Cr; pv = Cb; + + loop = 8; + do { + id->yuv2rgb (py, py + (id->uv_stride << 1), pu, pv, + dst, dst + id->rgb_stride, id->width); + py += id->uv_stride << 2; + pu += id->uv_stride; + pv += id->uv_stride; + dst += 2 * id->rgb_stride; + } while (--loop); +} + +static void __fastcall convert_start (void * _id, u8 * dest, int flags) +{ + convert_rgb_t * id = (convert_rgb_t *) _id; + id->rgb_ptr = dest; + switch (flags) { + case CONVERT_BOTTOM_FIELD: + id->rgb_ptr += id->rgb_stride_frame; + /* break thru */ + case CONVERT_TOP_FIELD: + id->uv_stride = id->uv_stride_frame << 1; + id->rgb_stride = id->rgb_stride_frame << 1; + break; + default: + id->uv_stride = id->uv_stride_frame; + id->rgb_stride = id->rgb_stride_frame; + } +} + +static void __fastcall convert_internal (int order, int bpp, int width, int height, + u32 accel, void * arg, convert_init_t * result) +{ + convert_rgb_t * id = (convert_rgb_t *) result->id; + + if (!id) { + result->id_size = sizeof (convert_rgb_t); + } else { + id->width = width; + id->uv_stride_frame = width >> 1; + id->rgb_stride_frame = ((bpp + 7) >> 3) * width; + + result->buf_size[0] = id->rgb_stride_frame * height; + result->buf_size[1] = result->buf_size[2] = 0; + result->start = convert_start; + + result->copy = NULL; + #ifdef ARCH_X86 + if ((result->copy == NULL) && (accel & MPEG2_ACCEL_X86_MMXEXT)) { + result->copy = yuv2rgb_init_mmxext (order, bpp); + } + if ((result->copy == NULL) && (accel & MPEG2_ACCEL_X86_MMX)) { + result->copy = yuv2rgb_init_mmx (order, bpp); + } + #endif + #ifdef LIBVO_MLIB + if ((result->copy == NULL) && (accel & MPEG2_ACCEL_MLIB)) { + result->copy = yuv2rgb_init_mlib (order, bpp); + } + #endif + if (result->copy == NULL) { + result->copy = convert_yuv2rgb_c; + id->yuv2rgb = yuv2rgb_c_init (order, bpp); + } + } +} + +void __fastcall convert_rgb32 (int width, int height, u32 accel, void * arg, + convert_init_t * result) +{ + convert_internal (CONVERT_RGB, 32, width, height, accel, arg, result); +} + +void __fastcall convert_rgb24 (int width, int height, u32 accel, void * arg, + convert_init_t * result) +{ + convert_internal (CONVERT_RGB, 24, width, height, accel, arg, result); +} + +void __fastcall convert_rgb16 (int width, int height, u32 accel, void * arg, + convert_init_t * result) +{ + convert_internal (CONVERT_RGB, 16, width, height, accel, arg, result); +} + +void __fastcall convert_rgb15 (int width, int height, u32 accel, void * arg, + convert_init_t * result) +{ + convert_internal (CONVERT_RGB, 15, width, height, accel, arg, result); +} + +void __fastcall convert_bgr32 (int width, int height, u32 accel, void * arg, + convert_init_t * result) +{ + convert_internal (CONVERT_BGR, 32, width, height, accel, arg, result); +} + +void __fastcall convert_bgr24 (int width, int height, u32 accel, void * arg, + convert_init_t * result) +{ + convert_internal (CONVERT_BGR, 24, width, height, accel, arg, result); +} + +void __fastcall convert_bgr16 (int width, int height, u32 accel, void * arg, + convert_init_t * result) +{ + convert_internal (CONVERT_BGR, 16, width, height, accel, arg, result); +} + +void __fastcall convert_bgr15 (int width, int height, u32 accel, void * arg, + convert_init_t * result) +{ + convert_internal (CONVERT_BGR, 15, width, height, accel, arg, result); +} + +__forceinline convert_t* convert_rgb (int order, int bpp) +{ + if (order == CONVERT_RGB || order == CONVERT_BGR) + switch (bpp) { + case 32: return (order == CONVERT_RGB) ? convert_rgb32 : convert_bgr32; + case 24: return (order == CONVERT_RGB) ? convert_rgb24 : convert_bgr24; + case 16: return (order == CONVERT_RGB) ? convert_rgb16 : convert_bgr16; + case 15: return (order == CONVERT_RGB) ? convert_rgb15 : convert_bgr15; + } + return NULL; +} diff --git a/pcsx2/IPU/yuv2rgb.h b/pcsx2/IPU/yuv2rgb.h new file mode 100644 index 0000000000..656a87f57a --- /dev/null +++ b/pcsx2/IPU/yuv2rgb.h @@ -0,0 +1,57 @@ +/* + * yuv2rgb.h + * Copyright (C) 2000-2002 Michel Lespinasse + * Copyright (C) 1999-2000 Aaron Holtzman + * Modified by Florin for PCSX2 emu + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec 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. + * + * mpeg2dec 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 + */ + +#ifndef YUV2RGB_H +#define YUV2RGB_H + +#define CONVERT_FRAME 0 +#define CONVERT_TOP_FIELD 1 +#define CONVERT_BOTTOM_FIELD 2 +#define CONVERT_BOTH_FIELDS 3 + +struct convert_init_t { + void * id; + int id_size; + int buf_size[3]; + void (__fastcall* start) (void * id, u8 * dest, int flags); + void (__fastcall* copy) (void * id, u8 * Y, u8 * Cr, u8 * Cb, unsigned int v_offset); +}; + +typedef void __fastcall convert_t (int width, int height, u32 accel, void * arg, + convert_init_t * result); + +convert_t convert_rgb32; +convert_t convert_rgb24; +convert_t convert_rgb16; +convert_t convert_rgb15; +convert_t convert_bgr32; +convert_t convert_bgr24; +convert_t convert_bgr16; +convert_t convert_bgr15; + +#define CONVERT_RGB 0 +#define CONVERT_BGR 1 +extern convert_t* convert_rgb (int order, int bpp); + +#endif /* YUV2RGB_H */ diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp new file mode 100644 index 0000000000..70b665235d --- /dev/null +++ b/pcsx2/Interpreter.cpp @@ -0,0 +1,284 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900.h" +#include "R5900OpcodeTables.h" + +#include + +using namespace R5900; // for OPCODE and OpcodeImpl + +extern int vu0branch, vu1branch; + +static int branch2 = 0; +static u32 cpuBlockCycles = 0; // 3 bit fixed point version of cycle count +static std::string disOut; + +// These macros are used to assemble the repassembler functions + +#ifdef PCSX2_DEVBUILD +static void debugI() +{ + //CPU_LOG("%s\n", disR5900Current.getCString()); + if (cpuRegs.GPR.n.r0.UD[0] || cpuRegs.GPR.n.r0.UD[1]) Console::Error("R0 is not zero!!!!"); +} +#else +static void debugI() {} +#endif + +//long int runs=0; + +static void execI() +{ +#ifdef _DEBUG + memRead32(cpuRegs.pc, &cpuRegs.code); + debugI(); +#else + cpuRegs.code = *(u32 *)PSM(cpuRegs.pc); +#endif + + const OPCODE& opcode = GetCurrentInstruction(); + //use this to find out what opcodes your game uses. very slow! (rama) + //runs++; + //if (runs > 1599999999){ //leave some time to startup the testgame + // if (opcode.Name[0] == 'L') { //find all opcodes beginning with "L" + // SysPrintf ("Load %s\n",opcode.Name); + // } + //} + + cpuBlockCycles += opcode.cycles; + cpuRegs.pc += 4; + + opcode.interpret(); +} + +static bool EventRaised = false; + +static __forceinline void _doBranch_shared(u32 tar) +{ + branch2 = cpuRegs.branch = 1; + execI(); + + // branch being 0 means an exception was thrown, since only the exception + // handler should ever clear it. + + if( cpuRegs.branch != 0 ) + { + cpuRegs.pc = tar; + cpuRegs.branch = 0; + } +} + +static void __fastcall doBranch( u32 target ) +{ + _doBranch_shared( target ); + cpuRegs.cycle += cpuBlockCycles >> 3; + cpuBlockCycles &= (1<<3)-1; + EventRaised |= intEventTest(); +} + +void __fastcall intDoBranch(u32 target) +{ + //SysPrintf("Interpreter Branch \n"); + _doBranch_shared( target ); + + if( Cpu == &intCpu ) + { + cpuRegs.cycle += cpuBlockCycles >> 3; + cpuBlockCycles &= (1<<3)-1; + EventRaised |= intEventTest(); + } +} + +void intSetBranch() { + branch2 = /*cpuRegs.branch =*/ 1; +} + +//////////////////////////////////////////////////////////////////// +// R5900 Branching Instructions! +// These are the interpreter versions of the branch instructions. Unlike other +// types of interpreter instructions which can be called safely from the recompilers, +// these instructions are not "recSafe" because they may not invoke the +// necessary branch test logic that the recs need to maintain sync with the +// cpuRegs.pc and delaySlot instruction and such. + +namespace R5900 { +namespace Interpreter { +namespace OpcodeImpl { + +/********************************************************* +* Jump to target * +* Format: OP target * +*********************************************************/ + +void J() { + doBranch(_JumpTarget_); +} + +void JAL() { + _SetLink(31); doBranch(_JumpTarget_); +} + +/********************************************************* +* Register branch logic * +* Format: OP rs, rt, offset * +*********************************************************/ +#define RepBranchi32(op) \ + if (cpuRegs.GPR.r[_Rs_].SD[0] op cpuRegs.GPR.r[_Rt_].SD[0]) doBranch(_BranchTarget_); \ + else intEventTest(); + + +void BEQ() { RepBranchi32(==) } // Branch if Rs == Rt +void BNE() { RepBranchi32(!=) } // Branch if Rs != Rt + +/********************************************************* +* Register branch logic * +* Format: OP rs, offset * +*********************************************************/ +#define RepZBranchi32(op) \ + if(cpuRegs.GPR.r[_Rs_].SD[0] op 0) { \ + doBranch(_BranchTarget_); \ + } + +#define RepZBranchLinki32(op) \ + _SetLink(31); \ + if(cpuRegs.GPR.r[_Rs_].SD[0] op 0) { \ + doBranch(_BranchTarget_); \ + } + +void BGEZ() { RepZBranchi32(>=) } // Branch if Rs >= 0 +void BGEZAL() { RepZBranchLinki32(>=) } // Branch if Rs >= 0 and link +void BGTZ() { RepZBranchi32(>) } // Branch if Rs > 0 +void BLEZ() { RepZBranchi32(<=) } // Branch if Rs <= 0 +void BLTZ() { RepZBranchi32(<) } // Branch if Rs < 0 +void BLTZAL() { RepZBranchLinki32(<) } // Branch if Rs < 0 and link + + +/********************************************************* +* Register branch logic Likely * +* Format: OP rs, offset * +*********************************************************/ +#define RepZBranchi32Likely(op) \ + if(cpuRegs.GPR.r[_Rs_].SD[0] op 0) { \ + doBranch(_BranchTarget_); \ + } else { cpuRegs.pc +=4; intEventTest(); } + +#define RepZBranchLinki32Likely(op) \ + _SetLink(31); \ + if(cpuRegs.GPR.r[_Rs_].SD[0] op 0) { \ + doBranch(_BranchTarget_); \ + } else { cpuRegs.pc +=4; intEventTest(); } + +#define RepBranchi32Likely(op) \ + if(cpuRegs.GPR.r[_Rs_].SD[0] op cpuRegs.GPR.r[_Rt_].SD[0]) { \ + doBranch(_BranchTarget_); \ + } else { cpuRegs.pc +=4; intEventTest(); } + + +void BEQL() { RepBranchi32Likely(==) } // Branch if Rs == Rt +void BNEL() { RepBranchi32Likely(!=) } // Branch if Rs != Rt +void BLEZL() { RepZBranchi32Likely(<=) } // Branch if Rs <= 0 +void BGTZL() { RepZBranchi32Likely(>) } // Branch if Rs > 0 +void BLTZL() { RepZBranchi32Likely(<) } // Branch if Rs < 0 +void BGEZL() { RepZBranchi32Likely(>=) } // Branch if Rs >= 0 +void BLTZALL() { RepZBranchLinki32Likely(<) } // Branch if Rs < 0 and link +void BGEZALL() { RepZBranchLinki32Likely(>=) } // Branch if Rs >= 0 and link + +/********************************************************* +* Register jump * +* Format: OP rs, rd * +*********************************************************/ +void JR() { + doBranch(cpuRegs.GPR.r[_Rs_].UL[0]); +} + +void JALR() { + u32 temp = cpuRegs.GPR.r[_Rs_].UL[0]; + if (_Rd_) { _SetLink(_Rd_); } + doBranch(temp); +} + +} } } // end namespace R5900::Interpreter::OpcodeImpl + +//////////////////////////////////////////////////////// + +void intAlloc() +{ + // fixme : detect cpu for use the optimize asm code +} + +void intReset() +{ + cpuRegs.branch = 0; + branch2 = 0; +} + +bool intEventTest() +{ + // Perform counters, ints, and IOP updates: + return _cpuBranchTest_Shared(); +} + +void intExecute() +{ + g_EEFreezeRegs = false; + + // Mem protection should be handled by the caller here so that it can be + // done in a more optimized fashion. + + EventRaised = false; + + while( !EventRaised ) + { + execI(); + } +} + +static void intExecuteBlock() +{ + g_EEFreezeRegs = false; + + branch2 = 0; + while (!branch2) execI(); +} + +static void intStep() +{ + g_EEFreezeRegs = false; + execI(); +} + +static void intClear(u32 Addr, u32 Size) +{ +} + +static void intShutdown() { +} + +R5900cpu intCpu = { + intAlloc, + intReset, + intStep, + intExecute, + intExecuteBlock, + intClear, + intShutdown +}; diff --git a/pcsx2/IopBios.cpp b/pcsx2/IopBios.cpp new file mode 100644 index 0000000000..6d767964ed --- /dev/null +++ b/pcsx2/IopBios.cpp @@ -0,0 +1,292 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include +#include "PsxCommon.h" + +namespace R3000A { + +const char *biosA0n[256] = { +// 0x00 + "open", "lseek", "read", "write", + "close", "ioctl", "exit", "sys_a0_07", + "getc", "putc", "todigit", "atof", + "strtoul", "strtol", "abs", "labs", +// 0x10 + "atoi", "atol", "atob", "setjmp", + "longjmp", "strcat", "strncat", "strcmp", + "strncmp", "strcpy", "strncpy", "strlen", + "index", "rindex", "strchr", "strrchr", +// 0x20 + "strpbrk", "strspn", "strcspn", "strtok", + "strstr", "toupper", "tolower", "bcopy", + "bzero", "bcmp", "memcpy", "memset", + "memmove", "memcmp", "memchr", "rand", +// 0x30 + "srand", "qsort", "strtod", "malloc", + "free", "lsearch", "bsearch", "calloc", + "realloc", "InitHeap", "_exit", "getchar", + "putchar", "gets", "puts", "printf", +// 0x40 + "sys_a0_40", "LoadTest", "Load", "Exec", + "FlushCache", "InstallInterruptHandler", "GPU_dw", "mem2vram", + "SendGPUStatus", "GPU_cw", "GPU_cwb", "SendPackets", + "sys_a0_4c", "GetGPUStatus", "GPU_sync", "sys_a0_4f", +// 0x50 + "sys_a0_50", "LoadExec", "GetSysSp", "sys_a0_53", + "_96_init()", "_bu_init()", "_96_remove()", "sys_a0_57", + "sys_a0_58", "sys_a0_59", "sys_a0_5a", "dev_tty_init", + "dev_tty_open", "sys_a0_5d", "dev_tty_ioctl","dev_cd_open", +// 0x60 + "dev_cd_read", "dev_cd_close", "dev_cd_firstfile", "dev_cd_nextfile", + "dev_cd_chdir", "dev_card_open", "dev_card_read", "dev_card_write", + "dev_card_close", "dev_card_firstfile", "dev_card_nextfile","dev_card_erase", + "dev_card_undelete","dev_card_format", "dev_card_rename", "dev_card_6f", +// 0x70 + "_bu_init", "_96_init", "_96_remove", "sys_a0_73", + "sys_a0_74", "sys_a0_75", "sys_a0_76", "sys_a0_77", + "_96_CdSeekL", "sys_a0_79", "sys_a0_7a", "sys_a0_7b", + "_96_CdGetStatus", "sys_a0_7d", "_96_CdRead", "sys_a0_7f", +// 0x80 + "sys_a0_80", "sys_a0_81", "sys_a0_82", "sys_a0_83", + "sys_a0_84", "_96_CdStop", "sys_a0_86", "sys_a0_87", + "sys_a0_88", "sys_a0_89", "sys_a0_8a", "sys_a0_8b", + "sys_a0_8c", "sys_a0_8d", "sys_a0_8e", "sys_a0_8f", +// 0x90 + "sys_a0_90", "sys_a0_91", "sys_a0_92", "sys_a0_93", + "sys_a0_94", "sys_a0_95", "AddCDROMDevice", "AddMemCardDevide", + "DisableKernelIORedirection", "EnableKernelIORedirection", "sys_a0_9a", "sys_a0_9b", + "SetConf", "GetConf", "sys_a0_9e", "SetMem", +// 0xa0 + "_boot", "SystemError", "EnqueueCdIntr", "DequeueCdIntr", + "sys_a0_a4", "ReadSector", "get_cd_status", "bufs_cb_0", + "bufs_cb_1", "bufs_cb_2", "bufs_cb_3", "_card_info", + "_card_load", "_card_auto", "bufs_cd_4", "sys_a0_af", +// 0xb0 + "sys_a0_b0", "sys_a0_b1", "do_a_long_jmp", "sys_a0_b3", + "?? sub_function", +}; + +const char *biosB0n[256] = { +// 0x00 + "SysMalloc", "sys_b0_01", "sys_b0_02", "sys_b0_03", + "sys_b0_04", "sys_b0_05", "sys_b0_06", "DeliverEvent", + "OpenEvent", "CloseEvent", "WaitEvent", "TestEvent", + "EnableEvent", "DisableEvent", "OpenTh", "CloseTh", +// 0x10 + "ChangeTh", "sys_b0_11", "InitPAD", "StartPAD", + "StopPAD", "PAD_init", "PAD_dr", "ReturnFromExecption", + "ResetEntryInt", "HookEntryInt", "sys_b0_1a", "sys_b0_1b", + "sys_b0_1c", "sys_b0_1d", "sys_b0_1e", "sys_b0_1f", +// 0x20 + "UnDeliverEvent", "sys_b0_21", "sys_b0_22", "sys_b0_23", + "sys_b0_24", "sys_b0_25", "sys_b0_26", "sys_b0_27", + "sys_b0_28", "sys_b0_29", "sys_b0_2a", "sys_b0_2b", + "sys_b0_2c", "sys_b0_2d", "sys_b0_2e", "sys_b0_2f", +// 0x30 + "sys_b0_30", "sys_b0_31", "open", "lseek", + "read", "write", "close", "ioctl", + "exit", "sys_b0_39", "getc", "putc", + "getchar", "putchar", "gets", "puts", +// 0x40 + "cd", "format", "firstfile", "nextfile", + "rename", "delete", "undelete", "AddDevice", + "RemoteDevice", "PrintInstalledDevices", "InitCARD", "StartCARD", + "StopCARD", "sys_b0_4d", "_card_write", "_card_read", +// 0x50 + "_new_card", "Krom2RawAdd", "sys_b0_52", "sys_b0_53", + "_get_errno", "_get_error", "GetC0Table", "GetB0Table", + "_card_chan", "sys_b0_59", "sys_b0_5a", "ChangeClearPAD", + "_card_status", "_card_wait", +}; + +const char *biosC0n[256] = { +// 0x00 + "InitRCnt", "InitException", "SysEnqIntRP", "SysDeqIntRP", + "get_free_EvCB_slot", "get_free_TCB_slot", "ExceptionHandler", "InstallExeptionHandler", + "SysInitMemory", "SysInitKMem", "ChangeClearRCnt", "SystemError", + "InitDefInt", "sys_c0_0d", "sys_c0_0e", "sys_c0_0f", +// 0x10 + "sys_c0_10", "sys_c0_11", "InstallDevices", "FlushStfInOutPut", + "sys_c0_14", "_cdevinput", "_cdevscan", "_circgetc", + "_circputc", "ioabort", "sys_c0_1a", "KernelRedirect", + "PatchAOTable", +}; + +//#define r0 (psxRegs.GPR.n.r0) +#define at (psxRegs.GPR.n.at) +#define v0 (psxRegs.GPR.n.v0) +#define v1 (psxRegs.GPR.n.v1) +#define a0 (psxRegs.GPR.n.a0) +#define a1 (psxRegs.GPR.n.a1) +#define a2 (psxRegs.GPR.n.a2) +#define a3 (psxRegs.GPR.n.a3) +#define t0 (psxRegs.GPR.n.t0) +#define t1 (psxRegs.GPR.n.t1) +#define t2 (psxRegs.GPR.n.t2) +#define t3 (psxRegs.GPR.n.t3) +#define t4 (psxRegs.GPR.n.t4) +#define t5 (psxRegs.GPR.n.t5) +#define t6 (psxRegs.GPR.n.t6) +#define t7 (psxRegs.GPR.n.t7) +#define s0 (psxRegs.GPR.n.s0) +#define s1 (psxRegs.GPR.n.s1) +#define s2 (psxRegs.GPR.n.s2) +#define s3 (psxRegs.GPR.n.s3) +#define s4 (psxRegs.GPR.n.s4) +#define s5 (psxRegs.GPR.n.s5) +#define s6 (psxRegs.GPR.n.s6) +#define s7 (psxRegs.GPR.n.s7) +#define t8 (psxRegs.GPR.n.t6) +#define t9 (psxRegs.GPR.n.t7) +#define k0 (psxRegs.GPR.n.k0) +#define k1 (psxRegs.GPR.n.k1) +#define gp (psxRegs.GPR.n.gp) +#define sp (psxRegs.GPR.n.sp) +#define fp (psxRegs.GPR.n.s8) +#define ra (psxRegs.GPR.n.ra) +#define pc0 (psxRegs.pc) + +#define Ra0 ((char*)PSXM(a0)) +#define Ra1 ((char*)PSXM(a1)) +#define Ra2 ((char*)PSXM(a2)) +#define Ra3 ((char*)PSXM(a3)) +#define Rv0 ((char*)PSXM(v0)) +#define Rsp ((char*)PSXM(sp)) + +void bios_write() { // 0x35/0x03 + + + if (a0 == 1) { // stdout + char *ptr = Ra1; + + while (a2 > 0) { + SysPrintf("%c", *ptr++); a2--; + } + pc0 = ra; return; + } + PSXBIOS_LOG("bios_%s: %x,%x,%x\n", biosB0n[0x35], a0, a1, a2); + + v0 = -1; + pc0 = ra; +} + +void bios_printf() { // 3f + char tmp[1024]; + char tmp2[1024]; + unsigned long save[4]; + char *ptmp = tmp; + int n=1, i=0, j; + + memcpy(save, (char*)PSXM(sp), 4*4); + psxMu32(sp) = a0; + psxMu32(sp + 4) = a1; + psxMu32(sp + 8) = a2; + psxMu32(sp + 12) = a3; + + while (Ra0[i]) { + switch (Ra0[i]) { + case '%': + j = 0; + tmp2[j++] = '%'; +_start: + switch (Ra0[++i]) { + case '.': + case 'l': + tmp2[j++] = Ra0[i]; goto _start; + default: + if (Ra0[i] >= '0' && Ra0[i] <= '9') { + tmp2[j++] = Ra0[i]; + goto _start; + } + break; + } + tmp2[j++] = Ra0[i]; + tmp2[j] = 0; + + switch (Ra0[i]) { + case 'f': case 'F': + ptmp+= sprintf(ptmp, tmp2, (float)psxMu32(sp + n * 4)); n++; break; + case 'a': case 'A': + case 'e': case 'E': + case 'g': case 'G': + ptmp+= sprintf(ptmp, tmp2, (double)psxMu32(sp + n * 4)); n++; break; + case 'p': + case 'i': + case 'd': case 'D': + case 'o': case 'O': + case 'x': case 'X': + ptmp+= sprintf(ptmp, tmp2, (unsigned int)psxMu32(sp + n * 4)); n++; break; + case 'c': + ptmp+= sprintf(ptmp, tmp2, (unsigned char)psxMu32(sp + n * 4)); n++; break; + case 's': + ptmp+= sprintf(ptmp, tmp2, (char*)PSXM(psxMu32(sp + n * 4))); n++; break; + case '%': + *ptmp++ = Ra0[i]; break; + } + i++; + break; + default: + *ptmp++ = Ra0[i++]; + } + } + *ptmp = 0; + + memcpy((char*)PSXM(sp), save, 4*4); + + Console::Write( Color_Cyan, "%s", params tmp); + + pc0 = ra; +} + +void bios_putchar () { // 3d + Console::Write( Color_Cyan, "%c", params a0 ); + pc0 = ra; +} + +void bios_puts () { // 3e/3f + Console::Write( Color_Cyan, Ra0 ); + pc0 = ra; +} + +void (*biosA0[256])(); +void (*biosB0[256])(); +void (*biosC0[256])(); + +void psxBiosInit() { + int i; + + for(i = 0; i < 256; i++) { + biosA0[i] = NULL; + biosB0[i] = NULL; + biosC0[i] = NULL; + } + biosA0[0x3e] = bios_puts; + biosA0[0x3f] = bios_printf; + + biosB0[0x3d] = bios_putchar; + biosB0[0x3f] = bios_puts; + +} + +void psxBiosShutdown() { +} + +} // end namespace R3000A \ No newline at end of file diff --git a/pcsx2/IopBios.h b/pcsx2/IopBios.h new file mode 100644 index 0000000000..f565df0373 --- /dev/null +++ b/pcsx2/IopBios.h @@ -0,0 +1,41 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __PSXBIOS_H__ +#define __PSXBIOS_H__ + +namespace R3000A +{ + extern const char *biosA0n[256]; + extern const char *biosB0n[256]; + extern const char *biosC0n[256]; + + void psxBiosInit(); + void psxBiosShutdown(); + void psxBiosException(); + void psxBiosFreeze(int Mode); + + extern void (*biosA0[256])(); + extern void (*biosB0[256])(); + extern void (*biosC0[256])(); + + extern void bios_write(); + extern void bios_printf(); + +} +#endif /* __PSXBIOS_H__ */ diff --git a/pcsx2/IopBios2.h b/pcsx2/IopBios2.h new file mode 100644 index 0000000000..68af2547f0 --- /dev/null +++ b/pcsx2/IopBios2.h @@ -0,0 +1,94 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +/***** sysmem imageInfo +00000800: 00 16 00 00 70 14 00 00 ¦ 01 01 00 00 01 00 00 00 +0000 next: .word ? //00001600 +0004 name: .word ? //00001470 +0008 version: .half ? //0101 +000A flags: .half ? //---- +000C index: .half ? //0001 +000E field_E: .half ? //---- +00000810: 90 08 00 00 A0 94 00 00 ¦ 30 08 00 00 40 0C 00 00 +0010 entry: .word ? //00000890 +0014 gp_value: .word ? //000094A0 +0018 p1_vaddr: .word ? //00000830 +001C text_size: .word ? //00000C40 +00000820: 40 00 00 00 10 00 00 00 ¦ 00 00 00 00 00 00 00 00 +0020 data_size: .word ? //00000040 +0024 bss_size: .word ? //00000010 +0028 field_28: .word ? //-------- +002C field_2C: .word ? //-------- +*****/ + +#ifndef __PSX_BIOS_H__ +#define __PSX_BIOS_H__ + +struct irxImageInfo { + u32 next, //+00 + name; //+04 + u16 version, //+08 + flags, //+0A + index, //+0C + _unkE; //+0E + u32 entry, //+10 + _gp, //+14 + vaddr, //+18 + text_size, //+1C + data_size, //+20 + bss_size, //+24 + _pad28, //+28 + _pad2C; //+2C +}; //=30 + +struct _sifServer { + int active; + u32 server; + u32 fhandler; +}; + +#define SIF_SERVERS 32 + +_sifServer sifServer[SIF_SERVERS]; + +// max modules/funcs + +#define IRX_MODULES 64 +#define IRX_FUNCS 256 + +struct irxFunc { + u32 num; + u32 entry; +}; + +struct irxModule { + int active; + u32 name[2]; + irxFunc funcs[IRX_FUNCS]; +}; + +irxModule irxMod[IRX_MODULES]; + + +void iopModulesInit(); +int iopSetImportFunc(u32 *ptr); +int iopSetExportFunc(u32 *ptr); +void sifServerCall(u32 server, u32 num, char *bin, int insize, char *bout, int outsize); +void sifAddServer(u32 server, u32 fhandler); + +#endif diff --git a/pcsx2/IopCounters.cpp b/pcsx2/IopCounters.cpp new file mode 100644 index 0000000000..bc73bc13ea --- /dev/null +++ b/pcsx2/IopCounters.cpp @@ -0,0 +1,726 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// Note on INTC usage: All counters code is always called from inside the context of an +// event test, so instead of using the iopTestIntc we just set the 0x1070 flags directly. +// The EventText function will pick it up. + +#include "PrecompiledHeader.h" + +#include +#include "PsxCommon.h" + +psxCounter psxCounters[8]; +s32 psxNextCounter; +u32 psxNextsCounter; +u8 psxhblankgate = 0; +u8 psxvblankgate = 0; +u8 psxcntmask = 0; + +// flags when the gate is off or counter disabled. (do not count) +#define IOPCNT_STOPPED (0x10000000ul) + +// used to disable targets until after an overflow +#define IOPCNT_FUTURE_TARGET (0x1000000000ULL) + +#define IOPCNT_ENABLE_GATE (1<<0) // enables gate-based counters +#define IOPCNT_INT_TARGET (1<<4) // 0x10 triggers an interrupt on targets +#define IOPCNT_INT_OVERFLOW (1<<5) // 0x20 triggers an interrupt on overflows +#define IOPCNT_ALT_SOURCE (1<<8) // 0x100 uses hblank on counters 1 and 3, and PSXCLOCK on counter 0 + +// Use an arbitrary value to flag HBLANK counters. +// These counters will be counted by the hblank gates coming from the EE, +// which ensures they stay 100% in sync with the EE's hblank counters. +#define PSXHBLANK 0x2001 + +static void psxRcntReset(int index) +{ + psxCounters[index].count = 0; + psxCounters[index].mode&= ~0x18301C00; + psxCounters[index].sCycleT = psxRegs.cycle; +} + +static void _rcntSet( int cntidx ) +{ + u64 overflowCap = (cntidx>=3) ? 0x100000000ULL : 0x10000; + u64 c; + + const psxCounter& counter = psxCounters[cntidx]; + + // psxNextCounter is relative to the psxRegs.cycle when rcntUpdate() was last called. + // However, the current _rcntSet could be called at any cycle count, so we need to take + // that into account. Adding the difference from that cycle count to the current one + // will do the trick! + + if( counter.mode & IOPCNT_STOPPED || counter.rate == PSXHBLANK) return; + + // check for special cases where the overflow or target has just passed + // (we probably missed it because we're doing/checking other things) + if( counter.count > overflowCap || counter.count > counter.target ) + { + psxNextCounter = 4; + return; + } + + c = (u64)((overflowCap - counter.count) * counter.rate) - (psxRegs.cycle - counter.sCycleT); + c += psxRegs.cycle - psxNextsCounter; // adjust for time passed since last rcntUpdate(); + if(c < (u64)psxNextCounter) psxNextCounter = (u32)c; + + //if((counter.mode & 0x10) == 0 || psxCounters[i].target > 0xffff) continue; + if( counter.target & IOPCNT_FUTURE_TARGET ) return; + + c = (s64)((counter.target - counter.count) * counter.rate) - (psxRegs.cycle - counter.sCycleT); + c += psxRegs.cycle - psxNextsCounter; // adjust for time passed since last rcntUpdate(); + if(c < (u64)psxNextCounter) psxNextCounter = (u32)c; +} + + +void psxRcntInit() { + int i; + + memzero_obj( psxCounters ); + + for (i=0; i<3; i++) { + psxCounters[i].rate = 1; + psxCounters[i].mode|= 0x0400; + psxCounters[i].target = IOPCNT_FUTURE_TARGET; + } + for (i=3; i<6; i++) { + psxCounters[i].rate = 1; + psxCounters[i].mode|= 0x0400; + psxCounters[i].target = IOPCNT_FUTURE_TARGET; + } + + psxCounters[0].interrupt = 0x10; + psxCounters[1].interrupt = 0x20; + psxCounters[2].interrupt = 0x40; + + psxCounters[3].interrupt = 0x04000; + psxCounters[4].interrupt = 0x08000; + psxCounters[5].interrupt = 0x10000; + + if (SPU2async != NULL) + { + psxCounters[6].rate = 768*12; + psxCounters[6].CycleT = psxCounters[6].rate; + psxCounters[6].mode = 0x8; + } + + if (USBasync != NULL) + { + psxCounters[7].rate = PSXCLK/1000; + psxCounters[7].CycleT = psxCounters[7].rate; + psxCounters[7].mode = 0x8; + } + + for (i=0; i<8; i++) + psxCounters[i].sCycleT = psxRegs.cycle; + + // Tell the IOP to branch ASAP, so that timers can get + // configured properly. + psxNextCounter = 1; + psxNextsCounter = psxRegs.cycle; +} + +static void __fastcall _rcntTestTarget( int i ) +{ + if( psxCounters[i].count < psxCounters[i].target ) return; + + PSXCNT_LOG("IOP Counter[%d] target 0x%I64x >= 0x%I64x (mode: %x)\n", + i, psxCounters[i].count, psxCounters[i].target, psxCounters[i].mode); + + if (psxCounters[i].mode & IOPCNT_INT_TARGET) + { + // Target interrupt + + if(psxCounters[i].mode & 0x80) + psxCounters[i].mode &= ~0x0400; // Interrupt flag + psxCounters[i].mode |= 0x0800; // Target flag + + psxHu32(0x1070) |= psxCounters[i].interrupt; + } + + if (psxCounters[i].mode & 0x08) + { + // Reset on target + psxCounters[i].count -= psxCounters[i].target; + if(!(psxCounters[i].mode & 0x40)) + { + SysPrintf("Counter %x repeat intr not set on zero ret, ignoring target\n", i); + psxCounters[i].target |= IOPCNT_FUTURE_TARGET; + } + } else psxCounters[i].target |= IOPCNT_FUTURE_TARGET; +} + + +static __forceinline void _rcntTestOverflow( int i ) +{ + u64 maxTarget = ( i < 3 ) ? 0xffff : 0xfffffffful; + if( psxCounters[i].count <= maxTarget ) return; + + PSXCNT_LOG("IOP Counter[%d] overflow 0x%I64x >= 0x%I64x (mode: %x)\n", + i, psxCounters[i].count, maxTarget, psxCounters[i].mode ); + + if(psxCounters[i].mode & IOPCNT_INT_OVERFLOW) + { + // Overflow interrupt + psxHu32(0x1070) |= psxCounters[i].interrupt; + psxCounters[i].mode |= 0x1000; // Overflow flag + if(psxCounters[i].mode & 0x80) + psxCounters[i].mode &= ~0x0400; // Interrupt flag + } + + // Update count and target. + // Count wraps around back to zero, while the target is restored (if needed). + // (high bit of the target gets set by rcntWtarget when the target is behind + // the counter value, and thus should not be flagged until after an overflow) + + psxCounters[i].count &= maxTarget; + psxCounters[i].target &= maxTarget; +} + +/* +Gate: + TM_NO_GATE 000 + TM_GATE_ON_Count 001 + TM_GATE_ON_ClearStart 011 + TM_GATE_ON_Clear_OFF_Start 101 + TM_GATE_ON_Start 111 + + V-blank ----+ +----------------------------+ +------ + | | | | + | | | | + +----+ +----+ + TM_NO_GATE: + + 0================================>============ + + TM_GATE_ON_Count: + + <---->0==========================><---->0===== + + TM_GATE_ON_ClearStart: + + 0====>0================================>0===== + + TM_GATE_ON_Clear_OFF_Start: + + 0====><-------------------------->0====><----- + + TM_GATE_ON_Start: + + <---->0==========================>============ +*/ + +static void _psxCheckStartGate( int i ) +{ + if(!(psxCounters[i].mode & IOPCNT_ENABLE_GATE)) return; //Ignore Gate + + switch((psxCounters[i].mode & 0x6) >> 1) + { + case 0x0: //GATE_ON_count - stop count on gate start: + + // get the current count at the time of stoppage: + psxCounters[i].count = ( i < 3 ) ? + psxRcntRcount16( i ) : psxRcntRcount32( i ); + psxCounters[i].mode |= IOPCNT_STOPPED; + return; + + case 0x1: //GATE_ON_ClearStart - count normally with resets after every end gate + // do nothing - All counting will be done on a need-to-count basis. + return; + + case 0x2: //GATE_ON_Clear_OFF_Start - start counting on gate start, stop on gate end + psxCounters[i].count = 0; + psxCounters[i].sCycleT = psxRegs.cycle; + psxCounters[i].mode &= ~IOPCNT_STOPPED; + break; + + case 0x3: //GATE_ON_Start - start and count normally on gate end (no restarts or stops or clears) + // do nothing! + return; + } + _rcntSet( i ); +} + +static void _psxCheckEndGate(int i) +{ + if(!(psxCounters[i].mode & IOPCNT_ENABLE_GATE)) return; //Ignore Gate + + switch((psxCounters[i].mode & 0x6) >> 1) + { + case 0x0: //GATE_ON_count - reset and start counting + case 0x1: //GATE_ON_ClearStart - count normally with resets after every end gate + psxCounters[i].count = 0; + psxCounters[i].sCycleT = psxRegs.cycle; + psxCounters[i].mode &= ~IOPCNT_STOPPED; + break; + + case 0x2: //GATE_ON_Clear_OFF_Start - start counting on gate start, stop on gate end + psxCounters[i].count = ( i < 3 ) ? + psxRcntRcount16( i ) : psxRcntRcount32( i ); + psxCounters[i].mode |= IOPCNT_STOPPED; + return; // do not set the counter + + case 0x3: //GATE_ON_Start - start and count normally (no restarts or stops or clears) + if( psxCounters[i].mode & IOPCNT_STOPPED ) + { + psxCounters[i].count = 0; + psxCounters[i].sCycleT = psxRegs.cycle; + psxCounters[i].mode &= ~IOPCNT_STOPPED; + } + break; + } + _rcntSet( i ); +} + +void psxCheckStartGate16(int i) +{ + assert( i < 3 ); + + if(i == 0) // hSync counting... + { + // AlternateSource/scanline counters for Gates 1 and 3. + // We count them here so that they stay nicely synced with the EE's hsync. + + const u32 altSourceCheck = IOPCNT_ALT_SOURCE | IOPCNT_ENABLE_GATE; + const u32 stoppedGateCheck = (IOPCNT_STOPPED | altSourceCheck ); + + // count if alt source is enabled and either: + // * the gate is enabled and not stopped. + // * the gate is disabled. + + if( (psxCounters[1].mode & altSourceCheck) == IOPCNT_ALT_SOURCE || + (psxCounters[1].mode & stoppedGateCheck ) == altSourceCheck ) + { + psxCounters[1].count++; + _rcntTestTarget( 1 ); + _rcntTestOverflow( 1 ); + } + + if( (psxCounters[3].mode & altSourceCheck) == IOPCNT_ALT_SOURCE || + (psxCounters[3].mode & stoppedGateCheck ) == altSourceCheck ) + { + psxCounters[3].count++; + _rcntTestTarget( 3 ); + _rcntTestOverflow( 3 ); + } + } + + _psxCheckStartGate( i ); +} + +void psxCheckEndGate16(int i) +{ + assert(i < 3); + _psxCheckEndGate( i ); +} + +static void psxCheckStartGate32(int i) +{ + // 32 bit gate is called for gate 3 only. Ever. + assert(i == 3); + _psxCheckStartGate( i ); +} + +static void psxCheckEndGate32(int i) +{ + assert(i == 3); + _psxCheckEndGate( i ); +} + + +void psxVBlankStart() +{ + cdvdVsync(); + psxHu32(0x1070) |= 1; + if(psxvblankgate & (1 << 1)) psxCheckStartGate16(1); + if(psxvblankgate & (1 << 3)) psxCheckStartGate32(3); +} + +void psxVBlankEnd() +{ + psxHu32(0x1070) |= 0x800; + if(psxvblankgate & (1 << 1)) psxCheckEndGate16(1); + if(psxvblankgate & (1 << 3)) psxCheckEndGate32(3); +} + +void psxRcntUpdate() +{ + int i; + //u32 change = 0; + + for (i=0; i<=5; i++) + { + s32 change = psxRegs.cycle - psxCounters[i].sCycleT; + + // don't count disabled, gated, or hblank counters... + // We can't check the ALTSOURCE flag because the PSXCLOCK source *should* + // be counted here. + + if( psxCounters[i].mode & (IOPCNT_STOPPED | IOPCNT_ENABLE_GATE) ) continue; + if( psxCounters[i].rate == PSXHBLANK ) continue; + if( change <= 0 ) continue; + + psxCounters[i].count += change / psxCounters[i].rate; + if(psxCounters[i].rate != 1) + { + change -= (change / psxCounters[i].rate) * psxCounters[i].rate; + psxCounters[i].sCycleT = psxRegs.cycle - change; + } + else + psxCounters[i].sCycleT = psxRegs.cycle; + } + + // Do target/overflow testing + // Optimization Note: This approach is very sound. Please do not try to unroll it + // as the size of the Test functions will cause code cache clutter and slowness. + + for( i=0; i<6; i++ ) + { + // don't do target/oveflow checks for hblankers. Those + // checks are done when the counters are updated. + if( psxCounters[i].rate == PSXHBLANK ) continue; + if( psxCounters[i].mode & IOPCNT_STOPPED ) continue; + + _rcntTestTarget( i ); + _rcntTestOverflow( i ); + + // perform second target test because if we overflowed above it's possible we + // already shot past our target if it was very near zero. + + //if( psxCounters[i].count >= psxCounters[i].target ) _rcntTestTarget( i ); + } + + psxNextCounter = 0xffffff; + psxNextsCounter = psxRegs.cycle; + + if(SPU2async) + { + const s32 difference = psxRegs.cycle - psxCounters[6].sCycleT; + s32 c = psxCounters[6].CycleT; + + if(difference >= psxCounters[6].CycleT) + { + SPU2async(difference); + psxCounters[6].sCycleT = psxRegs.cycle; + psxCounters[6].CycleT = psxCounters[6].rate; + } + else c -= difference; + psxNextCounter = c; + } + + if(USBasync) + { + const s32 difference = psxRegs.cycle - psxCounters[7].sCycleT; + s32 c = psxCounters[7].CycleT; + + if(difference >= psxCounters[7].CycleT) + { + USBasync(difference); + psxCounters[7].sCycleT = psxRegs.cycle; + psxCounters[7].CycleT = psxCounters[7].rate; + } + else c -= difference; + if (c < psxNextCounter) psxNextCounter = c; + } + + for (i=0; i<6; i++) _rcntSet( i ); +} + +void psxRcntWcount16(int index, u32 value) +{ + u32 change; + + assert( index < 3 ); + PSXCNT_LOG("IOP Counter[%d] writeCount16 = %x\n", index, value); + + if(psxCounters[index].rate != PSXHBLANK) + { + // Re-adjust the sCycleT to match where the counter is currently + // (remainder of the rate divided into the time passed will do the trick) + + change = psxRegs.cycle - psxCounters[index].sCycleT; + psxCounters[index].sCycleT = psxRegs.cycle - (change % psxCounters[index].rate); + } + + psxCounters[index].count = value & 0xffff; + psxCounters[index].target &= 0xffff; + _rcntSet( index ); +} + +void psxRcntWcount32(int index, u32 value) +{ + u32 change; + + assert( index >= 3 && index < 6 ); + PSXCNT_LOG("IOP Counter[%d] writeCount32 = %x\n", index, value); + + if(psxCounters[index].rate != PSXHBLANK) + { + // Re-adjust the sCycleT to match where the counter is currently + // (remainder of the rate divided into the time passed will do the trick) + + change = psxRegs.cycle - psxCounters[index].sCycleT; + psxCounters[index].sCycleT = psxRegs.cycle - (change % psxCounters[index].rate); + } + + psxCounters[index].count = value & 0xffffffff; + psxCounters[index].target &= 0xffffffff; + _rcntSet( index ); +} + +void psxRcnt0Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[0] writeMode = %lx\n", value); + + psxCounters[0].mode = value; + psxCounters[0].mode|= 0x0400; + psxCounters[0].rate = 1; + + if(value & IOPCNT_ALT_SOURCE) + psxCounters[0].rate = PSXPIXEL; + + if(psxCounters[0].mode & IOPCNT_ENABLE_GATE) + { + // gated counters are added up as per the h/vblank timers. + PSXCNT_LOG("IOP Counter[0] Gate Check set, value = %x\n", value); + psxhblankgate |= 1; + } + else psxhblankgate &= ~1; + + psxCounters[0].count = 0; + psxCounters[0].sCycleT = psxRegs.cycle; + psxCounters[0].target &= 0xffff; + + _rcntSet( 0 ); +} + +void psxRcnt1Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[0] writeMode = %lx\n", value); + + psxCounters[1].mode = value; + psxCounters[1].mode|= 0x0400; + psxCounters[1].rate = 1; + + if(value & IOPCNT_ALT_SOURCE) + psxCounters[1].rate = PSXHBLANK; + + if(psxCounters[1].mode & IOPCNT_ENABLE_GATE) + { + PSXCNT_LOG("IOP Counter[1] Gate Check set, value = %x\n", value); + psxvblankgate |= 1<<1; + } + else psxvblankgate &= ~(1<<1); + + psxCounters[1].count = 0; + psxCounters[1].sCycleT = psxRegs.cycle; + psxCounters[1].target &= 0xffff; + _rcntSet( 1 ); +} + +void psxRcnt2Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[0] writeMode = %lx\n", value); + + psxCounters[2].mode = value; + psxCounters[2].mode|= 0x0400; + + switch(value & 0x200) + { + case 0x200: psxCounters[2].rate = 8; break; + case 0x000: psxCounters[2].rate = 1; break; + } + + if((psxCounters[2].mode & 0x7) == 0x7 || (psxCounters[2].mode & 0x7) == 0x1) + { + //SysPrintf("Gate set on IOP C2, disabling\n"); + psxCounters[2].mode |= IOPCNT_STOPPED; + } + + psxCounters[2].count = 0; + psxCounters[2].sCycleT = psxRegs.cycle; + psxCounters[2].target &= 0xffff; + _rcntSet( 2 ); +} + +void psxRcnt3Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[3] writeMode = %lx\n", value); + + psxCounters[3].mode = value; + psxCounters[3].rate = 1; + psxCounters[3].mode|= 0x0400; + + if(value & IOPCNT_ALT_SOURCE) + psxCounters[3].rate = PSXHBLANK; + + if(psxCounters[3].mode & IOPCNT_ENABLE_GATE) + { + PSXCNT_LOG("IOP Counter[3] Gate Check set, value = %x\n", value); + psxvblankgate |= 1<<3; + } + else psxvblankgate &= ~(1<<3); + + psxCounters[3].count = 0; + psxCounters[3].sCycleT = psxRegs.cycle; + psxCounters[3].target &= 0xffffffff; + _rcntSet( 3 ); +} + +void psxRcnt4Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[4] writeMode = %lx\n", value); + + psxCounters[4].mode = value; + psxCounters[4].mode|= 0x0400; + + switch(value & 0x6000) + { + case 0x0000: psxCounters[4].rate = 1; break; + case 0x2000: psxCounters[4].rate = 8; break; + case 0x4000: psxCounters[4].rate = 16; break; + case 0x6000: psxCounters[4].rate = 256; break; + } + // Need to set a rate and target + if((psxCounters[4].mode & 0x7) == 0x7 || (psxCounters[4].mode & 0x7) == 0x1) + { + SysPrintf("Gate set on IOP C4, disabling\n"); + psxCounters[4].mode |= IOPCNT_STOPPED; + } + + psxCounters[4].count = 0; + psxCounters[4].sCycleT = psxRegs.cycle; + psxCounters[4].target &= 0xffffffff; + _rcntSet( 4 ); +} + +void psxRcnt5Wmode(u32 value) +{ + PSXCNT_LOG("IOP Counter[5] writeMode = %lx\n", value); + + psxCounters[5].mode = value; + psxCounters[5].mode|= 0x0400; + + switch(value & 0x6000) + { + case 0x0000: psxCounters[5].rate = 1; break; + case 0x2000: psxCounters[5].rate = 8; break; + case 0x4000: psxCounters[5].rate = 16; break; + case 0x6000: psxCounters[5].rate = 256; break; + } + // Need to set a rate and target + if((psxCounters[5].mode & 0x7) == 0x7 || (psxCounters[5].mode & 0x7) == 0x1) + { + SysPrintf("Gate set on IOP C5, disabling\n"); + psxCounters[5].mode |= IOPCNT_STOPPED; + } + + psxCounters[5].count = 0; + psxCounters[5].sCycleT = psxRegs.cycle; + psxCounters[5].target &= 0xffffffff; + _rcntSet( 5 ); +} + +void psxRcntWtarget16(int index, u32 value) +{ + assert( index < 3 ); + PSXCNT_LOG("IOP Counter[%d] writeTarget16 = %lx\n", index, value); + psxCounters[index].target = value & 0xffff; + + // protect the target from an early arrival. + // if the target is behind the current count, then set the target overflow + // flag, so that the target won't be active until after the next overflow. + + if(psxCounters[index].target <= psxRcntCycles(index)) + psxCounters[index].target |= IOPCNT_FUTURE_TARGET; + + _rcntSet( index ); +} + +void psxRcntWtarget32(int index, u32 value) +{ + assert( index >= 3 && index < 6); + PSXCNT_LOG("IOP Counter[%d] writeTarget32 = %lx\n", index, value); + + psxCounters[index].target = value; + + // protect the target from an early arrival. + // if the target is behind the current count, then set the target overflow + // flag, so that the target won't be active until after the next overflow. + + if(psxCounters[index].target <= psxRcntCycles(index)) + psxCounters[index].target |= IOPCNT_FUTURE_TARGET; + + _rcntSet( index ); +} + +u16 psxRcntRcount16(int index) +{ + u32 retval = (u32)psxCounters[index].count; + + assert( index < 3 ); + + PSXCNT_LOG("IOP Counter[%d] readCount16 = %lx\n", index, (u16)retval ); + + // Don't count HBLANK timers + // Don't count stopped gates either. + + if( !( psxCounters[index].mode & IOPCNT_STOPPED ) && + ( psxCounters[index].rate != PSXHBLANK ) ) + { + u32 delta = (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate); + retval += delta; + PSXCNT_LOG(" (delta = %lx)\n", delta ); + } + + return (u16)retval; +} + +u32 psxRcntRcount32(int index) +{ + u32 retval = (u32)psxCounters[index].count; + + assert( index >= 3 && index < 6 ); + + PSXCNT_LOG("IOP Counter[%d] readCount32 = %lx\n", index, retval ); + + if( !( psxCounters[index].mode & IOPCNT_STOPPED ) && + ( psxCounters[index].rate != PSXHBLANK ) ) + { + u32 delta = (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate); + retval += delta; + PSXCNT_LOG(" (delta = %lx)\n", delta ); + } + + return retval; +} + +u64 psxRcntCycles(int index) +{ + if(psxCounters[index].mode & IOPCNT_STOPPED || psxCounters[index].rate == PSXHBLANK ) return psxCounters[index].count; + return (u64)(psxCounters[index].count + (u32)((psxRegs.cycle - psxCounters[index].sCycleT) / psxCounters[index].rate)); +} + +void SaveState::psxRcntFreeze() +{ + Freeze(psxCounters); + Freeze(psxNextCounter); + Freeze(psxNextsCounter); +} diff --git a/pcsx2/IopCounters.h b/pcsx2/IopCounters.h new file mode 100644 index 0000000000..a8ce59d3f5 --- /dev/null +++ b/pcsx2/IopCounters.h @@ -0,0 +1,62 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __PSXCOUNTERS_H__ +#define __PSXCOUNTERS_H__ + +// fixme: sCycle, Cycle, and otarget are unused +// Can't remove them without making a new savestate version though. + +struct psxCounter { + u64 count, target; + u32 mode; + u32 rate, interrupt, otarget; + u32 sCycle, Cycle; + u32 sCycleT; + s32 CycleT; +}; + +extern psxCounter psxCounters[8]; +extern s32 psxNextCounter; +extern u32 psxNextsCounter; + +void psxRcntInit(); +void psxRcntUpdate(); +void cntspu2async(); +void psxRcntWcount16(int index, u32 value); +void psxRcntWcount32(int index, u32 value); +void psxRcnt0Wmode(u32 value); +void psxRcnt1Wmode(u32 value); +void psxRcnt2Wmode(u32 value); +void psxRcnt3Wmode(u32 value); +void psxRcnt4Wmode(u32 value); +void psxRcnt5Wmode(u32 value); +void psxRcntWtarget16(int index, u32 value); +void psxRcntWtarget32(int index, u32 value); +u16 psxRcntRcount16(int index); +u32 psxRcntRcount32(int index); +u64 psxRcntCycles(int index); + +void psxVBlankStart(); +void psxVBlankEnd(); +void psxCheckStartGate16(int i); +void psxCheckEndGate16(int i); +//static void psxCheckStartGate32(int i); +//static void psxCheckEndGate32(int i); + +#endif /* __PSXCOUNTERS_H__ */ diff --git a/pcsx2/IopDma.cpp b/pcsx2/IopDma.cpp new file mode 100644 index 0000000000..d6eb47c079 --- /dev/null +++ b/pcsx2/IopDma.cpp @@ -0,0 +1,382 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" + +using namespace R3000A; + +// Dma0/1 in Mdec.c +// Dma3 in CdRom.c +// Dma8 in PsxSpd.c +// Dma11/12 in PsxSio2.c + +int iopsifbusy[2] = { 0, 0 }; + +static void __fastcall psxDmaGeneric(u32 madr, u32 bcr, u32 chcr, u32 spuCore, _SPU2writeDMA4Mem spu2WriteFunc, _SPU2readDMA4Mem spu2ReadFunc ) +{ + const char dmaNum = spuCore ? '7' : '4'; + /*if (chcr & 0x400) DevCon::Status("SPU 2 DMA %c linked list chain mode! chcr = %x madr = %x bcr = %x\n", dmaNum, chcr, madr, bcr); + if (chcr & 0x40000000) DevCon::Notice("SPU 2 DMA %c Unusual bit set on 'to' direction chcr = %x madr = %x bcr = %x\n", dmaNum, chcr, madr, bcr); + if ((chcr & 0x1) == 0) DevCon::Status("SPU 2 DMA %c loading from spu2 memory chcr = %x madr = %x bcr = %x\n", dmaNum, chcr, madr, bcr);*/ + + const int size = (bcr >> 16) * (bcr & 0xFFFF); + + // Update the spu2 to the current cycle before initiating the DMA + + if(SPU2async) + { + SPU2async(psxRegs.cycle - psxCounters[6].sCycleT); + //Console::Status("cycles sent to SPU2 %x\n", psxRegs.cycle - psxCounters[6].sCycleT); + + psxCounters[6].sCycleT = psxRegs.cycle; + psxCounters[6].CycleT = size * 3; + + psxNextCounter -= (psxRegs.cycle-psxNextsCounter); + psxNextsCounter = psxRegs.cycle; + if(psxCounters[6].CycleT < psxNextCounter) + psxNextCounter = psxCounters[6].CycleT; + } + + switch (chcr) + { + case 0x01000201: //cpu to spu2 transfer + PSXDMA_LOG("*** DMA %c - mem2spu *** %lx addr = %lx size = %lx\n", dmaNum, chcr, madr, bcr); + spu2WriteFunc((u16 *)PSXM(madr), size*2); + break; + + case 0x01000200: //spu2 to cpu transfer + PSXDMA_LOG("*** DMA %c - spu2mem *** %lx addr = %lx size = %lx\n", dmaNum, chcr, madr, bcr); + spu2ReadFunc((u16 *)PSXM(madr), size*2); + psxCpu->Clear(spuCore ? HW_DMA7_MADR : HW_DMA4_MADR, size); + break; + + default: + Console::Error("*** DMA %c - SPU unknown *** %lx addr = %lx size = %lx\n", params dmaNum, chcr, madr, bcr); + break; + } +} + +void psxDma4(u32 madr, u32 bcr, u32 chcr) // SPU2's Core 0 +{ + psxDmaGeneric( madr, bcr, chcr, 0, SPU2writeDMA4Mem, SPU2readDMA4Mem ); +} + +int psxDma4Interrupt() +{ + HW_DMA4_CHCR &= ~0x01000000; + psxDmaInterrupt(4); + iopIntcIrq( 9 ); + return 1; +} + +void psxDma2(u32 madr, u32 bcr, u32 chcr) // GPU +{ + HW_DMA2_CHCR &= ~0x01000000; + psxDmaInterrupt(2); +} + +void psxDma6(u32 madr, u32 bcr, u32 chcr) +{ + u32 *mem = (u32 *)PSXM(madr); + + PSXDMA_LOG("*** DMA 6 - OT *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); + + if (chcr == 0x11000002) { + while (bcr--) { + *mem-- = (madr - 4) & 0xffffff; + madr -= 4; + } + mem++; *mem = 0xffffff; + } else { + // Unknown option + PSXDMA_LOG("*** DMA 6 - OT unknown *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); + } + HW_DMA6_CHCR &= ~0x01000000; + psxDmaInterrupt(6); +} + +void psxDma7(u32 madr, u32 bcr, u32 chcr) // SPU2's Core 1 +{ + psxDmaGeneric( madr, bcr, chcr, 1, SPU2writeDMA7Mem, SPU2readDMA7Mem ); +} + +int psxDma7Interrupt() +{ + HW_DMA7_CHCR &= ~0x01000000; + psxDmaInterrupt2(0); + //iopIntcIrq( 9 ); + return 1; + +} +extern int eesifbusy[2]; +void psxDma9(u32 madr, u32 bcr, u32 chcr) +{ + SIF_LOG("IOP: dmaSIF0 chcr = %lx, madr = %lx, bcr = %lx, tadr = %lx\n", chcr, madr, bcr, HW_DMA9_TADR); + + iopsifbusy[0] = 1; + psHu32(0x1000F240) |= 0x2000; + + if (eesifbusy[0] == 1) { + SIF0Dma(); + psHu32(0x1000F240) &= ~0x20; + psHu32(0x1000F240) &= ~0x2000; + } +} + +void psxDma10(u32 madr, u32 bcr, u32 chcr) { + SIF_LOG("IOP: dmaSIF1 chcr = %lx, madr = %lx, bcr = %lx\n", chcr, madr, bcr); + + iopsifbusy[1] = 1; + psHu32(0x1000F240) |= 0x4000; + + if (eesifbusy[1] == 1) { + FreezeXMMRegs(1); + SIF1Dma(); + psHu32(0x1000F240) &= ~0x40; + psHu32(0x1000F240) &= ~0x100; + psHu32(0x1000F240) &= ~0x4000; + FreezeXMMRegs(0); + } +} + +void psxDma8(u32 madr, u32 bcr, u32 chcr) { + + const int size = (bcr >> 16) * (bcr & 0xFFFF) * 8; + + switch (chcr & 0x01000201) { + case 0x01000201: //cpu to dev9 transfer + PSXDMA_LOG("*** DMA 8 - DEV9 mem2dev9 *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); + DEV9writeDMA8Mem((u32*)PSXM(madr), size); + break; + + case 0x01000200: //dev9 to cpu transfer + PSXDMA_LOG("*** DMA 8 - DEV9 dev9mem *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); + DEV9readDMA8Mem((u32*)PSXM(madr), size); + break; + + default: + PSXDMA_LOG("*** DMA 8 - DEV9 unknown *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); + break; + } + HW_DMA8_CHCR &= ~0x01000000; + psxDmaInterrupt2(1); +} + +void dev9Interrupt() { + if( (dev9Handler != NULL) && (dev9Handler() != 1) ) + return; + + iopIntcIrq( 13 ); + hwIntcIrq(INTC_SBUS); +} + +void dev9Irq(int cycles) { + PSX_INT(IopEvt_DEV9, cycles); +} + +void usbInterrupt() { + if( usbHandler != NULL && (usbHandler() != 1) ) + return; + + iopIntcIrq( 22 ); + hwIntcIrq(INTC_SBUS); +} + +void usbIrq(int cycles) { + PSX_INT(IopEvt_USB, cycles); +} + +void fwIrq() { + iopIntcIrq( 24 ); + hwIntcIrq(INTC_SBUS); +} + +void spu2DMA4Irq() { + SPU2interruptDMA4(); + //HW_DMA4_BCR = 0; + HW_DMA4_CHCR &= ~0x01000000; + psxDmaInterrupt(4); +} + +void spu2DMA7Irq() { + SPU2interruptDMA7(); + //HW_DMA7_BCR = 0; + HW_DMA7_CHCR &= ~0x01000000; + psxDmaInterrupt2(0); +} + +void spu2Irq() { + iopIntcIrq( 9 ); + hwIntcIrq(INTC_SBUS); +} + +void iopIntcIrq( uint irqType ) +{ + psxHu32(0x1070)|= 1<> 16) * (bcr & 0xFFFF); + + IopChannels[channel].Control = chcr | DMA_CTRL_ACTIVE; + IopChannels[channel].MemAddr = madr; + IopChannels[channel].ByteCount = size; +} + +void IopDmaUpdate(u32 elapsed) +{ + u32 MinDelay = 0xFFFFFFFF; + + for(int i=0;iControl&DMA_CTRL_ACTIVE) + { + ch->Target-=elapsed; + if(ch->Target<=0) + { + if(ch->ByteCount<=0) + { + ch->Control &= ~DMA_CTRL_ACTIVE; + RaiseDmaIrq(i); + IopDmaHandlers[i].Interrupt(i); + } + else + { + // TODO: Make sure it's the right order + DmaHandler handler = (ch->Control&DMA_CTRL_DIRECTION)?IopDmaHandlers[i].Read:IopDmaHandlers[i].Write; + + u32 BCount = 0; + s32 Target = (handler)?handler(i,(u32*)PSXM(ch->MemAddr),ch->ByteCount,&BCount):0; + + ch->Target = 100; + if(Target<0) + { + // TODO: ... What to do if the plugin errors? :P + } + else if(BCount!=0) + { + ch->MemAddr += BCount; + ch->ByteCount -= BCount; + + ch->Target = BCount / ch->Width; + } + + if (Target!=0) ch->Target=Target; + } + } + } + } +} + +s32 errDmaRead (s32 channel, u32* data, u32 wordsLeft, u32* wordsProcessed) +{ + Console::Error("ERROR: Tried to read using DMA %d (%s). Ignoring.",0,channel,IopDmaNames[channel]); + + *wordsProcessed = wordsLeft; + return 0; +} + +s32 errDmaWrite (s32 channel, u32* data, u32 wordsLeft, u32* wordsProcessed) +{ + Console::Error("ERROR: Tried to write using DMA %d (%s). Ignoring.",0,channel,IopDmaNames[channel]); + + *wordsProcessed = wordsLeft; + return 0; +} + + +#endif diff --git a/pcsx2/IopDma.h b/pcsx2/IopDma.h new file mode 100644 index 0000000000..7304c85ad8 --- /dev/null +++ b/pcsx2/IopDma.h @@ -0,0 +1,48 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __PSXDMA_H__ +#define __PSXDMA_H__ + +#include "PS2Edefs.h" + +void psxDma2(u32 madr, u32 bcr, u32 chcr); +void psxDma3(u32 madr, u32 bcr, u32 chcr); +void psxDma4(u32 madr, u32 bcr, u32 chcr); +void psxDma6(u32 madr, u32 bcr, u32 chcr); +void psxDma7(u32 madr, u32 bcr, u32 chcr); +void psxDma8(u32 madr, u32 bcr, u32 chcr); +void psxDma9(u32 madr, u32 bcr, u32 chcr); +void psxDma10(u32 madr, u32 bcr, u32 chcr); + +int psxDma4Interrupt(); +int psxDma7Interrupt(); +void dev9Interrupt(); +void dev9Irq(int cycles); +void usbInterrupt(); +void usbIrq(int cycles); +void fwIrq(); +void spu2Irq(); + +extern void iopIntcIrq( uint irqType ); +extern void iopTestIntc(); + +extern DEV9handler dev9Handler; +extern USBhandler usbHandler; + +#endif /* __PSXDMA_H__ */ diff --git a/pcsx2/IopHw.cpp b/pcsx2/IopHw.cpp new file mode 100644 index 0000000000..3040eec5b5 --- /dev/null +++ b/pcsx2/IopHw.cpp @@ -0,0 +1,1413 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" +#include "Misc.h" +#include "iR5900.h" + +// NOTE: Any modifications to read/write fns should also go into their const counterparts +// found in iPsxHw.cpp. + +void psxHwReset() { +/* if (Config.Sio) psxHu32(0x1070) |= 0x80; + if (Config.SpuIrq) psxHu32(0x1070) |= 0x200;*/ + + memzero_ptr<0x10000>(psxH); + +// mdecInit(); //intialize mdec decoder + cdrReset(); + cdvdReset(); + psxRcntInit(); + sioInit(); +// sio2Reset(); +} + +u8 psxHwRead8(u32 add) { + u8 hard; + + if (add >= 0x1f801600 && add < 0x1f801700) { + return USBread8(add); + } + + switch (add) { + case 0x1f801040: hard = sioRead8();break; + // case 0x1f801050: hard = serial_read8(); break;//for use of serial port ignore for now + + case 0x1f80146e: // DEV9_R_REV + return DEV9read8(add); + +#ifdef PCSX2_DEVBUILD + case 0x1f801100: + case 0x1f801104: + case 0x1f801108: + case 0x1f801110: + case 0x1f801114: + case 0x1f801118: + case 0x1f801120: + case 0x1f801124: + case 0x1f801128: + case 0x1f801480: + case 0x1f801484: + case 0x1f801488: + case 0x1f801490: + case 0x1f801494: + case 0x1f801498: + case 0x1f8014a0: + case 0x1f8014a4: + case 0x1f8014a8: + SysPrintf("8bit counter read %x\n", add); + hard = psxHu8(add); + return hard; +#endif + + case 0x1f801800: hard = cdrRead0(); break; + case 0x1f801801: hard = cdrRead1(); break; + case 0x1f801802: hard = cdrRead2(); break; + case 0x1f801803: hard = cdrRead3(); break; + + case 0x1f803100: // PS/EE/IOP conf related + hard = 0x10; // Dram 2M + break; + + case 0x1F808264: + hard = sio2_fifoOut();//sio2 serial data feed/fifo_out + PSXHW_LOG("SIO2 read8 DATAOUT %08X\n", hard); + return hard; + + default: + hard = psxHu8(add); + PSXHW_LOG("*Unknown 8bit read at address %lx\n", add); + return hard; + } + + PSXHW_LOG("*Known 8bit read at address %lx value %x\n", add, hard); + return hard; +} + +u16 psxHwRead16(u32 add) { + u16 hard; + + if (add >= 0x1f801600 && add < 0x1f801700) { + return USBread16(add); + } + + switch (add) { + + case 0x1f801070: PSXHW_LOG("IREG 16bit read %x\n", psxHu16(0x1070)); + return psxHu16(0x1070); + case 0x1f801074: PSXHW_LOG("IMASK 16bit read %x\n", psxHu16(0x1074)); + return psxHu16(0x1074); + + case 0x1f801040: + hard = sioRead8(); + hard|= sioRead8() << 8; + PAD_LOG("sio read16 %lx; ret = %x\n", add&0xf, hard); + return hard; + case 0x1f801044: + hard = sio.StatReg; + PAD_LOG("sio read16 %lx; ret = %x\n", add&0xf, hard); + return hard; + case 0x1f801048: + hard = sio.ModeReg; + PAD_LOG("sio read16 %lx; ret = %x\n", add&0xf, hard); + return hard; + case 0x1f80104a: + hard = sio.CtrlReg; + PAD_LOG("sio read16 %lx; ret = %x\n", add&0xf, hard); + return hard; + case 0x1f80104e: + hard = sio.BaudReg; + PAD_LOG("sio read16 %lx; ret = %x\n", add&0xf, hard); + return hard; + + //Serial port stuff not support now ;P + // case 0x1f801050: hard = serial_read16(); break; + // case 0x1f801054: hard = serial_status_read(); break; + // case 0x1f80105a: hard = serial_control_read(); break; + // case 0x1f80105e: hard = serial_baud_read(); break; + + case 0x1f801100: + hard = (u16)psxRcntRcount16(0); + PSXCNT_LOG("T0 count read16: %x\n", hard); + return hard; + case 0x1f801104: + hard = psxCounters[0].mode; + psxCounters[0].mode &= ~0x1800; + psxCounters[0].mode |= 0x400; + PSXCNT_LOG("T0 mode read16: %x\n", hard); + return hard; + case 0x1f801108: + hard = psxCounters[0].target; + PSXCNT_LOG("T0 target read16: %x\n", hard); + return hard; + case 0x1f801110: + hard = (u16)psxRcntRcount16(1); + PSXCNT_LOG("T1 count read16: %x\n", hard); + return hard; + case 0x1f801114: + hard = psxCounters[1].mode; + psxCounters[1].mode &= ~0x1800; + psxCounters[1].mode |= 0x400; + PSXCNT_LOG("T1 mode read16: %x\n", hard); + return hard; + case 0x1f801118: + hard = psxCounters[1].target; + PSXCNT_LOG("T1 target read16: %x\n", hard); + return hard; + case 0x1f801120: + hard = (u16)psxRcntRcount16(2); + PSXCNT_LOG("T2 count read16: %x\n", hard); + return hard; + case 0x1f801124: + hard = psxCounters[2].mode; + psxCounters[2].mode &= ~0x1800; + psxCounters[2].mode |= 0x400; + PSXCNT_LOG("T2 mode read16: %x\n", hard); + return hard; + case 0x1f801128: + hard = psxCounters[2].target; + PSXCNT_LOG("T2 target read16: %x\n", hard); + return hard; + + case 0x1f80146e: // DEV9_R_REV + return DEV9read16(add); + + case 0x1f801480: + hard = (u16)psxRcntRcount32(3); + PSXCNT_LOG("T3 count read16: %lx\n", hard); + return hard; + case 0x1f801484: + hard = psxCounters[3].mode; + psxCounters[3].mode &= ~0x1800; + psxCounters[3].mode |= 0x400; + PSXCNT_LOG("T3 mode read16: %lx\n", hard); + return hard; + case 0x1f801488: + hard = psxCounters[3].target; + PSXCNT_LOG("T3 target read16: %lx\n", hard); + return hard; + case 0x1f801490: + hard = (u16)psxRcntRcount32(4); + PSXCNT_LOG("T4 count read16: %lx\n", hard); + return hard; + case 0x1f801494: + hard = psxCounters[4].mode; + psxCounters[4].mode &= ~0x1800; + psxCounters[4].mode |= 0x400; + PSXCNT_LOG("T4 mode read16: %lx\n", hard); + return hard; + case 0x1f801498: + hard = psxCounters[4].target; + PSXCNT_LOG("T4 target read16: %lx\n", hard); + return hard; + case 0x1f8014a0: + hard = (u16)psxRcntRcount32(5); + PSXCNT_LOG("T5 count read16: %lx\n", hard); + return hard; + case 0x1f8014a4: + hard = psxCounters[5].mode; + psxCounters[5].mode &= ~0x1800; + psxCounters[5].mode |= 0x400; + PSXCNT_LOG("T5 mode read16: %lx\n", hard); + return hard; + case 0x1f8014a8: + hard = psxCounters[5].target; + PSXCNT_LOG("T5 target read16: %lx\n", hard); + return hard; + + case 0x1f801504: + hard = psxHu16(0x1504); + PSXHW_LOG("DMA7 BCR_size 16bit read %lx\n", hard); + return hard; + case 0x1f801506: + hard = psxHu16(0x1506); + PSXHW_LOG("DMA7 BCR_count 16bit read %lx\n", hard); + return hard; + //case 0x1f802030: hard = //int_2000???? + //case 0x1f802040: hard =//dip switches...?? + + default: + if (add>=0x1f801c00 && add<0x1f801e00) { + hard = SPU2read(add); + } else { + hard = psxHu16(add); + PSXHW_LOG("*Unknown 16bit read at address %lx\n", add); + } + return hard; + } + + + PSXHW_LOG("*Known 16bit read at address %lx value %x\n", add, hard); + return hard; +} + +u32 psxHwRead32(u32 add) { + u32 hard; + + if (add >= 0x1f801600 && add < 0x1f801700) { + return USBread32(add); + } + if (add >= 0x1f808400 && add <= 0x1f808550) {//the size is a complete guess.. + return FWread32(add); + } + + switch (add) { + case 0x1f801040: + hard = sioRead8(); + hard|= sioRead8() << 8; + hard|= sioRead8() << 16; + hard|= sioRead8() << 24; + PAD_LOG("sio read32 ;ret = %lx\n", hard); + return hard; + + // case 0x1f801050: hard = serial_read32(); break;//serial port + case 0x1f801060: + PSXHW_LOG("RAM size read %lx\n", psxHu32(0x1060)); + return psxHu32(0x1060); + case 0x1f801070: PSXHW_LOG("IREG 32bit read %x\n", psxHu32(0x1070)); + return psxHu32(0x1070); + case 0x1f801074: PSXHW_LOG("IMASK 32bit read %x\n", psxHu32(0x1074)); + return psxHu32(0x1074); + case 0x1f801078: + PSXHW_LOG("ICTRL 32bit read %x\n", psxHu32(0x1078)); + hard = psxHu32(0x1078); + psxHu32(0x1078) = 0; + return hard; + +/* case 0x1f801810: +// hard = GPU_readData(); + PSXHW_LOG("GPU DATA 32bit read %lx\n", hard); + return hard;*/ +/* case 0x1f801814: + hard = GPU_readStatus(); + PSXHW_LOG("GPU STATUS 32bit read %lx\n", hard); + return hard; +*/ +/* case 0x1f801820: hard = mdecRead0(); break; + case 0x1f801824: hard = mdecRead1(); break; +*/ + + case 0x1f8010a0: + PSXHW_LOG("DMA2 MADR 32bit read %lx\n", psxHu32(0x10a0)); + return HW_DMA2_MADR; + case 0x1f8010a4: + PSXHW_LOG("DMA2 BCR 32bit read %lx\n", psxHu32(0x10a4)); + return HW_DMA2_BCR; + case 0x1f8010a8: + PSXHW_LOG("DMA2 CHCR 32bit read %lx\n", psxHu32(0x10a8)); + return HW_DMA2_CHCR; + + case 0x1f8010b0: + PSXHW_LOG("DMA3 MADR 32bit read %lx\n", psxHu32(0x10b0)); + return HW_DMA3_MADR; + case 0x1f8010b4: + PSXHW_LOG("DMA3 BCR 32bit read %lx\n", psxHu32(0x10b4)); + return HW_DMA3_BCR; + case 0x1f8010b8: + PSXHW_LOG("DMA3 CHCR 32bit read %lx\n", psxHu32(0x10b8)); + return HW_DMA3_CHCR; + + case 0x1f801520: + PSXHW_LOG("DMA9 MADR 32bit read %lx\n", HW_DMA9_MADR); + return HW_DMA9_MADR; + case 0x1f801524: + PSXHW_LOG("DMA9 BCR 32bit read %lx\n", HW_DMA9_BCR); + return HW_DMA9_BCR; + case 0x1f801528: + PSXHW_LOG("DMA9 CHCR 32bit read %lx\n", HW_DMA9_CHCR); + return HW_DMA9_CHCR; + case 0x1f80152C: + PSXHW_LOG("DMA9 TADR 32bit read %lx\n", HW_DMA9_TADR); + return HW_DMA9_TADR; + + case 0x1f801530: + PSXHW_LOG("DMA10 MADR 32bit read %lx\n", HW_DMA10_MADR); + return HW_DMA10_MADR; + case 0x1f801534: + PSXHW_LOG("DMA10 BCR 32bit read %lx\n", HW_DMA10_BCR); + return HW_DMA10_BCR; + case 0x1f801538: + PSXHW_LOG("DMA10 CHCR 32bit read %lx\n", HW_DMA10_CHCR); + return HW_DMA10_CHCR; + +// case 0x1f8010f0: PSXHW_LOG("DMA PCR 32bit read " << psxHu32(0x10f0)); +// return HW_DMA_PCR; // dma rest channel + + case 0x1f8010f4: + PSXHW_LOG("DMA ICR 32bit read %lx\n", HW_DMA_ICR); + return HW_DMA_ICR; + +//SSBus registers + case 0x1f801000: + hard = psxHu32(0x1000); + PSXHW_LOG("SSBUS 32bit read %lx\n", hard); + return hard; + case 0x1f801004: + hard = psxHu32(0x1004); + PSXHW_LOG("SSBUS 32bit read %lx\n", hard); + return hard; + case 0x1f801008: + hard = psxHu32(0x1008); + PSXHW_LOG("SSBUS 32bit read %lx\n", hard); + return hard; + case 0x1f80100C: + hard = psxHu32(0x100C); + PSXHW_LOG("SSBUS dev1_delay 32bit read %lx\n", hard); + return hard; + case 0x1f801010: + hard = psxHu32(0x1010); + PSXHW_LOG("SSBUS rom_delay 32bit read %lx\n", hard); + return hard; + case 0x1f801014: + hard = psxHu32(0x1014); + PSXHW_LOG("SSBUS spu_delay 32bit read %lx\n", hard); + return hard; + case 0x1f801018: + hard = psxHu32(0x1018); + PSXHW_LOG("SSBUS dev5_delay 32bit read %lx\n", hard); + return hard; + case 0x1f80101C: + hard = psxHu32(0x101C); + PSXHW_LOG("SSBUS 32bit read %lx\n", hard); + return hard; + case 0x1f801020: + hard = psxHu32(0x1020); + PSXHW_LOG("SSBUS com_delay 32bit read %lx\n", hard); + return hard; + case 0x1f801400: + hard = psxHu32(0x1400); + PSXHW_LOG("SSBUS dev1_addr 32bit read %lx\n", hard); + return hard; + case 0x1f801404: + hard = psxHu32(0x1404); + PSXHW_LOG("SSBUS spu_addr 32bit read %lx\n", hard); + return hard; + case 0x1f801408: + hard = psxHu32(0x1408); + PSXHW_LOG("SSBUS dev5_addr 32bit read %lx\n", hard); + return hard; + case 0x1f80140C: + hard = psxHu32(0x140C); + PSXHW_LOG("SSBUS spu1_addr 32bit read %lx\n", hard); + return hard; + case 0x1f801410: + hard = psxHu32(0x1410); + PSXHW_LOG("SSBUS 32bit read %lx\n", hard); + return hard; + case 0x1f801414: + hard = psxHu32(0x1414); + PSXHW_LOG("SSBUS spu1_delay 32bit read %lx\n", hard); + return hard; + case 0x1f801418: + hard = psxHu32(0x1418); + PSXHW_LOG("SSBUS 32bit read %lx\n", hard); + return hard; + case 0x1f80141C: + hard = psxHu32(0x141C); + PSXHW_LOG("SSBUS 32bit read %lx\n", hard); + return hard; + case 0x1f801420: + hard = psxHu32(0x1420); + PSXHW_LOG("SSBUS 32bit read %lx\n", hard); + return hard; + + case 0x1f8010f0: + PSXHW_LOG("DMA PCR 32bit read %lx\n", HW_DMA_PCR); + return HW_DMA_PCR; + + case 0x1f8010c8: + PSXHW_LOG("DMA4 CHCR 32bit read %lx\n", HW_DMA4_CHCR); + return HW_DMA4_CHCR; // DMA4 chcr (SPU DMA) + + // time for rootcounters :) + case 0x1f801100: + hard = (u16)psxRcntRcount16(0); + PSXCNT_LOG("T0 count read32: %lx\n", hard); + return hard; + case 0x1f801104: + hard = (u16)psxCounters[0].mode; + PSXCNT_LOG("T0 mode read32: %lx\n", hard); + return hard; + case 0x1f801108: + hard = psxCounters[0].target; + PSXCNT_LOG("T0 target read32: %lx\n", hard); + return hard; + case 0x1f801110: + hard = (u16)psxRcntRcount16(1); + PSXCNT_LOG("T1 count read32: %lx\n", hard); + return hard; + case 0x1f801114: + hard = (u16)psxCounters[1].mode; + PSXCNT_LOG("T1 mode read32: %lx\n", hard); + return hard; + case 0x1f801118: + hard = psxCounters[1].target; + PSXCNT_LOG("T1 target read32: %lx\n", hard); + return hard; + case 0x1f801120: + hard = (u16)psxRcntRcount16(2); + PSXCNT_LOG("T2 count read32: %lx\n", hard); + return hard; + case 0x1f801124: + hard = (u16)psxCounters[2].mode; + PSXCNT_LOG("T2 mode read32: %lx\n", hard); + return hard; + case 0x1f801128: + hard = psxCounters[2].target; + PSXCNT_LOG("T2 target read32: %lx\n", hard); + return hard; + + case 0x1f801480: + hard = (u32)psxRcntRcount32(3); + PSXCNT_LOG("T3 count read32: %lx\n", hard); + return hard; + case 0x1f801484: + hard = (u16)psxCounters[3].mode; + PSXCNT_LOG("T3 mode read32: %lx\n", hard); + return hard; + case 0x1f801488: + hard = psxCounters[3].target; + PSXCNT_LOG("T3 target read32: %lx\n", hard); + return hard; + case 0x1f801490: + hard = (u32)psxRcntRcount32(4); + PSXCNT_LOG("T4 count read32: %lx\n", hard); + return hard; + case 0x1f801494: + hard = (u16)psxCounters[4].mode; + PSXCNT_LOG("T4 mode read32: %lx\n", hard); + return hard; + case 0x1f801498: + hard = psxCounters[4].target; + PSXCNT_LOG("T4 target read32: %lx\n", hard); + return hard; + case 0x1f8014a0: + hard = (u32)psxRcntRcount32(5); + PSXCNT_LOG("T5 count read32: %lx\n", hard); + return hard; + case 0x1f8014a4: + hard = (u16)psxCounters[5].mode; + PSXCNT_LOG("T5 mode read32: %lx\n", hard); + return hard; + case 0x1f8014a8: + hard = psxCounters[5].target; + PSXCNT_LOG("T5 target read32: %lx\n", hard); + return hard; + + case 0x1f801450: + hard = psxHu32(add); + PSXHW_LOG("%08X ICFG 32bit read %x\n", psxRegs.pc, hard); + return hard; + + + case 0x1F8010C0: + HW_DMA4_MADR = SPU2ReadMemAddr(0); + return HW_DMA4_MADR; + + case 0x1f801500: + HW_DMA7_MADR = SPU2ReadMemAddr(1); + PSXHW_LOG("DMA7 MADR 32bit read %lx\n", HW_DMA7_MADR); + return HW_DMA7_MADR; // DMA7 madr + case 0x1f801504: + PSXHW_LOG("DMA7 BCR 32bit read %lx\n", HW_DMA7_BCR); + return HW_DMA7_BCR; // DMA7 bcr + + case 0x1f801508: + PSXHW_LOG("DMA7 CHCR 32bit read %lx\n", HW_DMA7_CHCR); + return HW_DMA7_CHCR; // DMA7 chcr (SPU2) + + case 0x1f801570: + hard = psxHu32(0x1570); + PSXHW_LOG("DMA PCR2 32bit read %lx\n", hard); + return hard; + case 0x1f801574: + PSXHW_LOG("DMA ICR2 32bit read %lx\n", HW_DMA_ICR2); + return HW_DMA_ICR2; + + case 0x1F808200: + case 0x1F808204: + case 0x1F808208: + case 0x1F80820C: + case 0x1F808210: + case 0x1F808214: + case 0x1F808218: + case 0x1F80821C: + case 0x1F808220: + case 0x1F808224: + case 0x1F808228: + case 0x1F80822C: + case 0x1F808230: + case 0x1F808234: + case 0x1F808238: + case 0x1F80823C: + hard=sio2_getSend3((add-0x1F808200)/4); + PSXHW_LOG("SIO2 read param[%d] (%lx)\n", (add-0x1F808200)/4, hard); + return hard; + + case 0x1F808240: + case 0x1F808248: + case 0x1F808250: + case 0x1F80825C: + hard=sio2_getSend1((add-0x1F808240)/8); + PSXHW_LOG("SIO2 read send1[%d] (%lx)\n", (add-0x1F808240)/8, hard); + return hard; + + case 0x1F808244: + case 0x1F80824C: + case 0x1F808254: + case 0x1F808258: + hard=sio2_getSend2((add-0x1F808244)/8); + PSXHW_LOG("SIO2 read send2[%d] (%lx)\n", (add-0x1F808244)/8, hard); + return hard; + + case 0x1F808268: + hard=sio2_getCtrl(); + PSXHW_LOG("SIO2 read CTRL (%lx)\n", hard); + return hard; + + case 0x1F80826C: + hard=sio2_getRecv1(); + PSXHW_LOG("SIO2 read Recv1 (%lx)\n", hard); + return hard; + + case 0x1F808270: + hard=sio2_getRecv2(); + PSXHW_LOG("SIO2 read Recv2 (%lx)\n", hard); + return hard; + + case 0x1F808274: + hard=sio2_getRecv3(); + PSXHW_LOG("SIO2 read Recv3 (%lx)\n", hard); + return hard; + + case 0x1F808278: + hard=sio2_get8278(); + PSXHW_LOG("SIO2 read [8278] (%lx)\n", hard); + return hard; + + case 0x1F80827C: + hard=sio2_get827C(); + PSXHW_LOG("SIO2 read [827C] (%lx)\n", hard); + return hard; + + case 0x1F808280: + hard=sio2_getIntr(); + PSXHW_LOG("SIO2 read INTR (%lx)\n", hard); + return hard; + + default: + hard = psxHu32(add); + PSXHW_LOG("*Unknown 32bit read at address %lx: %lx\n", add, hard); + return hard; + } + PSXHW_LOG("*Known 32bit read at address %lx: %lx\n", add, hard); + return hard; +} + +int g_pbufi; +s8 g_pbuf[1024]; + +#define DmaExec(n) { \ + if (HW_DMA##n##_CHCR & 0x01000000 && \ + HW_DMA_PCR & (8 << (n * 4))) { \ + psxDma##n(HW_DMA##n##_MADR, HW_DMA##n##_BCR, HW_DMA##n##_CHCR); \ + } \ +} + +void psxHwWrite8(u32 add, u8 value) { + if (add >= 0x1f801600 && add < 0x1f801700) { + USBwrite8(add, value); return; + } + if((add & 0xf) == 0xa) SysPrintf("8bit write (possible chcr set) %x value %x\n", add, value); + switch (add) { + case 0x1f801040: + sioWrite8(value); + break; + // case 0x1f801050: serial_write8(value); break;//serial port + + case 0x1f801100: + case 0x1f801104: + case 0x1f801108: + case 0x1f801110: + case 0x1f801114: + case 0x1f801118: + case 0x1f801120: + case 0x1f801124: + case 0x1f801128: + case 0x1f801480: + case 0x1f801484: + case 0x1f801488: + case 0x1f801490: + case 0x1f801494: + case 0x1f801498: + case 0x1f8014a0: + case 0x1f8014a4: + case 0x1f8014a8: + SysPrintf("8bit counter write %x\n", add); + psxHu8(add) = value; + return; + case 0x1f801450: + if (value) { PSXHW_LOG("%08X ICFG 8bit write %lx\n", psxRegs.pc, value); } + psxHu8(0x1450) = value; + return; + + case 0x1f801800: cdrWrite0(value); break; + case 0x1f801801: cdrWrite1(value); break; + case 0x1f801802: cdrWrite2(value); break; + case 0x1f801803: cdrWrite3(value); break; + + case 0x1f80380c: + if (value == '\r') break; + if (value == '\n' || g_pbufi >= 1023) { + g_pbuf[g_pbufi++] = 0; g_pbufi = 0; + SysPrintf("%s\n", g_pbuf); + } + else g_pbuf[g_pbufi++] = value; + psxHu8(add) = value; + return; + + case 0x1F808260: + PSXHW_LOG("SIO2 write8 DATAIN <- %08X\n", value); + sio2_serialIn(value);return;//serial data feed/fifo + + default: + psxHu8(add) = value; + PSXHW_LOG("*Unknown 8bit write at address %lx value %x\n", add, value); + return; + } + psxHu8(add) = value; + PSXHW_LOG("*Known 8bit write at address %lx value %x\n", add, value); +} + +void psxHwWrite16(u32 add, u16 value) { + if (add >= 0x1f801600 && add < 0x1f801700) { + USBwrite16(add, value); return; + } + + if((add & 0xf) == 0x9) DevCon::WriteLn("16bit write (possible chcr set) %x value %x", params add, value); + + switch (add) { + case 0x1f801040: + sioWrite8((u8)value); + sioWrite8((u8)(value>>8)); + PAD_LOG ("sio write16 %lx, %x\n", add&0xf, value); + return; + case 0x1f801044: + PAD_LOG ("sio write16 %lx, %x\n", add&0xf, value); + return; + case 0x1f801048: + sio.ModeReg = value; + PAD_LOG ("sio write16 %lx, %x\n", add&0xf, value); + return; + case 0x1f80104a: // control register + sioWriteCtrl16(value); + PAD_LOG ("sio write16 %lx, %x\n", add&0xf, value); + return; + case 0x1f80104e: // baudrate register + sio.BaudReg = value; + PAD_LOG ("sio write16 %lx, %x\n", add&0xf, value); + return; + + //serial port ;P + // case 0x1f801050: serial_write16(value); break; + // case 0x1f80105a: serial_control_write(value);break; + // case 0x1f80105e: serial_baud_write(value); break; + // case 0x1f801054: serial_status_write(value); break; + + case 0x1f801070: + PSXHW_LOG("IREG 16bit write %x\n", value); +// if (Config.Sio) psxHu16(0x1070) |= 0x80; +// if (Config.SpuIrq) psxHu16(0x1070) |= 0x200; + psxHu16(0x1070) &= value; + return; + + case 0x1f801074: + PSXHW_LOG("IMASK 16bit write %x\n", value); + psxHu16(0x1074) = value; + iopTestIntc(); + return; + + case 0x1f801078: // see the 32-bit version for notes! + PSXHW_LOG("ICTRL 16bit write %x\n", value); + psxHu16(0x1078) = value; + iopTestIntc(); + return; + + case 0x1f8010c4: + PSXHW_LOG("DMA4 BCR_size 16bit write %lx\n", value); + psxHu16(0x10c4) = value; + return; // DMA4 bcr_size + + case 0x1f8010c6: + PSXHW_LOG("DMA4 BCR_count 16bit write %lx\n", value); + psxHu16(0x10c6) = value; return; // DMA4 bcr_count + + case 0x1f801100: + PSXCNT_LOG("COUNTER 0 COUNT 16bit write %x\n", value); + psxRcntWcount16(0, value); return; + case 0x1f801104: + PSXCNT_LOG("COUNTER 0 MODE 16bit write %x\n", value); + psxRcnt0Wmode(value); return; + case 0x1f801108: + PSXCNT_LOG("COUNTER 0 TARGET 16bit write %x\n", value); + psxRcntWtarget16(0, value); return; + + case 0x1f801110: + PSXCNT_LOG("COUNTER 1 COUNT 16bit write %x\n", value); + psxRcntWcount16(1, value); return; + case 0x1f801114: + PSXCNT_LOG("COUNTER 1 MODE 16bit write %x\n", value); + psxRcnt1Wmode(value); return; + case 0x1f801118: + PSXCNT_LOG("COUNTER 1 TARGET 16bit write %x\n", value); + psxRcntWtarget16(1, value); return; + + case 0x1f801120: + PSXCNT_LOG("COUNTER 2 COUNT 16bit write %x\n", value); + psxRcntWcount16(2, value); return; + case 0x1f801124: + PSXCNT_LOG("COUNTER 2 MODE 16bit write %x\n", value); + psxRcnt2Wmode(value); return; + case 0x1f801128: + PSXCNT_LOG("COUNTER 2 TARGET 16bit write %x\n", value); + psxRcntWtarget16(2, value); return; + + case 0x1f801450: + if (value) { PSXHW_LOG("%08X ICFG 16bit write %lx\n", psxRegs.pc, value); } + psxHu16(0x1450) = value/* & (~0x8)*/; + return; + + case 0x1f801480: + PSXCNT_LOG("COUNTER 3 COUNT 16bit write %lx\n", value); + psxRcntWcount32(3, value); return; + case 0x1f801484: + PSXCNT_LOG("COUNTER 3 MODE 16bit write %lx\n", value); + psxRcnt3Wmode(value); return; + case 0x1f801488: + PSXCNT_LOG("COUNTER 3 TARGET 16bit write %lx\n", value); + psxRcntWtarget32(3, value); return; + + case 0x1f801490: + PSXCNT_LOG("COUNTER 4 COUNT 16bit write %lx\n", value); + psxRcntWcount32(4, value); return; + case 0x1f801494: + PSXCNT_LOG("COUNTER 4 MODE 16bit write %lx\n", value); + psxRcnt4Wmode(value); return; + case 0x1f801498: + PSXCNT_LOG("COUNTER 4 TARGET 16bit write %lx\n", value); + psxRcntWtarget32(4, value); return; + + case 0x1f8014a0: + PSXCNT_LOG("COUNTER 5 COUNT 16bit write %lx\n", value); + psxRcntWcount32(5, value); return; + case 0x1f8014a4: + PSXCNT_LOG("COUNTER 5 MODE 16bit write %lx\n", value); + psxRcnt5Wmode(value); return; + case 0x1f8014a8: + PSXCNT_LOG("COUNTER 5 TARGET 16bit write %lx\n", value); + psxRcntWtarget32(5, value); return; + + case 0x1f801504: + psxHu16(0x1504) = value; + PSXHW_LOG("DMA7 BCR_size 16bit write %lx\n", value); + return; + case 0x1f801506: + psxHu16(0x1506) = value; + PSXHW_LOG("DMA7 BCR_count 16bit write %lx\n", value); + return; + default: + if (add>=0x1f801c00 && add<0x1f801e00) { + SPU2write(add, value); + return; + } + + psxHu16(add) = value; + PSXHW_LOG("*Unknown 16bit write at address %lx value %x\n", add, value); + return; + } + psxHu16(add) = value; + PSXHW_LOG("*Known 16bit write at address %lx value %x\n", add, value); +} + +#define DmaExec2(n) { \ + if (HW_DMA##n##_CHCR & 0x01000000 && \ + HW_DMA_PCR2 & (8 << ((n-7) * 4))) { \ + psxDma##n(HW_DMA##n##_MADR, HW_DMA##n##_BCR, HW_DMA##n##_CHCR); \ + } \ +} + +void psxHwWrite32(u32 add, u32 value) { + if (add >= 0x1f801600 && add < 0x1f801700) { + USBwrite32(add, value); return; + } + if (add >= 0x1f808400 && add <= 0x1f808550) { + FWwrite32(add, value); return; + } + switch (add) { + case 0x1f801040: + sioWrite8((u8)value); + sioWrite8((u8)((value&0xff) >> 8)); + sioWrite8((u8)((value&0xff) >> 16)); + sioWrite8((u8)((value&0xff) >> 24)); + PAD_LOG("sio write32 %lx\n", value); + return; + // case 0x1f801050: serial_write32(value); break;//serial port + case 0x1f801060: + PSXHW_LOG("RAM size write %lx\n", value); + psxHu32(add) = value; + return; // Ram size + +//------------------------------------------------------------------ + case 0x1f801070: + PSXHW_LOG("IREG 32bit write %lx\n", value); +// if (Config.Sio) psxHu32(0x1070) |= 0x80; +// if (Config.SpuIrq) psxHu32(0x1070) |= 0x200; + psxHu32(0x1070) &= value; + return; + + case 0x1f801074: + PSXHW_LOG("IMASK 32bit write %lx\n", value); + psxHu32(0x1074) = value; + iopTestIntc(); + return; + + case 0x1f801078: + PSXHW_LOG("ICTRL 32bit write %lx\n", value); + psxHu32(0x1078) = value; //1; //According to pSXAuthor this allways becomes 1 on write, but MHPB won't boot if value is not writen ;p + iopTestIntc(); + return; + +//------------------------------------------------------------------ + //SSBus registers + case 0x1f801000: + psxHu32(0x1000) = value; + PSXHW_LOG("SSBUS 32bit write %lx\n", value); + return; + case 0x1f801004: + psxHu32(0x1004) = value; + PSXHW_LOG("SSBUS 32bit write %lx\n", value); + return; + case 0x1f801008: + psxHu32(0x1008) = value; + PSXHW_LOG("SSBUS 32bit write %lx\n", value); + return; + case 0x1f80100C: + psxHu32(0x100C) = value; + PSXHW_LOG("SSBUS dev1_delay 32bit write %lx\n", value); + return; + case 0x1f801010: + psxHu32(0x1010) = value; + PSXHW_LOG("SSBUS rom_delay 32bit write %lx\n", value); + return; + case 0x1f801014: + psxHu32(0x1014) = value; + PSXHW_LOG("SSBUS spu_delay 32bit write %lx\n", value); + return; + case 0x1f801018: + psxHu32(0x1018) = value; + PSXHW_LOG("SSBUS dev5_delay 32bit write %lx\n", value); + return; + case 0x1f80101C: + psxHu32(0x101C) = value; + PSXHW_LOG("SSBUS 32bit write %lx\n", value); + return; + case 0x1f801020: + psxHu32(0x1020) = value; + PSXHW_LOG("SSBUS com_delay 32bit write %lx\n", value); + return; + case 0x1f801400: + psxHu32(0x1400) = value; + PSXHW_LOG("SSBUS dev1_addr 32bit write %lx\n", value); + return; + case 0x1f801404: + psxHu32(0x1404) = value; + PSXHW_LOG("SSBUS spu_addr 32bit write %lx\n", value); + return; + case 0x1f801408: + psxHu32(0x1408) = value; + PSXHW_LOG("SSBUS dev5_addr 32bit write %lx\n", value); + return; + case 0x1f80140C: + psxHu32(0x140C) = value; + PSXHW_LOG("SSBUS spu1_addr 32bit write %lx\n", value); + return; + case 0x1f801410: + psxHu32(0x1410) = value; + PSXHW_LOG("SSBUS 32bit write %lx\n", value); + return; + case 0x1f801414: + psxHu32(0x1414) = value; + PSXHW_LOG("SSBUS spu1_delay 32bit write %lx\n", value); + return; + case 0x1f801418: + psxHu32(0x1418) = value; + PSXHW_LOG("SSBUS 32bit write %lx\n", value); + return; + case 0x1f80141C: + psxHu32(0x141C) = value; + PSXHW_LOG("SSBUS 32bit write %lx\n", value); + return; + case 0x1f801420: + psxHu32(0x1420) = value; + PSXHW_LOG("SSBUS 32bit write %lx\n", value); + return; + +//------------------------------------------------------------------ + case 0x1f801080: + PSXHW_LOG("DMA0 MADR 32bit write %lx\n", value); + HW_DMA0_MADR = value; return; // DMA0 madr + case 0x1f801084: + PSXHW_LOG("DMA0 BCR 32bit write %lx\n", value); + HW_DMA0_BCR = value; return; // DMA0 bcr + case 0x1f801088: + PSXHW_LOG("DMA0 CHCR 32bit write %lx\n", value); + HW_DMA0_CHCR = value; // DMA0 chcr (MDEC in DMA) +// DmaExec(0); + return; + +//------------------------------------------------------------------ + case 0x1f801090: + PSXHW_LOG("DMA1 MADR 32bit write %lx\n", value); + HW_DMA1_MADR = value; return; // DMA1 madr + case 0x1f801094: + PSXHW_LOG("DMA1 BCR 32bit write %lx\n", value); + HW_DMA1_BCR = value; return; // DMA1 bcr + case 0x1f801098: + PSXHW_LOG("DMA1 CHCR 32bit write %lx\n", value); + HW_DMA1_CHCR = value; // DMA1 chcr (MDEC out DMA) +// DmaExec(1); + return; + +//------------------------------------------------------------------ + case 0x1f8010a0: + PSXHW_LOG("DMA2 MADR 32bit write %lx\n", value); + HW_DMA2_MADR = value; return; // DMA2 madr + case 0x1f8010a4: + PSXHW_LOG("DMA2 BCR 32bit write %lx\n", value); + HW_DMA2_BCR = value; return; // DMA2 bcr + case 0x1f8010a8: + PSXHW_LOG("DMA2 CHCR 32bit write %lx\n", value); + HW_DMA2_CHCR = value; // DMA2 chcr (GPU DMA) + DmaExec(2); + return; + +//------------------------------------------------------------------ + case 0x1f8010b0: + PSXHW_LOG("DMA3 MADR 32bit write %lx\n", value); + HW_DMA3_MADR = value; return; // DMA3 madr + case 0x1f8010b4: + PSXHW_LOG("DMA3 BCR 32bit write %lx\n", value); + HW_DMA3_BCR = value; return; // DMA3 bcr + case 0x1f8010b8: + PSXHW_LOG("DMA3 CHCR 32bit write %lx\n", value); + HW_DMA3_CHCR = value; // DMA3 chcr (CDROM DMA) + DmaExec(3); + + return; + +//------------------------------------------------------------------ + case 0x1f8010c0: + PSXHW_LOG("DMA4 MADR 32bit write %lx\n", value); + SPU2WriteMemAddr(0,value); + HW_DMA4_MADR = value; return; // DMA4 madr + case 0x1f8010c4: + PSXHW_LOG("DMA4 BCR 32bit write %lx\n", value); + HW_DMA4_BCR = value; return; // DMA4 bcr + case 0x1f8010c8: + PSXHW_LOG("DMA4 CHCR 32bit write %lx\n", value); + HW_DMA4_CHCR = value; // DMA4 chcr (SPU DMA) + DmaExec(4); + return; + +//------------------------------------------------------------------ +#if 0 + case 0x1f8010d0: break; //DMA5write_madr(); + case 0x1f8010d4: break; //DMA5write_bcr(); + case 0x1f8010d8: break; //DMA5write_chcr(); // Not yet needed?? +#endif + + case 0x1f8010e0: + PSXHW_LOG("DMA6 MADR 32bit write %lx\n", value); + HW_DMA6_MADR = value; return; // DMA6 madr + case 0x1f8010e4: + PSXHW_LOG("DMA6 BCR 32bit write %lx\n", value); + HW_DMA6_BCR = value; return; // DMA6 bcr + case 0x1f8010e8: + PSXHW_LOG("DMA6 CHCR 32bit write %lx\n", value); + HW_DMA6_CHCR = value; // DMA6 chcr (OT clear) + DmaExec(6); + return; + +//------------------------------------------------------------------ + case 0x1f801500: + PSXHW_LOG("DMA7 MADR 32bit write %lx\n", value); + SPU2WriteMemAddr(1,value); + HW_DMA7_MADR = value; return; // DMA7 madr + case 0x1f801504: + PSXHW_LOG("DMA7 BCR 32bit write %lx\n", value); + HW_DMA7_BCR = value; return; // DMA7 bcr + case 0x1f801508: + PSXHW_LOG("DMA7 CHCR 32bit write %lx\n", value); + HW_DMA7_CHCR = value; // DMA7 chcr (SPU2) + DmaExec2(7); + return; + +//------------------------------------------------------------------ + case 0x1f801510: + PSXHW_LOG("DMA8 MADR 32bit write %lx\n", value); + HW_DMA8_MADR = value; return; // DMA8 madr + case 0x1f801514: + PSXHW_LOG("DMA8 BCR 32bit write %lx\n", value); + HW_DMA8_BCR = value; return; // DMA8 bcr + case 0x1f801518: + PSXHW_LOG("DMA8 CHCR 32bit write %lx\n", value); + HW_DMA8_CHCR = value; // DMA8 chcr (DEV9) + DmaExec2(8); + return; + +//------------------------------------------------------------------ + case 0x1f801520: + PSXHW_LOG("DMA9 MADR 32bit write %lx\n", value); + HW_DMA9_MADR = value; return; // DMA9 madr + case 0x1f801524: + PSXHW_LOG("DMA9 BCR 32bit write %lx\n", value); + HW_DMA9_BCR = value; return; // DMA9 bcr + case 0x1f801528: + PSXHW_LOG("DMA9 CHCR 32bit write %lx\n", value); + HW_DMA9_CHCR = value; // DMA9 chcr (SIF0) + DmaExec2(9); + return; + case 0x1f80152c: + PSXHW_LOG("DMA9 TADR 32bit write %lx\n", value); + HW_DMA9_TADR = value; return; // DMA9 tadr + +//------------------------------------------------------------------ + case 0x1f801530: + PSXHW_LOG("DMA10 MADR 32bit write %lx\n", value); + HW_DMA10_MADR = value; return; // DMA10 madr + case 0x1f801534: + PSXHW_LOG("DMA10 BCR 32bit write %lx\n", value); + HW_DMA10_BCR = value; return; // DMA10 bcr + case 0x1f801538: + PSXHW_LOG("DMA10 CHCR 32bit write %lx\n", value); + HW_DMA10_CHCR = value; // DMA10 chcr (SIF1) + DmaExec2(10); + return; + +//------------------------------------------------------------------ + case 0x1f801540: + PSXHW_LOG("DMA11 SIO2in MADR 32bit write %lx\n", value); + HW_DMA11_MADR = value; return; + + case 0x1f801544: + PSXHW_LOG("DMA11 SIO2in BCR 32bit write %lx\n", value); + HW_DMA11_BCR = value; return; + case 0x1f801548: + PSXHW_LOG("DMA11 SIO2in CHCR 32bit write %lx\n", value); + HW_DMA11_CHCR = value; // DMA11 chcr (SIO2 in) + DmaExec2(11); + return; + +//------------------------------------------------------------------ + case 0x1f801550: + PSXHW_LOG("DMA12 SIO2out MADR 32bit write %lx\n", value); + HW_DMA12_MADR = value; return; + + case 0x1f801554: + PSXHW_LOG("DMA12 SIO2out BCR 32bit write %lx\n", value); + HW_DMA12_BCR = value; return; + case 0x1f801558: + PSXHW_LOG("DMA12 SIO2out CHCR 32bit write %lx\n", value); + HW_DMA12_CHCR = value; // DMA12 chcr (SIO2 out) + DmaExec2(12); + return; + +//------------------------------------------------------------------ + case 0x1f801570: + psxHu32(0x1570) = value; + PSXHW_LOG("DMA PCR2 32bit write %lx\n", value); + return; + case 0x1f8010f0: + PSXHW_LOG("DMA PCR 32bit write %lx\n", value); + HW_DMA_PCR = value; + return; + + case 0x1f8010f4: + PSXHW_LOG("DMA ICR 32bit write %lx\n", value); + { + u32 tmp = (~value) & HW_DMA_ICR; + HW_DMA_ICR = ((tmp ^ value) & 0xffffff) ^ tmp; + } + return; + + case 0x1f801574: + PSXHW_LOG("DMA ICR2 32bit write %lx\n", value); + { + u32 tmp = (~value) & HW_DMA_ICR2; + HW_DMA_ICR2 = ((tmp ^ value) & 0xffffff) ^ tmp; + } + return; + +//------------------------------------------------------------------ +/* case 0x1f801810: + PSXHW_LOG("GPU DATA 32bit write %lx\n", value); + GPU_writeData(value); return; + case 0x1f801814: + PSXHW_LOG("GPU STATUS 32bit write %lx\n", value); + GPU_writeStatus(value); return; +*/ +/* case 0x1f801820: + mdecWrite0(value); break; + case 0x1f801824: + mdecWrite1(value); break; +*/ + case 0x1f801100: + PSXCNT_LOG("COUNTER 0 COUNT 32bit write %lx\n", value); + psxRcntWcount16(0, value ); return; + case 0x1f801104: + PSXCNT_LOG("COUNTER 0 MODE 32bit write %lx\n", value); + psxRcnt0Wmode(value); return; + case 0x1f801108: + PSXCNT_LOG("COUNTER 0 TARGET 32bit write %lx\n", value); + psxRcntWtarget16(0, value ); return; + + case 0x1f801110: + PSXCNT_LOG("COUNTER 1 COUNT 32bit write %lx\n", value); + psxRcntWcount16(1, value ); return; + case 0x1f801114: + PSXCNT_LOG("COUNTER 1 MODE 32bit write %lx\n", value); + psxRcnt1Wmode(value); return; + case 0x1f801118: + PSXCNT_LOG("COUNTER 1 TARGET 32bit write %lx\n", value); + psxRcntWtarget16(1, value ); return; + + case 0x1f801120: + PSXCNT_LOG("COUNTER 2 COUNT 32bit write %lx\n", value); + psxRcntWcount16(2, value ); return; + case 0x1f801124: + PSXCNT_LOG("COUNTER 2 MODE 32bit write %lx\n", value); + psxRcnt2Wmode(value); return; + case 0x1f801128: + PSXCNT_LOG("COUNTER 2 TARGET 32bit write %lx\n", value); + psxRcntWtarget16(2, value); return; + + case 0x1f801480: + PSXCNT_LOG("COUNTER 3 COUNT 32bit write %lx\n", value); + psxRcntWcount32(3, value); return; + case 0x1f801484: + PSXCNT_LOG("COUNTER 3 MODE 32bit write %lx\n", value); + psxRcnt3Wmode(value); return; + case 0x1f801488: + PSXCNT_LOG("COUNTER 3 TARGET 32bit write %lx\n", value); + psxRcntWtarget32(3, value); return; + + case 0x1f801490: + PSXCNT_LOG("COUNTER 4 COUNT 32bit write %lx\n", value); + psxRcntWcount32(4, value); return; + case 0x1f801494: + PSXCNT_LOG("COUNTER 4 MODE 32bit write %lx\n", value); + psxRcnt4Wmode(value); return; + case 0x1f801498: + PSXCNT_LOG("COUNTER 4 TARGET 32bit write %lx\n", value); + psxRcntWtarget32(4, value); return; + + case 0x1f8014a0: + PSXCNT_LOG("COUNTER 5 COUNT 32bit write %lx\n", value); + psxRcntWcount32(5, value); return; + case 0x1f8014a4: + PSXCNT_LOG("COUNTER 5 MODE 32bit write %lx\n", value); + psxRcnt5Wmode(value); return; + case 0x1f8014a8: + PSXCNT_LOG("COUNTER 5 TARGET 32bit write %lx\n", value); + psxRcntWtarget32(5, value); return; + +//------------------------------------------------------------------ + case 0x1f8014c0: + PSXHW_LOG("RTC_HOLDMODE 32bit write %lx\n", value); + Console::Notice("** RTC_HOLDMODE 32bit write %lx", params value); + break; + + case 0x1f801450: + if (value) { PSXHW_LOG("%08X ICFG 32bit write %lx\n", psxRegs.pc, value); } +/* if (value && + psxSu32(0x20) == 0x20000 && + (psxSu32(0x30) == 0x20000 || + psxSu32(0x30) == 0x40000)) { // don't ask me why :P + psxSu32(0x20) = 0x10000; + psxSu32(0x30) = 0x10000; + }*/ + psxHu32(0x1450) = /*(*/value/* & (~0x8)) | (psxHu32(0x1450) & 0x8)*/; + return; + +//------------------------------------------------------------------ + case 0x1F808200: + case 0x1F808204: + case 0x1F808208: + case 0x1F80820C: + case 0x1F808210: + case 0x1F808214: + case 0x1F808218: + case 0x1F80821C: + case 0x1F808220: + case 0x1F808224: + case 0x1F808228: + case 0x1F80822C: + case 0x1F808230: + case 0x1F808234: + case 0x1F808238: + case 0x1F80823C: + PSXHW_LOG("SIO2 write param[%d] <- %lx\n", (add-0x1F808200)/4, value); + sio2_setSend3((add-0x1F808200)/4, value); return; + + case 0x1F808240: + case 0x1F808248: + case 0x1F808250: + case 0x1F808258: + PSXHW_LOG("SIO2 write send1[%d] <- %lx\n", (add-0x1F808240)/8, value); + sio2_setSend1((add-0x1F808240)/8, value); return; + + case 0x1F808244: + case 0x1F80824C: + case 0x1F808254: + case 0x1F80825C: + PSXHW_LOG("SIO2 write send2[%d] <- %lx\n", (add-0x1F808244)/8, value); + sio2_setSend2((add-0x1F808244)/8, value); return; + + case 0x1F808268: + PSXHW_LOG("SIO2 write CTRL <- %lx\n", value); + sio2_setCtrl(value); return; + + case 0x1F808278: + PSXHW_LOG("SIO2 write [8278] <- %lx\n", value); + sio2_set8278(value); return; + + case 0x1F80827C: + PSXHW_LOG("SIO2 write [827C] <- %lx\n", value); + sio2_set827C(value); return; + + case 0x1F808280: + PSXHW_LOG("SIO2 write INTR <- %lx\n", value); + sio2_setIntr(value); return; + +//------------------------------------------------------------------ + default: + psxHu32(add) = value; + PSXHW_LOG("*Unknown 32bit write at address %lx value %lx\n", add, value); + return; + } + psxHu32(add) = value; + PSXHW_LOG("*Known 32bit write at address %lx value %lx\n", add, value); +} + +u8 psxHw4Read8(u32 add) { + u8 hard; + + switch (add) { + case 0x1f402004: return cdvdRead04(); + case 0x1f402005: return cdvdRead05(); + case 0x1f402006: return cdvdRead06(); + case 0x1f402007: return cdvdRead07(); + case 0x1f402008: return cdvdRead08(); + case 0x1f40200A: return cdvdRead0A(); + case 0x1f40200B: return cdvdRead0B(); + case 0x1f40200C: return cdvdRead0C(); + case 0x1f40200D: return cdvdRead0D(); + case 0x1f40200E: return cdvdRead0E(); + case 0x1f40200F: return cdvdRead0F(); + case 0x1f402013: return cdvdRead13(); + case 0x1f402015: return cdvdRead15(); + case 0x1f402016: return cdvdRead16(); + case 0x1f402017: return cdvdRead17(); + case 0x1f402018: return cdvdRead18(); + case 0x1f402020: return cdvdRead20(); + case 0x1f402021: return cdvdRead21(); + case 0x1f402022: return cdvdRead22(); + case 0x1f402023: return cdvdRead23(); + case 0x1f402024: return cdvdRead24(); + case 0x1f402028: return cdvdRead28(); + case 0x1f402029: return cdvdRead29(); + case 0x1f40202A: return cdvdRead2A(); + case 0x1f40202B: return cdvdRead2B(); + case 0x1f40202C: return cdvdRead2C(); + case 0x1f402030: return cdvdRead30(); + case 0x1f402031: return cdvdRead31(); + case 0x1f402032: return cdvdRead32(); + case 0x1f402033: return cdvdRead33(); + case 0x1f402034: return cdvdRead34(); + case 0x1f402038: return cdvdRead38(); + case 0x1f402039: return cdvdRead39(); + case 0x1f40203A: return cdvdRead3A(); + default: + // note: use SysPrintF to notify console since this is a potentially serious + // emulation problem: + //PSXHW_LOG("*Unknown 8bit read at address %lx\n", add); + SysPrintf("*Unknown 8bit read at address %lx\n", add); + return 0; + } + + PSXHW_LOG("*Known 8bit read at address %lx value %x\n", add, hard); + + return hard; +} + +void psxHw4Write8(u32 add, u8 value) { + + switch (add) { + case 0x1f402004: cdvdWrite04(value); return; + case 0x1f402005: cdvdWrite05(value); return; + case 0x1f402006: cdvdWrite06(value); return; + case 0x1f402007: cdvdWrite07(value); return; + case 0x1f402008: cdvdWrite08(value); return; + case 0x1f40200A: cdvdWrite0A(value); return; + case 0x1f40200F: cdvdWrite0F(value); return; + case 0x1f402014: cdvdWrite14(value); return; + case 0x1f402016: cdvdWrite16(value); return; + case 0x1f402017: cdvdWrite17(value); return; + case 0x1f402018: cdvdWrite18(value); return; + case 0x1f40203A: cdvdWrite3A(value); return; + default: + //PSXHW_LOG("*Unknown 8bit write at address %lx value %x\n", add, value); + Console::Notice("*Unknown 8bit write at address %lx value %x", params add, value); + return; + } + PSXHW_LOG("*Known 8bit write at address %lx value %x\n", add, value); +} + +void psxDmaInterrupt(int n) { + if (HW_DMA_ICR & (1 << (16 + n))) { + HW_DMA_ICR|= (1 << (24 + n)); + psxRegs.CP0.n.Cause |= 1 << (9 + n); + iopIntcIrq( 3 ); + } +} + +void psxDmaInterrupt2(int n) { + if (HW_DMA_ICR2 & (1 << (16 + n))) { +/* if (HW_DMA_ICR2 & (1 << (24 + n))) { + SysPrintf("*PCSX2*: HW_DMA_ICR2 n=%d already set\n", n); + } + if (psxHu32(0x1070) & 8) { + SysPrintf("*PCSX2*: psxHu32(0x1070) 8 already set (n=%d)\n", n); + }*/ + HW_DMA_ICR2|= (1 << (24 + n)); + psxRegs.CP0.n.Cause |= 1 << (16 + n); + iopIntcIrq( 3 ); + } +} diff --git a/pcsx2/IopHw.h b/pcsx2/IopHw.h new file mode 100644 index 0000000000..4108b82e6b --- /dev/null +++ b/pcsx2/IopHw.h @@ -0,0 +1,130 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __PSXHW_H__ +#define __PSXHW_H__ + +#include "R3000A.h" +#include "IopMem.h" + +#define HW_DMA0_MADR (psxHu32(0x1080)) // MDEC in DMA +#define HW_DMA0_BCR (psxHu32(0x1084)) +#define HW_DMA0_CHCR (psxHu32(0x1088)) + +#define HW_DMA1_MADR (psxHu32(0x1090)) // MDEC out DMA +#define HW_DMA1_BCR (psxHu32(0x1094)) +#define HW_DMA1_CHCR (psxHu32(0x1098)) + +#define HW_DMA2_MADR (psxHu32(0x10a0)) // GPU DMA +#define HW_DMA2_BCR (psxHu32(0x10a4)) +#define HW_DMA2_CHCR (psxHu32(0x10a8)) +#define HW_DMA2_TADR (psxHu32(0x10ac)) + +#define HW_DMA3_MADR (psxHu32(0x10b0)) // CDROM DMA +#define HW_DMA3_BCR (psxHu32(0x10b4)) +#define HW_DMA3_BCR_L16 (psxHu16(0x10b4)) +#define HW_DMA3_BCR_H16 (psxHu16(0x10b6)) +#define HW_DMA3_CHCR (psxHu32(0x10b8)) + +#define HW_DMA4_MADR (psxHu32(0x10c0)) // SPU DMA +#define HW_DMA4_BCR (psxHu32(0x10c4)) +#define HW_DMA4_CHCR (psxHu32(0x10c8)) +#define HW_DMA4_TADR (psxHu32(0x10cc)) + +#define HW_DMA6_MADR (psxHu32(0x10e0)) // GPU DMA (OT) +#define HW_DMA6_BCR (psxHu32(0x10e4)) +#define HW_DMA6_CHCR (psxHu32(0x10e8)) + +#define HW_DMA7_MADR (psxHu32(0x1500)) // SPU2 DMA +#define HW_DMA7_BCR (psxHu32(0x1504)) +#define HW_DMA7_CHCR (psxHu32(0x1508)) + +#define HW_DMA8_MADR (psxHu32(0x1510)) // DEV9 DMA +#define HW_DMA8_BCR (psxHu32(0x1514)) +#define HW_DMA8_CHCR (psxHu32(0x1518)) + +#define HW_DMA9_MADR (psxHu32(0x1520)) // SIF0 DMA +#define HW_DMA9_BCR (psxHu32(0x1524)) +#define HW_DMA9_CHCR (psxHu32(0x1528)) +#define HW_DMA9_TADR (psxHu32(0x152c)) + +#define HW_DMA10_MADR (psxHu32(0x1530)) // SIF1 DMA +#define HW_DMA10_BCR (psxHu32(0x1534)) +#define HW_DMA10_CHCR (psxHu32(0x1538)) + +#define HW_DMA11_MADR (psxHu32(0x1540)) // SIO2 in +#define HW_DMA11_BCR (psxHu32(0x1544)) +#define HW_DMA11_CHCR (psxHu32(0x1548)) + +#define HW_DMA12_MADR (psxHu32(0x1550)) // SIO2 out +#define HW_DMA12_BCR (psxHu32(0x1554)) +#define HW_DMA12_CHCR (psxHu32(0x1558)) + +#define HW_DMA_PCR (psxHu32(0x10f0)) +#define HW_DMA_ICR (psxHu32(0x10f4)) + +#define HW_DMA_PCR2 (psxHu32(0x1570)) +#define HW_DMA_ICR2 (psxHu32(0x1574)) + +enum IopEventId +{ + IopEvt_Cdvd = 5 // General Cdvd commands (Seek, Standby, Break, etc) +, IopEvt_SIF0 = 9 +, IopEvt_SIF1 = 10 +, IopEvt_Dma11 = 11 +, IopEvt_Dma12 = 12 +, IopEvt_SIO = 16 +, IopEvt_Cdrom = 17 +, IopEvt_CdromRead = 18 +, IopEvt_CdvdRead = 19 +, IopEvt_DEV9 = 20 +, IopEvt_USB = 21 +}; + +extern void PSX_INT( IopEventId n, s32 ecycle); + +extern void psxSetNextBranch( u32 startCycle, s32 delta ); +extern void psxSetNextBranchDelta( s32 delta ); + +void psxHwReset(); +u8 psxHwRead8 (u32 add); +u16 psxHwRead16(u32 add); +u32 psxHwRead32(u32 add); + +void psxHwWrite8 (u32 add, u8 value); +void psxHwWrite16(u32 add, u16 value); +void psxHwWrite32(u32 add, u32 value); + +u8 psxHw4Read8 (u32 add); +void psxHw4Write8(u32 add, u8 value); + +void psxDmaInterrupt(int n); +void psxDmaInterrupt2(int n); + +int psxHwFreeze(gzFile f, int Mode); + +int psxHwConstRead8(u32 x86reg, u32 add, u32 sign); +int psxHwConstRead16(u32 x86reg, u32 add, u32 sign); +int psxHwConstRead32(u32 x86reg, u32 add); +void psxHwConstWrite8(u32 add, int mmreg); +void psxHwConstWrite16(u32 add, int mmreg); +void psxHwConstWrite32(u32 add, int mmreg); +int psxHw4ConstRead8 (u32 x86reg, u32 add, u32 sign); +void psxHw4ConstWrite8(u32 add, int mmreg); + +#endif /* __PSXHW_H__ */ diff --git a/pcsx2/IopMem.cpp b/pcsx2/IopMem.cpp new file mode 100644 index 0000000000..a83118ecbc --- /dev/null +++ b/pcsx2/IopMem.cpp @@ -0,0 +1,783 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" +#include "VU.h" +#include "iCore.h" +#include "Hw.h" +#include "iR3000A.h" + +int g_psxWriteOk=1; +static u32 writectrl; + +#ifdef PCSX2_VIRTUAL_MEM +void psxMemAlloc() +{ + // In VirtualMemory land all mem taken care by memAlloc +} + +void psxMemReset() +{ + memzero_ptr(psxM); +} + +void psxMemShutdown() +{ +} + +u8 psxMemRead8(u32 mem) +{ + u32 t = (mem >> 16) & 0x1fff; + + switch(t) { + case 0x1f80: + mem&= 0x1fffffff; + if (mem < 0x1f801000) + return psxHu8(mem); + else + return psxHwRead8(mem); + break; + +#ifdef _DEBUG + case 0x1d00: assert(0); +#endif + + case 0x1f40: + mem &= 0x1fffffff; + return psxHw4Read8(mem); + + case 0x1000: return DEV9read8(mem & 0x1FFFFFFF); + + default: + assert( g_psxWriteOk ); + return *(u8*)PSXM(mem); + } +} + +u16 psxMemRead16(u32 mem) +{ + u32 t = (mem >> 16) & 0x1fff; + + switch(t) { + case 0x1f80: + mem&= 0x1fffffff; + if (mem < 0x1f801000) + return psxHu16(mem); + else + return psxHwRead16(mem); + break; + + case 0x1d00: + SIF_LOG("Sif reg read %x value %x\n", mem, psxHu16(mem)); + switch(mem & 0xF0) + { + case 0x40: return psHu16(0x1000F240) | 0x0002; + case 0x60: return 0; + default: return *(u16*)(PS2MEM_HW+0xf200+(mem&0xf0)); + } + break; + + case 0x1f90: + return SPU2read(mem & 0x1FFFFFFF); + case 0x1000: + return DEV9read16(mem & 0x1FFFFFFF); + + default: + assert( g_psxWriteOk ); + return *(u16*)PSXM(mem); + } +} + +u32 psxMemRead32(u32 mem) +{ + u32 t = (mem >> 16) & 0x1fff; + + switch(t) { + case 0x1f80: + mem&= 0x1fffffff; + if (mem < 0x1f801000) + return psxHu32(mem); + else + return psxHwRead32(mem); + break; + + case 0x1d00: + SIF_LOG("Sif reg read %x value %x\n", mem, psxHu32(mem)); + switch(mem & 0xF0) + { + case 0x40: return psHu32(0x1000F240) | 0xF0000002; + case 0x60: return 0; + default: return *(u32*)(PS2MEM_HW+0xf200+(mem&0xf0)); + } + break; + + case 0x1fff: return g_psxWriteOk; + case 0x1000: + return DEV9read32(mem & 0x1FFFFFFF); + + default: + //assert(g_psxWriteOk); + if( mem == 0xfffe0130 ) + return writectrl; + else if( mem == 0xffffffff ) + return writectrl; + else if( g_psxWriteOk ) + return *(u32*)PSXM(mem); + else return 0; + } +} + +void psxMemWrite8(u32 mem, u8 value) +{ + u32 t = (mem >> 16) & 0x1fff; + + switch(t) { + case 0x1f80: + mem&= 0x1fffffff; + if (mem < 0x1f801000) + psxHu8(mem) = value; + else + psxHwWrite8(mem, value); + break; + + case 0x1f40: + mem&= 0x1fffffff; + psxHw4Write8(mem, value); + break; + + case 0x1d00: + SysPrintf("sw8 [0x%08X]=0x%08X\n", mem, value); + *(u8*)(PS2MEM_HW+0xf200+(mem&0xff)) = value; + break; + + case 0x1000: + DEV9write8(mem & 0x1fffffff, value); + return; + + default: + assert(g_psxWriteOk); + *(u8 *)PSXM(mem) = value; + psxCpu->Clear(mem&~3, 1); + break; + } +} + +void psxMemWrite16(u32 mem, u16 value) +{ + u32 t = (mem >> 16) & 0x1fff; + switch(t) { + case 0x1600: + //HACK: DEV9 VM crash fix + break; + case 0x1f80: + mem&= 0x1fffffff; + if (mem < 0x1f801000) + psxHu16(mem) = value; + else + psxHwWrite16(mem, value); + break; + + case 0x1d00: + switch (mem & 0xf0) { + case 0x10: + // write to ps2 mem + psHu16(0x1000F210) = value; + return; + case 0x40: + { + u32 temp = value & 0xF0; + // write to ps2 mem + if(value & 0x20 || value & 0x80) + { + psHu16(0x1000F240) &= ~0xF000; + psHu16(0x1000F240) |= 0x2000; + } + + if(psHu16(0x1000F240) & temp) psHu16(0x1000F240) &= ~temp; + else psHu16(0x1000F240) |= temp; + return; + } + case 0x60: + psHu32(0x1000F260) = 0; + return; + default: + assert(0); + } + return; + + case 0x1f90: + SPU2write(mem & 0x1FFFFFFF, value); return; + + case 0x1000: + DEV9write16(mem & 0x1fffffff, value); return; + default: + assert( g_psxWriteOk ); + *(u16 *)PSXM(mem) = value; + psxCpu->Clear(mem&~3, 1); + break; + } +} + +void psxMemWrite32(u32 mem, u32 value) +{ + u32 t = (mem >> 16) & 0x1fff; + switch(t) { + case 0x1f80: + mem&= 0x1fffffff; + if (mem < 0x1f801000) + psxHu32(mem) = value; + else + psxHwWrite32(mem, value); + break; + + case 0x1d00: + switch (mem & 0xf0) { + case 0x10: + // write to ps2 mem + psHu32(0x1000F210) = value; + return; + case 0x20: + // write to ps2 mem + psHu32(0x1000F220) &= ~value; + return; + case 0x30: + // write to ps2 mem + psHu32(0x1000F230) |= value; + return; + case 0x40: + { + u32 temp = value & 0xF0; + // write to ps2 mem + if(value & 0x20 || value & 0x80) + { + psHu32(0x1000F240) &= ~0xF000; + psHu32(0x1000F240) |= 0x2000; + } + + + if(psHu32(0x1000F240) & temp) psHu32(0x1000F240) &= ~temp; + else psHu32(0x1000F240) |= temp; + return; + } + case 0x60: + psHu32(0x1000F260) = 0; + return; + + default: + *(u32*)(PS2MEM_HW+0xf200+(mem&0xf0)) = value; + } + + return; + + case 0x1000: + DEV9write32(mem & 0x1fffffff, value); + return; + + case 0x1ffe: + if( mem == 0xfffe0130 ) { + writectrl = value; + switch (value) { + case 0x800: case 0x804: + case 0xc00: case 0xc04: + case 0xcc0: case 0xcc4: + case 0x0c4: + g_psxWriteOk = 0; + //PSXMEM_LOG("writectrl: writenot ok\n"); + break; + case 0x1e988: + case 0x1edd8: + g_psxWriteOk = 1; + //PSXMEM_LOG("writectrl: write ok\n"); + break; + default: + PSXMEM_LOG("unk %8.8lx = %x\n", mem, value); + break; + } + } + break; + + default: + + if( g_psxWriteOk ) { + *(u32 *)PSXM(mem) = value; + psxCpu->Clear(mem&~3, 1); + } + + break; + } +} + +#else + +// TLB functions + +#ifdef TLB_DEBUG_MEM +void* PSXM(u32 mem) +{ + return (psxMemRLUT[(mem) >> 16] == 0 ? NULL : (void*)(psxMemRLUT[(mem) >> 16] + ((mem) & 0xffff))); +} + +void* _PSXM(u32 mem) +{ + return ((void*)(psxMemRLUT[(mem) >> 16] + ((mem) & 0xffff))); +} +#endif + +u8 *psxM = NULL; +u8 *psxP = NULL; +u8 *psxH = NULL; +u8 *psxS = NULL; + +uptr *psxMemWLUT = NULL; +const uptr *psxMemRLUT = NULL; + +static u8* m_psxAllMem = NULL; +static const uint m_psxMemSize = + Ps2MemSize::IopRam + + Ps2MemSize::IopHardware + + 0x00010000 + // psxP + 0x00010000 ; // psxS + +void psxMemAlloc() +{ + if( m_psxAllMem == NULL ) + m_psxAllMem = vtlb_malloc( m_psxMemSize, 4096, 0x21000000 ); + + if( m_psxAllMem == NULL) + throw Exception::OutOfMemory( "psxMemAlloc > failed allocating memory for the IOP processor." ); + + u8* curpos = m_psxAllMem; + psxM = curpos; curpos += Ps2MemSize::IopRam; + psxP = curpos; curpos += 0x00010000; + psxH = curpos; curpos += Ps2MemSize::IopHardware; + psxS = curpos; //curpos += 0x00010000; + + psxMemWLUT = (uptr*)_aligned_malloc(0x10000 * sizeof(uptr) * 2, 16); + psxMemRLUT = psxMemWLUT + 0x10000; //(uptr*)_aligned_malloc(0x10000 * sizeof(uptr),16); +} + +// Note! Resetting the IOP's memory state is dependent on having *all* psx memory allocated, +// which is performed by MemInit and PsxMemInit() +void psxMemReset() +{ + jASSUME( psxMemWLUT != NULL ); + jASSUME( m_psxAllMem != NULL ); + + DbgCon::Status( "psxMemReset > Resetting core memory!" ); + + memzero_ptr<0x10000 * sizeof(uptr) * 2>( psxMemWLUT ); // clears both allocations, RLUT and WLUT + memzero_ptr( m_psxAllMem ); + + // Trick! We're accessing RLUT here through WLUT, since it's the non-const pointer. + // So the ones with a 1 prefixed (ala 0x18000, etc) are RLUT tables. + for (int i=0; i<0x0080; i++) + { + psxMemWLUT[i + 0x0000] = (uptr)&psxM[(i & 0x1f) << 16]; + psxMemWLUT[i + 0x8000] = (uptr)&psxM[(i & 0x1f) << 16]; + psxMemWLUT[i + 0xa000] = (uptr)&psxM[(i & 0x1f) << 16]; + + // RLUTs, accessed through WLUT. + psxMemWLUT[i + 0x10000] = (uptr)&psxM[(i & 0x1f) << 16]; + psxMemWLUT[i + 0x18000] = (uptr)&psxM[(i & 0x1f) << 16]; + psxMemWLUT[i + 0x1a000] = (uptr)&psxM[(i & 0x1f) << 16]; + } + + // A few single-page allocations... + psxMemWLUT[0x11f00] = (uptr)psxP; + psxMemWLUT[0x11f80] = (uptr)psxH; + psxMemWLUT[0x1bf80] = (uptr)psxH; + + psxMemWLUT[0x1f00] = (uptr)psxP; + psxMemWLUT[0x1f80] = (uptr)psxH; + psxMemWLUT[0xbf80] = (uptr)psxH; + + // Read-only memory areas, so don't map WLUT for these... + for (int i=0; i<0x0040; i++) + { + psxMemWLUT[i + 0x11fc0] = (uptr)&PS2MEM_ROM[i << 16]; + psxMemWLUT[i + 0x19fc0] = (uptr)&PS2MEM_ROM[i << 16]; + psxMemWLUT[i + 0x1bfc0] = (uptr)&PS2MEM_ROM[i << 16]; + } + + for (int i=0; i<0x0004; i++) + { + psxMemWLUT[i + 0x11e00] = (uptr)&PS2MEM_ROM1[i << 16]; + psxMemWLUT[i + 0x19e00] = (uptr)&PS2MEM_ROM1[i << 16]; + psxMemWLUT[i + 0x1be00] = (uptr)&PS2MEM_ROM1[i << 16]; + } + + // Scratchpad! (which is read only? (air)) + psxMemWLUT[0x11d00] = (uptr)psxS; + psxMemWLUT[0x1bd00] = (uptr)psxS; + + // why isn't scratchpad read/write? (air) + //for (i=0; i<0x0001; i++) psxMemWLUT[i + 0x1d00] = (uptr)&psxS[i << 16]; + //for (i=0; i<0x0001; i++) psxMemWLUT[i + 0xbd00] = (uptr)&psxS[i << 16]; + + // this one looks like an old hack for some special write-only memory area, + // but leaving it in for reference (air) + //for (i=0; i<0x0008; i++) psxMemWLUT[i + 0xbfc0] = (uptr)&psR[i << 16]; +} + +void psxMemShutdown() +{ + vtlb_free( m_psxAllMem, m_psxMemSize ); + m_psxAllMem = NULL; + //safe_aligned_free( m_psxAllMem ); + + psxM = psxP = psxH = psxS = NULL; + + safe_aligned_free(psxMemWLUT); + psxMemRLUT = NULL; +} + +u8 psxMemRead8(u32 mem) { + const u8* p; + u32 t; + + t = (mem >> 16) & 0x1fff; + if (t == 0x1f80) { + mem&= 0x1fffffff; + if (mem < 0x1f801000) + return psxHu8(mem); + else + return psxHwRead8(mem); + } else + if (t == 0x1f40) { + mem&= 0x1fffffff; + return psxHw4Read8(mem); + } else { + p = (const u8*)(psxMemRLUT[mem >> 16]); + if (p != NULL) { + return *(const u8 *)(p + (mem & 0xffff)); + } else { + if (t == 0x1000) return DEV9read8(mem & 0x1FFFFFFF); + PSXMEM_LOG("err lb %8.8lx\n", mem); + return 0; + } + } +} + +u16 psxMemRead16(u32 mem) { + const u8* p; + u32 t; + + t = (mem >> 16) & 0x1fff; + if (t == 0x1f80) { + mem&= 0x1fffffff; + if (mem < 0x1f801000) + return psxHu16(mem); + else + return psxHwRead16(mem); + } else { + p = (const u8*)(psxMemRLUT[mem >> 16]); + if (p != NULL) { + if (t == 0x1d00) { + u16 ret; + switch(mem & 0xF0) + { + case 0x00: + ret= psHu16(0x1000F200); + break; + case 0x10: + ret= psHu16(0x1000F210); + break; + case 0x40: + ret= psHu16(0x1000F240) | 0x0002; + break; + case 0x60: + ret = 0; + break; + default: + ret = psxHu16(mem); + break; + } + SIF_LOG("Sif reg read %x value %x\n", mem, ret); + return ret; + } + return *(const u16 *)(p + (mem & 0xffff)); + } else { + if (t == 0x1F90) + return SPU2read(mem & 0x1FFFFFFF); + if (t == 0x1000) return DEV9read16(mem & 0x1FFFFFFF); + PSXMEM_LOG("err lh %8.8lx\n", mem); + return 0; + } + } +} + +u32 psxMemRead32(u32 mem) { + const u8* p; + u32 t; + t = (mem >> 16) & 0x1fff; + if (t == 0x1f80) { + mem&= 0x1fffffff; + if (mem < 0x1f801000) + return psxHu32(mem); + else + return psxHwRead32(mem); + } else { + //see also Hw.c + p = (const u8*)(psxMemRLUT[mem >> 16]); + if (p != NULL) { + if (t == 0x1d00) { + u32 ret; + switch(mem & 0xF0) + { + case 0x00: + ret= psHu32(0x1000F200); + break; + case 0x10: + ret= psHu32(0x1000F210); + break; + case 0x20: + ret= psHu32(0x1000F220); + break; + case 0x30: // EE Side + ret= psHu32(0x1000F230); + break; + case 0x40: + ret= psHu32(0x1000F240) | 0xF0000002; + break; + case 0x60: + ret = 0; + break; + default: + ret = psxHu32(mem); + break; + } + SIF_LOG("Sif reg read %x value %x\n", mem, ret); + return ret; + } + return *(const u32 *)(p + (mem & 0xffff)); + } else { + if (t == 0x1000) return DEV9read32(mem & 0x1FFFFFFF); + + if (mem != 0xfffe0130) { +#ifdef PSXMEM_LOG + if (g_psxWriteOk) PSXMEM_LOG("err lw %8.8lx\n", mem); +#endif + } else { + return writectrl; + } + return 0; + } + } +} + +void psxMemWrite8(u32 mem, u8 value) { + char *p; + u32 t; + + t = (mem >> 16) & 0x1fff; + if (t == 0x1f80) { + mem&= 0x1fffffff; + if (mem < 0x1f801000) + psxHu8(mem) = value; + else + psxHwWrite8(mem, value); + } else + if (t == 0x1f40) { + mem&= 0x1fffffff; + psxHw4Write8(mem, value); + } else { + p = (char *)(psxMemWLUT[mem >> 16]); + if (p != NULL) { + *(u8 *)(p + (mem & 0xffff)) = value; + psxCpu->Clear(mem&~3, 1); + } else { + if ((t & 0x1FFF)==0x1D00) SysPrintf("sw8 [0x%08X]=0x%08X\n", mem, value); + if (t == 0x1d00) { + psxSu8(mem) = value; return; + } + if (t == 0x1000) { + DEV9write8(mem & 0x1fffffff, value); return; + } + PSXMEM_LOG("err sb %8.8lx = %x\n", mem, value); + } + } +} + +void psxMemWrite16(u32 mem, u16 value) { + char *p; + u32 t; + + t = (mem >> 16) & 0x1fff; + if (t == 0x1f80) { + mem&= 0x1fffffff; + if (mem < 0x1f801000) + psxHu16(mem) = value; + else + psxHwWrite16(mem, value); + } else { + p = (char *)(psxMemWLUT[mem >> 16]); + if (p != NULL) { + if ((t & 0x1FFF)==0x1D00) SysPrintf("sw16 [0x%08X]=0x%08X\n", mem, value); + *(u16 *)(p + (mem & 0xffff)) = value; + psxCpu->Clear(mem&~3, 1); + } else { + if (t == 0x1d00) { + switch (mem & 0xf0) { + case 0x10: + // write to ps2 mem + psHu16(0x1000F210) = value; + return; + case 0x40: + { + u32 temp = value & 0xF0; + // write to ps2 mem + if(value & 0x20 || value & 0x80) + { + psHu16(0x1000F240) &= ~0xF000; + psHu16(0x1000F240) |= 0x2000; + } + + + if(psHu16(0x1000F240) & temp) psHu16(0x1000F240) &= ~temp; + else psHu16(0x1000F240) |= temp; + return; + } + case 0x60: + psHu32(0x1000F260) = 0; + return; + + } + psxSu16(mem) = value; return; + } + if (t == 0x1F90) { + SPU2write(mem & 0x1FFFFFFF, value); return; + } + if (t == 0x1000) { + DEV9write16(mem & 0x1fffffff, value); return; + } + PSXMEM_LOG("err sh %8.8lx = %x\n", mem, value); + } + } +} + +void psxMemWrite32(u32 mem, u32 value) { + char *p; + u32 t; + + t = (mem >> 16) & 0x1fff; + if (t == 0x1f80) { + mem&= 0x1fffffff; + if (mem < 0x1f801000) + psxHu32(mem) = value; + else + psxHwWrite32(mem, value); + } else { + //see also Hw.c + p = (char *)(psxMemWLUT[mem >> 16]); + if (p != NULL) { + *(u32 *)(p + (mem & 0xffff)) = value; + psxCpu->Clear(mem&~3, 1); + } else { + if (mem != 0xfffe0130) { + if (t == 0x1d00) { + MEM_LOG("iop Sif reg write %x value %x\n", mem, value); + switch (mem & 0xf0) { + case 0x10: + // write to ps2 mem + psHu32(0x1000F210) = value; + return; + case 0x20: + // write to ps2 mem + psHu32(0x1000F220) &= ~value; + return; + case 0x30: + // write to ps2 mem + psHu32(0x1000F230) |= value; + return; + case 0x40: + { + u32 temp = value & 0xF0; + // write to ps2 mem + if(value & 0x20 || value & 0x80) + { + psHu32(0x1000F240) &= ~0xF000; + psHu32(0x1000F240) |= 0x2000; + } + + + if(psHu32(0x1000F240) & temp) psHu32(0x1000F240) &= ~temp; + else psHu32(0x1000F240) |= temp; + return; + } + case 0x60: + psHu32(0x1000F260) = 0; + return; + + } + psxSu32(mem) = value; + + // write to ps2 mem + if( (mem & 0xf0) != 0x60 ) + *(u32*)(PS2MEM_HW+0xf200+(mem&0xf0)) = value; + return; + } + if (t == 0x1000) { + DEV9write32(mem & 0x1fffffff, value); return; + } + + //if (!g_psxWriteOk) psxCpu->Clear(mem&~3, 1); + if (g_psxWriteOk) { PSXMEM_LOG("err sw %8.8lx = %x\n", mem, value); } + } else { + writectrl = value; + switch (value) { + case 0x800: case 0x804: + case 0xc00: case 0xc04: + case 0xcc0: case 0xcc4: + case 0x0c4: + if (g_psxWriteOk == 0) break; + g_psxWriteOk = 0; + + // Performance note: Use a for loop instead of memset/memzero + // This generates *much* more efficient code in this particular case (due to few iterations) + for (int i=0; i<0x0080; i++) + { + psxMemWLUT[i + 0x0000] = 0; + psxMemWLUT[i + 0x8000] = 0; + psxMemWLUT[i + 0xa000] = 0; + } + //PSXMEM_LOG("writectrl: writenot ok\n"); + break; + case 0x1e988: + case 0x1edd8: + if (g_psxWriteOk == 1) break; + g_psxWriteOk = 1; + for (int i=0; i<0x0080; i++) + { + psxMemWLUT[i + 0x0000] = (uptr)&psxM[(i & 0x1f) << 16]; + psxMemWLUT[i + 0x8000] = (uptr)&psxM[(i & 0x1f) << 16]; + psxMemWLUT[i + 0xa000] = (uptr)&psxM[(i & 0x1f) << 16]; + } + //PSXMEM_LOG("writectrl: write ok\n"); + break; + default: + PSXMEM_LOG("unk %8.8lx = %x\n", mem, value); + break; + } + } + } + } +} + +#endif diff --git a/pcsx2/IopMem.h b/pcsx2/IopMem.h new file mode 100644 index 0000000000..93d6e26eda --- /dev/null +++ b/pcsx2/IopMem.h @@ -0,0 +1,103 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __PSXMEMORY_H__ +#define __PSXMEMORY_H__ + +extern u8 *psxM; +extern u8 *psxP; +extern u8 *psxH; +extern u8 *psxS; +extern uptr *psxMemWLUT; +extern const uptr *psxMemRLUT; + +#ifdef TLB_DEBUG_MEM +void* PSXM(u32 mem); +void* _PSXM(u32 mem); +#else +#define PSXM(mem) (psxMemRLUT[(mem) >> 16] == 0 ? NULL : (void*)(psxMemRLUT[(mem) >> 16] + ((mem) & 0xffff))) +#define _PSXM(mem) ((const void*)(psxMemRLUT[(mem) >> 16] + ((mem) & 0xffff))) +#endif + +#define psxSs8(mem) psxS[(mem) & 0xffff] +#define psxSs16(mem) (*(s16*)&psxS[(mem) & 0xffff]) +#define psxSs32(mem) (*(s32*)&psxS[(mem) & 0xffff]) +#define psxSu8(mem) (*(u8*) &psxS[(mem) & 0xffff]) +#define psxSu16(mem) (*(u16*)&psxS[(mem) & 0xffff]) +#define psxSu32(mem) (*(u32*)&psxS[(mem) & 0xffff]) + +#define psxMs8(mem) psxM[(mem) & 0x1fffff] +#define psxMs16(mem) (*(s16*)&psxM[(mem) & 0x1fffff]) +#define psxMs32(mem) (*(s32*)&psxM[(mem) & 0x1fffff]) +#define psxMu8(mem) (*(u8*) &psxM[(mem) & 0x1fffff]) +#define psxMu16(mem) (*(u16*)&psxM[(mem) & 0x1fffff]) +#define psxMu32(mem) (*(u32*)&psxM[(mem) & 0x1fffff]) +#define psxMu64(mem) (*(u64*)&psxM[(mem) & 0x1fffff]) + +#define psxPs8(mem) psxP[(mem) & 0xffff] +#define psxPs16(mem) (*(s16*)&psxP[(mem) & 0xffff]) +#define psxPs32(mem) (*(s32*)&psxP[(mem) & 0xffff]) +#define psxPu8(mem) (*(u8*) &psxP[(mem) & 0xffff]) +#define psxPu16(mem) (*(u16*)&psxP[(mem) & 0xffff]) +#define psxPu32(mem) (*(u32*)&psxP[(mem) & 0xffff]) + +#define psxHs8(mem) psxH[(mem) & 0xffff] +#define psxHs16(mem) (*(s16*)&psxH[(mem) & 0xffff]) +#define psxHs32(mem) (*(s32*)&psxH[(mem) & 0xffff]) +#define psxHu8(mem) (*(u8*) &psxH[(mem) & 0xffff]) +#define psxHu16(mem) (*(u16*)&psxH[(mem) & 0xffff]) +#define psxHu32(mem) (*(u32*)&psxH[(mem) & 0xffff]) + +#define PSXMs8(mem) (*(s8 *)_PSXM(mem)) +#define PSXMs16(mem) (*(s16*)_PSXM(mem)) +#define PSXMs32(mem) (*(s32*)_PSXM(mem)) +#define PSXMu8(mem) (*(u8 *)_PSXM(mem)) +#define PSXMu16(mem) (*(u16*)_PSXM(mem)) +#define PSXMu32(mem) (*(u32*)_PSXM(mem)) + +void psxMemAlloc(); +void psxMemReset(); +void psxMemShutdown(); + +u8 psxMemRead8 (u32 mem); +u16 psxMemRead16(u32 mem); +u32 psxMemRead32(u32 mem); +void psxMemWrite8 (u32 mem, u8 value); +void psxMemWrite16(u32 mem, u16 value); +void psxMemWrite32(u32 mem, u32 value); + +// x86reg and mmreg are always x86 regs +void psxRecMemRead8(); +int psxRecMemConstRead8(u32 x86reg, u32 mem, u32 sign); + +void psxRecMemRead16(); +int psxRecMemConstRead16(u32 x86reg, u32 mem, u32 sign); + +void psxRecMemRead32(); +int psxRecMemConstRead32(u32 x86reg, u32 mem); + +void psxRecMemWrite8(); +int psxRecMemConstWrite8(u32 mem, int mmreg); + +void psxRecMemWrite16(); +int psxRecMemConstWrite16(u32 mem, int mmreg); + +void psxRecMemWrite32(); +int psxRecMemConstWrite32(u32 mem, int mmreg); + +#endif /* __PSXMEMORY_H__ */ diff --git a/pcsx2/IopSio2.cpp b/pcsx2/IopSio2.cpp new file mode 100644 index 0000000000..34d3c6e26e --- /dev/null +++ b/pcsx2/IopSio2.cpp @@ -0,0 +1,270 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" + +sio2Struct sio2; + +/* +w [8268]=0x3bc sio2_start/sio2man +r [8270] padman_start/padman + padman->7480[00]=bit4; + padman->7480[13]=bit5; + packetExchange(&703F8); +w [8268]|=0x0C; +........ +w [8268]|=0x01; + +only recv2 & dataout influences padman +*/ + +// 0xBF808200,0xBF808204,0xBF808208,0xBF80820C, +// 0xBF808210,0xBF808214,0xBF808218,0xBF80821C, packet->sendArray3 +// 0xBF808220,0xBF808224,0xBF808228,0xBF80822C, call12/13_s/getparams +// 0xBF808230,0xBF808234,0xBF808238,0xBF80823C, + +// 0xBF808240,0xBF808248,0xBF808250,0xBF808258, packet->sendArray1/call_7/8 +// 0xBF808244,0xBF80824C,0xBF808254,0xBF80825C, packet->sendArray2/call_9/10 + +// 0xBF808260, serial data/fifo in/out s/get8260_datain/out packet->sendbuf(nomem!) +// 0xBF808268, ctrl s/get8268_ctrl + +// 0xBF80826C, packet->recv1/2/3 get826C_recv1, get8270_recv2, get8274_recv3 +// 0xBF808270,0xBF808274, + +// 0xBF808278,0xBF80827C, s/get8278, s/get827C +// 0xBF808280 interrupt related s/get8280_intr + + +void sio2Reset() { + SysPrintf("Sio2 init\n"); + memzero_obj(sio2); + sio2.packet.recvVal1 = 0x1D100; // Nothing is connected at start +} + +u32 sio2_getRecv1() { + PAD_LOG("Reading Recv1 = %x\n",sio2.packet.recvVal1); + + return sio2.packet.recvVal1; +} + +u32 sio2_getRecv2() { + PAD_LOG("Reading Recv2 = %x\n",0xF); + + return 0xf; +}//0, 0x10, 0x20, 0x10 | 0x20; bits 4 & 5 + +u32 sio2_getRecv3() { + if(sio2.packet.recvVal3 == 0x8C || sio2.packet.recvVal3 == 0x8b || + sio2.packet.recvVal3 == 0x83) + { + PAD_LOG("Reading Recv3 = %x\n",sio2.packet.recvVal3); + + sio.packetsize = sio2.packet.recvVal3; + sio2.packet.recvVal3 = 0; // Reset + return sio.packetsize; + } + else + { + PAD_LOG("Reading Recv3 = %x\n",sio.packetsize << 16); + + return sio.packetsize << 16; + } +} + +void sio2_setSend1(u32 index, u32 value){sio2.packet.sendArray1[index]=value;} //0->3 +u32 sio2_getSend1(u32 index){return sio2.packet.sendArray1[index];} //0->3 +void sio2_setSend2(u32 index, u32 value){sio2.packet.sendArray2[index]=value;} //0->3 +u32 sio2_getSend2(u32 index){return sio2.packet.sendArray2[index];} //0->3 + +void sio2_setSend3(u32 index, u32 value) +{ +// int i; + sio2.packet.sendArray3[index]=value; +#ifdef PAD_LOG +// if (index==15){ +// for (i=0; i<4; i++){PAD_LOG("0x%08X ", sio2.packet.sendArray1[i]);}PAD_LOG("\n"); +// for (i=0; i<4; i++){PAD_LOG("0x%08X ", sio2.packet.sendArray2[i]);}PAD_LOG("\n"); +// for (i=0; i<8; i++){PAD_LOG("0x%08X ", sio2.packet.sendArray3[i]);}PAD_LOG("\n"); +// for ( ; i<16; i++){PAD_LOG("0x%08X ", sio2.packet.sendArray3[i]);}PAD_LOG("\n"); + PAD_LOG("[%d] : 0x%08X ", index,sio2.packet.sendArray3[index]); + PAD_LOG("\n"); +// } +#endif +} //0->15 + +u32 sio2_getSend3(u32 index) {return sio2.packet.sendArray3[index];} //0->15 + +void sio2_setCtrl(u32 value){ + sio2.ctrl=value; + if (sio2.ctrl & 1){ //recv packet + //handle data that had been sent + + iopIntcIrq( 17 ); + //SBUS + sio2.recvIndex=0; + sio2.ctrl &= ~1; + } else { // send packet + //clean up + sio2.packet.sendSize=0; //reset size + sio2.cmdport=0; + sio2.cmdlength=0; + sioWriteCtrl16(SIO_RESET); + } +} +u32 sio2_getCtrl(){return sio2.ctrl;} + +void sio2_setIntr(u32 value){sio2.intr=value;} +u32 sio2_getIntr(){ + return sio2.intr; +} + +void sio2_set8278(u32 value){sio2._8278=value;} +u32 sio2_get8278(){return sio2._8278;} +void sio2_set827C(u32 value){sio2._827C=value;} +u32 sio2_get827C(){return sio2._827C;} + +void sio2_serialIn(u8 value){ + u16 ctrl=0x0002; + if (sio2.packet.sendArray3[sio2.cmdport] && (sio2.cmdlength==0)) + { + + sio2.cmdlength=(sio2.packet.sendArray3[sio2.cmdport] >> 8) & 0x1FF; + ctrl &= ~0x2000; + ctrl |= (sio2.packet.sendArray3[sio2.cmdport] & 1) << 13; + //sioWriteCtrl16(SIO_RESET); + sioWriteCtrl16(ctrl); + PSXDMA_LOG("sio2_fifoIn: ctrl = %x, cmdlength = %x, cmdport = %d (%x)\n", ctrl, sio2.cmdlength, sio2.cmdport, sio2.packet.sendArray3[sio2.cmdport]); + + sio2.cmdport++; + } + + if (sio2.cmdlength) sio2.cmdlength--; + sioWrite8(value); + + if (sio2.packet.sendSize > BUFSIZE) {//asadr + SysPrintf("*PCSX2*: sendSize >= %d\n", BUFSIZE); + } else { + sio2.buf[sio2.packet.sendSize] = sioRead8(); + sio2.packet.sendSize++; + } +} +extern void SIODMAWrite(u8 value); + +void sio2_fifoIn(u8 value){ + u16 ctrl=0x0002; + if (sio2.packet.sendArray3[sio2.cmdport] && (sio2.cmdlength==0)) + { + + sio2.cmdlength=(sio2.packet.sendArray3[sio2.cmdport] >> 8) & 0x1FF; + ctrl &= ~0x2000; + ctrl |= (sio2.packet.sendArray3[sio2.cmdport] & 1) << 13; + //sioWriteCtrl16(SIO_RESET); + sioWriteCtrl16(ctrl); + PSXDMA_LOG("sio2_fifoIn: ctrl = %x, cmdlength = %x, cmdport = %d (%x)\n", ctrl, sio2.cmdlength, sio2.cmdport, sio2.packet.sendArray3[sio2.cmdport]); + + sio2.cmdport++; + } + + if (sio2.cmdlength) sio2.cmdlength--; + SIODMAWrite(value); + + if (sio2.packet.sendSize > BUFSIZE) {//asadr + SysPrintf("*PCSX2*: sendSize >= %d\n", BUFSIZE); + } else { + sio2.buf[sio2.packet.sendSize] = sioRead8(); + sio2.packet.sendSize++; + } +} + +u8 sio2_fifoOut(){ + if (sio2.recvIndex <= sio2.packet.sendSize){ + //PAD_LOG("READING %x\n",sio2.buf[sio2.recvIndex]); + return sio2.buf[sio2.recvIndex++]; + } else { + SysPrintf("*PCSX2*: buffer overrun\n"); + } + return 0; // No Data +} + +///////////////////////////////////////////////// +//////////////////////////////////////////// DMA +///////////////////////////////////////////////// + +void psxDma11(u32 madr, u32 bcr, u32 chcr) { + unsigned int i, j; + int size = (bcr >> 16) * (bcr & 0xffff); + PSXDMA_LOG("*** DMA 11 - SIO2 in *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); + + if (chcr != 0x01000201) return; + + for(i = 0; i < (bcr >> 16); i++) + { + sio.count = 1; + for(j = 0; j < ((bcr & 0xFFFF) * 4); j++) + { + sio2_fifoIn(PSXMu8(madr)); + madr++; + if(sio2.packet.sendSize == BUFSIZE) { + HW_DMA11_MADR = madr; + PSX_INT(IopEvt_Dma11,(size>>2)); // Interrupts should always occur at the end + return; + } + } + } + + HW_DMA11_MADR = madr; + PSX_INT(IopEvt_Dma11,(size>>2)); // Interrupts should always occur at the end +} + +void psxDMA11Interrupt() +{ + HW_DMA11_CHCR &= ~0x01000000; + psxDmaInterrupt2(4); +} + +void psxDma12(u32 madr, u32 bcr, u32 chcr) { + int size = ((bcr >> 16) * (bcr & 0xFFFF)) * 4; + PSXDMA_LOG("*** DMA 12 - SIO2 out *** %lx addr = %lx size = %lx\n", chcr, madr, size); + + if (chcr != 0x41000200) return; + + sio2.recvIndex = 0; // Set To start; saqib + + bcr = size; + while (bcr > 0) { + PSXMu8(madr) = sio2_fifoOut(); + bcr--; madr++; + if(sio2.recvIndex == sio2.packet.sendSize) break; + } + HW_DMA12_MADR = madr; + PSX_INT(IopEvt_Dma12,(size>>2)); // Interrupts should always occur at the end +} + +void psxDMA12Interrupt() +{ + HW_DMA12_CHCR &= ~0x01000000; + psxDmaInterrupt2(5); +} + +void SaveState::sio2Freeze() { + Freeze(sio2); +} + diff --git a/pcsx2/IopSio2.h b/pcsx2/IopSio2.h new file mode 100644 index 0000000000..b8642462f0 --- /dev/null +++ b/pcsx2/IopSio2.h @@ -0,0 +1,99 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __PSXSIO2_H__ +#define __PSXSIO2_H__ + + +#define BUFSIZE 8448 + +//from sio2man.c + +struct SIO2_packet { + unsigned int recvVal1; // 0x00 + unsigned int sendArray1[4]; // 0x04-0x10 + unsigned int sendArray2[4]; // 0x14-0x20 + + unsigned int recvVal2; // 0x24 + + unsigned int sendArray3[16]; // 0x28-0x64 + + unsigned int recvVal3; // 0x68 + + int sendSize; // 0x6C + int recvSize; // 0x70 + + unsigned char *sendBuf; // 0x74 + unsigned char *recvBuf; // 0x78 + + unsigned int dmacAddress1; + unsigned int dmacSize1; + unsigned int dmacCount1; + unsigned int dmacAddress2; + unsigned int dmacSize2; + unsigned int dmacCount2; +}; + +struct sio2Struct { + struct SIO2_packet packet; + u32 ctrl; + u32 intr; + u32 _8278, _827C; + int recvIndex; + u32 hackedRecv; + int cmdport; + int cmdlength; //length of a command sent to a port + //is less_equal than the dma send size + u8 buf[BUFSIZE]; +}; + +extern sio2Struct sio2; + +void sio2Reset(); + +u32 sio2_getRecv1(); +u32 sio2_getRecv2(); +u32 sio2_getRecv3(); +void sio2_setSend1(u32 index, u32 value); //0->3 +u32 sio2_getSend1(u32 index); //0->3 +void sio2_setSend2(u32 index, u32 value); //0->3 +u32 sio2_getSend2(u32 index); //0->3 +void sio2_setSend3(u32 index, u32 value); //0->15 +u32 sio2_getSend3(u32 index); //0->15 + +void sio2_setCtrl(u32 value); +u32 sio2_getCtrl(); +void sio2_setIntr(u32 value); +u32 sio2_getIntr(); +void sio2_set8278(u32 value); +u32 sio2_get8278(); +void sio2_set827C(u32 value); +u32 sio2_get827C(); + +void sio2_serialIn(u8 value); +void sio2_fifoIn(u8 value); +u8 sio2_fifoOut(); + +void psxDma11(u32 madr, u32 bcr, u32 chcr); +void psxDma12(u32 madr, u32 bcr, u32 chcr); + +void psxDMA11Interrupt(); +void psxDMA12Interrupt(); + +#endif /* __PSXSIO2_H__ */ + diff --git a/pcsx2/Linux/.pixmaps/pcsxAbout.xpm b/pcsx2/Linux/.pixmaps/pcsxAbout.xpm new file mode 100644 index 0000000000..2ebc445d15 --- /dev/null +++ b/pcsx2/Linux/.pixmaps/pcsxAbout.xpm @@ -0,0 +1,334 @@ +/* XPM */ +static char *pcsxAbout[] = { +/* columns rows colors chars-per-pixel */ +"314 176 152 2", +" c #252d42", +". c #282e45", +"X c #282f48", +"o c #273046", +"O c #283046", +"+ c #2a334b", +"@ c #2d3650", +"# c #2e3853", +"$ c #303651", +"% c #303955", +"& c #333d5b", +"* c #383f57", +"= c #383e5d", +"- c #35405e", +"; c #39415d", +": c #364161", +"> c #384465", +", c #3a4668", +"< c #3e4866", +"1 c #3c496c", +"2 c #3e4b70", +"3 c #40475b", +"4 c #41485d", +"5 c #404767", +"6 c #424a63", +"7 c #434c6c", +"8 c #484f61", +"9 c #464e70", +"0 c #44506f", +"q c #4c5263", +"w c #465071", +"e c #495273", +"r c #4d5679", +"t c #4e5974", +"y c #4e587b", +"u c #515665", +"i c #535866", +"p c #545968", +"a c #595d69", +"s c #50577b", +"d c #505a75", +"f c #51597d", +"g c #5c606a", +"h c #55617f", +"j c #5a637b", +"k c #64666d", +"l c #65686e", +"z c #666870", +"x c #6b6d71", +"c c #6e7073", +"v c #767676", +"b c #545d81", +"n c #566085", +"m c #586185", +"M c #5b648a", +"N c #5e6883", +"B c #5e688d", +"V c #666d84", +"C c #606a8b", +"Z c #686f86", +"A c #626b93", +"S c #656f98", +"D c #687085", +"F c #6a738c", +"G c #667097", +"H c #667098", +"J c #6d7691", +"K c #6b749d", +"L c #6e7893", +"P c #72788c", +"I c #757d93", +"U c #6d76a0", +"Y c #6e78a1", +"T c #717ba4", +"R c #747ea8", +"E c #7c8397", +"W c #79829a", +"Q c #7680ab", +"! c #7a84ae", +"~ c #7c85b1", +"^ c #7e88b3", +"/ c #808080", +"( c #80838f", +") c #888888", +"_ c #808799", +"` c #82899d", +"' c gray57", +"] c gray60", +"[ c #858da3", +"{ c #858eba", +"} c #8d93a5", +"| c #8f95a8", +" . c #8690bc", +".. c #8891bd", +"X. c #9096a9", +"o. c #999ead", +"O. c #9aa0af", +"+. c #9ca2b4", +"@. c #a2a2a2", +"#. c #aaaaaa", +"$. c #a5a9b6", +"%. c #a7acba", +"&. c #a8adbc", +"*. c #aab0bf", +"=. c #bababa", +"-. c #8e96c3", +";. c #8e98c4", +":. c #9099c6", +">. c #959dca", +",. c #96a0cc", +"<. c #98a0cd", +"1. c #9ca5d1", +"2. c #a4add9", +"3. c #aab0c0", +"4. c #b3b6c2", +"5. c #b4b8c4", +"6. c #b6bbc8", +"7. c #adb5e1", +"8. c #b0b7e3", +"9. c #b0b8e3", +"0. c #bec2cb", +"q. c #bfc7ef", +"w. c #bfc7f0", +"e. c #c2c2c2", +"r. c #c0c3cd", +"t. c gray80", +"y. c #c2c6d1", +"u. c #ccced6", +"i. c #cdd0d7", +"p. c #ced1d9", +"a. c #d0d2d7", +"s. c #d4d5d9", +"d. c #dddddd", +"f. c #c0c7f0", +"g. c #c3cbf2", +"h. c #c8cef4", +"j. c #cad0f5", +"k. c #d9dbe1", +"l. c #d2d8f6", +"z. c #dbdff8", +"x. c #dce0f8", +"c. c gray90", +"v. c #e5e7eb", +"b. c #e6e8ec", +"n. c #eeeeee", +"m. c #e1e5f9", +"M. c #ebeefb", +"N. c #eff1fc", +"B. c #f2f3f5", +"V. c #f0f1fc", +"C. c #f7f8fd", +"Z. c #fefefe", +/* pixels */ +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 1 2 1 1 2 1 1 2 2 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 1 2 , , , 1 , 1 , 1 1 , , , , 1 , , , , 1 , , , , , , > , > > , > , , : , , : , : , : , : > > > > : > > , : : : : > : : > : : : : : : : - : : - : - - : - - - - - - & - - & - - & & & & & & : & & & & & & & & & & & & & # # # # # & & # & # & # # & # # # # # # & # # & # # # # # # # # # # # @ # @ @ # @ @ @ @ @ @ @ @ @ X @ @ + @ @ + @ + @ + @ + + @ + + @ + + @ + + + + + + + + + + + + + + o + + o + + + o + X o X X + o o O O O O O X X O . . . . . . . . . o o . . . . o . o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 2 1 1 2 2 1 2 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 , 1 , , , , 2 1 , 1 , , , , , 1 , , , , , , , , , , , , > , , , > , > : , , : : , , : , : , : > > , : : > > : : : , : : : : > : : : : : : : : : - : - - - : - - - - - - & - - & - - & : & : & : & & & & & & & & & # & & # & & & & & : & # # & # & # & & # # & & # & # # # # # # # # # # # # # # @ # @ @ # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + @ + @ + @ + @ @ + + @ + + + + + + + + + + + + + + + o X + + o o + o + o + X X + + + o X o X X O O O O o o O X O . . . o o . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 2 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 , 2 , 1 1 1 , 1 1 1 1 , , 1 , 1 1 , 1 1 , 1 , 1 , , , , , , , , , , , > , , > , , > , , , : , : : , : , > : , > > : : > > : > : : > : : : : : > - : : - : - - : : : - : - - - - - & - & & - & - & & & & & & & & & & & & & & & & & # & # : # # # & & & & & # & # # & & # & # # # # # # # # & # # % # @ # # @ # # @ % @ @ @ @ @ @ @ @ @ @ @ @ X @ @ + @ @ @ + @ + @ + + + + + + + + + + + + + + + + o + + + + X + o + + o + o o @ + X + X o o o X + X O O O O X X . . . o o . . o . . . . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 1 2 1 2 1 1 2 1 1 1 1 1 1 2 , 2 2 1 1 1 1 , , 1 1 1 , 1 , , 1 , , , , , , , , , , , , , , > , , > , > , > , > : , , > , , , : , : > > : > > > : > : > : : > : : : : : : : : - : : - : : - - - - - - - - - - - - - - - - & : & & : & & & & & & & & & # & & & & & & & # & : # & # # & # # & # # & # # # & & & # # & # # # # # # # # # # @ # @ @ # @ % @ @ @ @ @ @ @ @ @ @ @ + @ + + @ + + @ + + @ + + @ + + + @ + + + + + + + X + + + + + X + o + + o + o o X X X o o + o X O O O O X O O O O . . . o . o . . o . . . . ", +"2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 1 2 1 1 1 2 1 2 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 2 , 1 , 1 1 , 1 1 , , , , , , , , , , , , , , , , , , , > , , > , : , : : : , : , > : , , : , : > > , : > : : , : , : : : : > : : : : : : : : - - - - - - - - - : & & - - - & & & : & & & & & & & & & & & & & & & & : # # & & # # & # & # & # & & # # & # # # # & # # & # # # # @ # % % # # # # @ # @ @ @ @ @ @ X @ @ @ @ @ @ @ + @ @ @ + @ @ + @ + + @ + + @ + + + + + + + + + + + + + + + + + + o o @ X o + o + o + o + + X X O O + O O O . . O . . . o o o o o . o o . . . . ", +"9 2 2 2 w 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 2 2 1 2 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 , 1 , 1 , 1 , , , 1 1 1 , 1 , , , , , , , , , , , , > , > > , , , > , , , : , : , , : : , : > > : : : , : : : : : : > : : : : : : - : : : - : : - - - - - - - & - : - & - & - & & & & & & & & & & & & & & & # & : # # # : # & & # & # & # & # # & # & # & # & # # # # # # # # % # @ # # # @ # # @ # @ @ @ @ @ # @ @ @ @ X @ @ @ + + @ @ @ + + + + + @ + @ + + + @ + + + + + + + X + X + o + o X + + o o + o o o + o o o o o o O O O O O O O . . O . o . o . . o . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 1 2 2 2 1 1 2 1 2 1 1 2 1 1 1 1 1 1 1 , 1 1 , 1 1 1 , , 1 1 , 1 , 1 , 1 , , 1 , , , , , , , , , : , > , , , , > , > > , , , : , , : , : : > , : , : > > : , : : : > : : : : : : : : : : : : - : - : : : - - : - - - : & & - & - - - & : & & : & & & & & & & & & & & # & : & & : # : & & # & # & # & # & # # # # # # # # # # # # # # # # # # # @ @ # @ @ # @ # @ @ @ # @ @ @ @ @ X @ @ @ + + + @ + @ @ + @ + + + + + + @ + + + + + + + + + o + o + X + o + + + X o + + o o + o o + o O O O O O O . . O . o . . o . . . o . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2 2 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 1 1 1 , 1 , 1 , 1 , 1 , , , 1 , , , , , , , , , , , > , > , > , , > : , , > > , , : , > > > : > : > : : > : > : : > : : : : : : : : - : : - : - - - - - - - - - & : - - & - & & & : & & & & & : & & & & & & & & & & # # & # # & # & & # & # # & & # & # & # & # # & & # # # # & # # # # # # # # @ # @ # @ @ @ @ @ @ @ @ @ @ @ @ @ + @ @ @ + + @ + @ + + + @ + @ + + + + + + + + + + + + + + X + X + o + o @ + o o + o o + o X + O O O O O O O O o o . . o o . . . o . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 1 2 1 2 2 1 2 1 2 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 2 , , , , , 1 , , 1 , , , , , , , , , , , , , , > , , > , , , , , , : , , : , , : > > , > > > > , : : , : : , : > : : : : : : > : : : - : - - : - - - - - - - - - : & & & & : & & : & & & & & & & & & & & & & # & # & # & # & & & # & # & # & # & # # & & # % # # & # # % @ & & @ & @ @ @ # @ @ # # @ @ @ @ @ @ @ @ # @ @ @ @ @ + @ + + @ @ + @ + @ @ + + @ + @ + + + + + + + + + + + o + + o + o + + + O + + O + + + X X o o o O O O O O X X O O O O O O . . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , , , 2 2 , 2 , , , , , , 1 , , , , , , , , , , , , , , , > , : , : , : , : , , : , > > : > : : > : : , : , : : : : > : : : : : : : : : : - : - : - - : - - - - - : & & : : & & : & & : & & & & & & & & & & & & & & & & & & & & # & # # & # & # & # & # # # % % # # # # # % @ @ @ @ @ % @ # # @ # @ # @ # # @ # @ X @ # X X # @ @ @ @ @ @ @ + + + @ + + + + + + + + + + + + + + + o + + + + o + + X + O O + + o O o X X X + o O X X X O O O O X . o O O O X o O . . o o o o o ", +"2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 1 2 1 2 1 1 2 1 2 2 2 1 1 2 2 1 2 1 1 1 1 , 1 1 1 1 , 1 1 1 , , 2 , 2 , 1 1 1 , 2 , , 1 , , , , , , , , , , , , , > , : : , , , : , , : : , > > : , : : : , , : : : , : > : , : , : : : : : - : : - : - : - - - - - - - & & : & : & & & & & & & : & & & & & & & & & & & & # & # & & & # & & # # & # # & & # # & # & # & # # # & & @ @ @ % @ @ % # @ @ @ # @ # @ @ @ # @ # X # # @ @ X @ @ @ @ @ + @ + @ + + @ + @ + + + + + + + + + + + + + + + X + X + + + o + o X o + X X X + o + + o O + O O O O O O X o o O O o . . . . o . o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 2 , 1 , , , , , , , , , , , 1 , , , , , , , , , > , , > , , , , : , , , : , , > , : , : , , : : : : , : : : : : : : : : : : : : : - : : - : - : - - - - - - : : : & & : & : & : & & & & & & # & & & & & & & & & & & & # & & # & & & # & & # # & & # # # # # & # # # @ @ & @ @ % # @ # # # @ @ # @ @ @ @ @ @ # # # @ @ @ @ @ + @ + + @ + + @ + @ + @ + + + @ + + + @ + + + + + + + + + + + + O + + + O + + X + o X X O O o o O O O O O O X X O X O O O o . . o . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 1 2 1 2 2 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , , 1 , , 1 , 2 1 , , , , , 1 , , , , , > , , , , , , > , : , , , , : , , : : , > > , : , : : > , : , : : : : : : : , : : : : : : : : : : : : - - - - - - - - & : & : & - & & & : & : & & & & & & & & & & & & & & & & # # & # & # & # & & & & # # # & # % & # & # # # # & # # & & @ % @ @ % @ @ @ @ @ # @ @ @ @ @ @ @ @ @ @ @ @ @ + @ @ + @ @ + @ @ + + @ + + + + + + + + + + + X + + + o + + X + + O + X + X + + O + O O O O O O O O O O O O . X O O o . o O . . o . o o . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 1 1 2 2 1 2 1 1 1 2 1 1 2 1 1 1 2 1 1 1 1 1 1 1 , 1 1 1 1 2 1 , 1 , 1 1 , , , 1 1 , , , , , , , , , , , , , > , , , , , : , : , : , , , : > > : , : , > > : > > : , : , : : : : : : : : : : : : : : : - : : - : - - - - - & : & & & : : & & & & & & & & : & & & & & & # & # & & & & & # & & & & # # # # & & # & # # # # # # # # # # # @ @ % @ @ @ @ @ & @ @ # # @ @ @ @ @ @ @ @ @ X @ X @ @ + + @ @ + @ @ + + @ + + + + + + + + + + + + + + + + + + + + + O + + o X + + o O O O O O + O O O O O O O o O . O O . . O . . . . o . o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 1 2 2 2 1 2 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 1 1 1 , 1 , 1 1 , 1 , , , , , , , , , , , , , , , : , : , , : , , : , , : , , > > , : > , : : : > > : , : , : : : : : : : : : : : : : : : - - : : - - - - - - : : & : : : & & : & & & : & & & # & & & & & & # & & & & & # & # & # # & & # & # & # # & # & # # & # # # # # # & @ @ @ % # # # # # @ @ @ @ @ @ @ @ @ @ @ X @ @ @ @ @ @ + + @ + + @ + + @ @ + + + + + + + + + + + o + + + o + + o o + + o O + + O + X O + O O O O O O O O O X O o O O O X o . . . . . o . . . o ", +"e 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 1 1 2 1 1 1 2 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 1 , 1 1 , , , , 1 , , 1 , , , , , , , , > , , , , , , , : , , , : , : , > > : > , : : , , : > > : : : : : , > : : : : : : : : : : : : : - - - - - - - - : & & - & & : & & & : & & & & & & & & & & & & & & & & # & & & & & # & & # & # & # & # # # & % & # # & # # # # # @ & % @ # @ @ # # @ # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + @ + @ @ + + @ + + + @ + + @ + + + + + + + + + + o + + X + + + + X X + X X + O O O + + O O O O O O O . X O . . . . . o . . o . . . . . . ", +"2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2 2 1 2 1 1 2 2 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 1 , 1 , 1 1 1 1 , , 1 > , 1 > , , 1 , , , , , , , , , , , , , , , , : : , , : > > > > > , > > > : : , : > : : > : > : > : : : : : : : - : - - - - - - - - - & - - : & & & & : - & & : & & & & : & & & & & & # & & & # & & & # & # & # & # & # & & # # # & # % % % % % % % % @ # # # # # @ @ @ # # @ @ # @ @ @ @ @ @ @ @ @ @ X @ @ @ @ @ @ @ X X @ + + @ + + @ + + + + + + + + + + + O + + + o + o + + O + O O O + O + X O O + O + X O X o O . . . . o o . . . . o . O O O O ", +"2 2 2 2 2 2 2 e 2 2 2 2 2 2 2 1 2 2 2 1 2 2 2 1 1 1 1 2 1 1 2 1 2 1 1 1 2 1 1 1 1 1 1 1 1 , 1 1 , 1 1 , 1 1 1 1 1 , , 1 1 , , 1 , , , , , , , , , : , , , , : , , : , , , , : , > > > > : : > : , : > : : : , : : > : : : : : : : : - : : : - : : : - : - - - - - - & : : : & & - - & & : & & & & & & & & & & & & & & & & # & & & # % & # & # & # # # & # & # # % % % @ % @ % % @ % @ # # @ % # @ @ # @ @ @ @ @ @ @ X @ @ @ @ @ @ @ X @ X @ X @ @ + @ + + + + + + + + + + + + + + + + + O X + + o + X o O O O + O O O O O O X X O O O O O O . . . o . . . . o o . o . O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 1 2 1 2 1 1 2 1 2 2 2 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 , 1 , , , , , 1 , , , 1 , , , , , , , , , , : , : : , : , : , , : , : > > > > > > > > > > : > : : : : : : : : : > : : : : : : - : : - - - : - - - - - & : & & & : : & & - - & & & & - & & # : & & & & & & & & & # # & & & & # & # & # # & & # % # # # @ % % % @ % % @ % # # # @ % # # @ # @ @ @ @ @ @ @ @ @ @ @ @ @ @ X @ @ @ @ @ @ @ + @ @ + @ + + + + + + + + + + + + O + + + + O + O O + + O + o + O + O + O o O + O O O O O O O X . . . o . . O O . o O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 1 , , 2 , , 1 , , 1 , 1 , , > 1 , , , , , , , , , , , , , , , , , , > , : , , , : > > > > > > > : : : , : > , : > > : > : : : : : : : : : : : - : : - - - - : - - - - : & : & & & - & & & & & & & & & & & # : # & # & & & & & & # # & % & & # # & % # & # # & % & % % % @ % % % # # # # # @ # @ # # @ @ # @ # @ @ @ @ @ @ @ @ X @ @ @ X @ @ @ X @ + @ + + + + + @ @ + + + X + + + + + + + O + + + + + o + + o + X + O O O O O X O O O O O X X . . . . o . X O o . . o O . O O O ", +"2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 2 2 1 2 2 1 1 2 2 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 , , 2 , 1 , 1 , , 1 , 1 , 1 , , , , , , , , , > , , , : , : , , : , > , : , > , , , > > > > > > > > : > > : : > : > : > : : : : : : > - : > - : : - - : - - - - - - - & - & & & : & & & & - & & & & & & & & & # & & & & & # & & & # # & & & & # # # % % & % # % # # % # @ % % # # % % @ % @ # # # $ # @ # @ @ @ @ @ @ @ @ @ @ # @ @ + @ @ @ X @ X @ @ + @ @ + + + + + + + + + + + + + + X + + + O O + O O + O O + o O + O + O O O O + X O O O O O . o . . . . o . O O . O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 1 2 2 1 1 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 , 1 1 1 , , , , , , 1 , , , , , , , , , , , , , , > , , , , > , > > > , : > > > > : > > > > > : > > : : : : > : > : : > : - - : - : - - : : - - - - - - - - - - : : & & : & - - & - & - & & & & & & : & & & # & & # & & & & # # # # & & & & % % % % % % # % % @ % % # % @ # # @ % # # @ # @ # @ @ # @ @ @ @ @ @ @ @ X @ @ @ @ + @ @ @ @ @ + + + + @ @ + + + + + + + + + + + + + + + + + + + + + + O + o + + O O O O + O O O O X O o O O . o O . o . . o O O O O O . O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 1 2 2 2 1 1 1 2 1 2 2 1 1 1 1 1 1 1 1 1 1 , 1 1 , 1 , 1 1 1 , 1 , 1 1 , 1 , , , , , , , , , , , , , , , , , , : , , , , : , > > > > > > > > > > : > > > > : > : , : : : : > : : : : - > > - - - : - - : : - - - - - & : & : : & & : : & & & & & & & & & & & # & & # & & & & & & # & & & & # & # & # % % % % # # % & # % % % # # # # @ % # # # # # @ @ # # @ # @ @ @ @ @ # X @ @ @ @ @ @ @ X + @ X @ + @ @ + + + + + + + + + + + + + o + O + + O + O O + O O + + o o O + O O O + + O O O O O O X O . o . o o . . . . O O O O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 2 2 2 1 1 2 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 , , 1 , , 1 , , , , , , , , , , , , , : , , , : , : , : , , > > > > > > > > > > : : > > > : > : : > : : : : > : : > - - - - : - : : - - : - - - - : & : & & & : & & & - & & & : & & & & & & & & & & # & & # & # & & # & # & & # & % % % % & # % # % # % % % % # # % @ % @ $ # # @ # @ @ # @ @ @ @ @ @ @ @ @ @ X @ @ X @ + @ @ X @ + @ + + @ + @ + + + @ + + + + + + + + + + O + + + O + O + o X + O X + O O O O O O O O O O O . O o o . o o . . O O O . O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 2 1 1 1 1 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 , , 1 1 , 1 1 1 , , 1 1 , 1 > 1 1 > 1 , , , , > , , , , , : , , : , , , , , > , > > > : , > > > > > > > > : > : , : , > : : : > : : : - : : : - : - : - : : - - - - - & : & : & & : & & & & & & : & & & & & : & & & : & & : & & & & # # & & # & # & # # & % % % % % % % # # # % % # # # % # @ % @ @ % @ # @ # @ # @ # @ @ @ @ # @ @ @ @ @ @ + + @ + + + @ + + + + + + @ + + @ + + + + X + + X + + O + + X X X + O + O O + + o o O O O O O O O O O X . . . o . . o . o . . . o o O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 2 1 2 1 1 2 1 2 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 , 1 , 1 , 1 1 1 > 1 > , , 1 , , , , , , , , , , , , , , > : , > > > > > , : , > : : > : > : > : : : : : > : : : : : : > : : : : : : - : - : - - - - - : & : & & : & & & : : & & & & & & & & # & & & & # & # # # & # & & # # & # & # & & % % % % % # % % % % # # # % # # @ @ % # # # # @ @ @ @ @ @ @ @ @ @ @ @ X @ @ @ @ + + @ @ + + @ @ + @ + + @ @ + + + + + + + + + + + + + X + + + + + + + X + O X O X X + O O O + O X O O O O O O . . o . o . O o O . o O O ", +"y 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 1 2 2 2 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 , 1 1 1 , 1 , 1 1 , , 1 > 1 1 > , 1 , , , , , > , , , , , , > , : , > , > , > , , : , : > > : > > : > : , : , : : : > : : : : : : - : : : - - - - - - - - - - - : : & : : & : & & & & - & : & & & & & & & & : # & & & & # & & & & & & & & # & # # % % % % % % % # # # % % # # # # % @ # @ # # @ # # # @ @ # @ @ @ @ @ @ # # @ @ @ @ @ + + @ @ X + @ + + @ X + + + + + + + + + + + + + O + + + X X + X X O + O + O O X X O O O O X O O O O X o O . . o . . o . . o O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 1 2 1 1 1 2 1 2 1 2 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 , 1 , , , 1 , 1 1 , 1 1 > 1 , , , , , , , , : , , , : , : , , , > > > > > > > > > > > > > > > > : , : : : : , : : : > : : : : : : : - : : : : - - : - - - - & & : & & & : & : & & & & & & & & & & : # # # # & & # & & & # & # & # # # & # & # & % % % % % % % % # % % % % # # # % # # # @ # @ # @ @ @ @ @ @ @ # X @ # X @ @ X + @ @ @ + @ @ @ + @ + + + @ + + + + + + + + + + + + + + + X + O O + + O X X + + O + X O O + O X + O O O O o O . O . . . . o . o . . . . O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 1 1 1 1 2 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 1 , , 1 , , , 1 > , , , , , , > , , , , , , , , , , , > : > > > > : , > , : : > , : > > : : : , : : : : : : : : : : : : : - - : - : - - - - - - - : : & : : & - & & & - & : & & & & & & & & & & & & : & & & & # & & # & & & # % % % % % % % % % % % % % % # # # % % # % # % # # # # # @ # @ @ @ @ @ @ @ @ @ @ X # @ @ @ + @ + @ + + + @ + + + + @ + + + + + + + + + + O + + + O + + + + O + O + O O O O O + O + X O X O O O O O O O o o o o o . . . . . . o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 1 2 2 2 1 2 2 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 , 1 , 1 1 1 1 , 1 , , , , , , , , , 1 , , , , , , , , , , , : , : : , , , , , > , > > , > : : , , : : , : > : : , : : : : , : : : : : : : : : : : - - - : - - - - - - & & : & & - : & : & & & & & & & & & & & & # & & & # # # & # & & % % & # % & % % % & % % % % % % # % # # # # # # # # # @ @ @ % @ # @ @ @ @ # @ @ @ @ @ X @ @ @ @ @ @ @ @ + @ @ X + @ + @ @ + + + + + + + + + + + + + + + O O + + + O O + O + O + + + O X O O O X O O O O O O O . O o o o . . . o . . . o o o o O ", +"2 2 2 2 2 2 2 y 2 2 2 2 2 2 2 2 2 1 2 1 1 2 1 1 2 1 1 1 1 1 2 1 1 2 1 1 1 1 1 1 1 1 , 1 1 1 1 1 1 1 , , , , , , 1 , 1 , , , , , , , , , , , , , , , > , , , > , : , , , > > , > : , , > > > , : : : > > : : : : : : : : : : : : : : : : - : : : - - - - - - - - : & : & : & & & & & & & & & & & & & & & & & & & & # & & & & # & % & % & % & % & % % % % % % % % % # % # # % # # % @ # # @ # @ @ @ # # # @ @ @ @ @ @ @ @ @ @ @ # + + @ + @ @ + @ @ @ + + + @ + + + + + + + + + + + + + + + + + O + O + O O X + O O O O + O O O O O O O O O O O o O . . . o . . o o o . o o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 1 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 1 1 1 1 1 , , 1 , , , 1 , , , , , , , , , > , , > , , , > : , , > > , > > > > : > > > : > : , > : , : , : , : : : : : : , : : : - : - - : : - - : - - - - & : & : & - & : : & & : & & & & & & & & & & & & : # & & # : # # & % & # & # % % % & % % % % % % % # # % # # # # $ # # # @ % % # @ @ @ @ @ @ @ @ @ @ @ @ X # @ X @ @ @ + @ + @ + + + @ + + + + @ + @ + + + + + + + + O O + + + O + + O + + + O O + O + O O + O O O O O O O O O O . . O . . . o . . . . o o O ", +"2 0 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 1 2 1 2 2 1 1 2 2 1 2 1 1 1 2 1 1 1 1 1 1 1 1 , 1 , 1 1 , 1 , 1 , 1 , 1 , 1 , 1 , , , , , , , , , , > , , , > > > > > , > , > > , : , : , : > : : > > > > > : : > : : > : : : : : : : : : : : : : & : & & : - - : - & : & : # : : : # : : & & & : & @ - - - # - - - @ - - @ # - # # - - # # - # # # # % % - # # % % # # # # # # # # # # % # # @ % # # @ @ @ @ @ @ @ @ @ @ + @ @ @ + @ + @ + + @ + + + + @ + @ + + + + + + + + + + + + + X X + + + + X + o + O + O O O + X O X o O + O O O O O O . . . . . o . . . . o o . o . O O ", +"2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2 1 1 2 2 1 2 1 2 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 , 1 1 1 1 , 1 , , 1 1 1 1 , , , , 1 , , 1 , , , , , , , , , , , , > 1 > 1 > 1 > > , > > , , : , > > : , , : : > : > : > : > > : : : : : : : : & : : : : : : : : : : - : & : : & : & : : : # # : : # # : : # - - # - @ - @ - - @ @ - - # - # # % # - # # % - - % % % # % # % % # & # # # # # # # # # # # @ @ # # @ # @ @ @ @ @ @ @ @ @ @ + @ @ # + @ @ + @ # @ @ + @ + + @ + @ + + + + + + + + + + + + X + + + o X + + O O + O O o o + o o o O O O O + O X X . . . o . . o . o . o . ", +"2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 1 2 2 2 2 2 1 1 2 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 , 1 , 1 , , , , , , , , , , , , , , , > , , , 1 > > 1 > > > > , : : , > > , : , : , , > > > > > : : : : > > : > : : : : : : - & : : : : : - & : & : & : & & : : # : : : # : : & : & & # - - - - - # - - - - - # - - # # - - # # - # - # % % % % % % % & # # # % # & # % @ % % # # # # @ # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + @ @ + @ + + + + + + + @ + + + + + + + + + + + o + + + + o X + o + O + X o + o + + + O O O O O O O X o X O . o o . O X O . . . . o O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 1 2 1 1 1 1 1 2 2 1 1 1 1 1 1 1 , 1 , 1 1 1 1 , 1 1 , 1 , 1 , 1 1 , , 1 1 , , 1 , , , , , , , , , > > > > > > > , > , , , : > , : > : , : : : > > > : > > : : : : : : : > : : : : : : : : : : & : : & : & : : & : & : : # & : : & & # : # & - - # # - # - # @ # - @ # - # - - # - # # - # % # % % % % % % % # # # & # # # # @ % @ % # @ @ # # @ @ @ # @ @ @ @ @ @ @ @ + @ + + # @ @ + @ + @ + @ + @ + @ + + + + + + + + + + + + + + o + + X + + X + + X + + O + o o O O + O O O O O X o O . . o . X . X . o o . o O O O O ", +"2 2 2 2 2 2 0 2 2 2 2 2 1 2 2 1 2 1 2 2 2 1 2 1 1 2 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 , 1 , 1 1 , , , , 1 , , 1 1 , , , , 1 , , , , , , > > 1 > > > > , , > , : > > , , : , > , : > , > > > : : > : > : : > : : : : : : : : : : & : & : : : & & : - - & : & & : & : & & : : # : # : # @ - - - - # - - - @ - # - # - # # % # - # # # % & % % % % % % % # % # # # % # % % # # # # # # @ # @ # @ # @ @ @ @ @ @ @ @ @ @ @ # + # + + @ + + # @ + + + + + + + + + + + + + + + + + + + + + o + X + + + o o X O O O X O O O O O + X O O . X X . . O o . . . . . . o O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 2 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , , 1 , 1 1 , , 1 , 1 , , , , , , , , , > , , , , , , , > , 1 : > > > , > , : : , : , > > : : > : : > > : > : : > : : : : : : : : : = : : : : & : & : : : & : : & : & & : & & : : # : & : : : - - @ - - - # @ - - # - - # # # - % - # # - - % % % % % % % % % % % % # # # % # # # # # @ # # @ @ @ # @ @ @ @ @ @ @ @ @ @ @ + @ + # # + @ + @ + + + + @ + # @ + + + + + + + + + o + + o X + + + + X X + o + + X + + O + O O O O O O O X . O o O O . o o . O . o o . o . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 1 2 1 2 1 1 1 2 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 1 1 1 1 , 1 1 1 , , , 1 , , 1 , , , , , , , , > > , > > > 1 > , , : , , , > > , : : , : > , > > > > : : > : > : : > : > : : : : : : : : : : & & : : : - & - : & : & : : : & : # : & & & & # : - - @ - # - - - # - # @ # - # - # - # # # # - % % % % % % % % % # # # % % # # # # @ % @ # @ @ @ @ @ @ @ @ @ @ @ @ @ @ + @ + @ @ @ @ + @ + @ + + @ + + + + + + + + + + + + + + + + + X + + + + + X X X X + o X + O + + O + O O O O O O O O . O O o o . O . X o o . . o . . O O O O ", +"2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 1 2 1 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 , , , , 1 , , 1 , 1 , , , , , , , , , , , > , , , , , 1 > > > , : , > > : , > , : > > > > : : : : > > > : > : > > : : : : : : : : : : : : : : : : : & : & : & : & & : & & & & # : : # : & & & # - - - - # # - - # - - - # - # - # # - - # # # % & % % % % # % % % % % % # # # # # @ % # # % @ # @ # @ @ @ @ + @ @ @ @ @ @ + @ + @ @ @ @ + @ # + + + # + @ + @ + + + + + + + + + + + + + X o + + + X + X + + o + X O O O O + O O O O O O O O O . . . . O O . . . . . O O ", +"2 2 2 2 2 2 r 2 2 2 2 2 2 2 2 2 2 1 1 2 1 2 1 1 1 1 2 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 , 1 1 , , 1 1 , , , 1 1 , , , 1 , , , , , , > , > , , > > , , , : , : , : : , : , : > : > , > > > : > : : : : : : : : : : : : : : - : - : - : - - - - - & : : & : & : : : : : : & : # & # : # - - - # - - # - - # - - # - # # & & # # & # & % % & # # & # # # # # - # # % # + - - + - + + - # @ @ @ # @ # @ @ + # # @ + @ + @ + + @ @ + @ + + + # + @ + + + + + + + + + + + o + + o + o + + + O O + O + O X X O X O O O + X O O O O O O X . . . . . o . . . o . o . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 1 2 2 1 1 2 1 2 1 1 1 2 1 2 1 1 1 2 1 1 1 , 2 1 1 1 1 1 1 1 1 1 , , 1 , , , , , 1 , , , , , , , , , , , , , > > 1 > : , , , , , , , : , , , > , > : > : : > > : > : : > : > : > : : : : : - - : - : - : - : - - : : & : & & & : # # : # : # : : : # - - # - - # - # @ # - # # - # # & # & & & # & # % & # & # % # % # # # # # # # # @ + + - O - + $ # # @ # @ @ @ @ # + @ + @ # @ @ + + # + @ + @ @ @ + + + @ + + + @ + + + + + + + + + + + + + + o + + + O + O + + + O + O X O X X O + O O O X O O . . . o o o . . . . . . . . O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 1 2 1 1 2 1 2 1 2 2 1 1 1 1 1 1 1 2 , 1 1 1 , , 1 1 1 1 , , 1 , 1 , , , 1 , , , , , , , , , , , > , , , > , > > , , : : , , : , : , : : > , : > : > : > > > : > > > : : > : : : : : : : : : : : - : - - : - & : & : & & : : # : : : : & : & : & & # - - # # # - - - # # - - # # - & # & # # & # & & & # # # & % % # # + - # # # # - + + + - O o + @ @ @ @ @ @ @ @ # # @ @ @ @ + @ # + @ @ @ + @ + + + @ + + @ + @ + + + + + + + + + + + + + + + o X + + X O + O O X + O + o o X X O O O O O X O O . . . o . . . . . . . o o . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 1 2 2 1 2 2 2 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 1 , 1 , , 1 1 , , 1 , , 1 , , , , , , , > , > , , , , > , , > , , : , , , : , , : , : , : > > : : : > : : > : : : : > : - : : : - : - : - : - - - - & : & : : & : : : # # : & & # : # : - - # - - - - # - - - - # - - # # & & # & # & # # % & # # % % # - # # # # # % # - @ - @ O - - + # @ # @ @ @ @ @ + @ @ # + # # @ + @ @ + + @ + @ # + + @ + + + + @ + + + + + + + + + o + + o + + + O X + O O + + O O O O X + X X O O O O + X O X O O o . . . o o o . o . o . o ", +"2 y 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 1 2 1 1 1 1 1 1 1 , 2 1 1 1 1 1 , 1 1 , 1 1 , , , 1 1 1 , 1 , , 1 1 , 1 , , , , , , , , , , > , > , , , > > > , : > , : , , : : , : , : : > : : , , : > > : : : : > : : : : : : : : : : - : - - - : - - - - - : & : & # : : : : # & : : : & # - # - - - @ @ - @ + - - # - # - & & & # # & # & # & # # % % % & - + # # - @ + - + - + @ + + - - # # # @ @ # @ @ # @ + + + @ @ @ @ @ + o @ @ @ @ + # + + + + + @ + + + + + + + + + + + + + + + o + + X O + O + O O O O O + + O + + + + O O + O O O O X X . . O . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 1 2 1 2 1 1 2 2 1 1 1 2 2 1 2 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 , 1 1 1 , , 1 , 1 1 , , 1 , , , 1 1 , , , , , , , , , , , , > , , , , , , , : , : , : , : : , : , > : : > > : : > > : : : : : : : : : : = : : - : : - - - - - - & & : : & : : # : : : & : & & : : - # - # - - - - - - - @ - # - # & # # & & # & # # # & & % # # # # + - + + + - @ @ @ + - - + + + @ # + # @ @ @ @ # + # @ # # + + + + # @ @ o + o + @ + + @ + + + + + + + + + + + + + o + o + + + O + X + O O O O + O + + + + + + + $ $ $ + $ + + + O O O O . . . . . . . o ", +"2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 1 2 2 1 2 2 2 1 2 1 1 2 1 1 1 1 1 2 1 1 1 1 , 1 1 1 1 , 1 1 1 1 , 1 1 , , 1 , 1 , , 1 , , , , , , , , , , , > , > , , > , > , , : : , , : , : , : , , : > : > : : : : > > : : : : : : : : : - : : : : : : - - : - - - - - - : & & : # : : # & # : # & & & & - - # - # - - # @ - - @ # - # - # & & # # & % & & # % % & % # % + - - + @ # + @ + - @ @ - + + - # @ # @ # @ @ @ + # + # @ + + @ @ + @ @ + @ @ + + + + + + + + + + @ + + + + + + + + o + + + o o + O O + O + O O + O + + @ + % % $ * # * * @ $ + + + O O O O o o o o . . o . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 1 2 2 1 2 2 1 1 2 1 1 1 1 1 1 1 1 1 2 1 , 1 1 1 1 1 , 1 , 1 , 1 1 , 1 , 1 , , , , , , , , , > , , , , , , > , > > , : , , , > > , : , : , : : , : , : , , : : : : : : : > : : : : : : : : - : - : - - : : - - - : & & : : & : & : : : : # : & : # & # - - - @ - - # - - @ - - # - - & # & & & # & # # & % # # # # & - @ + - + - - O - @ + - + - + + @ # # # + @ @ @ @ @ # # + # # @ + # @ @ + + @ @ @ + @ @ + + @ + + + + + + + + + + + + + + + + + O + + O + O + + + + + + % # * ; ; > : ; ; = * $ $ + + + O . . . . . o . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 1 1 2 1 1 2 2 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 1 1 , , , , 1 1 , 1 , , 1 1 , , , , , , , , , > , , , > , : , , , : , , : : , : , : , > : > : : , : > > : : : : : > : : : : : : : - : - : - - - - - - & - & : & : & & & : # : & & # : : & & & & & # & & & - 8 4 4 4 8 < 4 , , , < 4 4 4 8 4 4 4 4 4 4 4 4 4 4 4 4 8 4 4 - $ @ $ - + + - o # @ @ @ @ @ + @ + # @ + @ + # + @ @ + @ @ + @ + + + @ + + + @ + @ + + + + + + + + + + O + + + O + + X + + o + + + + + # = ; 5 7 2 e e e 1 7 < = # @ + + + O X . . . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 , 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 1 1 1 1 , 1 1 1 , , , , , , , , , , , , , , , , , , , > , > > , , : : , , : , , : , : , : > , : , > : : > : > : : : : : : : : : : - : : : : - : - - : - - : - : & : & & : & & & : & : & & & & & & & & & & & & 4 v v v v v v v v c v v v v v v v v v v v v v v v v v v v v v v k ; - + + - - $ # # @ # # @ # # @ @ @ @ @ @ + @ + @ @ + + @ @ + @ + @ + + @ + + + + + + O + + + + + + + O O + + O + X + o + O + + @ * * > 7 e s n M M m n f e 7 > * # + + O . . . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 1 2 2 2 1 2 2 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 > 1 , 1 1 , , , , , , , , , , , , , , , , , , , > , , , > , , , , , , : : , , : > , : > , : : , : > , : > : , : : : : : : : - : : : : : - : - : - - : - : & : : & : : & : & & & & : : & & & & & & & & & & # 4 v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v k $ @ O - $ @ # @ @ @ @ @ @ @ # @ # @ # # @ + @ @ + @ + @ + @ + + + + + + + + + + + + + + + + + + + + + O + + X + o + o + + $ # * > w f M S U T R T K S m f 9 : * % + + O X . . . X . . X . X ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 , 2 2 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 1 1 1 , 1 , 1 , 1 1 , 1 , , 1 , , , , , , > > , , , : , , , : : , , , , : > > , : , : : , > : > : : : : : : : , : : : : : > - : - - - : - : - : - - : & : & & : & & & : & : & & & & & & & & & & & & & & & 8 v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v x ; - + O # @ @ # @ @ @ # @ + @ + + + # + @ + @ @ + @ + @ + + @ + + @ + + + + + + + + O + + X + O + + + + + + + X + + + @ $ ; > 0 f C K ! ..;.;.;. .~ Y B f 7 : * $ + O . . . . . X X X X X X X X X . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 2 1 2 2 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 , 1 1 , 1 1 > 1 1 , , 1 > 1 > 1 , , , , , , , , , , , , , > > , , , : , , , : , > , > : , > > : > : : > : : > > : : : : , : : : - - : : : - : - - : - : & : & - : & : & & & : & & & & : & & & : & & & & & & # & < v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v l - # @ @ @ @ @ # @ @ @ @ @ + @ # + @ + @ @ + @ + @ + @ @ + + + + @ + @ + + + + + + + + + + O + + + O O X + + o X + + $ # ; 7 y A R { <.2.7.9.7.2.>.{ R A r 7 = $ $ + X O . O X + X X $ + + + X X X X ", +"2 2 2 y 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 2 1 2 2 1 1 1 1 1 1 , 1 1 1 1 1 1 , 1 1 1 1 > 1 1 , , , , 1 , 1 > , , , , , , , > > , , : , , , > : , : , : , , > , : , , : > : > : > , : : > : : : > : > : : : : > : : : - : - > - - : - : & : - : : & & : & : & & : : # & : # & & & & & & & & & < v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v k # $ # @ # @ @ @ @ @ @ @ @ @ @ @ @ # + @ @ + @ + @ + @ + @ + + + + @ + + + @ + + + + + + + + O + + + O + + O + + + % = , e M K ..1.9.w.j.j.j.w.7.1.{ Y M w 5 * $ + + O O . + + + $ $ $ $ $ * $ $ $ + ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 1 2 2 2 1 1 1 2 1 1 2 1 1 1 1 1 1 1 , 1 1 , 1 , 1 1 1 , 1 > 1 1 , , 1 , , , 1 , , 1 , , , , , , , , , , > , , , , > : , , , , : > : > > : , , : : , , , : : , : : > , : : : : : : : - : : - : : : - : : - - : - - - & : & : : - & & & & & : : & & & # & & & & & & # 8 v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v 8 $ # @ @ @ # # # @ @ @ @ @ @ + @ + + # @ @ + @ @ + + + + + @ + @ + + + + + + + + + + + + + + + O + + + O + + + + # ; 7 f S ~ <.9.g.l.x.x.x.l.h.9.>.! G r 6 & $ + + O O + + + % * * = = 3 * * = $ $ ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 2 1 2 1 2 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 , , 1 , 1 , 1 > , 1 > , , , , , , , , , , , , > > > , , , : , > > , , > > > : : , : > : : : , : : > : > : : : > : : : : : : : : : - - - - : - & : : & : & : & & & : & & : & & : # : # : & & & & & & & 0 v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v c # # @ # # @ + @ @ @ @ @ @ @ @ @ @ @ @ + + @ + + @ + @ @ + + + + + + @ + + + + + + + + O + O + + O + o + o + + @ % : 7 n U ..2.w.l.m.M.N.M.m.l.w.2.{ Y b 9 = $ + + + + + $ % = > 6 7 9 9 9 7 5 5 = ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 1 2 1 2 1 1 2 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , , 1 , 1 , , 1 , , 1 , , , , , 1 , , , , , > , > > , , , , , > , > , : , , : , : , : , : : > > : , : : > : : : : : : : : : : : - : - - - : : : & : & & & & : & & : - & : & # : : # : & : & & & & & & & & , D c v c v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v 8 # @ @ # # @ # @ @ @ @ + @ @ @ + @ + @ @ + @ @ + @ + @ + @ + + + + + + + + + + + + + + + + + + + + O + + + + + * > e m T :.8.h.z.M.C.C.C.M.z.j.7.;.T M w ; $ $ X + $ $ * ; 7 w r b m M m b s 9 5 = % $ X $ . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 , 1 1 1 1 1 1 1 1 1 1 , 1 , , 1 , 1 , , 1 , , , , , , , , , , , , , , , > > > , , > > > , : : , : , : , : : , : > , : : : : > : > : > : : : : : - : - : > - & & : : : : : : & : & : : # - & & - & & : : # : & & & & & & & # : h l l l l l l l l l l l l l l l l l l l l l l v v v v v v v v v v v v v v v c @ # @ @ @ @ # + @ @ @ @ @ @ @ @ @ @ @ + @ + + @ + @ + + + + @ + + + + + + # + @ @ + $ + + + + O + X + X + + $ & ; e C R :.9.h.m.V.Z.Z.Z.V.x.j.7.:.T M e 5 $ $ + + $ * : 7 r b S K T T T K A m s 7 3 * $ . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 1 1 2 1 2 1 2 1 2 2 2 2 1 1 1 1 2 1 1 1 1 1 1 1 1 , , 1 , 1 , , , , 1 , , , , 1 , 1 , , , , 1 , , , , , , , > , > > , > , , > , , > , > , , : , : , : > , : > : : > : > : > : > : : : : > : : > - : - > - : : & : & : : & : & : & - - : & : & & & & # & : & & & & & & & & & & - & & # & # - # - - # & - % # - # # # & % % # g v v v v v v v v v v v v v v - - # @ @ @ @ @ @ @ @ @ @ @ + @ @ + + @ + @ + + @ X + @ + + + + @ + + + @ % # # # @ % $ % + + + + X + X + + + & > e M R ;.7.h.m.M.C.C.C.M.x.h.7.;.T M w ; * $ + $ * ; 7 s A T R ..-.-.-.{ ! K B s 7 3 $ + . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 , 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 , , , , , , , , , , , , , , > > , > , > , > > , > > , > , : > , : , : , > : , : > : > > : : : : : : : : : : : - - : - : - - & : : : & : : & & - - - - & & - & & & : : : # # & & & & & # & & & & & & & & # & # - # # - # # # # # # & % % # # # @ g v v v v v v v v v v v v v i o @ @ # @ # @ @ + @ @ @ @ + @ + @ @ + @ + @ + @ @ + @ + @ + @ + @ @ # % # % % = # * * * @ # % $ + + + + + @ * ; w b K { 2.q.l.m.M.V.M.m.l.w.2.{ K b 7 ; $ $ $ $ = 6 r C R { >.2.2.8.7.2.>.{ Y C s 4 * * + . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 1 2 2 1 1 1 2 1 1 2 1 2 1 1 2 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 1 1 , 1 , , , 1 , , , , , , , , , , , , , , , , , > , , > , , , : , : > , : : , : : > : : , : : > : : : > > : : > : : : : : - : : : > - - > : : & : : : & : : - : & : # - & & : # : & : : & & & & & & & & & # & # & & # & # - # # - # # % % & # # # & # & # # # - v v v v v v v v v v v v v c @ @ @ + # # @ # + + # @ @ @ @ @ + @ @ @ + @ + + X @ + + @ X + + @ $ % & = = : < < > : ; = # @ $ + + + + + $ # = 7 b S ~ <.9.h.l.z.x.x.l.h.9.>.~ G s 7 * % $ $ % 3 e m K { 1.8.f.h.j.h.f.8.1.{ U b r ; * . . . ", +"2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 , , , 1 1 1 , , , , , , , , , , , > , , , : , , > : > , > , > , > , : , > > > , : : , > > > > : : : > : : > : : : : : - : - - - - - : & : & : : & : & & : : : & & & & : & # & : # & & & & & & # & & & & & & & & # # - - # # - # - & % & % & # % # # # # p v v v v v v v v v v v v v - @ @ @ # + # + # # # @ + @ + @ + + @ @ @ + + + @ + @ + + @ @ @ # * = : 5 7 e e e e e 7 5 > = $ $ $ + + + + $ & , e b Y { 1.9.w.g.j.j.w.9.1.{ K M e : % % $ % = 7 s G R <.8.h.l.z.m.z.l.h.8.>.R A s 5 * + . . ", +"2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 2 1 2 1 1 2 1 2 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 , 1 1 , , 1 , 1 1 , , , , 1 1 , , , , , , , > , , > , > > , > , , , , > > > > > , , > , > : > > > : : > > : : : > : : : > : : : : : - > - > - - : - : - & : : & & : & : & # : : : & : & : # : & & & & & & & & & # & & & & & & & # - # # # # - % # & # % # # & % - # # ; v v v v v v v v v v v v v < @ @ @ @ @ # # # # + + @ @ + @ @ + @ + @ + @ + + + @ + @ + $ % % : 5 7 r b b M M M m f r 7 < = # $ + + + + $ % = 7 y A T { >.2.7.7.7.2.>.{ T A r 7 = * + % % = 7 b K .2.f.l.m.M.M.M.m.l.g.2...K b 7 . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 1 2 1 2 2 1 2 2 2 1 2 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 1 1 1 , 1 1 , 1 , 1 , 1 , , , , , , , , , , , , , , , , , , , , > , > , > > , > > : : , : > , : , : , : : > > : : > : > : : : : : : : : - - - : - - & : : : : & : : & : # - : - & & & # : : & # : & & & & & & & & & & & # # & # & - # # - - # # # % # & # # % # # # # # # l v v v v v v v v v v v v q - @ @ @ @ @ @ @ + @ # @ @ @ + @ @ @ + + @ + @ @ + + @ + $ % & : 5 w f M K K R Q R K S M b 9 5 = & $ + + + $ $ = > 7 b A Y ~ -.;.:.-.-.~ Y C f 9 > * $ + $ % : w b Y ;.8.h.z.M.C.C.C.M.z.j.2.-.T b 9 ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 1 2 2 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 , 1 , 2 , 2 , , 1 , 1 , , , 1 , 1 , , , , , , , , , , , , , , > > > , , > > > > > > : , > : , : > > > > : > > : > > : : : > > : : : : : : : : : : : & : : & : : - & - - & & & : & E u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.u.} 6 % & & # & & # # # & % % % % % # # % # % # * i v v v v v v v v v v v v a - o - O + + + - + + + + + @ @ + @ @ + @ # + + + @ + $ @ @ # = < e f G Y ^ ..:.>.;...^ K S b 9 < = @ + + + + $ % = : 9 s M S K T R T K A m s 7 : * $ $ + $ $ ; e C T :.8.h.m.N.C.Z.C.N.x.h.8.:.T N w ; $ + O . . ", +"2 2 2 2 2 y 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , 2 , , 1 , 1 , , 1 , , , , , , , 1 , , , , , > , > , > , > , > > , > , > , > , : > : : , , : > : > : > : : : : : : : : : : : : : : : : & : : : : & : : & : & = - - : & & : o.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.B.E % & & # # & & & # & % % % % % % % % # # # q v v v v v v v v v v v v k o - @ @ - + + O - + - O - + @ + @ + + + + + @ + + @ @ @ $ ; ; 0 f A R .<.2.7.9.7.2.<...R C f 7 : # $ $ + + + $ % * 3 7 e s m M M m m f w 7 > * % $ + + + * ; w b T ;.8.g.z.M.C.Z.C.M.z.h.7...T b 9 * $ + O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 2 2 2 1 2 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 2 1 1 , 1 , 1 , 1 , 1 , , , , , , , , , , , , , > , > , > , > > > > > > > > : > : > , : : , > > > > > > : > : : : > : : : : : : : : : - & : & : & : : & : : : & : & : & & +.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.E # & # & & # & # % % % % % % % % % % $ # 3 v v v v v v v v v v v v k - o O @ + O - - O @ + + + + + # @ @ # + # @ + + + @ + # % ; < r M Y .1.9.w.j.j.j.q.9.1...K M e 7 = $ + + + + + $ % = : < 5 w w e w 7 5 5 ; * % + + + + + $ ; 7 b K { 2.f.l.m.M.N.N.m.l.f.2.{ K b 9 * $ + . ", +"2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2 1 1 1 2 1 1 2 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 , 1 1 , 1 , , , 1 , 1 1 , , , , , 1 , , , , , , , , , > , , , , , , > > 1 > > > , > , , > , > : : , : : > : : : : > : : > : : : : : : : : : : : : : - : : : & & : : & & : & : & : & o.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.m & & & # & # & & % % % % % % % % # # # 4 v v v v v v v v v v v v k o - - @ @ - O + + @ - o - + + # + + + @ + + @ # + + @ @ ; ; 0 f S ^ <.9.j.l.x.x.x.l.j.9.<.^ S s 7 = $ $ + + + + + $ @ # = = ; > ; ; ; = * % + + + + O + + $ * 7 r A { >.8.g.l.z.x.z.l.g.8.>.R S s 6 * $ O . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 1 2 2 1 2 1 2 , 2 2 2 1 1 2 1 1 1 1 1 1 1 1 1 , 1 , , 1 , 1 1 1 , , , 1 , 1 1 , , 1 , , , , , , , , > , , , , > , > 1 > , > > > > > , : , : , , : , > : > > > : > : > : : > : : : : : : : : : : - : : : & : : & & : : & : & : & & o.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.k.# & & % % % # % % % % % % % % % # # # 4 v v v v v v v v v v v v k + O $ - + + + + # + + - + + @ @ + + + + + + + @ + + @ # ; ; w n Y ..2.g.l.m.M.N.M.m.l.f.2. .K m w ; * + + + + O + + O $ $ % % $ * % * $ $ $ + + O O O O + $ % 5 r b K { 1.8.f.g.h.h.f.8.1.{ Y b w : $ $ X . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 1 2 1 1 2 1 1 2 2 2 , 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 , 1 , 1 1 1 1 1 , , 1 , , , , , , , , , , , , , > , , , > > , > , > > > , , > > > : , : , : : , : > > : > : > : > > : > : : : : : : : - : - : : - : & : : & : : & & : & : & = : o.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.C.L & # & % & & % % % % % % % % % % # % 4 v v v v v v v v v v v v k - O - + - + + - + # + + @ + + @ @ @ @ @ @ @ # + @ @ # # - > e M Q :.7.j.x.N.C.Z.C.M.x.h.8.:.Y B e > * % + + + X + + + + + + $ $ + $ + + + + + O X O O + + % = 5 s C U { >.2.8.8.8.2.>.{ T C s 5 * $ + . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 1 2 2 2 1 2 2 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 , 1 , , 1 , 1 , , 1 , , , , , , , , , , , , , , , > , , > > > > , > > > > > > , : , : , : : , > : > > : > : : : : : : : : : : : : : : : : : & : : : & : : : : & : & : : & & t D V V V N D V N V V V x V V V o.Z.Z.Z.Z.Z.Z.Z.Z.Z.0.# & & % % % % % % % % % % & % # % # 4 v v v v v v v v v v v v k - - o + + + @ @ + # # + + # + @ @ @ @ + @ + + @ + + # # - < r B Q :.9.j.x.N.C.Z.Z.B.m.j.9.>.Q B e > # @ + + X O + X X + + + + + + + O + O O O X X X O X O + $ % ; 7 f C K ! { ;.:.-.{ R Y C s 7 = $ + + . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 1 2 1 1 2 1 , 2 1 1 1 1 1 1 1 2 , 1 1 1 1 1 , 1 1 1 1 1 , 1 , 1 , , 1 , , , 1 1 , , , , , , > , , > , , , , , > , , > > , , > , > : , : , : , : : > > : : > : > : : > > : > : : : : : : : - : : : & : : : : & & : : & : & & : & & & - & & & - - - & & - & & & % # } Z.Z.Z.Z.Z.Z.Z.Z.Z.< # % & % & % % % % % % % # % % % % 8 v v v v v v v v v v v v k o O + - - o @ @ @ # + o - + @ @ + + + @ + @ + + @ @ # # - < e M Q ;.7.h.x.N.C.C.C.N.x.h.9.:.R M e > * + + + + O O O O X O + O O + O O O X O X O O O O O O + $ % 5 7 f b S K T T Y K S b s 7 > = $ + X . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 1 2 , 2 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 , 1 1 , 1 , 1 , 1 , 1 , 1 , 1 , , , 1 , , , , , , , , > , , > , > , , > > , > > > > > > , , > > > > > > > > : > : > : > > - > : : : : : - : : : - - - : : - - - : & : : & : & : & : & : & - & & & & & & & & & # & & & & = v.Z.Z.Z.Z.Z.Z.Z.Z.P # % % & # # # # # & # # # # % # # i v v v v v v v v v v v v k @ @ - + @ @ @ @ @ @ + @ @ + @ @ + @ @ + @ + + + + $ + % = < w n Y ..2.f.l.m.N.B.N.x.l.f.2...U b 9 : # @ + + O O O O O O O O O O O O O O o . O O X O O O O O X + $ * ; 6 e r b m M b b r e 7 3 * $ + + ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 2 1 , , 1 2 2 2 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 1 , 1 , , , 1 , , , , , , , , , , , , > , > , > , > , > , > , : : > : > , : : : > > > : > : : > : : > : : : : : : : > - > : & - : - : & & & & : : & & & & & & - & - & & & & & & & & & & & & # } Z.Z.Z.Z.Z.Z.Z.Z.o.& & # & # & & & # # & & # % # # # l v v v v v v v v v v v v u # @ + o - @ @ @ @ @ @ @ o - + @ @ + @ + + + @ @ + @ + % % ; 9 b H ^ <.9.h.l.x.x.x.l.g.8.<.! S b 7 = % @ + X O + + O + O O O + O O O X X o o O O X O O X + + $ * ; : 6 9 w e e 7 6 : * $ $ + X . ", +"2 y 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 1 2 : , & 1 : 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 1 , , , , 1 , , , , 1 , , , , , , , , > , , > , , , , > > > > , > > > > , > > > , : : > > > > > > : > : > : > : : : : : : : : - : - - - : : - : : : : : & & & : & : & : - & & - - & & - & & & & & & & % & j Z.Z.Z.Z.Z.Z.Z.Z.u.% & % % & # # & # # # # # % % # - v v v v v v v v v v v v v q - o + @ @ @ @ @ @ @ @ + + @ + @ @ + + @ + @ + @ + + % # % ; 1 r M Y .1.9.f.j.j.j.q.9.2...K M r < = # @ + + O O O O O X + O O O O O O O X X O O o O . . X X + $ $ $ ; ; ; = = * * * $ + + X . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 L J J L J W W W W W W B Q L L L C 7 2 1 1 1 1 1 1 , 1 1 1 1 1 1 , , , C J F J J F F L F F J J F J , , , , , > , , , > 9 F F F F F F F F F C F F F 2 , : > > > : : F D F D D D F 7 : : : - - : : : Z F Z Z Z Z Z - & : & & : : : & : & - & & & - & & - & & & & & & & & & & # & Z.Z.Z.Z.Z.Z.Z.Z.u.# # & % # # & # & # & # & % # % u v v v v v v v v v v v v v ; @ @ o - @ @ @ @ @ + @ @ @ @ @ @ + @ @ + @ + + + + + + $ % = > 1 b A R ..<.2.9.9.9.2.<. .R A f 1 ; * % + O + + O + O O + O O O O O O O X o o O O O O X . . X . X + + + + $ $ $ $ * $ $ + + + X . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 Z.Z.Z.Z.Z.Z.Z.Z.Z.m.C.C.Z.Z.Z.Z.Z.b.J 1 1 1 1 1 1 1 1 , , 1 , 1 1 I b.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.> , , , , > , > w &.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.F : , : > : > > 6.Z.Z.Z.Z.Z.Z.` : : : > : : : I Z.Z.Z.Z.Z.Z.u.& : & & : : & : & & & & & - & & & & & & & & & & & & & & % & & B.Z.Z.Z.Z.Z.Z.Z.B.# & & # % & # & # # % % # # # - c v v v v v v v v v v v v v + + - # @ @ @ @ @ @ @ @ @ @ @ @ @ + @ @ + @ + @ + + @ + @ % # ; < w b A K ^ ..;.>.;...^ Y A f e < = @ + + + + O + O + O O O O O + O O O . O O O . o . . . . . . + + + + + + $ + + + O . . + . . O . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 Z.Z.Z.Z.Z.Z.Z.Z.Z.N.V.Z.Z.Z.Z.Z.Z.Z.Z.I 1 1 1 1 1 1 1 1 1 1 1 , [ Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z., , , > , , , , k.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.F > : > > > : > D Z.Z.Z.Z.Z.Z.r.: > : : : : > 6.Z.Z.Z.Z.Z.Z.I : : - : : & - & : & : & : & - - & & & - & & & & & & & & & % & u.Z.Z.Z.Z.Z.Z.Z.Z.& % % # & # & # % & % # # # @ a v v v v v v v v v v v v v k - - O @ @ - o @ @ @ @ + @ @ + + @ @ + + @ + @ + @ + + @ + @ % # ; 5 w b B S Y Y Q R Y S M s w < * % @ @ O O O + O O O + X + O O O O O O O o . O X O O o o . o . . . . . . O O O . + O O O O + + + + + + + + ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.B.e 1 1 1 1 , 1 , 1 1 1 H Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z., , , , , , , &.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.C.Z.F > , : > : : > > v.Z.Z.Z.Z.Z.Z.7 : : : : : 7 Z.Z.Z.Z.Z.Z.v.: : & & : & : - - & & : - & & & & & - & & & & & & & & & & & & & B.Z.Z.Z.Z.Z.Z.Z.B.# & % & # # # % # # # # # % i v v v v v v v v v v v v v v 4 - O + @ @ @ @ @ @ @ @ @ @ @ + + @ + @ @ + @ + @ + @ + + @ + # % = - > 7 r f m B B M n b e 7 > = % @ + + + O O O + O O O X O O O O O O O O O o . o o . . . . X . . . . . . X . . X . O + O X $ $ $ $ $ $ + ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.3.1 1 1 1 1 1 1 1 , , p.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z., , , , , > y Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.C.C.F > : , : , : > : | Z.Z.Z.Z.Z.Z.` : : : : : ` Z.Z.Z.Z.Z.Z.o.: & : : & : : & - & : & : & - & - - & & & & & & & & # & & & # & Z.Z.Z.Z.Z.Z.Z.Z.s.i i i p p p i p i p p a k v v v v v v v v v v v v v v v c $ + + - # @ @ + @ @ @ @ + @ @ @ @ @ @ @ + @ + @ + @ + + @ + @ + % # = = > < 7 e e e e e 7 , > ; # $ @ + + O + + O X X + O + O O O O O O O O O O o X O . . O . . . o X . . O + + $ $ % * * 3 * 3 * * $ $ ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 Z.Z.Z.Z.Z.Z.k.L L L L L L +.Z.Z.Z.Z.Z.Z.B.1 1 1 1 , 2 , 1 1 C Z.Z.Z.Z.Z.Z.Z.+.J J J J P F F J J F , , , > , , &.Z.Z.Z.Z.Z.Z.k.W F F F F F F F F F 2 > > > : > : : : 0 B.Z.Z.Z.Z.Z.r.: : : : : k.Z.Z.Z.Z.Z.Z.7 : - : - - - : & : & - - - & - - - & = & & : & & & & # - # - & V Z.Z.Z.Z.Z.Z.Z.Z.d.v v v v v v v v v v v v v v v v v v v v v v v v v v v v a - + @ @ - @ @ @ + @ @ o + # # @ @ @ @ + @ + @ + + @ + + @ + + @ $ $ % & = * : : < < > : ; * # % $ $ + + + O O O + X X O O X O X O + X O O X O O O O O . . o . X O o X X X X X $ % & 3 5 9 9 9 e 7 5 ; = % $ X X ", +"2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 2 2 1 1 Z.Z.Z.Z.Z.Z.p.2 1 1 1 1 1 2 +.Z.Z.Z.Z.Z.Z.W 1 1 1 1 1 1 1 1 &.Z.Z.Z.Z.Z.Z.+., , , , , , 1 , , , , , , , , , , b.Z.Z.Z.Z.Z.B.9 > > , > > > > > > > > > > : > > > > : > 5.Z.Z.Z.Z.Z.Z.7 : : : d Z.Z.Z.Z.Z.Z.4.- : - - - - - & : & : & - & & & & & & & & & & - & & & & & - # 8 0.Z.Z.Z.Z.Z.Z.Z.Z.e.v v v v v v v v v v v v v v v v v v v v v v v v v v v v ; $ + O - O @ @ @ + - + - # + + @ @ + + + @ + @ @ + @ @ + @ @ @ + $ $ $ % % % = & & * * * # % % $ + + + O + O + X O + + O O + X X O O X X O X O O O O . O . o o O o X + + $ * = 5 9 s b b b b b s 9 7 = * $ O X X ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 Z.Z.Z.Z.Z.Z.p.2 2 1 2 1 1 2 e Z.Z.Z.Z.Z.Z.+.1 1 1 , 1 1 1 , p.Z.Z.Z.Z.Z.Z.w , 1 , , 1 , , , , , , , , , , , d Z.Z.Z.Z.Z.Z.&.> > > , > > > > > > > > > > > > : > > > : N Z.Z.Z.Z.Z.Z.` > - : %.Z.Z.Z.Z.Z.Z.C = - - - - - - & : & : - & - - & & : & : & & & & & & = & - & q / B.Z.Z.Z.Z.Z.Z.Z.Z.#.v v v v v v v v v v v v v v v v v v v v v v v v v v v u # - - O + - + @ @ @ + @ @ @ @ + # @ @ @ @ @ + o + @ + @ $ @ # # % # # $ # % # # * $ % # % $ $ $ + + + + X + X + O O O O O + X X + O O O O O O X O o X X o . . X X o O X O + $ * = 7 r b C S T T T K A b s 7 : * + X X ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 Z.Z.Z.Z.Z.Z.p.1 1 1 1 1 2 1 2 0.Z.Z.Z.Z.Z.p., 1 1 1 1 , , 1 Z.Z.Z.Z.Z.Z.0., 1 1 , , , , 1 , , , , , > , , , F Z.Z.Z.Z.Z.Z.F , > > > > , > > > > > > > : > > > - > > : : k.Z.Z.Z.Z.Z.u.- - - v.Z.Z.Z.Z.Z.k.- - - : - - - - : & : & - & & & & - & & & & & & & & & % & # 8 v t.Z.Z.Z.Z.Z.Z.Z.Z.Z.) v v v v v v v v v v v v v v v v v v v v v v v v v v l * @ + + - O + O - @ @ @ @ @ + # # + @ o @ @ + @ + @ @ + $ # # % = = = ; * = & & & # $ @ + $ + + + + X + O + + O O O + O + O o X o o O O O O X X O O O O . o . . O O X X + + * = 7 s C Y { { -.-.-.{ ! K C s 7 * % $ O ", +"2 2 2 2 9 2 2 2 2 2 2 1 2 2 1 2 2 2 2 Z.Z.Z.Z.Z.Z.p.1 2 2 1 1 2 1 2 +.Z.Z.Z.Z.Z.b.1 1 1 1 1 1 1 1 Z.Z.Z.Z.Z.Z.+.1 , , 1 1 , , , , , , , , , , > , +.Z.Z.Z.Z.Z.Z.9 , > , > > , > > > > : > > > > > : > : - > > W Z.Z.Z.Z.Z.Z.t > I Z.Z.Z.Z.Z.Z._ - : - - - - - - & : & : & & - - - 5 V V V V V V V V V V V V =.d.Z.Z.Z.Z.Z.Z.Z.Z.Z.d.v v v v v v v v v v v v v v v v v v v v v v v v v v c ; # # + - - @ + + - + + + + + + + + + # + # + @ + @ @ @ # # # ; - > , < , 5 > : = = # % $ @ + + + + O O + X O O + + + + O O O O o O o O O O X O O X O O O O X o o . O . o O O + $ * 7 s C T { >.2.2.8.8.2.>.{ Y M s 7 * $ + ", +"2 2 2 2 2 2 2 2 2 2 9 2 2 1 2 2 2 1 1 Z.Z.Z.Z.Z.Z.p.2 7 1 2 1 1 1 1 L Z.Z.Z.Z.Z.Z.1 , 1 1 1 1 , 1 Z.Z.Z.Z.Z.Z.+., 1 , , , , , , 1 , , , > , , , , +.Z.Z.Z.Z.Z.Z., > > , > > > > > > > > > > : > > > > : > > > : B.Z.Z.Z.Z.Z.} - 6.Z.Z.Z.Z.Z.B.7 - - : - - - - - : & : & & : - e 4.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.#.v v v v v v v v v v v v v v v v v v v v v v v v v v ; $ # % + + O O - O - + - + - o + - + + + @ + + + @ # @ $ # = = > 7 1 9 e e e w 7 < : = # % @ + + + + O + + + + O O O O O + O + + O O O O O + X O o X X . X o o o . . O o o o O O O $ * : e m K { 1.8.f.h.j.h.f.8.1.{ K m 9 ; $ + ", +"2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 Z.Z.Z.Z.Z.Z.p.1 2 1 1 1 2 1 1 L Z.Z.Z.Z.Z.Z.1 1 1 1 1 , 1 1 Z.Z.Z.Z.Z.Z.+., 1 1 , 1 , , 1 , , , , , , , , , +.Z.Z.Z.Z.Z.Z., , > > , , > > > > > > > > > > > : - > > - - > %.Z.Z.Z.Z.Z.k.1 Z.Z.Z.Z.Z.Z.5.- : - - - - - - - & : & : : - j B.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.c.v v v v v v v v v v v v v v v v v v v v v v v v v k * % % $ # - + + - O - O + # @ - + - o - + @ @ + # # + + - # = : , 7 t b n M A M M b r 7 5 = % $ @ + + + + X + O O + O + O O + O O O O O O O O O O o O O O . o . . . . o . . X X X + $ * 5 r C ! >.8.h.l.z.z.z.l.g.9.>.~ C r 5 * + ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 Z.Z.Z.Z.Z.Z.p.2 1 2 2 1 1 1 1 L Z.Z.Z.Z.Z.Z.1 1 1 1 , 1 1 , Z.Z.Z.Z.Z.Z.+., , , , , , , , , , , , , , , , , +.Z.Z.Z.Z.Z.Z.F > > , > > > > > > > > > > > > > > > > > > > : t Z.Z.Z.Z.Z.Z.+.Z.Z.Z.Z.Z.Z.r : - : - - - - - - : & - & & e B.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.' v v v v v v v v v v v v v v v v v v v v v v v k 8 $ % $ $ @ + @ @ + - + + + - @ + o o - + + + # + # + + + @ @ & ; 7 e b M H Y R R R K S M b w , = * $ + + + O + X O + + O O + O O + O O + O O O O O O . o o O o o . o o . o . . o O + + = 7 f K .2.f.l.m.M.V.M.m.l.q.2.{ K f 7 = $ ", +"2 e 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 1 Z.Z.Z.Z.Z.Z.p.2 1 2 1 2 1 1 1 W Z.Z.Z.Z.Z.Z.1 1 1 , 1 1 1 1 Z.Z.Z.Z.Z.Z.+., , , , 1 , , , , 1 , , , , , , , I Z.Z.Z.Z.Z.Z.%., > , > > > > > > > : > : > > > : > : : > > > : u.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.u.- : - - : - - - - - - = - - = u.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.#.v v c a p p q t p i p p u i i i u u u p u 6 4 & # # # # # # # # @ O @ @ - + + + - - O + + + + + + # + + @ @ @ - ; < 0 n A Y ^ -.:.>.:...^ T C f e < = @ + + + + + O + X + O + O O + + X O O O O O O O O O O O O . . o . . . o o . . . . . O + $ ; 9 m T ;.7.h.z.N.C.Z.C.M.z.h.7...T m 9 = $ ", +"2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 Z.Z.Z.Z.Z.Z.p.1 2 1 1 1 1 1 1 +.Z.Z.Z.Z.Z.b.1 1 1 1 1 , 1 1 Z.Z.Z.Z.Z.Z.+.1 1 1 , , , , , , , , , , , , , , N Z.Z.Z.Z.Z.Z.B.w > , , , > > > > , : > > > > > > : , > : - > > I Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z._ : - - & - - - - - - - - & - V Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.n.' v v c - & % & # & % # % % # % % # % % # # $ % @ % # # # # # @ # @ @ - - @ + O - O o + + @ + - - + # + # @ $ # # ; ; 0 f A Q ..<.2.7.9.9.2.<. .Q A f 1 : * % @ + + X + X + O + O O O O O X O O O + O O O O O O o . o . . o . . . . . o . O O % ; e m T -.8.h.z.N.Z.Z.C.N.x.h.8.:.T m 9 ; $ ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 Z.Z.Z.Z.Z.Z.p.1 1 1 2 2 1 1 1 p.Z.Z.Z.Z.Z.p.1 1 , 1 1 1 , 1 Z.Z.Z.Z.Z.Z.+., , 1 , 1 , , 1 , 1 , , , > , , > , B.Z.Z.Z.Z.Z.Z.k.I F D F F F F N > : > : > : > : , : : > > : : : v.Z.Z.Z.Z.Z.Z.Z.Z.Z.v.: - : : : : : : - - - - & - - 0.Z.Z.Z.Z.Z.Z.Z.Z.Z.v.u.u.u.u.s.d.d.d.d.d.d.t.=.O.v v v v ; % # % # % % % % % & % % % % # & # # % @ % % # # # # @ # @ # @ @ O @ + - O - + @ - + + + + @ @ @ + @ $ # - ; 1 t B Y .1.9.w.j.j.j.f.9.1. .T M r < = % + + + + O + O + O O + + O + X + X X X O O O O O O O X . . . . . o . . . X o . . . O $ $ = 9 m T ;.7.h.l.M.C.C.C.M.x.h.7.;.K m 7 = $ ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 Z.Z.Z.Z.Z.Z.p.2 1 1 1 1 2 1 n Z.Z.Z.Z.Z.Z.&.1 1 1 1 1 1 , , Z.Z.Z.Z.Z.Z.+.1 , , , , , , , , , , , , , , , , , 6.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.C.Z.v.W > > > > : , : : > : : > > : O.Z.Z.Z.Z.Z.Z.Z.Z.Z.O.- : - : & : & - - - - - - - 5 Z.Z.Z.Z.Z.Z.Z.Z.Z.4.= & & & ; c v v v v v v v v v v v v k - & & # & % & % % % % % % # % # # & % % % % @ # # # # @ # # @ - O - @ + - + + @ @ @ + + - + + + @ @ @ @ # - ; 1 n H ^ 1.9.j.l.x.x.x.l.j.9.,.^ S b 7 ; % $ + + O + + O O + + O O + X O o X X + O O O O O O X X . . . . . . o . O O O + $ = 7 b K .2.f.l.m.M.N.M.m.l.f.2. .K h 7 = $ ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 Z.Z.Z.Z.Z.Z.p.2 1 1 2 2 2 1 0.Z.Z.Z.Z.Z.Z.W 1 1 1 , 1 1 1 1 Z.Z.Z.Z.Z.Z.+., , 1 1 1 , , 1 , , , , , , , , , > d Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.! ; > > : : : > , : : : : : d Z.Z.Z.Z.Z.Z.Z.Z.Z.d : - : : & : : & : : : - - - I Z.Z.Z.Z.Z.Z.Z.Z.B.< & & & & - v v v v v v v v v v v v v q % % % % & % % % % & % % % % # & # # # # # # # # # # # # @ # # + + - O @ @ - + @ @ @ + - O + + @ @ @ @ @ # - ; e m Y -.2.q.l.x.M.N.M.m.l.q.2...Y m w : % % + O + + X + O O + + O O O O O + o o X X O O O O . O . . o o . . o o . . o . . O + $ * 6 s S R >.8.h.l.z.x.z.l.g.8.>.! S r 5 * $ . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 Z.Z.Z.Z.Z.Z.p.[ +.+.+.+.+.p.Z.Z.Z.Z.Z.Z.B.1 1 1 1 1 1 , 1 , Z.Z.Z.Z.Z.Z.+., 1 1 , , , , , , , , , , , , , , > , 6.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.B.t > > , > , : : > : , > : : 6.Z.Z.Z.Z.Z.Z.Z.6.: - : - : : : & : : & & - & & o.Z.Z.Z.Z.Z.Z.Z.Z.4.& & & & & 4 v v v v v v v v v v v v v ; % % & % & % % & % # % % % % % # # # # # & # # # # # # # # @ @ @ @ @ @ - @ + O @ @ @ @ @ + - + @ + + @ # # - < e B Q ;.7.h.x.N.C.Z.C.M.x.j.7.;.R M e < ; # + + + X + O + X O O + + O + O X O O + O O o O O . O . . o o . . . . . . . . . O X $ * ; e b Y { <.8.f.g.j.h.f.7.<.{ Y b w ; $ + . ", +"2 2 2 r 2 2 2 2 2 2 2 2 2 1 1 2 2 2 1 Z.Z.Z.Z.Z.Z.p.p.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.+., 1 1 1 1 1 1 1 1 Z.Z.Z.Z.Z.Z.+., 1 , , , , , , 1 , , , , , , , , , , 0 k.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.C.0.> > , : : : : > : : : , : 4.Z.Z.Z.Z.Z.Z.Z.4.: - - : & - : : & - & : - - & 0.Z.Z.Z.Z.Z.Z.Z.Z.E & & & & & q v v v v v v v v v v v v v - # & % % % % % % % % % & % % % # & & # # # # # # # # # # @ # @ - @ @ @ O O @ - + + @ @ + + + + @ + @ @ # @ ; < r B Q >.9.j.x.N.C.Z.Z.N.x.j.9.>.Q B e < = + @ + O + O + O + O + O O O O O O O + O O O O O O . . o . . o . . . . O . . O X + $ = 7 s C U .>.2.7.9.7.2.>.{ Y M r 5 * $ O . ", +"2 2 2 2 2 2 2 2 2 2 2 2 7 2 2 1 2 2 1 Z.Z.Z.Z.Z.Z.p.p.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.B.e 1 1 1 1 1 , 1 , 1 Z.Z.Z.Z.Z.Z.+.1 , 1 1 , 1 , , , , , , , , , > , , , , w 6.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.C.B.> - : : , : , : > : : : 7 B.Z.Z.Z.Z.Z.Z.Z.B.7 : : - : : & & : : : & & : & p.Z.Z.Z.Z.Z.Z.Z.Z.D - & & & * i v v v v v v v v v v v v z # & % % % & % % % % % % # # % % # # # # # # # # # # # # # # # @ + @ - O + - @ @ - + + # - + - o + @ @ @ $ - ; ; t B R ;.9.h.x.N.C.Z.C.N.x.j.7.:.Y M e < * # # + + + O X + O + O O + O + O O O O O O O o O O . . . . . o . . . . o . o X X X $ % = 7 f C K ! .;.:.;.{ ! K C s 7 ; % + O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 Z.Z.Z.Z.Z.Z.p.p.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.B.V 1 1 1 1 1 , 1 1 1 1 Z.Z.Z.Z.Z.Z.+., 1 , , , , , 1 , , , , , > , , , > , > , , 0 F F F F F F W 5.Z.Z.Z.Z.Z.Z.Z.I : > > : > > : > > : > | Z.Z.Z.Z.Z.Z.Z.Z.Z.} : : = : : - - : : & : : & : u.Z.Z.Z.Z.Z.Z.Z.Z.V & & & & # a v v v v v v v v v v v v z - # & # & # - # # # & # % % % % # % # # % % # # # # # # # # # # @ # # @ @ @ @ @ @ @ @ @ + @ @ + @ @ @ + @ * & < 9 M U -.2.w.l.m.M.N.N.x.l.g.2...K M w : = $ + + + + + O O + O O + O X O O X X X O O O O X O o o o o o . o . . . . . o o . O + $ * 3 7 r b S K T R K K A b r 7 * ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 Z.Z.Z.Z.Z.Z.p.p.Z.Z.Z.Z.Z.Z.Z.Z.B.6.n 1 1 1 1 1 1 1 1 1 , 1 Z.Z.Z.Z.Z.Z.+.1 , , 1 1 , , , , , , , , , , , , , , , ; , , , > > > > > > , I Z.Z.Z.Z.Z.Z.+.> : > : > : > : : : : v.Z.Z.Z.Z.Z.Z.Z.Z.Z.v.: : : : & - - : & : & : - & u.Z.Z.Z.Z.Z.Z.Z.Z.V & - & - & p v v v v v v v v v v v v z # # # & # & # # & # & # % % % % # % % % # # # # # @ # # @ % # # # @ @ @ @ @ @ @ + @ @ @ @ @ @ @ + + @ @ @ # # > 2 b H ^ 1.9.j.l.x.x.x.l.h.9.,.^ S f 7 ; = + + + O O O + + X + X + O + O O + o + O O O O X O X X o o X o . . . o o o . . . X O + + $ % = 5 w r b m M m f r e 6 ; * ", +"2 2 2 2 r 2 2 2 2 2 2 2 2 2 2 2 2 1 1 Z.Z.Z.Z.Z.Z.p.2 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , Z.Z.Z.Z.Z.Z.+., 1 , 1 1 , , , , , , , , , , > , , > > ; , , , , > > , > > > > b.Z.Z.Z.Z.Z.0.> : > > : > : : > > I Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.I : : : & : - & : : & & = : u.Z.Z.Z.Z.Z.Z.Z.Z.V & & & & & g v v v v v v v v v v v v g # - # & & # - - # # # & % % % % % % % # % # % # # % # # # % @ # @ @ @ @ # @ @ @ @ @ @ @ @ @ @ @ @ @ + @ @ # * ; 1 r B T ..1.9.q.h.j.j.q.9.1. .Y M r < = $ + + + O + + O O + O + O + O O O O X o O O O O O O O O O . o o . . . X . X . O O O X $ % * ; 7 7 7 9 9 8 5 ; * + + ", +"2 2 r 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 Z.Z.Z.Z.Z.Z.p.1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 1 Z.Z.Z.Z.Z.Z.+., , , , , , , 1 , , , 1 , , , , , > , , , > , > > , > , > > , > 5.Z.Z.Z.Z.Z.p.> > > : > : > > : : p.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.u.- - : : = : : : & : - - & p.Z.Z.Z.Z.Z.Z.Z.Z.V & - & & - p v v v v v v v v v v v v p - # - # # & # # & & # # % % % % % # # % % % # # # # # $ # @ @ $ @ # # @ @ # @ @ @ @ @ + @ @ @ @ # + @ + @ % # = , w f A Q ..1.2.7.9.9.2.<. .Q A b 7 ; = # + + + + O + + + O + O O O O + + O O O O X O O X O O X X O . O . . . o . . . . . O O X $ + % % % = ; ; * * * $ % ", +"2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 Z.Z.Z.Z.Z.Z.p.2 1 2 1 1 2 2 1 1 1 1 1 1 1 1 , 1 1 1 1 1 1 , Z.Z.Z.Z.Z.Z.+.1 , , , , 1 1 , 1 , , , , , , , , > , > , , > , > , , > , : > > +.Z.Z.Z.Z.Z.u.: > > > > : > : : d Z.Z.Z.Z.Z.Z.u.Z.Z.Z.Z.Z.Z.t - : : & : : & : & & - & u.Z.Z.Z.Z.Z.Z.Z.Z.V & & & & & p v v v v v v v v v v v v p - # - - & # - # # & & # & % % % % % % # # # # % % @ # @ % @ % @ # @ @ # @ # @ @ @ @ @ @ @ @ @ + + + @ + + # % # - < w n A R ^ ..>.>.>...^ Y A f 9 < = # % + + + O + O + O + + O + + O O O + X o X O O O O O O X . . o o . . . . O . . . + + $ $ $ $ $ $ $ $ $ + . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 Z.Z.Z.Z.Z.Z.p.2 7 1 1 1 2 2 1 1 2 1 1 1 1 1 1 1 1 , 1 , 1 1 Z.Z.Z.Z.Z.Z.+., 1 1 , , , , , , , , , , , , , , , , , , > , > > > > > > , > > +.Z.Z.Z.Z.Z.p.> > : : > > : > , %.Z.Z.Z.Z.Z.Z.j v.Z.Z.Z.Z.Z.4.- : & : : - - : - - - - u.Z.Z.Z.Z.Z.Z.Z.Z.Z & & - & & p v v v v v v v v v v v v p # # # # # & # % & # # # % % % % # # # % # % # % # # # % @ # @ # @ # # @ # @ @ @ @ @ @ @ @ + @ + @ @ + # @ # # % = - 1 e f B H Y Y Q Y Y S M y e 5 ; # % + + + O O + + O O X O O O O O + O O o + X X O O O O O X O O o . . o . o . o o X o X . . . O X X O + + + + O X X . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 Z.Z.Z.Z.Z.Z.p.1 1 2 1 2 1 1 1 1 1 2 1 1 1 1 1 1 , , 1 1 1 , Z.Z.Z.Z.Z.Z.+.1 , , , , , 1 , , , , , , , , , , , , , , > , > , , > > > , : , +.Z.Z.Z.Z.Z.u.: > > : > > > : 1 B.Z.Z.Z.Z.Z.b.: %.Z.Z.Z.Z.Z.B.7 : : : & - - - - : & : p.Z.Z.Z.Z.Z.Z.Z.Z.V & & & & & p v v v v v v v v v v v v p # - & % - # % % # # & & % % % % % % % # # # # # # # @ % % @ @ $ @ @ @ @ @ @ @ @ @ # @ @ @ @ + @ # + # + @ + @ % # = ; < 0 t f m B B B m f r w < = % % % + + + + + O + + + + O X O O + O + O o o O + O O O O O O X o o . . o . . . o X X . . X X . . X . O . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 Z.Z.Z.Z.Z.Z.p.2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 Z.Z.Z.Z.Z.Z.&., 1 , 1 1 , , , , 1 , , , , , , , , > , , , > , > > , , > > , , u.Z.Z.Z.Z.Z.%.> : > > : : : : ` Z.Z.Z.Z.Z.Z.| : d Z.Z.Z.Z.Z.Z.} : & & : - - : & - - & u.Z.Z.Z.Z.Z.Z.Z.Z.F & & & & & p v v v v v v v v v v v v p # - # # - # & % # & # # % % % % % # # # % # % # $ # % @ @ % @ % @ @ + - @ @ @ @ @ @ @ + @ @ @ # + # + @ + @ + + # % & ; > 7 0 w e r e e 1 < - ; * % + + + + O + + + O + + O + + O + O O O O O O O O O O O X X O X o . . o . X O . . . O O . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 Z.Z.Z.Z.Z.Z.p.2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 b.Z.Z.Z.Z.Z.k., 1 1 , , , , , , , , , , , > , , , , , : , , : , > , > > > > 0 Z.Z.Z.Z.Z.Z.| , > : : > : > : v.Z.Z.Z.Z.Z.Z.t : : k.Z.Z.Z.Z.Z.v.: : & : : & : : - : & p.Z.Z.Z.Z.Z.Z.Z.Z.V & & & & & p v v v v v v v v v v v v i = % % & # % & # - - @ - + - + - % % % % # % # # % # # # # # # % @ @ # @ # @ @ @ @ @ @ + @ @ @ + # + @ @ + + @ @ @ # # # - - ; > , > < > - ; % # # @ + + + + + + + + + + $ + + X + O + O O X O O X O O O O O O O . X O O X X X X X O + + X X X X X . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 Z.Z.Z.Z.Z.Z.f.1 1 1 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.Z.Z.Z.Z.Z.Z.J , , , , 1 , , 1 , , , , , , , , , , , , , , , > , > , > > , X.Z.Z.Z.Z.Z.Z.h > > > > > > , D Z.Z.Z.Z.Z.Z.k.: : : } Z.Z.Z.Z.Z.Z.I : : & & : & & - & - u.Z.Z.Z.Z.Z.Z.Z.Z.V & & & & & p v v v v v v v v v v v v p % # & # # & # & + + - + - + - + % # % # # % # # # # # # # @ + # - # @ @ @ @ @ @ @ @ @ @ @ @ + @ + @ + # @ + + @ @ @ # # # # # - ; & ; % * # $ @ $ + + $ + + + $ + $ % $ + @ $ + @ + + O X + O + X + O O O X X . O O O X X O X O + + + $ + + + O O O . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 Z.Z.Z.Z.Z.Z.p.1 2 1 2 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 , 1 [ Z.Z.Z.Z.Z.Z.k.e , 1 1 , , , , , , , , , , , , , , , , , , , , > , > > > m B.Z.Z.Z.Z.Z.v.> > > > > : : > r.Z.Z.Z.Z.Z.Z.X.: : : d Z.Z.Z.Z.Z.Z.u.& : : : : & : & - & p.Z.Z.Z.Z.Z.Z.Z.Z.k.u.u.u.u.u.s.d.d.d.d.d.d.d.d.d.d.d.d.s.u.$.% & & # & # - + + - + - + - % # # # % % # # # # # # # # % # + @ @ @ @ @ @ @ # @ @ @ + # + # # + @ + @ @ @ + + + + # # @ @ # # # @ $ # $ @ @ + + + + + # @ $ * # * = & * * @ @ + + + + O O O O O O O O O O O . . O O O O + + $ $ $ * $ * $ $ $ + + . X . X ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 Z.Z.Z.Z.Z.Z.p.2 1 2 1 1 1 1 1 1 1 2 1 1 1 1 1 , 1 1 1 1 , 1 e B.Z.Z.Z.Z.Z.Z.B.p.p.p.p.p.p.i.i.i.i., , , , , : N i.i.i.i.i.u.p.u.p.p.p.Z.Z.Z.Z.Z.Z.Z.O.: > > : : > > t Z.Z.Z.Z.Z.Z.Z.7 : : : - k.Z.Z.Z.Z.Z.Z.e : & : & : : - : & u.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.u.& % # & # & + - - + - @ - + % % # % # # % # % # # # # @ @ @ @ @ @ @ # @ # @ @ @ + @ # + # + + # + @ + + + @ @ @ + @ @ @ @ @ @ @ @ + @ + + + + + + @ @ @ * = ; > : 5 : ; = & # # % + + + O O O O O O X O O o . O O O + + $ % * * * ; = = = * * $ $ + O X . ", +"2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 2 1 Z.Z.Z.Z.Z.Z.p.1 2 1 1 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 , 2 1 1 +.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z., , , , , , F Z.C.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.M.1 : > > > > : > %.Z.Z.Z.Z.Z.Z.r.: : : : : ` Z.Z.Z.Z.Z.Z.4.- - : : & & - : & p.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.u.% % - - - - - @ - + O $ - - % % # # & % # # # # # # @ # @ # @ @ - # @ @ @ @ @ @ @ @ @ @ + @ + @ + + @ + @ + + + @ + + @ @ + $ + + $ + + + + + + + # * ; > 5 7 e w e e 9 7 5 : * * + + + + X O O O O O O O O O O + + $ $ % = : 5 9 9 e 9 9 5 ; * * @ X X X ", +"2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 1 2 Z.Z.Z.Z.Z.Z.p.1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , , 1 e k.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z., , , , > , F Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.W : : > > : > > 7 B.Z.Z.Z.Z.Z.Z.` : : : : : 7 Z.Z.Z.Z.Z.Z.B.7 - & : & : - & & u.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.t.% % + $ + O @ - + - - + - + # # # # # # # # # # # % # # # @ @ @ + @ @ @ @ @ @ + @ @ + @ @ @ @ @ @ @ + @ + @ @ + + + + + + + + + + + + + + + @ # $ % = > 0 e f m M M C b f e 9 : & $ $ + + X O O O O O X X O O O + + $ = = 6 e r b b C b b f e 6 ; & $ $ X ", +"2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 Z.Z.Z.Z.Z.Z.p.1 2 1 1 1 1 1 1 2 1 1 1 , 1 , 2 1 1 1 , 1 1 1 , , C k.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z., , > , , , F Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.W : , : , : > : > ` Z.Z.Z.Z.Z.Z.B.7 : : : : : : r.Z.Z.Z.Z.Z.Z.} - & : : & - & - p.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.t.- ; - - ; - - - - @ ; - - $ % # # # # # # # % % # # @ # # # - + @ @ # # @ @ @ @ @ + @ @ @ @ @ @ @ + + @ @ @ + @ + + @ + @ + + + + + + + + + @ $ * = < e s B G U R R T K G M s 9 5 = $ $ + O X X O X O O O X O + + $ * > 9 s m S K T T T K G m r 7 ; * $ X X . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 p.p.p.p.p.p.3.1 2 2 1 2 1 1 1 1 1 1 1 2 2 1 , 1 1 1 1 1 1 1 , 1 1 , [ 0.p.p.p.p.p.p.p.p.p.p.p.p.p., , , , , > N p.p.p.u.u.u.p.u.p.u.p.p.p.p.&.d , , : , : > > > : %.u.p.u.p.u.p.+.: : : : : : - I u.u.u.p.u.u.4.- : : & : - - & I B.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.t.; 4 3 ; 3 3 ; 4 8 ; 4 4 ; 4 , > 4 4 4 4 4 < 3 3 3 3 3 3 3 3 - + @ @ @ @ @ @ @ @ @ @ @ @ @ + + @ @ + @ @ + + + # + # + + + + $ + + + + + + + @ # ; > 0 f A K ! { ;.>.:...! Y A f w : * $ + + + O O O O O O O O + $ * : 7 s M Y ! .;.:.-.{ R Y C s 7 = % $ X . ", +"2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2 1 1 1 1 2 1 2 1 1 2 2 1 1 1 1 1 1 1 2 1 , 2 , 1 1 1 , 1 1 1 , 1 , 1 , 1 , , , 1 , , > , , , , , , , , , , , , > , > , > > > > > , > : , , : , > , : : > > : : : , : : : > : : : : : : : : : : : - : : - : : & : & : - - - - - & V D V V V V V V V N V V E ] ] ] ] ] ] ] ] ] ] ] ] ] ] ' v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v ; O - o O - O O + $ O + - + - O @ @ + @ + @ + @ + + + @ + + @ + + @ + + + + + % + = : 7 f S T ..<.2.8.9.7.2.<.{ R B y 7 : * + + O O O O O X O + + + # : 7 s A Y { >.2.8.8.8.2.>.{ T A s 5 = $ + + . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 2 1 2 2 2 2 1 2 1 1 2 1 1 1 1 1 2 1 1 , 1 2 , , 1 1 , 2 , 1 , 1 , , , , 1 , 1 , , 1 , , , , , , , , , > , , > , , , > > , > , > > > > , : : > , : : , : > > : : > : : > : : : > : : : : : : : - - - : : & & : & : : & - - & - & - & - - & & - & - - & & & i v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v ; - o - - + + - O - - $ O + - + + @ @ + @ + + @ @ + @ + + + + + + + + + + + + + * = 5 e M K .1.9.g.h.h.j.q.9.1.{ Y M e > & @ + + O O O O O O O + @ * : e b U { <.7.f.h.h.h.f.8.1.{ K b e ; % X X . . ", +"r 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 2 1 1 2 1 1 2 1 2 1 1 1 1 1 1 1 2 1 1 1 1 1 , 1 , 1 , 1 1 1 1 1 , , , , , , , , , , , , , , , , , , > , > > , > , > , > > > > > , , , : : > > : > : > : > : : , : > : : : : : : > : : : : - : - : : : : : & : : - - - - - - - & - & & - - & & & & & i v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v 3 $ - o - O O - + + - + + @ O + + @ + @ @ + @ + + @ + + @ + + + + + + + + + + % # : 7 b S ! <.9.h.l.x.m.x.l.g.9.>.~ A b 7 = % + + X O O O O O O + % = 5 s S ! >.8.h.l.z.m.z.l.h.8.>.R S s 5 * $ + . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 2 2 1 2 1 1 1 1 1 1 2 2 1 1 1 , 1 1 1 1 1 1 1 1 , 1 1 , , 1 , 1 , 1 1 1 , , , 1 , , , , , , > , , , , , , , > > , : , > , > > : , : : , : : , > > > : : > : : : : > : : : > : : : : : : : - : : & : & : & : & & - & - - & & & & & & - & & & & & ; i v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v 3 O o - O - + O - o o + @ @ @ + @ @ + + + @ + @ + + @ + + + # + + + + + + + + # * > e m Y ..2.g.l.m.M.V.M.m.l.w.2.-.U b 7 ; % $ + O O O O O O + $ $ : 7 b U { 2.f.l.m.M.N.M.m.l.f.2.{ K b 7 * $ + . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 1 2 1 2 1 2 1 2 1 2 2 1 2 1 1 1 1 1 1 1 1 , , 1 , 1 1 , 1 1 1 , 1 , , 1 , , , , , , , , , , , , , > , > , , > , , > > > > > , > , > , > : , , : > : > > > : > > : > : : : > > : : : : - : : : : & : : & : : & : : : - - - & - & - - : & & & - & & & & & p v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v 3 $ O - o - O - @ @ @ @ @ - O + + @ @ @ @ + @ + # + + @ + + + + + @ + + + + # $ & 5 e M T :.8.h.x.M.C.C.C.N.x.h.8.:.T m w : * $ + + O O O O X + + $ : w b R -.8.h.z.N.C.Z.C.M.z.h.7...T b e ; * + . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , , , 1 1 , , 1 , 1 1 , 1 , , , , , , , , , , , , > , > , , , , , , > > > > : , , > : , : , > > : > : : > : > : : : : : : : : : : : : - : : : : : & - : & & - - - - & - - & & - & - & & & & & & i v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v ; - - O + o O - @ @ @ @ @ + @ @ @ + + @ + @ @ + @ + + # + + @ + + + + + + + @ % & > r M R >.9.h.x.V.C.Z.C.N.m.h.9.:.R M e > $ + + O O O O X O + + * 3 w M R ;.9.h.m.B.C.Z.C.N.z.j.8.:.R b e ; % + . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 1 1 1 2 1 1 2 , 1 1 1 , 1 1 , 1 1 1 1 1 1 , 1 , 1 , 1 , , , , 1 , , , , , , , , , > > , , , , > > , > > , > > > , : : , : , : : : > : > > : : : : : : : : : : : : : - : : : : - : & : : : : : & & - - - - & & - & & & - & & & & - & u v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v 3 o - - - - O O + - @ @ O - + @ @ @ @ + + @ + @ + + + + + + + + + @ + + + $ + @ = 5 e M R :.8.h.x.N.C.Z.C.M.x.j.7.;.T C e > * + + O O O O O O + + % ; w b R -.8.h.z.M.C.C.C.M.x.h.7.-.T b e = $ + . . ", +"2 2 r 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 1 2 1 1 2 2 1 2 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 , , 1 , 1 , 1 , 1 , , 1 , , , , , , , , , , , , , , , , > > , , > > , > > > , : , , : , : , : > : : , : : : , > > : > : : : : : : : : : - : - : & : & : & : & - - - & - - & & - - - & - & - & % - ; v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v 3 - o O O + + - + O @ @ + + @ @ + + @ @ @ + @ + + @ @ + @ + @ + + + + + + + + @ * : e b K ..2.w.l.m.M.V.N.m.l.q.2. .K m 9 : # $ O O O X O O X O $ $ ; 7 b Y { 2.f.l.m.M.B.M.m.l.f.2.{ K f 9 * $ + . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 1 2 2 1 1 1 2 1 2 1 2 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 , , 1 , 1 1 , , 1 , , , , , , , 1 , > , , , , > , , , , > , > > , > > > > : , , > : > > : > > > : > > > : : > > : : : : : : : : : - : : & : : - : - : : & : - & : & : - & : & - & & & & - & & & & - u p g z l l l l l l l l g l l k g l k k k k k k k k k k l l k l k k k k k k k k k k k k ; @ # @ # @ @ @ - + + - O O + @ @ @ + + @ + + @ @ + + + + + + + + + + + + + + % % : 7 f S ~ <.9.h.l.z.m.z.l.g.8.>.! G f 7 = $ + O O O O X O X + + $ * 7 s S ! >.8.h.l.z.z.z.l.h.8.>.! A s 5 % $ ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 2 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 , 1 1 1 1 , 1 , 1 , 1 , 1 1 , 1 , , , , , , , , , , , , > , > > > , > > > , > > , > : : , , : > > : > > : : : > : : : > : : > : : : : : : : : : : & - : : & : : & - - & : & & : & - & & - & - & & & & & & & & & & & - & & & % % & - # - - - - @ - - @ - @ - @ - # @ - + - # - @ + + - + - # + # @ @ # @ @ @ @ @ @ + + + O + - - + @ + @ @ + @ + @ + @ + @ @ @ + + + + + + + + + @ $ = 5 r M T ..1.9.q.h.h.h.f.9.1. .T m e 5 * @ + + O O O O X + O + $ & : e M Y { 1.8.f.h.h.h.f.8.1.{ U b 9 5 * $ ", +"r 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 1 1 1 2 2 1 1 2 2 1 1 1 1 1 , 1 1 1 1 , , 1 1 1 , 1 , , 1 , , 1 , , , , , , , , , , , , , , , > , > > , , , > , , > > > > > , > , : : , , : > : > > > > > > : : : : : : : : : : : : & : : : : : & : : & : : : & : - : & & & - & & & & - & & & - & & & & - # % & % % & & & # - @ - @ + + - @ @ - + - + - - @ - + - # + - - + - + + + - - + + - O @ # # @ @ @ - - o - @ @ O + @ @ + @ @ @ + + + @ + + + + + @ + + + @ + + + # $ * : 9 f A T ..<.2.8.8.8.2.>.{ T A s 7 = * $ + O O O O O O O O + $ $ = 7 r A Y { >.2.8.8.7.2.>.{ T C s 5 = $ + ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 2 1 1 1 2 1 1 1 1 1 , 1 1 1 1 1 1 1 1 , , 1 1 , 1 1 , 1 1 , , 1 1 1 , , 1 , , , , , , , , , , , , , > > , > > , , > > , : , : , > > : > > > > : : : : : > > : : : : : > : : : - : : : : & : : & : - & & & : & & & & : & & - - & & & & & - # & - # & - # & & & & & # & & # - @ - - - + - - + - @ - + # + + - + # - @ + - @ + - - + + + - $ @ @ # @ @ @ @ + + + @ @ + @ @ + @ @ + @ + + @ + + @ + + @ + + @ + + + + + + + $ % = 5 e f A K ! ..;.>.;.{ { Y A f 9 5 = # + O O O O X O O X O O X $ & ; 7 s C U ! { ;.:.;. .! U C s 7 ; % $ X ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 , , 1 , , , 1 , 1 , , , , , , , , , , , , , > > , > , , > > > > > , : : , : , > > > : > > : : : : : : : : : > : : : : : : : & : : : : : & : & & : - - - : - & & - - & & - - & & & & - - & & & & - & # % & # & # & - @ - @ - @ - # @ - + - @ # # - # - + - + @ + - - + + @ - + + + @ # @ # @ @ @ @ @ @ @ + + @ @ @ @ + @ @ + @ + @ + @ + + @ + + + + + + + + + + + @ $ # = 5 9 s M S K T R T U S M f w 5 * @ + + + O O O X O O o O O + + % * : 7 s m A K T T T K A m s 7 ; $ ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 1 2 1 2 2 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 , 1 1 1 , 1 , , 1 , 1 1 , 1 , 1 , 1 , , , , , , , , , , > > , , , > , , > > > > , > > : , , : > : : , > > : > > > > : : : : : : : : : - : : : : : : & : & : : : : : : - & - & & : - & & - & & & & & & - # - # # & & # & & & & & & & # @ - @ - @ - @ @ - + @ @ - # - + # # # # @ - # + @ + - + + + - - @ $ - @ @ @ @ @ @ @ @ @ @ @ @ + @ @ @ + @ + @ + @ + + @ + + + @ + @ + + + + + + + $ % % = 5 9 e b m M C C b f e 7 : = $ @ + O O O O O + O X O X X + + $ = ; 7 e s b b m m b r e 6 = * $ ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 2 1 1 2 2 1 2 1 1 1 1 2 1 1 1 1 1 1 1 , 1 1 , 1 1 1 1 1 1 1 , 1 , , , , , 1 , , , , , , , , , , , > , , , , > > > > , , > > > , , , : > , , : : > > : > : : > > > : > > : > : : : : : : : : & : : & : - : & : & - & & - - & & - & - & - - & & & & & - & & & & & & & # & & # & % - @ - @ - + @ - # @ - @ - @ - # # # + # - # - + + - - @ - + + + - @ + + @ @ @ @ @ - @ @ + O @ + @ + @ + @ + + @ @ + @ + + @ + + + @ + + + + + + + + @ $ % = : 5 7 e e e e w 7 5 ; * # % + + + O O O O O O O O X X O O + $ $ * = 5 6 9 e w w 9 6 3 * * $ . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 1 1 2 2 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 , , 1 1 , , , 1 , , 1 , , , , 1 , , , , , , , , , > , > , , > > , > > , > > > , : : , : : , : : > : > > : : > : > : : : : : : : : : : : : : : : : : & & : & : & : - - & - - & - & & & & & & - & & - # & & & & # & & & & & % & @ - @ - # - - @ - - + - # # @ @ - # - # + # # # - + + + - + - + + # @ # # @ @ @ + O @ @ @ - + @ @ + @ @ + @ + @ + @ + + + + @ + + + + + + + + + + + + @ @ % * & : 3 : > : : * = & $ $ + + O + O O O X O O O O O . X O O X + + % & * = = = ; = = * $ . $ . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 , 1 1 1 , , 1 1 , 1 , , , 1 , , , , , , , , , , , , , , , , , , > , : > > > > , , : : , : , : > > , : : > : > : : : : : : : : : : : : : : : : - : : - & : - : : & : & & : & & & : & - & & & - & - & & & & & & & & & & # & & & & # & # & # & & & # & # # & & @ # & @ # @ & @ @ & @ & @ @ @ @ @ @ @ & @ @ @ @ @ # @ @ @ @ @ @ @ X @ @ + @ @ @ + @ + + @ + @ + + + + @ + + + + + + + + + + @ + @ @ # % % & % & & # @ @ @ + + + X X + O O O O X O O O O O O O O + + $ $ $ * $ * $ + $ + O O O . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 2 1 1 2 2 2 1 1 2 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 , 1 , 1 , , , , , , , , , , , , , : , , , : , , : > > , : , > : , > , , : : : : : > > > > > > : > > : : > : : > : - : : - - : - & : : & & - - : & : : & & : & & - & & & & & & & & & & & & & & # & & & & # & & & & # # & # # # & # & # # # # & @ & # % # # % & @ # # @ & # @ # # @ @ @ @ @ # @ @ @ @ # X @ @ @ @ @ + @ + @ + + @ @ + + + @ + + + + + + + + + + + + + + + o @ @ @ @ # # # @ @ + @ + + + X o o o o O O O O O O X X O O X X O O + + + + + + + + + + O O . . ", +"2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 2 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 , 1 1 , , 1 1 , , 1 , , , , 1 , , , , , , , , , , , , , : , > , , , > > , : : , : , , : , : , , > > : : > : > : : : : : : : : : : : : : : : - : : : - : : & - & : & : : & : : & - & - & & & & & & & & & & & & # & & & & & & # & # & & & & & # # # & & # @ & @ & @ @ # @ % # @ @ @ @ # # # @ @ @ & # @ # @ @ @ # @ @ @ @ @ @ @ @ @ @ + + + @ + + + @ @ + @ + @ + + + + + + + + + o + + X + + + @ + + + @ + + + + + + X o O O o X X O O O O O O O O O O . X O O . O O + + . O + . O o . ", +"2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 2 2 1 2 2 1 1 2 1 2 1 2 1 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 , 1 , 1 1 , 1 , , 1 1 , 1 , , 1 , , , , , , , , , , , , : , , , > , , , > , , , , : , : : : , , : : > : > > : > : > : > : > : : : : : : : : : - : - - : : - : : : : & : & & : & : & & & - & & - & - & & & & & & & & # & # & # & # & & & # # & # & # & # & # & @ & # # & # & # # # # & # # @ & @ @ # @ @ @ @ @ @ @ @ @ @ @ @ @ @ X @ + @ @ @ @ @ + @ + + @ + + + + + + @ + + + + + + + @ o @ + + X o o + + o + + X + X X + o + O + X X + X O O O O X O O X X O . o O O . . . . o o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 1 2 2 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 1 , , 1 , , , 1 , , , , , , , , , , , , , , , , , , > , > , : : , , , : , , , : : , , : : > > : : > > > : > > : : : : : : : : : : : : : - : : - - - - & : & & & : & & & & : & & - & & & & & & & & & & & & & & & & & & & & # # # & & & # & # # # & # # & & @ & # # # % # # % @ # # # # @ @ & @ @ @ @ @ @ @ @ @ @ X @ @ # @ @ @ @ + @ @ + @ + @ + + @ + + + + + + + + + + + + + + + o + o + o + + o + X + + o O O O O O o o o O O O O O O O o o O O X O X o . . . . . . O . ", +"2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 1 2 1 2 1 2 2 1 1 1 2 2 2 2 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 , 1 , , 1 1 , 1 , 1 , , 1 1 , 1 , , , , , , , : , , : , , > , , > , , , : , , , : : , , : , : , , : , , : : : > : : > > : : : : : - : - : : : - - - : - : : : : & : : & : : & : & : & - & & - & & & & & & & & & & & & # & & # & & & & & # # & # & # & # & & @ # @ @ @ & % # % # # & # # # # @ @ # @ @ @ @ @ @ @ @ @ # # @ @ X # X @ + @ + + @ + @ @ + @ + + + @ + + @ + + + + o + X @ + + + + + + + o + + + o o X + + O + o + O X O X O O O O O X o o X O O O . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 1 2 2 1 1 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 , 1 1 , , 1 , 1 1 , , 1 , , , , , , , , , , , , , , > , : , , : , : , : : > , : : , : : : , : : > : > : : > : : > : : : : : : : : - : : - - - - : & : & : & : & : & & & - & & : & - & & & & & & & & & & & # & # # & # & & # & # & # & # & # % # @ # # & % # % @ # % # # # # # # @ # # @ @ & @ @ @ # @ @ @ @ # @ # X @ @ + @ + @ @ + @ + + @ + + @ @ + + + + + + + + + + + o o @ + + + X + + o o X o + + O O O O o + + o O O O O O O O X O O O O o X o . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 2 2 1 2 2 1 1 1 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 1 1 1 , 1 , 1 , 1 1 , , , 1 , , , , , , , , , , , , , , : , > , , > , , , , : > , > , , , : , : , , : : > > : > : > : : : > : : : : : : : - - : - - : - : - & : & : & : & : & & : & & - & & & & - & & & & & & & & # & & & & & & & # & # & & # & # # # & & # & @ & # % % % % # # # % @ # # # # # @ # @ @ @ @ @ @ @ @ @ @ # X # & X @ @ @ @ + @ @ + @ @ + @ + + + + + + @ + + + + + + + X @ o + X + X + o + + + X o + O + + O O X o o O O O O X O X o o O X . . . . . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 2 1 2 2 1 2 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 1 , 1 1 , 1 , , 1 , , , , 1 , , , , , , , , , , , , , , , : , : , > > > > > > > > > , : , : : > : : > : > : : : > : : : : : : : - : - : : : : & : & : & : & : & - - & - & - & - - & & & & & & & & & & & & & # & & & # & & # & & # # # & # & @ & # # @ & # # % # @ # # # # # @ # # # @ @ # # # @ # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + @ + @ @ @ + + + + + + + + + + + X + + + + + X + + X + + + + + X + O O O O O + + X X O O O O O O X O . o . . X O O O O . . o . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 1 2 2 1 1 2 1 2 2 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 , 1 , 1 , 1 , 1 1 , 1 , , , 1 , , 1 1 , , , , , , , , , , , , : , , : , , , , , , , , , : > > > > : > : , : > > > > > > : : > : : : : : : : : : : : - : - & - - : & : : & & : : & & & - & - & & & & & & & & & & & # & & # & # & & & # & & & # # & & & & # & # & @ & & # # % % & # & # # % @ % # # @ @ # @ @ @ @ @ @ @ @ @ @ @ @ # X # # X + @ + + + @ + @ + + + + + @ + @ + + + + + + + + + + + + + + X + + X o O O + X o + O O O O O + o O O O O O O X X . o o . o . o . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 2 1 1 2 1 2 1 2 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 , 1 , , , , 1 , , , , , , , , , , , > , , , , , > > : > > : , > > > > : , : : > > : : > : : : : : : > : : : : : : : - - : - : : : : & : : & : & : & : & : & : & & - & & & & & & & & & & & & & % & & & & & # & # & & # # # # & # # # & @ & & @ & # @ # # # % % @ & & X @ @ @ @ @ @ @ # @ # @ @ @ @ @ X @ @ @ @ + @ @ @ @ @ + + + @ + @ @ + + + @ + + + + + + + o + X + X + o + o X + + + O + o O O + + o + o O O O X + X O O o O O o o . . . O O . . o o O . o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 2 2 1 2 1 2 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 , 1 1 , , 1 , 1 , 1 , 1 , 1 , , , , , 1 , , 1 , , , , , , : , , , : , : , , : , > , > , > > > > : : , : > : > > > : > > > : : : : : : : : - : : : : - : & : : : : & : & : & : & & : & & : - & & - & - & & & & & & & & & & & # & # & # & & & # & & & & # # & & @ & @ @ & # @ & # # % @ @ @ @ @ & & @ @ @ # # @ # @ @ @ @ @ @ @ @ X @ @ @ @ @ + + @ + + @ @ + + @ + + + @ + + + + + + + + + + + + + + + + + + o + o + + + O + O O X O X O O O X O X X O O . O . o o . . . O . . o o ", +"2 2 y 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 2 1 2 2 1 1 2 2 1 1 1 2 1 1 2 1 1 1 1 1 1 , 1 1 1 , 1 1 1 1 1 1 1 , 1 , , 1 , , , 1 , 1 , , , , , , , , , , , , , : , > , > > , : , , > > > > > > > > > > : : > > > : : > : : : : : : : : : : : - : : - : & : & - - : & & : : & : & & & & & & & : & & & & & & & & & & & & & & # & & & # & & # & & # & & # # & # & @ # # # # # # # # # # @ & & @ @ @ @ & @ @ @ # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + + @ + + + + @ + + @ + + + + + + + + + + + + + + o + + o + + o O O + O X O O O O + O O O O O O O O O O O O O X X . . O o . o . o . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 1 1 1 2 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 , 1 , 1 , 1 , , 1 , , , , , , , , , , , , , > > , , > , > , > , , , : , : > > > > : > > : > > > : : : : > : : > : > : : : : : : : : - - : : & : : - - - - : & & : & : : & : & & & & & & - & & & & & & # & & & & & & # # & # & & # # & # # & # & # @ & & @ @ & & # & # % # @ @ @ @ @ @ @ @ # @ # # @ @ @ @ X @ @ @ @ @ @ @ + @ + @ @ + @ @ + @ @ + + + + + + + @ + + + + + + + + + + + + + O + + O + O + X + + O O o O O + O O O O O O O O O O O O O X o O o . . o ", +"2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 2 1 1 2 1 2 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 1 , 1 1 1 , 1 , 1 , , 1 , 1 , , , , , , , , , : , , , , , , > , > , > : , : , > > > > > > > > > > > > > : > > : > : : > : : : : : : : - : - : - - : - - - - & - & : & & & : & & & : & - & - & & & & & & & & & # # & # & & # & & # & # & & # & # # & # & # @ & # # # # # @ # % # @ @ @ @ & @ & @ @ @ # @ @ @ @ @ # @ @ @ @ @ X @ @ + @ + @ + @ + @ + + + @ + + + @ + + + + + + + + + o + + o + o + + O + O + O + O O O + O O O O O O O O O O O X o O O . O . X O o o o o . ", +"2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 2 2 2 1 2 2 1 2 2 1 2 1 2 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 , 1 1 , 1 , , , , , , , , , , , , , , , > , , > , , > , > , > , , , : > > > > > > > > : : : > > : > : : : > : : : : : : : : : - : - : : - - : - - : - : & : & : & & : & & & & & & & & & & & & & & & & & & & & & & & # & # & & # & # & # # & # & # @ # & @ & @ # # # # & @ & @ @ @ @ @ # @ # @ # @ @ @ @ @ @ # X @ @ @ @ @ + @ + @ + @ + + + @ + @ + + + + + + + + + + + + + + X + + + O O + O + O + O O + O + O + O O O O O X O X O O O . . O o O o . . . . . ", +"2 2 2 2 2 2 2 2 y 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 , 2 1 1 , , 1 , 1 , 1 1 , 1 1 , 1 , , , , , , , , , , , , , , : , , : , , : , , : , , , > , > > > : , : > > > : > : > : : > : : : > : : : : : > : : : : : - - - : : - - : - - - & : & & & : : - & - - & - & & & & & & & & & # & & & & # & # & # & & # & # & # & # # & # & # & & & # # & # # # # # # # # # # @ # # @ @ @ @ @ # @ @ @ @ X @ @ @ @ @ @ @ @ X @ X @ + @ + @ + + @ + + + @ + + + + + + O + + + + + + + + X O + O O O + O + O O o X O O O O O O O o o o X X o O X . O O O O O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 1 2 2 2 1 1 1 1 2 1 2 1 1 1 1 1 1 1 2 , 1 , 2 1 1 1 , 1 , 1 1 , , 1 , 1 , 1 1 , , 1 , , , , , , , , , , , , , , , , , > , : , : > , : , > , > > > > > > : > > : > : > : : > : : : : : : : : : : - - - - - - & : : & : & : : & & & & & & & - & & & & & & & & & & & & & # & & & & & & # & & # & # & # & # & # # # @ # # # # # # & # # # # # # # # @ @ @ # # @ @ @ @ @ @ @ @ @ @ @ @ @ X @ @ X @ @ X @ + @ + + + @ + + @ + + + X + + + + + + + + + X o + O + + O + + O + O O O X + O o O O O O O O o O O O O o O O O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 1 2 1 2 1 2 1 1 2 1 2 1 1 1 1 1 1 1 , 2 1 1 1 , , 1 1 1 1 , 1 , , 1 , 1 , , 1 , 1 , , , , , , , , : , , , , , : > , : , , : , : : , > > > : > > > : > : > : > : : > : > > : : : : : : : - - : : : - - : & : - - & : - & : & : & - & & & & & & & & & & & & & & & & & # # & & # & & # # & # & # & & # # & & # # & # # # # # # # # # # # @ @ # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ X @ X @ @ @ + + + + + + + + + + + + + + + + + + + O o + + + + O + O O O O o O O O + + O O O O O O O O X O O X O o o O . O O O O O O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 2 2 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 , 1 , 1 1 1 , 1 , , 1 , 1 , , , , 1 , , , , , , , , , , , , , , , , : , , , , , , : , > , , : , : > > > : : > > > : > : > : : : : : : : : : : : : : : : - - - - : - - - - : & : & & : & & & - - & & - & & & & & & & & & # & & & & & # & & & # & # & # & # # # & @ # & # # & # # # & # # # # # # # # # # # # @ # @ @ @ @ @ @ @ @ @ @ @ X @ @ @ @ @ @ @ @ X @ + @ @ + @ + @ + + + + + + + + O + + + O + + X + o + + o + + + O + O O o O + O O X O O O O O X O O X O O O O O O . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 1 2 1 2 1 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 , 1 1 , 1 , 1 , 1 , 1 , 1 , , , , , , , , , , , , , > , , , > , , > , , > : , : , > : , , : , : > > > : : > : > > : : > : : : : : : : - : : : - : - : - : - - - - - & : & & - & : : & - & & & & & & & & & & # & & & & & & & # # & & & # & & # & # & & # # & # # # # # & # # # & # # # # # & # # @ @ @ @ # @ # @ @ # @ @ @ X @ @ @ @ @ @ @ @ @ @ @ @ + @ + + @ + + @ @ + + + + + + + + + + + + X + + O + O + o + + X + O O O O O O O O + X O O O O O O O . . o . O O O O o O O o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 1 1 2 2 1 2 2 1 2 1 2 2 1 2 1 1 1 1 1 1 , 1 1 1 1 1 , 1 1 , 1 1 , 1 , 1 , 1 , 1 1 , 1 , , , , , , , , , , , > , , > , , : , > , , , : , : , : > > > > > > > > > > : > : > : : : : : : : : : : - : : - - - : - - - - - - : & & : & : & & - & & & - & - & & & & & & & & & & & # & & & & # # & # & & # & # & # & # # & # # # # # # # # # # # # # # # # # # # @ # @ @ @ @ @ @ @ @ @ @ @ X @ @ @ X + X @ @ X @ + + + + + + + + + + + + + + + + + + + + o + o + X + O + o O + X X + + O O O O O O X X O O O . X O . o . o o O O O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 1 1 1 1 1 , 1 , 1 , 1 , , , , 1 , , , , , , , , , , , , , > , > > > , , : , : , , : > , > > > > > > : > > : : > : > : : : > : > : : : : : : - : : - - : - - - - - - : & : & & & : & & & & & - & & - & & & & & & & & & & & # & & # & # & & # & # # & & & # # # # # & # % & # # # # # # # # # # @ # # @ # # @ @ @ @ @ @ @ @ X @ @ @ @ @ X + @ X @ @ X @ + + @ + @ + + + + + + + + X + + + + O + + + + + + O + + O + X O O O O O + + O O O O O O O O . . o . o o . O o O o O O o ", +"2 y 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 1 2 2 2 2 1 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 , 1 , 1 1 , 1 , 1 1 , , , , , , , , , , , , , , > > , , , > , : , , , : , : , , : > > > > > : > > : > > : : : : > : > : : : : : : : : - : : - : - - : - - - - & : & : : & & & : - & - & & & & & & & & & & # & & # & & # & # & & & # & # & & # # # # & & # & # % % # # # # # # # # # # # # @ @ # # @ @ # @ @ @ # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + @ + + + + + @ + + + + + + + + + + + + + X + o + X + O o O + o + O + O O O O O O O O O O O . . O X . . . . o o o O O o O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 1 2 1 2 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 1 , 1 , 1 , 1 1 , 1 1 , , , , , , , , , , , , , : , , , , > , , > , , , : > , : : , : > > , > > : > > > > : : > : > : > : : : : : : : : : : : - : : : & : : & : : & : & : & : : & & & & - & - & & & & & & & & & & & & & & # & & & # & & # & & @ & # & # # # # & # & % # # % # # # & # # # # # # # @ @ @ # @ @ @ @ @ @ @ @ @ @ @ @ X @ @ @ @ + @ + @ X + @ + + + @ + + @ + + + + + + O @ O + O + + + O + + X + O O + O O O O O X + X O O O O O X X o O O . . o . . . O o . . o . O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 1 , , 1 , 1 , 1 , , 1 , 1 , , , , , , , > , , > , > , > : , , , , , , : , > > > > > > > : : > > > > : : : : > : : : > : : : : : : : - : : : & : : : & : & : & : & & : & : & & & & & & & & & & & & & & # # & & & & # & & # & & # # & # & & # & & & & # # & # % # # # # @ # # # # @ # @ # # @ # @ @ @ @ @ @ @ @ @ @ @ @ @ @ X X @ @ @ @ + @ + @ + + + + + + + + @ + + + + + + + + O + o + + + O + X + O O O + O X + X O X O O O O O O O o . . . o . . . . o o o . . O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 2 2 1 1 2 1 2 2 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 , 1 , , 1 , , , 1 , 1 , , , , , , , , , , , > , , , , > : , : , > : , , > > , : > > > > > : > : > : : > > : : > : : : : : : - : - - : - : : : & : & & : : & : : & : & & - & - & & & & & & & & & & # & & & # & & & & # & & # & # & # & # # # # # # # # # # % # # # % # # # # # # # @ # @ # @ @ @ # @ @ @ # # @ @ @ @ @ @ @ @ @ @ @ + + @ + @ + + + @ + + + + + + + + + X + + + + + O + + O + O O + X O + + O + X o + + o O O O X O X X o . O o . o o o o . O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 2 1 1 1 1 2 2 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 , 1 1 , 1 1 1 , 1 , 1 1 , 1 , 1 , 1 , , , , , , , , , , , , , , , , : , , > , , , , > , > > , : : > > > > > > > > : > : > > : > : : > : : : : - : : : : - - : & & - : : - : & : & & & & & : & & - & & : & & & & & & & & & & & & & # & # & & & # & & # & # & & # & # # & & # # # # & & # # # # # # # # # # @ @ @ # @ @ @ @ @ @ X @ @ @ @ X @ @ X @ @ @ X @ + @ + + @ + + @ + + + + + + + + + + + O + + + + O O + + X + O + O O O + X O O O O O O O X O O X o o . . . . o . o . o . o . O O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 1 1 2 1 1 1 2 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 , 1 , 1 1 1 1 , 1 , 1 , , , 1 , , , , 1 , , , , , , , , , , , , , : , , : : , > , > , : , : > : > > : : > > > : > : : > : : : : : : : : : - : : : : : : : : : & : & : : & : : & : & & & & & & - & & & & & & & & & & & & & & # # & & # & & # & # & # & & # & & # # # # # # % # % # # # # # # @ # # # # @ @ @ # @ @ # @ @ @ @ @ X X @ + @ + @ @ + + @ + + + + + @ + + + + + + + + + + + X + + + + + X + + X O O + O O O O O O O + o O O O + O O . . O . o o . . . . . . . o O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 1 2 2 1 1 1 2 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 1 , , 1 , 1 , 1 , 1 1 , , , , 1 , , , , , , , , , , , , , , , , , : , , : , : > : , : , > > > : > > : : > : > : : : > : : : : : : : : : : : - : : & : & : & & : & & : & : & & & & - : & & & & & & & & & & & # & & & # # & & & # & # # & & & # & # # # # # # # & # # # # # # # # # # # # # # # # @ # @ # # @ @ @ @ @ @ @ @ # @ @ + @ @ @ + @ @ + + @ @ + + + @ + + + + + + + + + + + + O + X + + + X O + + O + X + + O + O O + X O O O O O . O . O . . o o . . . o o . . . O O ", +"2 2 2 2 2 2 2 2 1 2 2 2 2 , 2 2 1 2 2 1 1 2 2 1 1 1 2 1 1 2 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 , 1 , , 1 1 , , , , , , 1 , , , , , , , : , : , : , , , : , > > > : > , : > > > > > > > : : : > : > : : > : : : : : : : : & : : & : & : - : & : & & : : & : & : & - & & - - & & & & & & & & & & & & # & & & & & # & & & & # & & # & # & # & # # & & # % # # # % % # # # # # @ @ @ # # @ @ @ @ @ @ @ X # @ @ @ X @ @ @ @ + @ + @ @ @ @ + + + + @ + @ + + + + + + + + + + + + O + + O + + O O + + O O O + O O O o O O O O O O O . . o o . . . . . o o o . O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 1 2 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 , 1 1 , 1 1 , 1 , , 1 , 1 , , , 1 , , , , , , , , , , : , , , , , , > > , , > , , > , , : , > > > : > : > > > > : : > : > : : > : : : : : : : : : : : : - - : & : - : & & : & : & & - & & & - & & & & & & & # & & & & & & & # # & & # & # # & # # # & # & # # # & # # # # % % % % @ # # # # # # # # @ # @ # @ @ @ @ @ @ @ # @ @ # @ @ + + @ + @ + + + + @ + @ + + + + + + + + + + + + + + X + + O + + + O + + O O + O + O O O O O o O O O O O O . o . O . O o . . . o . o o O O ", +"2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 1 2 2 2 1 1 2 1 2 1 1 1 2 1 2 , 1 1 1 1 1 , 2 2 , 1 1 1 , 1 1 , , 1 1 , , 1 1 , , , , , 1 , , , , , , , , > , , > , > , > , : > , : , > : : , : > > > > > > : > > : : > : : : > : : : > : : : : : : - : - : - : & & : - & : : : & : & & : : & & & & & & : & & & & & & & & # & & & # & # & & & & # & # & # # & # # # & # & # # & # # # # # # # # # # # # @ @ - @ @ @ @ o - + o - @ @ @ @ @ @ @ + @ + + @ @ + @ + @ + @ + @ + + + + + + + + + + + + + + + + o + + X X + O + + X + + O O o O O O O O X X O X O X O o o O o O X X O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 2 2 1 1 2 2 1 2 1 1 2 1 2 1 1 2 , , 1 1 1 , 1 1 1 1 , 1 , , 1 , 1 1 , , 1 , 1 , , 1 , , , , , , , , , , > , > , , , , , , : , , , > , > , : , > : > > : > > : > : : > : > : : : : : : : : : - : - - : : : : - : : & & & : : : & & : & : & & & & & & & & & & & & & & & & & & & & # # & & # & # & # # & & # # # & # # # # # & # # # # # # # # @ @ # + # @ o @ - o @ @ + @ @ @ + @ + @ @ + @ @ + @ @ + + @ + + + + + + + + + + + + + + + + X + + + o + o + + O + O O X X O O O + O O O O O + O O o X O O O O X O O X O o o O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 2 2 1 1 1 2 , 2 1 2 1 1 1 2 , 1 , , 1 1 1 1 1 , 1 , 1 1 , 1 , 1 , , , , , , , , 1 , , , , > , , , > , > > , : , : : , , : > : , : : > > > > : > : > > : : : > : : : : : : : : : : : - : - : - : : & & : & & : & : & & & & & & : & & & & & & & & & & & & & & & # & & # # & & & # & # & # & & & # & & # # # # & # & # # # & # # # # # # # # + @ @ - @ - o @ @ @ @ @ @ @ + @ + @ @ + @ @ + + @ + @ + + + + + @ + + + + + + + + X X + + + + + + o o + O O O o + o X O O O + O O O O O O O X X o o O X o O X o o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 2 1 1 1 2 1 1 1 1 2 1 1 1 1 1 , 2 1 1 1 1 1 1 1 1 1 , 1 1 1 1 1 , 1 , , 1 , , 1 , 1 , , , , , , , , , , , > , , , > , , , > , , > > , > > : , > > > > > > : > : : > > : : > : : > : : : : : - : : - : : : & : : - : & : & : & : & : : & & & & & & & & & & & & & & # & # & & & # & & & # & # & & # & # # # # # # # & # # # # # # # # # # # # # # # # @ + - @ @ @ o - + @ @ @ @ @ + o - + @ + @ @ + @ + + @ + + + @ @ + + + + + + + + + + + + + X + o + o + + O + + O + X + + o O O O O O O O O O O X X . . . . o O o O X . O O ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 1 2 2 1 1 2 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , 1 1 , 1 , 1 , 1 , , , , , , , , , , , , , > , , , , , > > , , : , , , : > > , > , : > > : > > : > > : > > : > : : : : : : > : : - : : : - : & : : & : & : & : & : & & : & & & & & & : & & & & & & & & & # & & & & & & # # & & & # & # & # & # & # # # # # & & # # # & # # # # # # # # # # @ + + - o + + @ o + @ @ + @ @ + + @ @ + @ @ + + @ @ @ + + + @ + + + + + + + + + + + X + + + + + X + X + O + O o O X + O O O O O O O O O O X O o . o O . . o o o . o . X O O . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 2 2 2 1 2 2 2 1 1 1 1 2 1 1 1 1 1 1 1 , 1 , , 1 1 1 1 1 , , 1 , 1 , , , , 1 1 , 1 1 1 , , , , , , , , , , : , , , > , , : , > , > , : : , : > > : > > > > : > > : : > : > > > : : : & : : : - : - - : - & : - - - : : & & : : & : & & : & & & & & & & & & # & & & & & & # & & & & & & # & & & # & # # # & & # & # # # # # # # # # # # # # # # # @ # - + o - - @ @ - - o @ @ @ + @ @ @ + + @ @ + @ + + + + @ + + + + + + + + + + + + + + + + X + X + X + O + O + + o O O + + + O + O O O O O o X O . X o o O o O X O o . . O . O ", +"y 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 1 2 1 1 1 1 1 2 1 2 , 1 1 1 2 2 1 1 1 1 1 1 1 1 1 , 1 1 1 1 , , 1 1 , 1 , , , , , , , , , , , , , , , : , > , , > > > > , > , > : , : : , : , , : : > > : : : : > : > : : : : : : : : : : : - : : - : : & : & : & & & : & & : & & : & & : & : & & & & & & & & & & # & # & # & # # # # & # # & & & & # # # & # & # & # & # # # # # # # # # # @ # # @ + @ @ @ @ @ @ o - @ @ @ @ + @ + @ @ + @ + @ + @ + @ + + @ + + + @ + + + + + + + X + + + + + X + O + O + o + + + X O O O + O O O O O O X O X . . . . o X O X . o . . O . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 1 1 2 2 1 2 1 2 1 2 1 1 1 1 1 2 , 1 1 1 1 1 1 1 1 1 , 1 , , 1 , 1 1 , , 1 , , , 1 , , , , , , > , , , , > , , , , , , , , , : , : > , > , , : , > : > > > : > > > > : : : : : : : > : : : : : : : : - : : - : - : - - & : : & & & : & : & & & & & & & & & & & & & & & & & & & & & & & & & # & & # # # # & & # # & # # # # # # # # # & # # # # # # @ # @ - @ @ @ @ @ @ @ @ + @ @ @ @ @ # + @ @ @ + + @ + @ + + + + @ + + + + + + + + + + + + + o + + + X + O + O + o X O O + O O O O O O O O O O O O o . o . O o X . . . . O O . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 1 2 2 1 2 1 1 1 1 2 1 2 , 1 2 1 2 , 1 1 2 , 1 1 1 1 1 , 1 1 1 , 1 , 1 1 , 1 1 , , , , , , , , , , > , > , , , > > > , : , , , > , > : , , : > : , > > : > : > > > : : : : > > : : : : : : : : & : & : : & : - - - - & : & : & : - & & & : & & & & & & & & & & & # & & # & & & # & # # & & # # & # & & # # & # # # # # # # # # # # # # # # # # # # # - o # @ @ @ @ @ + @ @ @ @ + @ + + @ @ @ + + + + @ + + + + + @ + + @ + + + + + + + + o + o + + + O + X + O O + + O + O + O + O O O O O O O O O O X . . . . o o . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 1 2 2 2 1 1 1 1 1 2 1 1 1 1 , 2 , 1 , , 1 1 , 1 1 , 1 , , 1 , , , , , , , 1 , 1 , , , , , , , , , > , , , > , , , : > , : > , : : , > > : > > : > : : : : > > > : : : : : : : : : : : : : : : : - : - - - - - & : & & & - - : & & & & & & & & & & & & & & & & & & # & & & & & # & & & # & # & # & # # & & # & # & # # # # # # # # # # @ # # - @ # @ @ @ @ @ @ @ @ @ @ @ @ + @ @ + @ @ @ @ @ @ + + @ + @ + + + + + + + + + + + + + + + + + o + X + + + + O O + O O O O O O O O O O X O O O X O X . . . . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 1 2 2 1 2 1 1 1 2 1 1 1 1 2 2 , 1 1 1 1 2 1 1 1 1 1 1 , 1 1 1 , , 1 1 , 1 , , 1 1 , 1 , , , , , , , , > , , , > > > > > : , : , > , , > , > > : : , > > > > : > > > : : : : > : : : : : : : : : : : & : : & - : & : - - - : : & - & & - & & & & - & & - & & & & & # & & & & & & # & # & & # & # # & # & # & # # & # # # # # & # & # # # # # # # # @ # @ o @ @ # # @ @ @ @ @ @ @ @ + @ @ + @ @ + @ + + + + + + + + + + + + + + + + + + o + + + o o + o + + O + O + O O O X + X + + O O + O O X O O O X O O X o o o o . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 1 1 2 2 1 1 2 1 2 1 2 1 2 1 1 1 1 2 , 1 , 1 1 1 1 , 1 1 , 1 , 1 , , 1 , , , , , , , , , , , , , , , > , , , > , , : , , > , : : , : , : , > : , : > : > : > : : > : > : > : : : : : : : : : : : : : : : & : : & - : & & : & : & - & : & : & & & & & & & & & & & & # & & # & & & & & & & # & # # & & # # & # # & # & # # # # # # # # # # # # @ # @ @ @ # @ @ @ @ @ @ @ @ @ @ @ @ + @ + + @ @ @ + @ @ + @ @ @ + @ + + + + + + + + + + + + + + + + + X + O + O + + + O O O O O O O X O O O O O O O X X O . . . . . o . o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 , , 1 , 1 , 1 , , 1 , 1 , 1 , , , , 1 , , , , , , , , > , > > > , , , : , , : , > : , > , : > > > : > : > > : > : : > : > : : > : : : : : : : & : & & : : & : & & - & : & : - & - & - & - & & - & & & & & & & & # & & & & # & & # & # & & # & # & # # & & # # # # & # # & # # # & # # # # # # # # # # @ # @ # @ @ @ @ @ @ + @ @ @ @ @ # @ + + + @ + + + + + + + + @ + @ @ + + + + + + + + o + + + X + + O + + O + O + O + O O X O X O O O X o o O O o O O X o . o . o . . . . ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 , 1 , 1 , 1 1 1 1 1 1 , , 1 1 , 1 , , 1 1 , , 1 , , , , , , , , , , , , , > , > , , : , , , > , : , : , > > : , > : : > : : > > : : : : : : : : : : : : : : : : : : : : - : : : : & : & - & & - & - & & & & & & & & & & & & & & & & & & & # & # & # # & # & # & & & # # & # # # # & # # & # # # # # # # # # @ @ @ # @ @ @ @ @ @ @ + @ @ @ @ + + @ + @ @ + @ + @ @ @ @ + + @ + + + + + + + + + + + o + + + o + + X O + O O + O O O O O O O + O O O O O O O o O O O o o o . o . . . . . ", +"2 2 y 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 1 1 1 2 1 2 2 2 1 1 1 2 , 2 1 1 1 1 1 1 2 , 1 1 , 1 1 1 1 1 1 , 1 1 , , , , , 1 , , , , , , , , , , , , , , , , > , , : , > , : : , , : , : : , , : : > > > > : > : : > : > > : : : : : : : : : & & : : & & : - - & : & & & - - & - & - & & & - & & & & & & & & & & # & & # & & & # # & & # & # & # # # & # # # & # # # # # # # # # # # # # # # # # # @ # @ @ @ @ @ @ @ + @ @ @ @ @ @ @ + @ @ + @ + + + @ + @ + + + + + + + + + + + + + + + + + + + X + + + X O O + + O X O O O + X O X O O O O O O O O O O . . o o . . o o ", +"2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 1 2 1 1 1 2 1 2 1 1 1 1 1 1 1 2 , 2 1 1 1 1 , 1 , , , 1 , , , 1 , 1 , , , , , , , , , , > , , > , > > , , > , , > , > > , , : , : > > : , : > > : : > > : > : : : : : : : : : : : - : : : : & : : : & : - - : : & : & - - - & & - - & & & & & & & & & & & & & & & & & # & # & # & & # & # & # # & # # # & # # # & # # # # # # # # # # # # # # @ @ # @ @ @ @ @ @ @ @ @ + @ + @ + @ + @ @ + @ @ + + @ + + + + + + + + + + + + + + o + + o + o + + X X + O + O + O + O + O X O O O O O O O O O O O . o . . . . . o o " +}; diff --git a/pcsx2/Linux/ConfigDlg.cpp b/pcsx2/Linux/ConfigDlg.cpp new file mode 100644 index 0000000000..7261de8312 --- /dev/null +++ b/pcsx2/Linux/ConfigDlg.cpp @@ -0,0 +1,598 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "ConfigDlg.h" + +using namespace std; +using namespace R5900; + +static void FindComboText(GtkWidget *combo, char plist[255][255], GList *list, char *conf) +{ + if (strlen(conf) > 0) SetActiveComboItem(GTK_COMBO_BOX(combo), plist, list, conf); +} + + +static bool GetComboText(GtkWidget *combo, char plist[255][255], char *conf) +{ + int i; + + char *tmp = (char*)gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo)); + + if (tmp == NULL) return FALSE; + for (i=2;i<255;i+=2) { + if (!strcmp(tmp, plist[i-1])) { + strcpy(conf, plist[i-2]); + break; + } + } + return TRUE; +} + +static void ConfPlugin(PluginConf confs, char* plugin, const char* name) +{ + void *drv; + void (*conf)(); + char file[g_MaxPath]; + + GetComboText(confs.Combo, confs.plist, plugin); + strcpy(file, Config.PluginsDir); + strcat(file, plugin); + + drv = SysLoadLibrary(file); + #ifndef LOCAL_PLUGIN_INIS + getcwd(file, ARRAYSIZE(file)); /* store current dir */ + chdir(Config.PluginsDir); /* change dirs so that plugins can find their config file*/ + #endif + if (drv == NULL) return; + + conf = (void (*)()) SysLoadSym(drv, name); + if (SysLibError() == NULL) conf(); + #ifndef LOCAL_PLUGIN_INIS + chdir(file); /* change back*/ + #endif + SysCloseLibrary(drv); +} + + +static void TestPlugin(PluginConf confs, char* plugin, const char* name) +{ + void *drv; + s32 (* (*conf)())(); + char file[g_MaxPath]; + int ret = 0; + + GetComboText(confs.Combo, confs.plist, plugin); + strcpy(file, Config.PluginsDir); + strcat(file, plugin); + + drv = SysLoadLibrary(file); + getcwd(file, ARRAYSIZE(file)); /* store current dir */ + chdir(Config.PluginsDir); /* change dirs so that plugins can find their config file*/ + if (drv == NULL) return; + + conf = (s32 (* (*)())()) SysLoadSym(drv, name); + if (SysLibError() == NULL) ret = (s32) conf(); + chdir(file); /* change back*/ + SysCloseLibrary(drv); + + if (ret == 0) + Msgbox::Alert("This plugin reports that should work correctly"); + else + Msgbox::Alert("This plugin reports that should not work correctly"); +} + +void OnConf_Gs(GtkMenuItem *menuitem, gpointer user_data) +{ + char file[255]; + + getcwd(file, ARRAYSIZE(file)); + chdir(Config.PluginsDir); + gtk_widget_set_sensitive(MainWindow, FALSE); + GSconfigure(); + chdir(file); + gtk_widget_set_sensitive(MainWindow, TRUE); +} + +void OnConf_Pads(GtkMenuItem *menuitem, gpointer user_data) { + char file[255]; + + getcwd(file, ARRAYSIZE(file)); + chdir(Config.PluginsDir); + gtk_widget_set_sensitive(MainWindow, FALSE); + PAD1configure(); + if (strcmp(Config.PAD1, Config.PAD2)) PAD2configure(); + chdir(file); + gtk_widget_set_sensitive(MainWindow, TRUE); +} + +void OnConf_Spu2(GtkMenuItem *menuitem, gpointer user_data) { + char file[255]; + + getcwd(file, ARRAYSIZE(file)); + chdir(Config.PluginsDir); + gtk_widget_set_sensitive(MainWindow, FALSE); + SPU2configure(); + gtk_widget_set_sensitive(MainWindow, TRUE); + chdir(file); +} + +void OnConf_Cdvd(GtkMenuItem *menuitem, gpointer user_data) { + char file[255]; + + getcwd(file, ARRAYSIZE(file)); + chdir(Config.PluginsDir); + gtk_widget_set_sensitive(MainWindow, FALSE); + CDVDconfigure(); + gtk_widget_set_sensitive(MainWindow, TRUE); + chdir(file); +} + +void OnConf_Dev9(GtkMenuItem *menuitem, gpointer user_data) { + char file[255]; + + getcwd(file, ARRAYSIZE(file)); + chdir(Config.PluginsDir); + gtk_widget_set_sensitive(MainWindow, FALSE); + DEV9configure(); + gtk_widget_set_sensitive(MainWindow, TRUE); + chdir(file); +} + +void OnConf_Usb(GtkMenuItem *menuitem, gpointer user_data) { + char file[255]; + + getcwd(file, ARRAYSIZE(file)); + chdir(Config.PluginsDir); + gtk_widget_set_sensitive(MainWindow, FALSE); + USBconfigure(); + gtk_widget_set_sensitive(MainWindow, TRUE); + chdir(file); +} + +void OnConf_Fw(GtkMenuItem *menuitem, gpointer user_data) { + char file[255]; + + getcwd(file, ARRAYSIZE(file)); + chdir(Config.PluginsDir); + gtk_widget_set_sensitive(MainWindow, FALSE); + FWconfigure(); + gtk_widget_set_sensitive(MainWindow, TRUE); + chdir(file); +} + +void SetActiveComboItem(GtkComboBox *widget,char plist[255][255], GList *list, char *conf) +{ + GList *temp; + int i = 0, pindex = 0, item = -1; + + if (strlen(conf) > 0) { + for (i=2;i<255;i+=2) { + if (!strcmp(conf, plist[i-2])) { + pindex = i - 1; + break; + } + } + } + + i = 0; + temp = list; + + while (temp) + { + if (!strcmp(plist[pindex],(char*)temp->data)) + item = i; + + temp = temp->next; + i++; + } + + if (item <= 0) item = 0; + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), item); +} + +void OnConfConf_Ok(GtkButton *button, gpointer user_data) { + applychanges = TRUE; + + if (!GetComboText(GSConfS.Combo, GSConfS.plist, Config.GS)) + applychanges = FALSE; + if (!GetComboText(PAD1ConfS.Combo, PAD1ConfS.plist, Config.PAD1)) + applychanges = FALSE; + if (!GetComboText(PAD2ConfS.Combo, PAD2ConfS.plist, Config.PAD2)) + applychanges = FALSE; + if (!GetComboText(SPU2ConfS.Combo, SPU2ConfS.plist, Config.SPU2)) + applychanges = FALSE; + if (!GetComboText(CDVDConfS.Combo, CDVDConfS.plist, Config.CDVD)) + applychanges = FALSE; + if (!GetComboText(DEV9ConfS.Combo, DEV9ConfS.plist, Config.DEV9)) + applychanges = FALSE; + if (!GetComboText(USBConfS.Combo, USBConfS.plist, Config.USB)) + applychanges = FALSE; + if (!GetComboText(FWConfS.Combo, FWConfS.plist, Config.FW)) + applychanges = FALSE; + if (!GetComboText(BiosConfS.Combo, BiosConfS.plist, Config.Bios)) + applychanges = FALSE; + + SaveConfig(); + + if (configuringplug == FALSE) { + ReleasePlugins(); + LoadPlugins(); + } + + gtk_widget_destroy(ConfDlg); + if (MainWindow) gtk_widget_set_sensitive(MainWindow, TRUE); + gtk_main_quit(); +} + +void OnConfConf_GsConf(GtkButton *button, gpointer user_data) { + ConfPlugin(GSConfS, Config.GS, "GSconfigure"); +} + +void OnConfConf_GsTest(GtkButton *button, gpointer user_data) { + TestPlugin(GSConfS, Config.GS, "GStest"); +} + +void OnConfConf_GsAbout(GtkButton *button, gpointer user_data) { + ConfPlugin(GSConfS, Config.GS, "GSabout"); +} + +void OnConfConf_Pad1Conf(GtkButton *button, gpointer user_data) { + ConfPlugin(PAD1ConfS, Config.PAD1, "PADconfigure"); +} + +void OnConfConf_Pad1Test(GtkButton *button, gpointer user_data) { + TestPlugin(PAD1ConfS, Config.PAD1, "PADtest"); +} + +void OnConfConf_Pad1About(GtkButton *button, gpointer user_data) { + ConfPlugin(PAD1ConfS, Config.PAD1, "PADabout"); +} + +void OnConfConf_Pad2Conf(GtkButton *button, gpointer user_data) { + ConfPlugin(PAD2ConfS, Config.PAD2, "PADconfigure"); +} + +void OnConfConf_Pad2Test(GtkButton *button, gpointer user_data) { + TestPlugin(PAD2ConfS, Config.PAD2, "PADtest"); +} + +void OnConfConf_Pad2About(GtkButton *button, gpointer user_data) { + ConfPlugin(PAD2ConfS, Config.PAD2, "PADabout"); +} + +void OnConfConf_Spu2Conf(GtkButton *button, gpointer user_data) { + ConfPlugin(SPU2ConfS, Config.SPU2, "SPU2configure"); +} + +void OnConfConf_Spu2Test(GtkButton *button, gpointer user_data) { + TestPlugin(SPU2ConfS, Config.SPU2, "SPU2test"); +} + +void OnConfConf_Spu2About(GtkButton *button, gpointer user_data) { + ConfPlugin(SPU2ConfS, Config.SPU2, "SPU2about"); +} + +void OnConfConf_CdvdConf(GtkButton *button, gpointer user_data) { + ConfPlugin(CDVDConfS, Config.CDVD, "CDVDconfigure"); +} + +void OnConfConf_CdvdTest(GtkButton *button, gpointer user_data) { + TestPlugin(CDVDConfS, Config.CDVD, "CDVDtest"); +} + +void OnConfConf_CdvdAbout(GtkButton *button, gpointer user_data) { + ConfPlugin(CDVDConfS, Config.CDVD, "CDVDabout"); +} + +void OnConfConf_Dev9Conf(GtkButton *button, gpointer user_data) { + ConfPlugin( DEV9ConfS, Config.DEV9, "DEV9configure"); +} + +void OnConfConf_Dev9Test(GtkButton *button, gpointer user_data) { + TestPlugin(DEV9ConfS, Config.DEV9, "DEV9test"); +} + +void OnConfConf_Dev9About(GtkButton *button, gpointer user_data) { + ConfPlugin( DEV9ConfS, Config.DEV9, "DEV9about"); +} + +void OnConfConf_UsbConf(GtkButton *button, gpointer user_data) { + ConfPlugin(USBConfS, Config.USB, "USBconfigure"); +} + +void OnConfConf_UsbTest(GtkButton *button, gpointer user_data) { + TestPlugin(USBConfS, Config.USB, "USBtest"); +} + +void OnConfConf_UsbAbout(GtkButton *button, gpointer user_data) { + ConfPlugin(USBConfS, Config.USB, "USBabout"); +} + +void OnConfConf_FWConf(GtkButton *button, gpointer user_data) { + ConfPlugin( FWConfS, Config.FW, "FWconfigure"); +} + +void OnConfConf_FWTest(GtkButton *button, gpointer user_data) { + TestPlugin(FWConfS, Config.FW, "FWtest"); +} + +void OnConfConf_FWAbout(GtkButton *button, gpointer user_data) { + ConfPlugin( FWConfS, Config.FW, "FWabout"); +} + + +void SetComboToGList(GtkComboBox *widget, GList *list) +{ + GList *temp; + + while (gtk_combo_box_get_active_text(widget) != NULL) + { + gtk_combo_box_remove_text(GTK_COMBO_BOX(widget), 0); + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0); + } + + temp = list; + while (temp != NULL) + { + gtk_combo_box_append_text(GTK_COMBO_BOX (widget), (char*)temp->data); + + temp = temp->next; + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0); +} + +static void ConfCreatePConf(const char *name, PluginConf *confs, char *config) +{ + char tmp[50]; + + sprintf (tmp, "GtkCombo_%s", name); + confs->Combo = lookup_widget(ConfDlg, tmp); + SetComboToGList(GTK_COMBO_BOX(confs->Combo), confs->PluginNameList); + FindComboText(confs->Combo, confs->plist, confs->PluginNameList, config); +} + +void UpdateConfDlg() { + FindPlugins(); + + ConfCreatePConf("Gs", &GSConfS, Config.GS); + ConfCreatePConf("Pad1", &PAD1ConfS, Config.PAD1); + ConfCreatePConf("Pad2", &PAD2ConfS, Config.PAD2); + ConfCreatePConf("Spu2", &SPU2ConfS, Config.SPU2); + ConfCreatePConf("Cdvd", &CDVDConfS, Config.CDVD); + ConfCreatePConf("Dev9", &DEV9ConfS, Config.DEV9); + ConfCreatePConf("Usb", &USBConfS, Config.USB); + ConfCreatePConf("FW", &FWConfS, Config.FW); + ConfCreatePConf("Bios", &BiosConfS, Config.Bios); +} + +void GetDirectory(GtkWidget *topWindow, const char *message, char *reply) +{ + gchar *File; + GtkWidget *dialog; + gint result; + + dialog = gtk_file_chooser_dialog_new (message, GTK_WINDOW (topWindow), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); + result = gtk_dialog_run (GTK_DIALOG (dialog)); + + switch (result) + { + case (GTK_RESPONSE_OK): + File = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (dialog)); + + strcpy(reply, File); + if (reply[strlen(reply)-1] != '/') + strcat(reply, "/"); + + default: + gtk_widget_destroy (dialog); + } +} + +void OnConfConf_PluginsPath(GtkButton *button, gpointer user_data) +{ + char reply[g_MaxPath]; + + GetDirectory(ConfDlg,"Choose the Plugin Directory:", reply); + strcpy(Config.PluginsDir, reply); + + UpdateConfDlg(); +} + +void OnConfConf_BiosPath(GtkButton *button, gpointer user_data) +{ + char reply[g_MaxPath]; + + GetDirectory(ConfDlg,"Choose the Bios Directory:", reply); + strcpy(Config.BiosDir, reply); + + UpdateConfDlg(); +} + +void OnConf_Conf(GtkMenuItem *menuitem, gpointer user_data) { + FindPlugins(); + + ConfDlg = create_ConfDlg(); + gtk_window_set_title(GTK_WINDOW(ConfDlg), "Configuration"); + + UpdateConfDlg(); + + gtk_widget_show_all(ConfDlg); + if (MainWindow) gtk_widget_set_sensitive(MainWindow, FALSE); + gtk_main(); +} + +static void ComboAddPlugin(char name[g_MaxPath], PluginConf *confs, u32 version, struct dirent *ent) { + sprintf (name, "%s %ld.%ld.%ld", PS2EgetLibName(), (version>>8)&0xff ,version&0xff, (version>>24)&0xff); + confs->plugins+=2; + strcpy(confs->plist[confs->plugins-1], name); + strcpy(confs->plist[confs->plugins-2], ent->d_name); + confs->PluginNameList = g_list_append(confs->PluginNameList, confs->plist[confs->plugins-1]); +} + +void FindPlugins() { + DIR *dir; + struct dirent *ent; + void *Handle; + char plugin[g_MaxPath],name[g_MaxPath]; + + GSConfS.plugins = 0; CDVDConfS.plugins = 0; DEV9ConfS.plugins = 0; + PAD1ConfS.plugins = 0; PAD2ConfS.plugins = 0; SPU2ConfS.plugins = 0; + USBConfS.plugins = 0; FWConfS.plugins = 0; BiosConfS.plugins = 0; + GSConfS.PluginNameList = NULL; CDVDConfS.PluginNameList = NULL; DEV9ConfS.PluginNameList = NULL; + PAD1ConfS.PluginNameList = NULL; PAD2ConfS.PluginNameList = NULL; SPU2ConfS.PluginNameList = NULL; + USBConfS.PluginNameList = NULL; FWConfS.PluginNameList = NULL; BiosConfS.PluginNameList = NULL; + + dir = opendir(Config.PluginsDir); + if (dir == NULL) { + Msgbox::Alert("Could not open '%s' directory", params Config.PluginsDir); + return; + } + while ((ent = readdir(dir)) != NULL) { + u32 version; + u32 type; + + sprintf (plugin, "%s%s", Config.PluginsDir, ent->d_name); + + if (strstr(plugin, ".so") == NULL) continue; + Handle = dlopen(plugin, RTLD_NOW); + if (Handle == NULL) + { + Console::Error("Can't open %s: %s\n", params ent->d_name, dlerror()); + continue; + } + + PS2EgetLibType = (_PS2EgetLibType) dlsym(Handle, "PS2EgetLibType"); + PS2EgetLibName = (_PS2EgetLibName) dlsym(Handle, "PS2EgetLibName"); + PS2EgetLibVersion2 = (_PS2EgetLibVersion2) dlsym(Handle, "PS2EgetLibVersion2"); + + if (PS2EgetLibType == NULL) + { + Console::Error("PS2EgetLibType==NULL for %s", params ent->d_name); + continue; + } + if (PS2EgetLibName == NULL) + { + Console::Error("PS2EgetLibName==NULL for %s", params ent->d_name); + continue; + } + if (PS2EgetLibVersion2 == NULL) + { + Console::Error("PS2EgetLibVersion2==NULL for %s", params ent->d_name); + continue; + } + + type = PS2EgetLibType(); + + if (type & PS2E_LT_GS) + { + version = PS2EgetLibVersion2(PS2E_LT_GS); + + if (((version >> 16)&0xff) == PS2E_GS_VERSION) + ComboAddPlugin(name, &GSConfS, version, ent); + else + Console::Notice("Plugin %s: Version %x != %x", params plugin, (version >> 16)&0xff, PS2E_GS_VERSION); + } + if (type & PS2E_LT_PAD) + { + _PADquery query; + + query = (_PADquery)dlsym(Handle, "PADquery"); + version = PS2EgetLibVersion2(PS2E_LT_PAD); + + if (((version >> 16)&0xff) == PS2E_PAD_VERSION && query) + { + if (query() & 0x1) ComboAddPlugin(name, &PAD1ConfS, version, ent); + if (query() & 0x2) ComboAddPlugin(name, &PAD2ConfS, version, ent); + } + else + Console::Notice("Plugin %s: Version %x != %x", params plugin, (version >> 16)&0xff, PS2E_PAD_VERSION); + } + if (type & PS2E_LT_SPU2) + { + version = PS2EgetLibVersion2(PS2E_LT_SPU2); + + if (((version >> 16)&0xff) == PS2E_SPU2_VERSION) + ComboAddPlugin(name, &SPU2ConfS, version, ent); + else + Console::Notice("Plugin %s: Version %x != %x", params plugin, (version >> 16)&0xff, PS2E_SPU2_VERSION); + } + if (type & PS2E_LT_CDVD) + { + version = PS2EgetLibVersion2(PS2E_LT_CDVD); + + if (((version >> 16)&0xff) == PS2E_CDVD_VERSION) + ComboAddPlugin(name, &CDVDConfS, version, ent); + else + Console::Notice("Plugin %s: Version %x != %x", params plugin, (version >> 16)&0xff, PS2E_CDVD_VERSION); + } + if (type & PS2E_LT_DEV9) + { + version = PS2EgetLibVersion2(PS2E_LT_DEV9); + + if (((version >> 16)&0xff) == PS2E_DEV9_VERSION) + ComboAddPlugin(name, &DEV9ConfS, version, ent); + else + Console::Notice("DEV9Plugin %s: Version %x != %x", params plugin, (version >> 16)&0xff, PS2E_DEV9_VERSION); + } + if (type & PS2E_LT_USB) + { + version = PS2EgetLibVersion2(PS2E_LT_USB); + + if (((version >> 16)&0xff) == PS2E_USB_VERSION) + ComboAddPlugin(name, &USBConfS, version, ent); + else + Console::Notice("USBPlugin %s: Version %x != %x", params plugin, (version >> 16)&0xff, PS2E_USB_VERSION); + } + if (type & PS2E_LT_FW) + { + version = PS2EgetLibVersion2(PS2E_LT_FW); + + if (((version >> 16)&0xff) == PS2E_FW_VERSION) + ComboAddPlugin(name, &FWConfS, version, ent); + else + Console::Notice("FWPlugin %s: Version %x != %x", params plugin, (version >> 16)&0xff, PS2E_FW_VERSION); + } + } + closedir(dir); + + dir = opendir(Config.BiosDir); + if (dir == NULL) + { + Msgbox::Alert("Could not open '%s' directory", params Config.BiosDir); + return; + } + + while ((ent = readdir(dir)) != NULL) { + struct stat buf; + char description[50]; //2002-09-28 (Florin) + + sprintf (plugin, "%s%s", Config.BiosDir, ent->d_name); + if (stat(plugin, &buf) == -1) continue; + if (buf.st_size > (1024*4096)) continue; //2002-09-28 (Florin) + if (!IsBIOS(ent->d_name, description)) continue;//2002-09-28 (Florin) + + BiosConfS.plugins+=2; + snprintf(BiosConfS.plist[BiosConfS.plugins-1], sizeof(BiosConfS.plist[0]), "%s (", description); + strncat(BiosConfS.plist[BiosConfS.plugins-1], ent->d_name, min(sizeof(BiosConfS.plist[0]-2), strlen(ent->d_name))); + strcat(BiosConfS.plist[BiosConfS.plugins-1], ")"); + strcpy(BiosConfS.plist[BiosConfS.plugins-2], ent->d_name); + BiosConfS.PluginNameList = g_list_append(BiosConfS.PluginNameList, BiosConfS.plist[BiosConfS.plugins-1]); + } + closedir(dir); +} diff --git a/pcsx2/Linux/ConfigDlg.h b/pcsx2/Linux/ConfigDlg.h new file mode 100644 index 0000000000..10c9064048 --- /dev/null +++ b/pcsx2/Linux/ConfigDlg.h @@ -0,0 +1,72 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + + #ifndef __CONFIGDLG_H__ +#define __CONFIGDLG_H__ + +#include "Linux.h" +#ifdef __cplusplus +extern "C" { +#endif + +#include "support.h" +#include "callbacks.h" +#include "interface.h" + +#ifdef __cplusplus +} +#endif +#include "R3000A.h" +#include "IopMem.h" + + +// Helper Functions +void FindPlugins(); +void OnConf_Gs(GtkMenuItem *menuitem, gpointer user_data); +void OnConf_Pads(GtkMenuItem *menuitem, gpointer user_data); +void OnConf_Cpu(GtkMenuItem *menuitem, gpointer user_data); +void OnConf_Conf(GtkMenuItem *menuitem, gpointer user_data); +typedef struct { + GtkWidget *Combo; + GList *PluginNameList; + char plist[255][255]; + int plugins; +} PluginConf; + +PluginConf GSConfS; +PluginConf PAD1ConfS; +PluginConf PAD2ConfS; +PluginConf SPU2ConfS; +PluginConf CDVDConfS; +PluginConf DEV9ConfS; +PluginConf USBConfS; +PluginConf FWConfS; +PluginConf BiosConfS; + +GtkWidget *ConfDlg; + +_PS2EgetLibType PS2EgetLibType = NULL; +_PS2EgetLibVersion2 PS2EgetLibVersion2 = NULL; +_PS2EgetLibName PS2EgetLibName = NULL; + +void SetActiveComboItem(GtkComboBox *widget,char plist[255][255], GList *list, char *conf); +void SetComboToGList(GtkComboBox *widget, GList *list); +static void ConfPlugin(PluginConf confs, char* plugin, const char* name); +static void TestPlugin(PluginConf confs, char* plugin, const char* name); + +#endif // __CONFIGDLG_H__ \ No newline at end of file diff --git a/pcsx2/Linux/DebugDlg.cpp b/pcsx2/Linux/DebugDlg.cpp new file mode 100644 index 0000000000..2f881fd56a --- /dev/null +++ b/pcsx2/Linux/DebugDlg.cpp @@ -0,0 +1,397 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "DebugDlg.h" +using namespace R5900; +//using namespace Dynarec; + +void UpdateDebugger() { + + char *str; + int i; + std::string output; + + DebugAdj->value = (gfloat)dPC/4; + gtk_list_store_clear(ListDVModel); + + for (i=0; i<23; i++) { + GtkTreeIter iter; + u32 *mem; + u32 pc = dPC + i*4; + if (DebugMode) { + mem = (u32*)PSXM(pc); + } + else + mem = (u32*)PSM(pc); + + if (mem == NULL) { + sprintf(nullAddr, "%8.8lX:\tNULL MEMORY", pc); + str = nullAddr; + } + else + { + std::string output; + + disR5900Fasm(output, *mem, pc); + output.copy( str, 256 ); + } + gtk_list_store_append(ListDVModel, &iter); + gtk_list_store_set(ListDVModel, &iter, 0, str, -1); + + } +} + +void OnDebug_Close(GtkButton *button, gpointer user_data) { + ClosePlugins(); + gtk_widget_destroy(DebugWnd); + gtk_main_quit(); + gtk_widget_set_sensitive(MainWindow, TRUE); +} + +void OnDebug_ScrollChange(GtkAdjustment *adj) { + dPC = (u32)adj->value*4; + dPC&= ~0x3; + + UpdateDebugger(); +} + +void OnSetPC_Ok(GtkButton *button, gpointer user_data) { + char *str = (char*)gtk_entry_get_text(GTK_ENTRY(SetPCEntry)); + + sscanf(str, "%lx", &dPC); + dPC&= ~0x3; + + gtk_widget_destroy(SetPCDlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); + UpdateDebugger(); +} + +void OnSetPC_Cancel(GtkButton *button, gpointer user_data) { + gtk_widget_destroy(SetPCDlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); +} + +void OnDebug_SetPC(GtkButton *button, gpointer user_data) { + SetPCDlg = create_SetPCDlg(); + + SetPCEntry = lookup_widget(SetPCDlg, "GtkEntry_dPC"); + + gtk_widget_show_all(SetPCDlg); + gtk_widget_set_sensitive(DebugWnd, FALSE); + gtk_main(); +} + +void OnSetBPA_Ok(GtkButton *button, gpointer user_data) { + char *str = (char*)gtk_entry_get_text(GTK_ENTRY(SetBPAEntry)); + + sscanf(str, "%lx", &dBPA); + dBPA&= ~0x3; + + gtk_widget_destroy(SetBPADlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); + UpdateDebugger(); +} + +void OnSetBPA_Cancel(GtkButton *button, gpointer user_data) { + gtk_widget_destroy(SetBPADlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); +} + +void OnDebug_SetBPA(GtkButton *button, gpointer user_data) { + SetBPADlg = create_SetBPADlg(); + + SetBPAEntry = lookup_widget(SetBPADlg, "GtkEntry_BPA"); + + gtk_widget_show_all(SetBPADlg); + gtk_widget_set_sensitive(DebugWnd, FALSE); + gtk_main(); +} + +void OnSetBPC_Ok(GtkButton *button, gpointer user_data) { + char *str = (char*)gtk_entry_get_text(GTK_ENTRY(SetBPCEntry)); + + sscanf(str, "%lx", &dBPC); + + gtk_widget_destroy(SetBPCDlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); + UpdateDebugger(); +} + +void OnSetBPC_Cancel(GtkButton *button, gpointer user_data) { + gtk_widget_destroy(SetBPCDlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); +} + +void OnDebug_SetBPC(GtkButton *button, gpointer user_data) { + SetBPCDlg = create_SetBPCDlg(); + + SetBPCEntry = lookup_widget(SetBPCDlg, "GtkEntry_BPC"); + + gtk_widget_show_all(SetBPCDlg); + gtk_widget_set_sensitive(DebugWnd, FALSE); + gtk_main(); +} + +void OnDebug_ClearBPs(GtkButton *button, gpointer user_data) { + dBPA = -1; + dBPC = -1; +} + +void OnDumpC_Ok(GtkButton *button, gpointer user_data) { + FILE *f; + char *str = (char*)gtk_entry_get_text(GTK_ENTRY(DumpCFEntry)); + u32 addrf, addrt; + + sscanf(str, "%lx", &addrf); addrf&=~0x3; + str = (char*)gtk_entry_get_text(GTK_ENTRY(DumpCTEntry)); + sscanf(str, "%lx", &addrt); addrt&=~0x3; + + f = fopen("dump.txt", "w"); + if (f == NULL) return; + + while (addrf != addrt) { + u32 *mem; + + if (DebugMode) { + mem = (u32*)PSXM(addrf); + } + else { + mem = (u32*)PSM(addrf); + } + + if (mem == NULL) { + sprintf(nullAddr, "%8.8lX:\tNULL MEMORY", addrf); + str = nullAddr; + } + else + { + std::string output; + + disR5900Fasm(output, *mem, addrf); + output.copy( str, 256 ); + } + + fprintf(f, "%s\n", str); + addrf+= 4; + } + + fclose(f); + gtk_widget_destroy(DumpCDlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); +} + +void OnDumpC_Cancel(GtkButton *button, gpointer user_data) { +gtk_widget_destroy(DumpCDlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); +} + +void OnDebug_DumpCode(GtkButton *button, gpointer user_data) { + DumpCDlg = create_DumpCDlg(); + + DumpCFEntry = lookup_widget(DumpCDlg, "GtkEntry_DumpCF"); + DumpCTEntry = lookup_widget(DumpCDlg, "GtkEntry_DumpCT"); + + gtk_widget_show_all(DumpCDlg); + gtk_widget_set_sensitive(DebugWnd, FALSE); + gtk_main(); +} + +void OnDumpR_Ok(GtkButton *button, gpointer user_data) { + FILE *f; + char *str = (char*)gtk_entry_get_text(GTK_ENTRY(DumpRFEntry)); + u32 addrf, addrt; + + sscanf(str, "%lx", &addrf); addrf&=~0x3; + str = (char*)gtk_entry_get_text(GTK_ENTRY(DumpRTEntry)); + sscanf(str, "%lx", &addrt); addrt&=~0x3; + + f = fopen("dump.txt", "w"); + if (f == NULL) return; + + while (addrf != addrt) { + u32 *mem; + u32 out; + + if (DebugMode) { + mem = (u32*)PSXM(addrf); + } else { + mem = (u32*)PSM(addrf); + } + if (mem == NULL) out = 0; + else out = *mem; + + fwrite(&out, 4, 1, f); + addrf+= 4; + } + + fclose(f); + gtk_widget_destroy(DumpRDlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); +} + +void OnDumpR_Cancel(GtkButton *button, gpointer user_data) { + gtk_widget_destroy(DumpRDlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); +} + +void OnDebug_RawDump(GtkButton *button, gpointer user_data) { + DumpRDlg = create_DumpRDlg(); + + DumpRFEntry = lookup_widget(DumpRDlg, "GtkEntry_DumpRF"); + DumpRTEntry = lookup_widget(DumpRDlg, "GtkEntry_DumpRT"); + + gtk_widget_show_all(DumpRDlg); + gtk_widget_set_sensitive(DebugWnd, FALSE); + gtk_main(); +} + +void OnDebug_Step(GtkButton *button, gpointer user_data) { + Cpu->Step(); + dPC = cpuRegs.pc; + UpdateDebugger(); +} + +void OnDebug_Skip(GtkButton *button, gpointer user_data) { + cpuRegs.pc+= 4; + dPC = cpuRegs.pc; + UpdateDebugger(); +} + +int HasBreakPoint(u32 pc) { + if (pc == dBPA) return 1; + if (DebugMode == 0) { + if ((cpuRegs.cycle - 10) <= dBPC && + (cpuRegs.cycle + 10) >= dBPC) return 1; + } else { + if ((psxRegs.cycle - 100) <= dBPC && + (psxRegs.cycle + 100) >= dBPC) return 1; + } + return 0; +} + +void OnDebug_Go(GtkButton *button, gpointer user_data) { + for (;;) { + if (HasBreakPoint(cpuRegs.pc)) break; + Cpu->Step(); + } + dPC = cpuRegs.pc; + UpdateDebugger(); +} + +void OnDebug_Log(GtkButton *button, gpointer user_data) { +#ifdef PCSX2_DEVBUILD + //Log = 1 - Log; +#endif +} + +void OnDebug_EEMode(GtkToggleButton *togglebutton, gpointer user_data) { + DebugMode = 0; + dPC = cpuRegs.pc; + UpdateDebugger(); +} + +void OnDebug_IOPMode(GtkToggleButton *togglebutton, gpointer user_data) { + DebugMode = 1; + dPC = psxRegs.pc; + UpdateDebugger(); +} + +void OnMemWrite32_Ok(GtkButton *button, gpointer user_data) { + char *mem = (char*)gtk_entry_get_text(GTK_ENTRY(MemEntry)); + char *data = (char*)gtk_entry_get_text(GTK_ENTRY(DataEntry)); + + printf("memWrite32: %s, %s\n", mem, data); + memWrite32(strtol(mem, (char**)NULL, 0), strtol(data, (char**)NULL, 0)); + gtk_widget_destroy(MemWriteDlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); +} + +void OnMemWrite32_Cancel(GtkButton *button, gpointer user_data) { + gtk_widget_destroy(MemWriteDlg); + gtk_main_quit(); + gtk_widget_set_sensitive(DebugWnd, TRUE); +} + +void OnDebug_memWrite32(GtkButton *button, gpointer user_data) { + MemWriteDlg = create_MemWrite32(); + + MemEntry = lookup_widget(MemWriteDlg, "GtkEntry_Mem"); + DataEntry = lookup_widget(MemWriteDlg, "GtkEntry_Data"); + + gtk_widget_show_all(MemWriteDlg); + gtk_widget_set_sensitive(DebugWnd, FALSE); + gtk_main(); + + UpdateDebugger(); +} + +void OnDebug_Debugger(GtkMenuItem *menuitem, gpointer user_data) { + GtkWidget *scroll; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + if (OpenPlugins(NULL) == -1) return; + + /*if (!efile) + efile=GetPS2ElfName(elfname); + if (efile) + loadElfFile(elfname); + efile=0;*/ + + dPC = cpuRegs.pc; + + DebugWnd = create_DebugWnd(); + + ListDVModel = gtk_list_store_new (1, G_TYPE_STRING); + ListDV = lookup_widget(DebugWnd, "GtkList_DisView"); + gtk_tree_view_set_model(GTK_TREE_VIEW(ListDV), GTK_TREE_MODEL(ListDVModel)); + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("heading", renderer, + "text", 0, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (ListDV), column); + scroll = lookup_widget(DebugWnd, "GtkVScrollbar_VList"); + + DebugAdj = GTK_RANGE(scroll)->adjustment; + DebugAdj->lower = (gfloat)0x00000000/4; + DebugAdj->upper = (gfloat)0xffffffff/4; + DebugAdj->step_increment = (gfloat)1; + DebugAdj->page_increment = (gfloat)20; + DebugAdj->page_size = (gfloat)23; + + gtk_signal_connect(GTK_OBJECT(DebugAdj), + "value_changed", GTK_SIGNAL_FUNC(OnDebug_ScrollChange), + NULL); + + UpdateDebugger(); + + gtk_widget_show_all(DebugWnd); + gtk_widget_set_sensitive(MainWindow, FALSE); + gtk_main(); +} \ No newline at end of file diff --git a/pcsx2/Linux/DebugDlg.h b/pcsx2/Linux/DebugDlg.h new file mode 100644 index 0000000000..fd8582c6ec --- /dev/null +++ b/pcsx2/Linux/DebugDlg.h @@ -0,0 +1,57 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + + #ifndef __DEBUGDLG_H__ +#define __DEBUGDLG_H__ + +#include "Linux.h" +#ifdef __cplusplus +extern "C" { +#endif + +#include "support.h" +#include "callbacks.h" +#include "interface.h" + +#ifdef __cplusplus +} +#endif +#include "R3000A.h" +#include "IopMem.h" + + +GtkWidget *ListDV; +GtkListStore *ListDVModel; +GtkWidget *SetPCDlg, *SetPCEntry; +GtkWidget *SetBPADlg, *SetBPAEntry; +GtkWidget *SetBPCDlg, *SetBPCEntry; +GtkWidget *DumpCDlg, *DumpCTEntry, *DumpCFEntry; +GtkWidget *DumpRDlg, *DumpRTEntry, *DumpRFEntry; +GtkWidget *MemWriteDlg, *MemEntry, *DataEntry; +GtkAdjustment *DebugAdj; + +extern int efile; +extern char elfname[g_MaxPath]; + +int DebugMode; // 0 - EE | 1 - IOP +static u32 dPC, dBPA = -1, dBPC = -1; +static char nullAddr[g_MaxPath]; + +GtkWidget *DebugWnd; + +#endif // __DEBUGDLG_H__ diff --git a/pcsx2/Linux/GtkGui.cpp b/pcsx2/Linux/GtkGui.cpp new file mode 100644 index 0000000000..3dbd118939 --- /dev/null +++ b/pcsx2/Linux/GtkGui.cpp @@ -0,0 +1,987 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "GtkGui.h" + +using namespace R5900; + +void On_Dialog_Cancelled(GtkButton* button, gpointer user_data) { + gtk_widget_destroy((GtkWidget*)gtk_widget_get_toplevel ((GtkWidget*)button)); + gtk_widget_set_sensitive(MainWindow, TRUE); + gtk_main_quit(); +} + +void StartGui() { + GtkWidget *Menu; + GtkWidget *Item; + + u32 i; + + add_pixmap_directory(".pixmaps"); + MainWindow = create_MainWindow(); + + if (SVN_REV != 0) + gtk_window_set_title(GTK_WINDOW(MainWindow), "PCSX2 "PCSX2_VERSION" "SVN_REV" Playground"); + else + gtk_window_set_title(GTK_WINDOW(MainWindow), "PCSX2 "PCSX2_VERSION" Playground"); + + // status bar + pStatusBar = gtk_statusbar_new (); + gtk_box_pack_start (GTK_BOX(lookup_widget(MainWindow, "status_box")), pStatusBar, TRUE, TRUE, 0); + gtk_widget_show (pStatusBar); + + gtk_statusbar_push(GTK_STATUSBAR(pStatusBar),0, + "F1 - save, F2 - next state, Shift+F2 - prev state, F3 - load, F8 - snapshot"); + + // add all the languages + Item = lookup_widget(MainWindow, "GtkMenuItem_Language"); + Menu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(Item), Menu); + + for (i=0; i < langsMax; i++) { + Item = gtk_check_menu_item_new_with_label(ParseLang(langs[i].lang)); + gtk_widget_show(Item); + gtk_container_add(GTK_CONTAINER(Menu), Item); + gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM(Item), TRUE); + if (!strcmp(Config.Lang, langs[i].lang)) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(Item), TRUE); + + gtk_signal_connect(GTK_OBJECT(Item), "activate", + GTK_SIGNAL_FUNC(OnLanguage), + (gpointer)(uptr)i); + } + + // check the appropriate menu items + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(lookup_widget(MainWindow, "enable_console1")), Config.PsxOut); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(lookup_widget(MainWindow, "enable_patches1")), Config.Patch); + + // disable anything not implemented or not working properly. + gtk_widget_set_sensitive(GTK_WIDGET(lookup_widget(MainWindow, "patch_browser1")), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(lookup_widget(MainWindow, "patch_finder2")), FALSE); + #ifndef PCSX2_DEVBUILD + gtk_widget_set_sensitive(GTK_WIDGET(lookup_widget(MainWindow, "GtkMenuItem_Logging")), FALSE); + #endif + + gtk_widget_show_all(MainWindow); + gtk_window_activate_focus(GTK_WINDOW(MainWindow)); + gtk_main(); +} + +void RunGui() { + StartGui(); +} + +void FixCPUState(void) +{ + //Config.sseMXCSR = LinuxsseMXCSR; + //Config.sseVUMXCSR = LinuxsseVUMXCSR; + SetCPUState(Config.sseMXCSR, Config.sseVUMXCSR); +} + +void OnDestroy(GtkObject *object, gpointer user_data) {} + +gboolean OnDelete(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + pcsx2_exit(); + return (FALSE); +} +int Pcsx2Configure() { + if (!UseGui) return 0; + + configuringplug = TRUE; + MainWindow = NULL; + OnConf_Conf(NULL, 0); + configuringplug = FALSE; + + return applychanges; +} + +void OnLanguage(GtkMenuItem *menuitem, gpointer user_data) { + ChangeLanguage(langs[(int)(uptr)user_data].lang); + gtk_widget_destroy(MainWindow); + gtk_main_quit(); + while (gtk_events_pending()) gtk_main_iteration(); + StartGui(); +} + +void SignalExit(int sig) { + ClosePlugins(); + pcsx2_exit(); +} + +void ExecuteCpu() +{ + // Make sure any left-over recovery states are cleaned up. + safe_delete( g_RecoveryState ); + + // Destroy the window. Ugly thing. + gtk_widget_destroy(MainWindow); + gtk_main_quit(); + while (gtk_events_pending()) gtk_main_iteration(); + + g_GameInProgress = true; + m_ReturnToGame = false; + + signal(SIGINT, SignalExit); + signal(SIGPIPE, SignalExit); + + // Make sure any left-over recovery states are cleaned up. + safe_delete( g_RecoveryState ); + + // Just in case they weren't initialized earlier (no harm in calling this multiple times) + if (OpenPlugins(NULL) == -1) return; + + // this needs to be called for every new game! (note: sometimes launching games through bios will give a crc of 0) + if( GSsetGameCRC != NULL ) GSsetGameCRC(ElfCRC, g_ZeroGSOptions); + + // Optimization: We hardcode two versions of the EE here -- one for recs and one for ints. + // This is because recs are performance critical, and being able to inline them into the + // function here helps a small bit (not much but every small bit counts!). + + g_EmulationInProgress = true; + g_ReturnToGui = false; + + PCSX2_MEM_PROTECT_BEGIN(); + + if( CHECK_EEREC ) + { + while( !g_ReturnToGui ) + { + recExecute(); + SysUpdate(); + } + } + else + { + while( !g_ReturnToGui ) + { + Cpu->Execute(); + SysUpdate(); + } + } + PCSX2_MEM_PROTECT_END(); +} + +void RunExecute( const char* elf_file, bool use_bios ) +{ + + // (air notes:) + // If you want to use the new to-memory savestate feature, take a look at the new + // RunExecute in WinMain.c, and secondly the CpuDlg.c or AdvancedDlg.cpp. The + // objects used are MemoryAlloc, memLoadingState, and memSavingState. + + // It's important to make sure to reset the CPU and the plugins correctly, which is + // where the new RunExecute comes into play. It can be kind of tricky knowing + // when to call cpuExecuteBios and loadElfFile, and with what parameters. + + // (or, as an alternative maybe we should switch to wxWidgets and have a unified + // cross platform gui?) - Air + + try + { + cpuReset(); + } + + catch( std::exception& ex ) + { + Msgbox::Alert( ex.what() ); + return; + } + + if (OpenPlugins(NULL) == -1) + { + RunGui(); + return; + } + + if (elf_file == NULL ) + { + if (g_RecoveryState != NULL) + { + try + { + memLoadingState( *g_RecoveryState ).FreezeAll(); + } + catch( std::runtime_error& ex ) + { + Msgbox::Alert( + "Gamestate recovery failed. Your game progress will be lost (sorry!)\n" + "\nError: %s\n", params ex.what() ); + + // Take the user back to the GUI... + safe_delete( g_RecoveryState ); + ClosePlugins(); + return; + } + safe_delete( g_RecoveryState ); + } + else + { + // Not recovering a state, so need to execute the bios and load the ELF information. + + // if the elf_file is null we use the CDVD elf file. + // But if the elf_file is an empty string then we boot the bios instead. + + char ename[g_MaxPath]; + ename[0] = 0; + if( !use_bios ) + GetPS2ElfName( ename ); + + loadElfFile( ename ); + } + } + else + { + // Custom ELF specified (not using CDVD). + // Run the BIOS and load the ELF. + + loadElfFile( elf_file ); + } + + FixCPUState(); + + ExecuteCpu(); +} + +void OnFile_RunCD(GtkMenuItem *menuitem, gpointer user_data) { + safe_free( g_RecoveryState ); + ResetPlugins(); + RunExecute( NULL ); +} + +void OnRunElf_Ok(GtkButton* button, gpointer user_data) { + gchar *File; + + File = (gchar*)gtk_file_selection_get_filename(GTK_FILE_SELECTION(FileSel)); + strcpy(elfname, File); + gtk_widget_destroy(FileSel); + + RunExecute(elfname); +} + +void OnRunElf_Cancel(GtkButton* button, gpointer user_data) { + gtk_widget_destroy(FileSel); +} + +void OnFile_LoadElf(GtkMenuItem *menuitem, gpointer user_data) { + GtkWidget *Ok,*Cancel; + + FileSel = gtk_file_selection_new("Select Psx Elf File"); + + Ok = GTK_FILE_SELECTION(FileSel)->ok_button; + gtk_signal_connect (GTK_OBJECT(Ok), "clicked", GTK_SIGNAL_FUNC(OnRunElf_Ok), NULL); + gtk_widget_show(Ok); + + Cancel = GTK_FILE_SELECTION(FileSel)->cancel_button; + gtk_signal_connect (GTK_OBJECT(Cancel), "clicked", GTK_SIGNAL_FUNC(OnRunElf_Cancel), NULL); + gtk_widget_show(Cancel); + + gtk_widget_show(FileSel); + gdk_window_raise(FileSel->window); +} +void pcsx2_exit() +{ + DIR *dir; + struct dirent *ent; + void *Handle; + char plugin[g_MaxPath]; + + // with this the problem with plugins that are linked with the pthread + // library is solved + + dir = opendir(Config.PluginsDir); + if (dir != NULL) { + while ((ent = readdir(dir)) != NULL) { + sprintf (plugin, "%s%s", Config.PluginsDir, ent->d_name); + + if (strstr(plugin, ".so") == NULL) continue; + Handle = dlopen(plugin, RTLD_NOW); + if (Handle == NULL) continue; + } + } + + printf("PCSX2 Quitting\n"); + + if (UseGui) + { + gtk_main_quit(); + SysClose(); + gtk_exit(0); + } + else + { + SysClose(); + exit(0); + } +} +void OnFile_Exit(GtkMenuItem *menuitem, gpointer user_data) +{ + pcsx2_exit(); +} + +void OnEmu_Run(GtkMenuItem *menuitem, gpointer user_data) +{ + if( g_EmulationInProgress ) + ExecuteCpu(); + else + RunExecute( NULL, true ); // boots bios if no savestate is to be recovered + +} + +void OnEmu_Reset(GtkMenuItem *menuitem, gpointer user_data) +{ + SysReset(); +} + + + void ResetMenuSlots(GtkMenuItem *menuitem, gpointer user_data) { + GtkWidget *Item; + char str[g_MaxPath]; + int i; + + for (i=0; i<5; i++) { + sprintf(str, "GtkMenuItem_LoadSlot%d", i+1); + Item = lookup_widget(MainWindow, str); + if (Slots[i] == -1) + gtk_widget_set_sensitive(Item, FALSE); + else + gtk_widget_set_sensitive(Item, TRUE); + } + } + +/*void UpdateMenuSlots(GtkMenuItem *menuitem, gpointer user_data) { + char str[g_MaxPath]; + int i = 0; + + for (i=0; i<5; i++) { + sprintf(str, SSTATES_DIR "/%8.8X.%3.3d", ElfCRC, i); + Slots[i] = CheckState(str); + } +}*/ + +void States_Load(string file, int num = -1 ) +{ + efile = 2; + try + { + // when we init joe it'll throw an UnsupportedStateVersion. + // So reset the cpu afterward so that trying to load a bum save + // doesn't fry the current emulation state. + gzLoadingState joe( file ); + + // Make sure the cpu and plugins are ready to be state-ified! + cpuReset(); + OpenPlugins( NULL ); + + joe.FreezeAll(); + } + catch( Exception::UnsupportedStateVersion& ) + { + if( num != -1 ) + Msgbox::Alert("Savestate slot %d is an unsupported version." , params num); + else + Msgbox::Alert( "%s : This is an unsupported savestate version." , params file.c_str()); + + // At this point the cpu hasn't been reset, so we can return + // control to the user safely... + + return; + } + catch( std::exception& ex ) + { + if (num != -1) + Console::Error("Error occured while trying to load savestate slot %d", params num); + else + Console::Error("Error occured while trying to load savestate file: %d", params file.c_str()); + + Console::Error( "%s", params ex.what() ); + + // The emulation state is ruined. Might as well give them a popup and start the gui. + + Msgbox::Alert( + "An error occured while trying to load the savestate data.\n" + "Pcsx2 emulation state has been reset." + ); + + cpuShutdown(); + return; + } + + ExecuteCpu(); +} + +void States_Load(int num) { + string Text; + + SaveState::GetFilename( Text, num ); + + struct stat buf; + if( stat(Text.c_str(), &buf ) == -1 ) + { + Console::Notice( "Saveslot %d is empty.", params num ); + return; + } + States_Load( Text, num ); +} + +void States_Save( string file, int num = -1 ) +{ + try + { + gzSavingState(file).FreezeAll(); + if( num != -1 ) + Console::Notice("State saved to slot %d", params num ); + else + Console::Notice( "State saved to file: %s", params file.c_str() ); + } + catch( std::exception& ex ) + { + if( num != -1 ) + Msgbox::Alert("An error occurred while trying to save to slot %d", params num ); + else + Msgbox::Alert("An error occurred while trying to save to file: %s", params file.c_str() ); + + Console::Error("Save state request failed with the following error:" ); + Console::Error( "%s", params ex.what() ); + } +} + +void States_Save(int num) { + string Text; + + SaveState::GetFilename( Text, num ); + States_Save( Text, num ); +} + +void OnStates_Load1(GtkMenuItem *menuitem, gpointer user_data) { States_Load(0); } +void OnStates_Load2(GtkMenuItem *menuitem, gpointer user_data) { States_Load(1); } +void OnStates_Load3(GtkMenuItem *menuitem, gpointer user_data) { States_Load(2); } +void OnStates_Load4(GtkMenuItem *menuitem, gpointer user_data) { States_Load(3); } +void OnStates_Load5(GtkMenuItem *menuitem, gpointer user_data) { States_Load(4); } + +void OnLoadOther_Ok(GtkButton* button, gpointer user_data) { + gchar *File; + char str[g_MaxPath]; + + File = (gchar*)gtk_file_selection_get_filename(GTK_FILE_SELECTION(FileSel)); + strcpy(str, File); + gtk_widget_destroy(FileSel); + + States_Load( str ); +} + +void OnLoadOther_Cancel(GtkButton* button, gpointer user_data) { + gtk_widget_destroy(FileSel); +} + +void OnStates_LoadOther(GtkMenuItem *menuitem, gpointer user_data) { + GtkWidget *Ok,*Cancel; + + FileSel = gtk_file_selection_new(_("Select State File")); + gtk_file_selection_set_filename(GTK_FILE_SELECTION(FileSel), SSTATES_DIR "/"); + + Ok = GTK_FILE_SELECTION(FileSel)->ok_button; + gtk_signal_connect (GTK_OBJECT(Ok), "clicked", GTK_SIGNAL_FUNC(OnLoadOther_Ok), NULL); + gtk_widget_show(Ok); + + Cancel = GTK_FILE_SELECTION(FileSel)->cancel_button; + gtk_signal_connect (GTK_OBJECT(Cancel), "clicked", GTK_SIGNAL_FUNC(OnLoadOther_Cancel), NULL); + gtk_widget_show(Cancel); + + gtk_widget_show(FileSel); + gdk_window_raise(FileSel->window); +} + +void OnStates_Save1(GtkMenuItem *menuitem, gpointer user_data) { States_Save(0); } +void OnStates_Save2(GtkMenuItem *menuitem, gpointer user_data) { States_Save(1); } +void OnStates_Save3(GtkMenuItem *menuitem, gpointer user_data) { States_Save(2); } +void OnStates_Save4(GtkMenuItem *menuitem, gpointer user_data) { States_Save(3); } +void OnStates_Save5(GtkMenuItem *menuitem, gpointer user_data) { States_Save(4); } + +void OnSaveOther_Ok(GtkButton* button, gpointer user_data) { + gchar *File; + char str[g_MaxPath]; + + File = (gchar*)gtk_file_selection_get_filename(GTK_FILE_SELECTION(FileSel)); + strcpy(str, File); + gtk_widget_destroy(FileSel); + + States_Save( str ); +} + +void OnSaveOther_Cancel(GtkButton* button, gpointer user_data) { + gtk_widget_destroy(FileSel); +} + +void OnStates_SaveOther(GtkMenuItem *menuitem, gpointer user_data) { + GtkWidget *Ok,*Cancel; + + FileSel = gtk_file_selection_new(_("Select State File")); + gtk_file_selection_set_filename(GTK_FILE_SELECTION(FileSel), SSTATES_DIR "/"); + + Ok = GTK_FILE_SELECTION(FileSel)->ok_button; + gtk_signal_connect (GTK_OBJECT(Ok), "clicked", GTK_SIGNAL_FUNC(OnSaveOther_Ok), NULL); + gtk_widget_show(Ok); + + Cancel = GTK_FILE_SELECTION(FileSel)->cancel_button; + gtk_signal_connect (GTK_OBJECT(Cancel), "clicked", GTK_SIGNAL_FUNC(OnSaveOther_Cancel), NULL); + gtk_widget_show(Cancel); + + gtk_widget_show(FileSel); + gdk_window_raise(FileSel->window); +} + +//2002-09-28 (Florin) +void OnArguments_Ok(GtkButton *button, gpointer user_data) { + char *str; + + str = (char*)gtk_entry_get_text(GTK_ENTRY(widgetCmdLine)); + memcpy(args, str, g_MaxPath); + + gtk_widget_destroy(CmdLine); + gtk_widget_set_sensitive(MainWindow, TRUE); + gtk_main_quit(); +} + +void OnEmu_Arguments(GtkMenuItem *menuitem, gpointer user_data) { + GtkWidget *widgetCmdLine; + + CmdLine = create_CmdLine(); + gtk_window_set_title(GTK_WINDOW(CmdLine), _("Program arguments")); + + widgetCmdLine = lookup_widget(CmdLine, "GtkEntry_dCMDLINE"); + + gtk_entry_set_text(GTK_ENTRY(widgetCmdLine), args); + gtk_widget_show_all(CmdLine); + gtk_widget_set_sensitive(MainWindow, FALSE); + gtk_main(); +} + +void OnCpu_Ok(GtkButton *button, gpointer user_data) { + u32 newopts = 0; + + //Cpu->Shutdown(); + //vu0Shutdown(); + //vu1Shutdown(); + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkCheckButton_EERec")))) + newopts |= PCSX2_EEREC; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkCheckButton_VU0rec")))) + newopts |= PCSX2_VU0REC; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkCheckButton_VU1rec")))) + newopts |= PCSX2_VU1REC; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkCheckButton_MTGS")))) + newopts |= PCSX2_GSMULTITHREAD; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_LimitNormal")))) + newopts |= PCSX2_FRAMELIMIT_NORMAL; + else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_LimitLimit")))) + newopts |= PCSX2_FRAMELIMIT_LIMIT; + else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_LimitFS")))) + newopts |= PCSX2_FRAMELIMIT_SKIP; + else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_VUSkip")))) + newopts |= PCSX2_FRAMELIMIT_VUSKIP; + + Config.CustomFps = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(lookup_widget(CpuDlg, "CustomFPSLimit"))); + Config.CustomFrameSkip = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(lookup_widget(CpuDlg, "FrameThreshold"))); + Config.CustomConsecutiveFrames = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(lookup_widget(CpuDlg, "FramesBeforeSkipping"))); + Config.CustomConsecutiveSkip = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(lookup_widget(CpuDlg, "FramesToSkip"))); + + if (Config.Options != newopts) + { + SysRestorableReset(); + + if( (Config.Options&PCSX2_GSMULTITHREAD) ^ (newopts&PCSX2_GSMULTITHREAD) ) + { + // gotta shut down *all* the plugins. + ResetPlugins(); + } + Config.Options = newopts; + } + else + UpdateVSyncRate(); + + SaveConfig(); + + gtk_widget_destroy(CpuDlg); + if (MainWindow) gtk_widget_set_sensitive(MainWindow, TRUE); + gtk_main_quit(); +} + +void OnConf_Cpu(GtkMenuItem *menuitem, gpointer user_data) +{ + char str[512]; + + CpuDlg = create_CpuDlg(); + gtk_window_set_title(GTK_WINDOW(CpuDlg), _("Configuration")); + + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkCheckButton_EERec")), !!CHECK_EEREC); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkCheckButton_VU0rec")), !!CHECK_VU0REC); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkCheckButton_VU1rec")), !!CHECK_VU1REC); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkCheckButton_MTGS")), !!CHECK_MULTIGS); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_LimitNormal")), CHECK_FRAMELIMIT==PCSX2_FRAMELIMIT_NORMAL); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_LimitLimit")), CHECK_FRAMELIMIT==PCSX2_FRAMELIMIT_LIMIT); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_LimitFS")), CHECK_FRAMELIMIT==PCSX2_FRAMELIMIT_SKIP); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_VUSkip")), CHECK_FRAMELIMIT==PCSX2_FRAMELIMIT_VUSKIP); + + sprintf(str, "Cpu Vendor: %s", cpuinfo.x86ID); + gtk_label_set_text(GTK_LABEL(lookup_widget(CpuDlg, "GtkLabel_CpuVendor")), str); + sprintf(str, "Familly: %s", cpuinfo.x86Fam); + gtk_label_set_text(GTK_LABEL(lookup_widget(CpuDlg, "GtkLabel_Family")), str); + sprintf(str, "Cpu Speed: %d MHZ", cpuinfo.cpuspeed); + gtk_label_set_text(GTK_LABEL(lookup_widget(CpuDlg, "GtkLabel_CpuSpeed")), str); + + strcpy(str,"Features: "); + if(cpucaps.hasMultimediaExtensions) strcat(str,"MMX"); + if(cpucaps.hasStreamingSIMDExtensions) strcat(str,",SSE"); + if(cpucaps.hasStreamingSIMD2Extensions) strcat(str,",SSE2"); + if(cpucaps.hasStreamingSIMD3Extensions) strcat(str,",SSE3"); + if(cpucaps.hasAMD64BitArchitecture) strcat(str,",x86-64"); + gtk_label_set_text(GTK_LABEL(lookup_widget(CpuDlg, "GtkLabel_Features")), str); + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(lookup_widget(CpuDlg, "CustomFPSLimit")), (gdouble)Config.CustomFps); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(lookup_widget(CpuDlg, "FrameThreshold")), (gdouble)Config.CustomFrameSkip); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(lookup_widget(CpuDlg, "FramesBeforeSkipping")), (gdouble)Config.CustomConsecutiveFrames); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(lookup_widget(CpuDlg, "FramesToSkip")), (gdouble)Config.CustomConsecutiveSkip); + + gtk_widget_show_all(CpuDlg); + if (MainWindow) gtk_widget_set_sensitive(MainWindow, FALSE); + gtk_main(); +} + +void OnLogging_Ok(GtkButton *button, gpointer user_data) { +#ifdef PCSX2_DEVBUILD + GtkWidget *Btn; + char str[32]; + int i, ret; + + + for (i=0; i<32; i++) { + if (((i > 16) && (i < 20)) || (i == 29)) + continue; + + sprintf(str, "Log%d", i); + Btn = lookup_widget(LogDlg, str); + ret = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(Btn)); + if (ret) varLog|= 1< 16) && (i < 20)) || (i == 29)) + continue; + + sprintf(str, "Log%d", i); + Btn = lookup_widget(LogDlg, str); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(Btn), varLog & (1<> 13) + { + case 0: + set_checked(AdvDlg, "radio_EE_Round_Near", TRUE); + break; + case 1: + set_checked(AdvDlg, "radio_EE_Round_Negative",TRUE); + break; + case 2: + set_checked(AdvDlg, "radio_EE_Round_Positive", TRUE); + break; + case 3: + set_checked(AdvDlg, "radio_EE_Round_Zero", TRUE); + break; + } + + switch((Config.sseVUMXCSR & 0x6000) >> 13) + { + case 0: + set_checked(AdvDlg, "radio_VU_Round_Near", TRUE); + break; + case 1: + set_checked(AdvDlg, "radio_VU_Round_Negative",TRUE); + break; + case 2: + set_checked(AdvDlg, "radio_VU_Round_Positive", TRUE); + break; + case 3: + set_checked(AdvDlg, "radio_VU_Round_Zero", TRUE); + break; + } + + + switch(Config.eeOptions) + { + case FLAG_EE_CLAMP_NONE: + set_checked(AdvDlg, "radio_EE_Clamp_None", TRUE); + break; + case FLAG_EE_CLAMP_NORMAL: + set_checked(AdvDlg, "radio_EE_Clamp_Normal",TRUE); + break; + case FLAG_EE_CLAMP_EXTRA_PRESERVE: + set_checked(AdvDlg, "radio_EE_Clamp_Extra_Preserve", TRUE); + break; + } + + switch(Config.vuOptions) + { + case FLAG_VU_CLAMP_NONE: + set_checked(AdvDlg, "radio_VU_Clamp_None", TRUE); + break; + case FLAG_VU_CLAMP_NORMAL: + set_checked(AdvDlg, "radio_VU_Clamp_Normal",TRUE); + break; + case FLAG_VU_CLAMP_EXTRA: + set_checked(AdvDlg, "radio_VU_Clamp_Extra", TRUE); + break; + case FLAG_VU_CLAMP_EXTRA_PRESERVE: + set_checked(AdvDlg, "radio_VU_Clamp_Extra_Preserve", TRUE); + break; + } + set_checked(AdvDlg, "check_EE_Flush_Zero", (Config.sseMXCSR & FLAG_FLUSH_ZERO) ? TRUE : FALSE); + set_checked(AdvDlg, "check_EE_Denormal_Zero", (Config.sseMXCSR & FLAG_DENORMAL_ZERO) ? TRUE : FALSE); + + set_checked(AdvDlg, "check_VU_Flush_Zero", (Config.sseVUMXCSR & FLAG_FLUSH_ZERO) ? TRUE : FALSE); + set_checked(AdvDlg, "check_VU_Denormal_Zero", (Config.sseVUMXCSR & FLAG_DENORMAL_ZERO) ? TRUE : FALSE); +} +void on_Advanced(GtkMenuItem *menuitem, gpointer user_data) +{ + AdvDlg = create_AdvDlg(); + + setAdvancedOptions(); + + gtk_widget_show_all(AdvDlg); + gtk_widget_set_sensitive(MainWindow, FALSE); + gtk_main(); + } + +void on_Advanced_Defaults(GtkButton *button, gpointer user_data) +{ + Config.sseMXCSR = DEFAULT_sseMXCSR; + Config.sseVUMXCSR = DEFAULT_sseVUMXCSR; + Config.eeOptions = DEFAULT_eeOptions; + Config.vuOptions = DEFAULT_vuOptions; + + setAdvancedOptions(); + } + +void on_Advanced_OK(GtkButton *button, gpointer user_data) +{ + Config.sseMXCSR &= 0x1fbf; + Config.sseVUMXCSR &= 0x1fbf; + Config.eeOptions = 0; + Config.vuOptions = 0; + + Config.sseMXCSR |= is_checked(AdvDlg, "radio_EE_Round_Near") ? FLAG_ROUND_NEAR : 0; + Config.sseMXCSR |= is_checked(AdvDlg, "radio_EE_Round_Negative") ? FLAG_ROUND_NEGATIVE : 0; + Config.sseMXCSR |= is_checked(AdvDlg, "radio_EE_Round_Positive") ? FLAG_ROUND_POSITIVE : 0; + Config.sseMXCSR |= is_checked(AdvDlg, "radio_EE_Round_Zero") ? FLAG_ROUND_ZERO : 0; + + Config.sseMXCSR |= is_checked(AdvDlg, "check_EE_Denormal_Zero") ? FLAG_DENORMAL_ZERO : 0; + Config.sseMXCSR |= is_checked(AdvDlg, "check_EE_Flush_Zero") ? FLAG_FLUSH_ZERO : 0; + + Config.sseVUMXCSR |= is_checked(AdvDlg, "radio_VU_Round_Near") ? FLAG_ROUND_NEAR : 0; + Config.sseVUMXCSR |= is_checked(AdvDlg, "radio_VU_Round_Negative") ? FLAG_ROUND_NEGATIVE : 0; + Config.sseVUMXCSR |= is_checked(AdvDlg, "radio_VU_Round_Positive") ? FLAG_ROUND_POSITIVE : 0; + Config.sseVUMXCSR |= is_checked(AdvDlg, "radio_VU_Round_Zero") ? FLAG_ROUND_ZERO : 0; + + Config.sseVUMXCSR |= is_checked(AdvDlg, "check_VU_Denormal_Zero") ? FLAG_DENORMAL_ZERO : 0; + Config.sseVUMXCSR |= is_checked(AdvDlg, "check_VU_Flush_Zero") ? FLAG_FLUSH_ZERO : 0; + + Config.eeOptions |= is_checked(AdvDlg, "radio_EE_Clamp_None") ? FLAG_EE_CLAMP_NONE : 0; + Config.eeOptions |= is_checked(AdvDlg, "radio_EE_Clamp_Normal") ? FLAG_EE_CLAMP_NORMAL : 0; + Config.eeOptions |= is_checked(AdvDlg, "radio_EE_Clamp_Extra_Preserve") ? FLAG_EE_CLAMP_EXTRA_PRESERVE : 0; + + Config.vuOptions |= is_checked(AdvDlg, "radio_VU_Clamp_None") ? FLAG_VU_CLAMP_NONE : 0; + Config.vuOptions |= is_checked(AdvDlg, "radio_VU_Clamp_Normal") ? FLAG_VU_CLAMP_NORMAL : 0; + Config.vuOptions |= is_checked(AdvDlg, "radio_VU_Clamp_Extra") ? FLAG_VU_CLAMP_EXTRA : 0; + Config.vuOptions |= is_checked(AdvDlg, "radio_VU_Clamp_Extra_Preserve") ? FLAG_VU_CLAMP_EXTRA_PRESERVE : 0; + + SetCPUState(Config.sseMXCSR, Config.sseVUMXCSR); + SaveConfig(); + + gtk_widget_destroy(AdvDlg); + gtk_widget_set_sensitive(MainWindow, TRUE); + gtk_main_quit(); +} diff --git a/pcsx2/Linux/GtkGui.h b/pcsx2/Linux/GtkGui.h new file mode 100644 index 0000000000..395a3867ac --- /dev/null +++ b/pcsx2/Linux/GtkGui.h @@ -0,0 +1,83 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + + #ifndef __GTKGUI_H__ +#define __GTKGUI_H__ + + +#include "PrecompiledHeader.h" +#include "Linux.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "support.h" +#include "callbacks.h" +#include "interface.h" + +#ifdef __cplusplus +} +#endif + +bool applychanges = FALSE; +bool configuringplug = FALSE; +bool UseGui = TRUE; + + MemoryAlloc* g_RecoveryState = NULL; +bool g_GameInProgress = false; // Set TRUE if a game is actively running. + +//static bool AccBreak = false; +static bool m_ReturnToGame = false; // set to exit the RunGui message pump + + +int efile = 0; +char elfname[g_MaxPath]; +int Slots[5] = { -1, -1, -1, -1, -1 }; + +GtkWidget *CpuDlg; + +// Functions Callbacks +void OnFile_LoadElf(GtkMenuItem *menuitem, gpointer user_data); +void OnFile_Exit(GtkMenuItem *menuitem, gpointer user_data); +void OnEmu_Run(GtkMenuItem *menuitem, gpointer user_data); +void OnEmu_Reset(GtkMenuItem *menuitem, gpointer user_data); +void OnEmu_Arguments(GtkMenuItem *menuitem, gpointer user_data); +void OnLanguage(GtkMenuItem *menuitem, gpointer user_data); +void OnHelp_Help(); +void OnHelp_About(GtkMenuItem *menuitem, gpointer user_data); +void ExecuteCpu(); + +void StartGui(); +void pcsx2_exit(); +GtkWidget *MainWindow; +GtkWidget *pStatusBar = NULL, *Status_Box; +GtkWidget *CmdLine; //2002-09-28 (Florin) +GtkWidget *widgetCmdLine; +GtkWidget *LogDlg; +GtkWidget *FileSel; +GtkWidget *AboutDlg, *about_version , *about_authors, *about_greets; + +void init_widgets(); + +GtkAccelGroup *AccelGroup; + +GtkWidget *GameFixDlg, *SpeedHacksDlg, *AdvDlg; + +#endif + diff --git a/pcsx2/Linux/Linux.h b/pcsx2/Linux/Linux.h new file mode 100644 index 0000000000..a8b11759d3 --- /dev/null +++ b/pcsx2/Linux/Linux.h @@ -0,0 +1,218 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __LINUX_H__ +#define __LINUX_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "PrecompiledHeader.h" +#include "Paths.h" +#include "Common.h" + +#include "Counters.h" +#include "VUmicro.h" +#include "Plugins.h" +#include "x86/ix86/ix86.h" +#include "x86/iR5900.h" + +/* Misc.c */ +extern void vu0Shutdown(); +extern void vu1Shutdown(); +extern void SaveConfig(); + +extern bool UseGui; + +extern int efile; +extern int g_SaveGSStream; +extern int g_ZeroGSOptions; +extern void FixCPUState(void); + +/* LnxMain */ +extern void InitLanguages(); +extern char *GetLanguageNext(); +extern void CloseLanguages(); +extern void ChangeLanguage(char *lang); +extern void StartGui(); +extern void RunGui(); +extern int Pcsx2Configure(); +extern GtkWidget *CpuDlg; +extern void SysMessage(const char *fmt, ...); + +/* Config.c */ +extern int LoadConfig(); +extern void SaveConfig(); + +/* GtkGui */ +extern void init_widgets(); +extern MemoryAlloc* g_RecoveryState; +extern void SysRestorableReset(); +extern void SysDetect(); +extern void RunExecute( const char* elf_file, bool use_bios = false); +extern void ExecuteCpu(); + +extern bool g_ReturnToGui; // set to exit the execution of the emulator and return control to the GUI +extern bool g_EmulationInProgress; // Set TRUE if a game is actively running (set to false on reset) + +typedef struct { + char lang[g_MaxPath]; +} _langs; + +typedef enum +{ + GS, + PAD1, + PAD2, + SPU, + CDVD, + DEV9, + USB, + FW, + BIOS +} plugin_types; + +extern GtkWidget *MainWindow; +extern bool applychanges; +extern bool configuringplug; + +GtkWidget *check_eerec, *check_vu0rec, *check_vu1rec; +GtkWidget *check_mtgs , *check_cpu_dc; +GtkWidget *check_console , *check_patches; +GtkWidget *radio_normal_limit, *radio_limit_limit, *radio_fs_limit, *radio_vuskip_limit; + +_langs *langs; +unsigned int langsMax; + +char cfgfile[g_MaxPath]; + +/* Hacks */ + +int Config_hacks_backup; + +#define is_checked(main_widget, widget_name) (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(main_widget, widget_name)))) +#define set_checked(main_widget,widget_name, state) gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(main_widget, widget_name)), state) + +#define set_flag(v, flag, value) if (value == TRUE) v |= flag; else v &= flag; +#define get_flag(v,flag) ((v & (1 << flag)) != 0) + +/*static __forceinline void print_flags(char *name, u32 num, char *flag_names[16]) +{ + int i; + + DevCon::WriteLn("%s:", name); + + if (flag_names != NULL) + { + for(i=0; i<=15; i++) + DevCon::WriteLn("%s %x: %x", params flag_names[i], (1< +#include + +#include "LnxMain.h" + +using namespace R5900; + +DIR *dir; + +#ifdef PCSX2_DEVBUILD +TESTRUNARGS g_TestRun; +#endif + +GtkWidget *MsgDlg; + +static int sinit=0; + +// These two status vars replace the old g_GameInProgress status var. + +bool g_ReturnToGui = false; // set to exit the execution of the emulator and return control to the GUI +bool g_EmulationInProgress = false; // Set TRUE if a game is actively running (set to false on reset) + +int main(int argc, char *argv[]) { + char *file = NULL; + char elfname[g_MaxPath]; + int i = 1; + + efile = 0; +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, "Langs"); + textdomain(PACKAGE); +#endif + + printf("\n"); + mkdir(CONFIG_DIR, 0755); + + strcpy(cfgfile, CONFIG_DIR "/pcsx2.cfg"); + +#ifdef PCSX2_DEVBUILD + memset(&g_TestRun, 0, sizeof(g_TestRun)); +#endif + + while(i < argc) { + char* token = argv[i++]; + + if( stricmp(token, "-help") == 0 || stricmp(token, "--help") == 0 || stricmp(token, "-h") == 0 ) { + //Msgbox::Alert( phelpmsg ); + return 0; + } + else if( stricmp(token, "-efile") == 0 ) { + token = argv[i++]; + if( token != NULL ) { + efile = atoi(token); + } + } + else if( stricmp(token, "-nogui") == 0 ) { + UseGui = FALSE; + } + else if( stricmp(token, "-loadgs") == 0 ) { + g_pRunGSState = argv[i++]; + } +#ifdef PCSX2_DEVBUILD + else if( stricmp(token, "-image") == 0 ) { + g_TestRun.pimagename = argv[i++]; + } + else if( stricmp(token, "-log") == 0 ) { + g_TestRun.plogname = argv[i++]; + } + else if( stricmp(token, "-logopt") == 0 ) { + token = argv[i++]; + if( token != NULL ) { + if( token[0] == '0' && token[1] == 'x' ) token += 2; + sscanf(token, "%x", &varLog); + } + } + else if( stricmp(token, "-frame") == 0 ) { + token = argv[i++]; + if( token != NULL ) { + g_TestRun.frame = atoi(token); + } + } + else if( stricmp(token, "-numimages") == 0 ) { + token = argv[i++]; + if( token != NULL ) { + g_TestRun.numimages = atoi(token); + } + } + else if( stricmp(token, "-jpg") == 0 ) { + g_TestRun.jpgcapture = 1; + } + else if( stricmp(token, "-gs") == 0 ) { + token = argv[i++]; + g_TestRun.pgsdll = token; + } + else if( stricmp(token, "-cdvd") == 0 ) { + token = argv[i++]; + g_TestRun.pcdvddll = token; + } + else if( stricmp(token, "-spu") == 0 ) { + token = argv[i++]; + g_TestRun.pspudll = token; + } + else if( stricmp(token, "-test") == 0 ) { + g_TestRun.enabled = 1; + } +#endif + else if( stricmp(token, "-pad") == 0 ) { + token = argv[i++]; + printf("-pad ignored\n"); + } + else if( stricmp(token, "-loadgs") == 0 ) { + token = argv[i++]; + g_pRunGSState = token; + } + else { + file = token; + printf("opening file %s\n", file); + } + } + +#ifdef PCSX2_DEVBUILD + g_TestRun.efile = efile; + g_TestRun.ptitle = file; +#endif + + // make gtk thread safe if using MTGS + if( CHECK_MULTIGS ) { + g_thread_init(NULL); + gdk_threads_init(); + } + + if (UseGui) { + gtk_init(NULL, NULL); + } + + if (LoadConfig() == -1) { + + memset(&Config, 0, sizeof(Config)); + strcpy(Config.BiosDir, DEFAULT_BIOS_DIR "/"); + strcpy(Config.PluginsDir, DEFAULT_PLUGINS_DIR "/"); + Config.Patch = 1; + Config.Options = PCSX2_EEREC | PCSX2_VU0REC | PCSX2_VU1REC; + Config.sseMXCSR = DEFAULT_sseMXCSR; + Config.sseVUMXCSR = DEFAULT_sseVUMXCSR; + + Msgbox::Alert("Pcsx2 needs to be configured"); + Pcsx2Configure(); + + return 0; + } + + InitLanguages(); + + if( Config.PsxOut ) { + // output the help commands + Console::WriteLn("\tF1 - save state"); + Console::WriteLn("\t(Shift +) F2 - cycle states"); + Console::WriteLn("\tF3 - load state"); + +#ifdef PCSX2_DEVBUILD + Console::WriteLn("\tF10 - dump performance counters"); + Console::WriteLn("\tF11 - save GS state"); + Console::WriteLn("\tF12 - dump hardware registers"); +#endif + } + + if (!SysInit()) return 1; + +#ifdef PCSX2_DEVBUILD + if( g_pRunGSState ) { + LoadGSState(g_pRunGSState); + SysClose(); + return 0; + } +#endif + + if (UseGui && (file == NULL)) { + StartGui(); + return 0; + } + + if (OpenPlugins(file) == -1) return -1; + + SysReset(); + + FixCPUState(); + cpuExecuteBios(); + if (file) strcpy(elfname, file); + if (!efile) efile=GetPS2ElfName(elfname); + loadElfFile(elfname); + + ExecuteCpu(); + + return 0; +} + +void InitLanguages() { + char *lang; + int i = 1; + + if (Config.Lang[0] == 0) { + strcpy(Config.Lang, "en"); + } + + langs = (_langs*)malloc(sizeof(_langs)); + strcpy(langs[0].lang, "en"); + dir = opendir(LANGS_DIR); + + while ((lang = GetLanguageNext()) != NULL) { + langs = (_langs*)realloc(langs, sizeof(_langs)*(i+1)); + strcpy(langs[i].lang, lang); + i++; + } + + CloseLanguages(); + langsMax = i; +} + +char *GetLanguageNext() { + struct dirent *ent; + + if (dir == NULL) return NULL; + for (;;) { + ent = readdir(dir); + if (ent == NULL) return NULL; + + if (!strcmp(ent->d_name, ".")) continue; + if (!strcmp(ent->d_name, "..")) continue; + break; + } + + return ent->d_name; +} + +void CloseLanguages() { + if (dir) closedir(dir); +} + +void ChangeLanguage(char *lang) { + strcpy(Config.Lang, lang); + SaveConfig(); +} + +/* Quick macros for checking shift, control, alt, and caps lock. */ +#define SHIFT_EVT(evt) ((evt == XK_Shift_L) || (evt == XK_Shift_R)) +#define CTRL_EVT(evt) ((evt == XK_Control_L) || (evt == XK_Control_L)) +#define ALT_EVT(evt) ((evt == XK_Alt_L) || (evt == XK_Alt_R)) +#define CAPS_LOCK_EVT(evt) (evt == XK_Caps_Lock) + +void KeyEvent(keyEvent* ev) { + static int shift = 0; + + if (ev == NULL) return; + + if( GSkeyEvent != NULL ) GSkeyEvent(ev); + + if (ev->evt == KEYPRESS) + { + if (SHIFT_EVT(ev->key)) + shift = 1; + if (CAPS_LOCK_EVT(ev->key)) + { + //Set up anything we want to happen while caps lock is down. + //Config_hacks_backup = Config.Hacks; + } + + switch (ev->key) + { + case XK_F1: case XK_F2: case XK_F3: case XK_F4: + case XK_F5: case XK_F6: case XK_F7: case XK_F8: + case XK_F9: case XK_F10: case XK_F11: case XK_F12: + try + { + ProcessFKeys(ev->key-XK_F1 + 1, shift); + } + catch( Exception::CpuStateShutdown& ) + { + // Woops! Something was unrecoverable. Bummer. + // Let's give the user a RunGui! + + g_EmulationInProgress = false; + g_ReturnToGui = true; + } + break; + + case XK_Escape: + signal(SIGINT, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + + #ifdef PCSX2_DEVBUILD + if( g_SaveGSStream >= 3 ) { + g_SaveGSStream = 4;// gs state + break; + } + #endif + + ClosePlugins(); + if (!UseGui) exit(0); + + // fixme: The GUI is now capable of recieving control back from the + // emulator. Which means that when I set g_ReturnToGui here, the emulation + // loop in ExecuteCpu() will exit. You should be able to set it up so + // that it returns control to the existing GTK event loop, instead of + // always starting a new one via RunGui(). (but could take some trial and + // error) -- (air) + g_ReturnToGui = true; + RunGui(); + break; + + default: + GSkeyEvent(ev); + break; + } + } + else if (ev->evt == KEYRELEASE) + { + if (SHIFT_EVT(ev->key)) + shift = 0; + if (CAPS_LOCK_EVT(ev->key)) + { + //Release caps lock + //Config_hacks_backup = Config.Hacks; + } + } + + return; +} + +void OnMsg_Ok() { + gtk_widget_destroy(MsgDlg); + gtk_main_quit(); +} + +void SysMessage(const char *fmt, ...) { + va_list list; + char msg[512]; + + va_start(list,fmt); + vsnprintf(msg,511,fmt,list); + msg[511] = '\0'; + va_end(list); + + Msgbox::Alert(msg); +} + +bool SysInit() +{ + if( sinit ) return true; + sinit = true; + + mkdir(SSTATES_DIR, 0755); + mkdir(MEMCARDS_DIR, 0755); + + mkdir(LOGS_DIR, 0755); + +#ifdef PCSX2_DEVBUILD + if( g_TestRun.plogname != NULL ) + emuLog = fopen(g_TestRun.plogname, "w"); + if( emuLog == NULL ) + emuLog = fopen(LOGS_DIR "/emuLog.txt","wb"); +#endif + + if( emuLog != NULL ) + setvbuf(emuLog, NULL, _IONBF, 0); + + PCSX2_MEM_PROTECT_BEGIN(); + SysDetect(); + if( !SysAllocateMem() ) + return false; // critical memory allocation failure; + + SysAllocateDynarecs(); + PCSX2_MEM_PROTECT_END(); + + while (LoadPlugins() == -1) { + if (Pcsx2Configure() == FALSE) + { + Msgbox::Alert("Configuration failed. Exiting."); + exit(1); + } + } + + return true; +} + +void SysRestorableReset() +{ + // already reset? and saved? + if( !g_EmulationInProgress ) return; + if( g_RecoveryState != NULL ) return; + + try + { + g_RecoveryState = new MemoryAlloc( "Memory Savestate Recovery" ); + memSavingState( *g_RecoveryState ).FreezeAll(); + cpuShutdown(); + g_EmulationInProgress = false; + } + catch( Exception::RuntimeError& ex ) + { + Msgbox::Alert( + "Pcsx2 gamestate recovery failed. Some options may have been reverted to protect your game's state.\n" + "Error: %s", params ex.cMessage() ); + safe_delete( g_RecoveryState ); + } +} + +void SysReset() +{ + if (!sinit) return; + + g_EmulationInProgress = false; + safe_free( g_RecoveryState ); + + ResetPlugins(); + + ElfCRC = 0; +} + +void SysClose() { + if (sinit == 0) return; + cpuShutdown(); + ClosePlugins(); + ReleasePlugins(); + + if (emuLog != NULL) + { + fclose(emuLog); + emuLog = NULL; + } + sinit=0; +} + +void SysPrintf(const char *fmt, ...) { + va_list list; + char msg[512]; + + va_start(list,fmt); + vsnprintf(msg,511,fmt,list); + msg[511] = '\0'; + va_end(list); + + Console::Write( msg ); +} + +void *SysLoadLibrary(const char *lib) { + return dlopen(lib, RTLD_NOW); +} + +void *SysLoadSym(void *lib, const char *sym) { + return dlsym(lib, sym); +} + +const char *SysLibError() { + return dlerror(); +} + +void SysCloseLibrary(void *lib) { + dlclose(lib); +} + +void SysUpdate() { + KeyEvent(PAD1keyEvent()); + KeyEvent(PAD2keyEvent()); +} + +void SysRunGui() { + RunGui(); +} + +void *SysMmap(uptr base, u32 size) +{ + u8 *Mem; + Mem = mmap((uptr*)base, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + if (Mem == MAP_FAILED) Console::Notice("Mmap Failed!"); + + return Mem; +} + +void SysMunmap(uptr base, u32 size) +{ + munmap((uptr*)base, size); +} diff --git a/pcsx2/Linux/LnxMain.h b/pcsx2/Linux/LnxMain.h new file mode 100644 index 0000000000..8c5b24538a --- /dev/null +++ b/pcsx2/Linux/LnxMain.h @@ -0,0 +1,53 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __LNXMAIN_H__ +#define __LNXMAIN_H__ + +#include "Linux.h" + +char* g_pRunGSState = NULL; + +const char* phelpmsg = + "\tpcsx2 [options] [file]\n\n" + "-cfg [file] {configuration file}\n" + "-efile [efile] {0 - reset, 1 - runcd (default), 2 - loadelf}\n" + "-help {display this help file}\n" + "-nogui {Don't use gui when launching}\n" + "-loadgs [file} {Loads a gsstate}\n" + "\n" +#ifdef PCSX2_DEVBUILD + "Testing Options: \n" + "\t-frame [frame] {game will run up to this frame before exiting}\n" + "\t-image [name] {path and base name of image (do not include the .ext)}\n" + "\t-jpg {save images to jpg format}\n" + "\t-log [name] {log path to save log file in}\n" + "\t-logopt [hex] {log options in hex (see debug.h) }\n" + "\t-numimages [num] {after hitting frame, this many images will be captures every 20 frames}\n" + "\t-test {Triggers testing mode (only for dev builds)}\n" + "\n" +#endif + "Load Plugins:\n" + "\t-cdvd [libpath] {specify the library load path of the CDVD plugin}\n" + "\t-gs [libpath] {specify the library load path of the GS plugin}\n" + "-pad [tsxcal] {specify to hold down on the triangle, square, circle, x, start, select buttons}\n" + "\t-spu [libpath] {specify the library load path of the SPU2 plugin}\n" + "\n"; + + #endif + \ No newline at end of file diff --git a/pcsx2/Linux/LnxThreads.cpp b/pcsx2/Linux/LnxThreads.cpp new file mode 100644 index 0000000000..89854955f3 --- /dev/null +++ b/pcsx2/Linux/LnxThreads.cpp @@ -0,0 +1,200 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "Threading.h" +#include "Linux.h" +#include "../x86/ix86/ix86.h" + +// Note: assuming multicore is safer because it forces the interlocked routines to use +// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not +// having the LOCK prefix is very bad indeed. + +static bool isMultiCore = true; // assume more than one CPU (safer) + +namespace Threading +{ + // Note: Apparently this solution is Linux/Solaris only. + // FreeBSD/OsX need something far more complicated (apparently) + void CountLogicalCores( int LogicalCoresPerPhysicalCPU, int PhysicalCoresPerPhysicalCPU ) + { + const uint numCPU = sysconf( _SC_NPROCESSORS_ONLN ); + if( numCPU > 0 ) + { + isMultiCore = numCPU > 1; + cpuinfo.LogicalCores = numCPU; + cpuinfo.PhysicalCores = ( numCPU / LogicalCoresPerPhysicalCPU ) * PhysicalCoresPerPhysicalCPU; + } + else + { + // Indeterminate? + cpuinfo.LogicalCores = 1; + cpuinfo.PhysicalCores = 1; + } + } + + __forceinline void Timeslice() + { + usleep(500); + } + + // For use in spin/wait loops, Acts as a hint to Intel CPUs and should, in theory + // improve performance and reduce cpu power consumption. + __forceinline void SpinWait() + { + // If this doesn't compile you can just comment it out (it only serves as a + // performance hint and isn't required). + __asm__ ( "pause" ); + } + + void* Thread::_internal_callback( void* itsme ) + { + jASSUME( itsme != NULL ); + Thread& owner = *((Thread*)itsme); + + try + { + owner.m_returncode = owner.Callback(); + } + catch( std::exception& ex ) + { + Console::Error( "Thread terminated abnormally with error:\n%s", params ex.what() ); + owner.m_returncode = -1; + } + + owner.m_terminated = true; + return NULL; + } + + ///////////////////////////////////////////////////////////////////////// + // Cross-platform atomic operations for GCC. + // These are much faster than the old versions for single core CPUs. + // Note, I've disabled the single core optimization, because pcsx2 shouldn't + // ever create threads on single core CPUs anyway. + + __forceinline long pcsx2_InterlockedExchange(volatile long* Target, long Value) + { + long result; + /* + * The XCHG instruction always locks the bus with or without the + * LOCKED prefix. This makes it significantly slower than CMPXCHG on + * uni-processor machines. The Windows InterlockedExchange function + * is nearly 3 times faster than the XCHG instruction, so this routine + * is not yet very useful for speeding up pthreads. + */ + + + if( true ) //isMultiCore ) + { + __asm__ __volatile__ ( + "xchgl %2,%1" + :"=r" (result) + :"m" (*Target), "0" (Value)); + } + else + { + /* + * Faster version of XCHG for uni-processor systems because + * it doesn't lock the bus. If an interrupt or context switch + * occurs between the movl and the cmpxchgl then the value in + * 'location' may have changed, in which case we will loop + * back to do the movl again. + */ + + __asm__ __volatile__ ( + "0:\n\t" + "movl %1,%%eax\n\t" + "cmpxchgl %2,%1\n\t" + "jnz 0b" + :"=&a" (result) + :"m" (*Target), "r" (Value)); + } + + return result; + } + + __forceinline long pcsx2_InterlockedExchangeAdd(volatile long* Addend, long Value) + { + if( true ) //isMultiCore ) + { + __asm__ __volatile__( + ".intel_syntax\n" + "lock xadd [%0], %%eax\n" + ".att_syntax\n" : : "r"(Addend), "a"(Value) : "memory"); + } + else + { + __asm__ __volatile__( + ".intel_syntax\n" + "xadd [%0], %%eax\n" + ".att_syntax\n" : : "r"(Addend), "a"(Value) : "memory"); + } + } + + __forceinline long pcsx2_InterlockedCompareExchange(volatile long *dest, long value, long comp) + { + long result; + + if( true ) //isMultiCore ) + { + __asm__ __volatile__ ( + "lock\n\t" + "cmpxchgl %2,%1" /* if (EAX == [location]) */ + /* [location] = value */ + /* else */ + /* EAX = [location] */ + :"=a" (result) + :"m" (*dest), "r" (value), "a" (comp)); + } + else + { + __asm__ __volatile__ ( + "cmpxchgl %2,%1" /* if (EAX == [location]) */ + /* [location] = value */ + /* else */ + /* EAX = [location] */ + :"=a" (result) + :"m" (*dest), "r" (value), "a" (comp)); + } + + return result; + } + + #ifdef __x86_64__ + __forceinline void pcsx2_InterlockedExchange64(volatile s64* Target, s64 Value) + { + __asm__ __volatile__( + ".intel_syntax\n" + "lock xchg [%0], %%rax\n" + ".att_syntax\n" : : "r"(Target), "a"(Value) : "memory" + ); + return 0; + } + + __forceinline s64 pcsx2_InterlockedCompareExchange64(volatile s64* dest, s64 exch, s64 comp) + { + s64 old; + __asm__ __volatile__( + "lock; cmpxchgq %q2, %q1" + : "=a" (old) + : "r" (exch), "m" (*dest), "a" (comp) + ); + return old; + } +#endif + +} diff --git a/pcsx2/Linux/Makefile.am b/pcsx2/Linux/Makefile.am new file mode 100644 index 0000000000..224c135bcf --- /dev/null +++ b/pcsx2/Linux/Makefile.am @@ -0,0 +1,17 @@ +AUTOMAKE_OPTIONS = foreign +INCLUDES = $(shell pkg-config --cflags gtk+-2.0) -I@srcdir@/../ -I@srcdir@/../common/ + +bin_PROGRAMS = pcsx2 + +# the application source, library search path, and link libraries +pcsx2_SOURCES = Pref.cpp interface.c ConfigDlg.cpp DebugDlg.cpp GtkGui.cpp LnxMain.cpp LnxConsole.cpp LnxThreads.cpp support.c + +pcsx2_LDFLAGS = + +pcsx2_DEPENDENCIES = ../libpcsx2.a ../IPU/libIPU.a ../IPU/mpeg2lib/libmpeg2IPU.a ../RDebug/libRDebug.a ../tinyxml/libtinyxml.a +pcsx2_DEPENDENCIES += ../x86/libx86recomp.a ../x86/ix86/libix86.a +pcsx2_DEPENDENCIES += ../DebugTools/libDebugTools.a + +pcsx2_LDADD = ../libpcsx2.a ../IPU/libIPU.a ../IPU/mpeg2lib/libmpeg2IPU.a ../RDebug/libRDebug.a ../tinyxml/libtinyxml.a +pcsx2_LDADD += ../x86/libx86recomp.a ../x86/ix86/libix86.a +pcsx2_LDADD += ../DebugTools/libDebugTools.a \ No newline at end of file diff --git a/pcsx2/Linux/Pref.cpp b/pcsx2/Linux/Pref.cpp new file mode 100644 index 0000000000..c804fe1c57 --- /dev/null +++ b/pcsx2/Linux/Pref.cpp @@ -0,0 +1,174 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "Linux.h" + +FILE *pref_file; +char *data; + +static void SetValue( const char *name, char *var) +{ + fprintf (pref_file,"%s = %s\n", name, var); +} + +static void SetValuel( const char *name, s32 var) +{ + fprintf (pref_file,"%s = %x\n", name, var); +} + +#define GetValue(name, var) {\ + char * tmp; \ + tmp = strstr(data, name); \ + if (tmp != NULL) { \ + tmp+=strlen(name); \ + while ((*tmp == ' ') || (*tmp == '=')) tmp++; \ + if (*tmp != '\n') sscanf(tmp, "%s", var); \ + } \ +} + +#define GetValuel(name, var) {\ + char * tmp; \ + tmp = strstr(data, name); \ + if (tmp != NULL) { \ + tmp+=strlen(name); \ + while ((*tmp == ' ') || (*tmp == '=')) tmp++; \ + if (*tmp != '\n') sscanf(tmp, "%x", &var); \ + } \ +} + +int LoadConfig() { + struct stat buf; + int size; + + if (stat(cfgfile, &buf) == -1) return -1; + size = buf.st_size; + + pref_file = fopen(cfgfile,"r"); + if (pref_file == NULL) return -1; + + data = (char*)malloc(size); + if (data == NULL) return -1; + + fread(data, 1, size, pref_file); + fclose(pref_file); + + GetValue("Bios", Config.Bios); + Config.Lang[0] = 0; + GetValue("Lang", Config.Lang); + GetValuel("Ps2Out", Config.PsxOut); + GetValuel("ThPriority", Config.ThPriority); + GetValue("PluginsDir", Config.PluginsDir); + GetValue("BiosDir", Config.BiosDir); + GetValue("Mcd1", Config.Mcd1); + GetValue("Mcd2", Config.Mcd2); + + GetValue("GS", Config.GS); + GetValue("SPU2", Config.SPU2); + GetValue("CDVD", Config.CDVD); + GetValue("PAD1", Config.PAD1); + GetValue("PAD2", Config.PAD2); + GetValue("DEV9", Config.DEV9); + GetValue("USB", Config.USB); + GetValue("FW", Config.FW); + + GetValuel("Patch", Config.Patch); +#ifdef PCSX2_DEVBUILD + GetValuel("varLog", varLog); +#endif + GetValuel("Options", Config.Options); + GetValuel("Hacks", Config.Hacks); + GetValuel("Fixes", Config.GameFixes); + + GetValuel("CustomFps", Config.CustomFps); + GetValuel("CustomFrameskip", Config.CustomFrameSkip); + GetValuel("CustomConsecutiveFrames", Config.CustomConsecutiveFrames); + GetValuel("CustomConsecutiveSkip", Config.CustomConsecutiveSkip); + + // Note - order is currently important. + GetValuel("sseMXCSR", Config.sseMXCSR); + GetValuel("sseVUMXCSR", Config.sseVUMXCSR); + GetValuel("eeOptions", Config.eeOptions); + GetValuel("vuOptions", Config.vuOptions); + + free(data); + +#ifdef ENABLE_NLS + if (Config.Lang[0]) { + extern int _nl_msg_cat_cntr; + + setenv("LANGUAGE", Config.Lang, 1); + ++_nl_msg_cat_cntr; + } +#endif + + return 0; +} + +///////////////////////////////////////////////////////// + +void SaveConfig() { + + pref_file = fopen(cfgfile,"w"); + if (pref_file == NULL) return; + + SetValue("Bios", Config.Bios); + SetValue("Lang", Config.Lang); + SetValue("PluginsDir", Config.PluginsDir); + SetValue("BiosDir", Config.BiosDir); + SetValuel("Ps2Out", Config.PsxOut); + SetValuel("ThPriority", Config.ThPriority); + SetValue("Mcd1", Config.Mcd1); + SetValue("Mcd2", Config.Mcd2); + + SetValue("GS", Config.GS); + SetValue("SPU2", Config.SPU2); + SetValue("CDVD", Config.CDVD); + SetValue("PAD1", Config.PAD1); + SetValue("PAD2", Config.PAD2); + SetValue("DEV9", Config.DEV9); + SetValue("USB", Config.USB); + SetValue("FW", Config.FW); + + SetValuel("Options", Config.Options); + + // Remove Fast Branches hack for now: + Config.Hacks &= ~0x80; + + SetValuel("Hacks", Config.Hacks); + SetValuel("Fixes", Config.GameFixes); + + SetValuel("Patch", Config.Patch); + + SetValuel("CustomFps", Config.CustomFps); + SetValuel("CustomFrameskip", Config.CustomFrameSkip); + SetValuel("CustomConsecutiveFrames", Config.CustomConsecutiveFrames); + SetValuel("CustomConsecutiveSkip", Config.CustomConsecutiveSkip); + + SetValuel("sseMXCSR", Config.sseMXCSR); + SetValuel("sseVUMXCSR", Config.sseVUMXCSR); + SetValuel("eeOptions", Config.eeOptions); + SetValuel("vuOptions", Config.vuOptions); + +#ifdef PCSX2_DEVBUILD + SetValuel("varLog", varLog); +#endif + + fclose(pref_file); + + return; +} diff --git a/pcsx2/Linux/buildgui.sh b/pcsx2/Linux/buildgui.sh new file mode 100644 index 0000000000..ef9beeb9f4 --- /dev/null +++ b/pcsx2/Linux/buildgui.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# Pcsx2 - Pc Ps2 Emulator +# Copyright (C) 2002-2008 Pcsx2 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 +# +# builds the GUI C classes +mkdir temp +cp pcsx2.glade temp/ +cd temp +glade-2 --write-source pcsx2.glade +rm src/main.c +cp src/*.h src/*.c ../ +cd .. +/bin/rm -rf temp diff --git a/pcsx2/Linux/callbacks.h b/pcsx2/Linux/callbacks.h new file mode 100644 index 0000000000..4ed41f544d --- /dev/null +++ b/pcsx2/Linux/callbacks.h @@ -0,0 +1,387 @@ +#include + + +void +on_Advanced_Defaults (GtkButton *button, + gpointer user_data); + +void +on_Advanced_OK (GtkButton *button, + gpointer user_data); + +void +On_Dialog_Cancelled (GtkButton *button, + gpointer user_data); + +void +on_Speed_Hack_OK (GtkButton *button, + gpointer user_data); + +void +on_Game_Fix_OK (GtkButton *button, + gpointer user_data); + +void +OnMemWrite32_Ok (GtkButton *button, + gpointer user_data); + +void +OnArguments_Ok (GtkButton *button, + gpointer user_data); + +void +OnDumpR_Ok (GtkButton *button, + gpointer user_data); + +void +OnDumpC_Ok (GtkButton *button, + gpointer user_data); + +void +OnSetBPC_Ok (GtkButton *button, + gpointer user_data); + +void +OnSetBPA_Ok (GtkButton *button, + gpointer user_data); + +void +OnSetBPA_Cancel (GtkButton *button, + gpointer user_data); + +void +OnSetPC_Ok (GtkButton *button, + gpointer user_data); + +void +OnDebug_EEMode (GtkToggleButton *togglebutton, + gpointer user_data); + +void +OnDebug_IOPMode (GtkToggleButton *togglebutton, + gpointer user_data); + +void +OnDebug_Step (GtkButton *button, + gpointer user_data); + +void +OnDebug_Skip (GtkButton *button, + gpointer user_data); + +void +OnDebug_Go (GtkButton *button, + gpointer user_data); + +void +OnDebug_Log (GtkButton *button, + gpointer user_data); + +void +OnDebug_SetPC (GtkButton *button, + gpointer user_data); + +void +OnDebug_SetBPA (GtkButton *button, + gpointer user_data); + +void +OnDebug_SetBPC (GtkButton *button, + gpointer user_data); + +void +OnDebug_ClearBPs (GtkButton *button, + gpointer user_data); + +void +OnDebug_DumpCode (GtkButton *button, + gpointer user_data); + +void +OnDebug_RawDump (GtkButton *button, + gpointer user_data); + +void +OnDebug_Close (GtkButton *button, + gpointer user_data); + +void +OnDebug_memWrite32 (GtkButton *button, + gpointer user_data); + +void +OnConfConf_FWConf (GtkButton *button, + gpointer user_data); + +void +OnConfConf_FWTest (GtkButton *button, + gpointer user_data); + +void +OnConfConf_FWAbout (GtkButton *button, + gpointer user_data); + +void +OnConfConf_UsbConf (GtkButton *button, + gpointer user_data); + +void +OnConfConf_UsbTest (GtkButton *button, + gpointer user_data); + +void +OnConfConf_UsbAbout (GtkButton *button, + gpointer user_data); + +void +OnConfConf_CdvdConf (GtkButton *button, + gpointer user_data); + +void +OnConfConf_CdvdTest (GtkButton *button, + gpointer user_data); + +void +OnConfConf_CdvdAbout (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Dev9Conf (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Dev9Test (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Dev9About (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Spu2Conf (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Spu2Test (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Spu2About (GtkButton *button, + gpointer user_data); + +void +OnConfConf_GsConf (GtkButton *button, + gpointer user_data); + +void +OnConfConf_GsTest (GtkButton *button, + gpointer user_data); + +void +OnConfConf_GsAbout (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Pad1Conf (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Pad1Test (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Pad1About (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Pad2Conf (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Pad2Test (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Pad2About (GtkButton *button, + gpointer user_data); + +void +OnConfConf_PluginsPath (GtkButton *button, + gpointer user_data); + +void +OnConfConf_BiosPath (GtkButton *button, + gpointer user_data); + +void +OnConfConf_Ok (GtkButton *button, + gpointer user_data); + +void +OnHelpAbout_Ok (GtkButton *button, + gpointer user_data); + +void +OnDestroy (GtkObject *object, + gpointer user_data); + +gboolean +OnDelete (GtkWidget *widget, + GdkEvent *event, + gpointer user_data); + +void +OnFile_RunCD (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnFile_LoadElf (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_Load1 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_Load2 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_Load3 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_Load4 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_Load5 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_LoadOther (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_Save1 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_Save2 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_Save3 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_Save4 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_Save5 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnStates_SaveOther (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnFile_Exit (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnEmu_Run (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnEmu_Reset (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnEmu_Arguments (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnConf_Conf (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnConf_Gs (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnConf_Pads (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnConf_Spu2 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnConf_Cdvd (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnConf_Dev9 (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnConf_Usb (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnConf_Fw (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnConf_Cpu (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_Game_Fixes (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_Speed_Hacks (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_Advanced (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_patch_browser1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_patch_finder2_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_enable_console1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_enable_patches1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnDebug_Debugger (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnDebug_Logging (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnHelp_About (GtkMenuItem *menuitem, + gpointer user_data); + +void +OnCpu_Ok (GtkButton *button, + gpointer user_data); + +void +OnLogging_Ok (GtkButton *button, + gpointer user_data); diff --git a/pcsx2/Linux/interface.c b/pcsx2/Linux/interface.c new file mode 100644 index 0000000000..ce4861d8f6 --- /dev/null +++ b/pcsx2/Linux/interface.c @@ -0,0 +1,4016 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "callbacks.h" +#include "interface.h" +#include "support.h" + +#define GLADE_HOOKUP_OBJECT(component,widget,name) \ + g_object_set_data_full (G_OBJECT (component), name, \ + gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref) + +#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \ + g_object_set_data (G_OBJECT (component), name, widget) + +GtkWidget* +create_AdvDlg (void) +{ + GtkWidget *AdvDlg; + GtkWidget *dialog_vbox4; + GtkWidget *hbox33; + GtkWidget *vbox52; + GtkWidget *frame24; + GtkWidget *alignment19; + GtkWidget *vbox53; + GtkWidget *frame26; + GtkWidget *alignment21; + GtkWidget *hbox35; + GtkWidget *radio_EE_Round_Near; + GSList *radio_EE_Round_Near_group = NULL; + GtkWidget *radio_EE_Round_Negative; + GtkWidget *radio_EE_Round_Positive; + GtkWidget *radio_EE_Round_Zero; + GtkWidget *label72; + GtkWidget *frame27; + GtkWidget *alignment22; + GtkWidget *hbox37; + GtkWidget *radio_EE_Clamp_None; + GSList *radio_EE_Clamp_None_group = NULL; + GtkWidget *radio_EE_Clamp_Normal; + GtkWidget *radio_EE_Clamp_Extra_Preserve; + GtkWidget *label73; + GtkWidget *hbox34; + GtkWidget *check_EE_Flush_Zero; + GtkWidget *check_EE_Denormal_Zero; + GtkWidget *label70; + GtkWidget *frame25; + GtkWidget *alignment20; + GtkWidget *vbox54; + GtkWidget *frame28; + GtkWidget *alignment23; + GtkWidget *hbox36; + GtkWidget *radio_VU_Round_Near; + GSList *radio_VU_Round_Near_group = NULL; + GtkWidget *radio_VU_Round_Negative; + GtkWidget *radio_VU_Round_Positive; + GtkWidget *radio_VU_Round_Zero; + GtkWidget *label74; + GtkWidget *frame29; + GtkWidget *alignment24; + GtkWidget *hbox38; + GtkWidget *radio_VU_Clamp_None; + GSList *radio_VU_Clamp_None_group = NULL; + GtkWidget *radio_VU_Clamp_Normal; + GtkWidget *radio_VU_Clamp_Extra; + GtkWidget *radio_VU_Clamp_Extra_Preserve; + GtkWidget *label75; + GtkWidget *table7; + GtkWidget *check_VU_Flush_Zero; + GtkWidget *check_VU_Denormal_Zero; + GtkWidget *Check_Set_OU_Flags; + GtkWidget *check_Emulate_DaZ; + GtkWidget *label71; + GtkWidget *vbox51; + GtkWidget *frame30; + GtkWidget *alignment25; + GtkWidget *label81; + GtkWidget *label78; + GtkWidget *frame31; + GtkWidget *alignment26; + GtkWidget *label84; + GtkWidget *label79; + GtkWidget *frame32; + GtkWidget *alignment27; + GtkWidget *label83; + GtkWidget *label80; + GtkWidget *dialog_action_area4; + GtkWidget *AdvDefaultBtn; + GtkWidget *button79; + GtkWidget *button80; + + AdvDlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (AdvDlg), _("Advanced Options")); + gtk_window_set_type_hint (GTK_WINDOW (AdvDlg), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox4 = GTK_DIALOG (AdvDlg)->vbox; + gtk_widget_show (dialog_vbox4); + + hbox33 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox33); + gtk_box_pack_start (GTK_BOX (dialog_vbox4), hbox33, TRUE, TRUE, 0); + + vbox52 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox52); + gtk_box_pack_start (GTK_BOX (hbox33), vbox52, TRUE, TRUE, 0); + + frame24 = gtk_frame_new (NULL); + gtk_widget_show (frame24); + gtk_box_pack_start (GTK_BOX (vbox52), frame24, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame24), GTK_SHADOW_NONE); + + alignment19 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment19); + gtk_container_add (GTK_CONTAINER (frame24), alignment19); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment19), 0, 0, 12, 0); + + vbox53 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox53); + gtk_container_add (GTK_CONTAINER (alignment19), vbox53); + + frame26 = gtk_frame_new (NULL); + gtk_widget_show (frame26); + gtk_box_pack_start (GTK_BOX (vbox53), frame26, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame26), GTK_SHADOW_NONE); + + alignment21 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment21); + gtk_container_add (GTK_CONTAINER (frame26), alignment21); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment21), 0, 0, 12, 0); + + hbox35 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox35); + gtk_container_add (GTK_CONTAINER (alignment21), hbox35); + + radio_EE_Round_Near = gtk_radio_button_new_with_mnemonic (NULL, _("Nearest")); + gtk_widget_show (radio_EE_Round_Near); + gtk_box_pack_start (GTK_BOX (hbox35), radio_EE_Round_Near, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_EE_Round_Near), radio_EE_Round_Near_group); + radio_EE_Round_Near_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_EE_Round_Near)); + + radio_EE_Round_Negative = gtk_radio_button_new_with_mnemonic (NULL, _("Negative")); + gtk_widget_show (radio_EE_Round_Negative); + gtk_box_pack_start (GTK_BOX (hbox35), radio_EE_Round_Negative, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_EE_Round_Negative), radio_EE_Round_Near_group); + radio_EE_Round_Near_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_EE_Round_Negative)); + + radio_EE_Round_Positive = gtk_radio_button_new_with_mnemonic (NULL, _("Positive")); + gtk_widget_show (radio_EE_Round_Positive); + gtk_box_pack_start (GTK_BOX (hbox35), radio_EE_Round_Positive, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_EE_Round_Positive), radio_EE_Round_Near_group); + radio_EE_Round_Near_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_EE_Round_Positive)); + + radio_EE_Round_Zero = gtk_radio_button_new_with_mnemonic (NULL, _("Chop/Zero")); + gtk_widget_show (radio_EE_Round_Zero); + gtk_box_pack_start (GTK_BOX (hbox35), radio_EE_Round_Zero, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_EE_Round_Zero), radio_EE_Round_Near_group); + radio_EE_Round_Near_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_EE_Round_Zero)); + + label72 = gtk_label_new (_("Round Mode")); + gtk_widget_show (label72); + gtk_frame_set_label_widget (GTK_FRAME (frame26), label72); + gtk_label_set_use_markup (GTK_LABEL (label72), TRUE); + + frame27 = gtk_frame_new (NULL); + gtk_widget_show (frame27); + gtk_box_pack_start (GTK_BOX (vbox53), frame27, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame27), GTK_SHADOW_NONE); + + alignment22 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment22); + gtk_container_add (GTK_CONTAINER (frame27), alignment22); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment22), 0, 0, 12, 0); + + hbox37 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox37); + gtk_container_add (GTK_CONTAINER (alignment22), hbox37); + + radio_EE_Clamp_None = gtk_radio_button_new_with_mnemonic (NULL, _("None")); + gtk_widget_show (radio_EE_Clamp_None); + gtk_box_pack_start (GTK_BOX (hbox37), radio_EE_Clamp_None, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_EE_Clamp_None), radio_EE_Clamp_None_group); + radio_EE_Clamp_None_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_EE_Clamp_None)); + + radio_EE_Clamp_Normal = gtk_radio_button_new_with_mnemonic (NULL, _("Normal")); + gtk_widget_show (radio_EE_Clamp_Normal); + gtk_box_pack_start (GTK_BOX (hbox37), radio_EE_Clamp_Normal, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_EE_Clamp_Normal), radio_EE_Clamp_None_group); + radio_EE_Clamp_None_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_EE_Clamp_Normal)); + + radio_EE_Clamp_Extra_Preserve = gtk_radio_button_new_with_mnemonic (NULL, _("Extra + Preserve Sign")); + gtk_widget_show (radio_EE_Clamp_Extra_Preserve); + gtk_box_pack_start (GTK_BOX (hbox37), radio_EE_Clamp_Extra_Preserve, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_EE_Clamp_Extra_Preserve), radio_EE_Clamp_None_group); + radio_EE_Clamp_None_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_EE_Clamp_Extra_Preserve)); + + label73 = gtk_label_new (_("Clamp Mode")); + gtk_widget_show (label73); + gtk_frame_set_label_widget (GTK_FRAME (frame27), label73); + gtk_label_set_use_markup (GTK_LABEL (label73), TRUE); + + hbox34 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox34); + gtk_box_pack_start (GTK_BOX (vbox53), hbox34, TRUE, TRUE, 0); + + check_EE_Flush_Zero = gtk_check_button_new_with_mnemonic (_("Flush to Zero")); + gtk_widget_show (check_EE_Flush_Zero); + gtk_box_pack_start (GTK_BOX (hbox34), check_EE_Flush_Zero, FALSE, FALSE, 0); + + check_EE_Denormal_Zero = gtk_check_button_new_with_mnemonic (_("Denormals are Zero")); + gtk_widget_show (check_EE_Denormal_Zero); + gtk_box_pack_start (GTK_BOX (hbox34), check_EE_Denormal_Zero, FALSE, FALSE, 0); + + label70 = gtk_label_new (_("EE Recs Options")); + gtk_widget_show (label70); + gtk_frame_set_label_widget (GTK_FRAME (frame24), label70); + gtk_label_set_use_markup (GTK_LABEL (label70), TRUE); + + frame25 = gtk_frame_new (NULL); + gtk_widget_show (frame25); + gtk_box_pack_start (GTK_BOX (vbox52), frame25, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame25), GTK_SHADOW_NONE); + + alignment20 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment20); + gtk_container_add (GTK_CONTAINER (frame25), alignment20); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment20), 0, 0, 12, 0); + + vbox54 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox54); + gtk_container_add (GTK_CONTAINER (alignment20), vbox54); + + frame28 = gtk_frame_new (NULL); + gtk_widget_show (frame28); + gtk_box_pack_start (GTK_BOX (vbox54), frame28, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame28), GTK_SHADOW_NONE); + + alignment23 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment23); + gtk_container_add (GTK_CONTAINER (frame28), alignment23); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment23), 0, 0, 12, 0); + + hbox36 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox36); + gtk_container_add (GTK_CONTAINER (alignment23), hbox36); + + radio_VU_Round_Near = gtk_radio_button_new_with_mnemonic (NULL, _("Nearest")); + gtk_widget_show (radio_VU_Round_Near); + gtk_box_pack_start (GTK_BOX (hbox36), radio_VU_Round_Near, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_VU_Round_Near), radio_VU_Round_Near_group); + radio_VU_Round_Near_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_VU_Round_Near)); + + radio_VU_Round_Negative = gtk_radio_button_new_with_mnemonic (NULL, _("Negative")); + gtk_widget_show (radio_VU_Round_Negative); + gtk_box_pack_start (GTK_BOX (hbox36), radio_VU_Round_Negative, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_VU_Round_Negative), radio_VU_Round_Near_group); + radio_VU_Round_Near_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_VU_Round_Negative)); + + radio_VU_Round_Positive = gtk_radio_button_new_with_mnemonic (NULL, _("Positive")); + gtk_widget_show (radio_VU_Round_Positive); + gtk_box_pack_start (GTK_BOX (hbox36), radio_VU_Round_Positive, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_VU_Round_Positive), radio_VU_Round_Near_group); + radio_VU_Round_Near_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_VU_Round_Positive)); + + radio_VU_Round_Zero = gtk_radio_button_new_with_mnemonic (NULL, _("Zero")); + gtk_widget_show (radio_VU_Round_Zero); + gtk_box_pack_start (GTK_BOX (hbox36), radio_VU_Round_Zero, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_VU_Round_Zero), radio_VU_Round_Near_group); + radio_VU_Round_Near_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_VU_Round_Zero)); + + label74 = gtk_label_new (_("Round Mode")); + gtk_widget_show (label74); + gtk_frame_set_label_widget (GTK_FRAME (frame28), label74); + gtk_label_set_use_markup (GTK_LABEL (label74), TRUE); + + frame29 = gtk_frame_new (NULL); + gtk_widget_show (frame29); + gtk_box_pack_start (GTK_BOX (vbox54), frame29, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame29), GTK_SHADOW_NONE); + + alignment24 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment24); + gtk_container_add (GTK_CONTAINER (frame29), alignment24); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment24), 0, 0, 12, 0); + + hbox38 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox38); + gtk_container_add (GTK_CONTAINER (alignment24), hbox38); + + radio_VU_Clamp_None = gtk_radio_button_new_with_mnemonic (NULL, _("None")); + gtk_widget_show (radio_VU_Clamp_None); + gtk_box_pack_start (GTK_BOX (hbox38), radio_VU_Clamp_None, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_VU_Clamp_None), radio_VU_Clamp_None_group); + radio_VU_Clamp_None_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_VU_Clamp_None)); + + radio_VU_Clamp_Normal = gtk_radio_button_new_with_mnemonic (NULL, _("Normal")); + gtk_widget_show (radio_VU_Clamp_Normal); + gtk_box_pack_start (GTK_BOX (hbox38), radio_VU_Clamp_Normal, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_VU_Clamp_Normal), radio_VU_Clamp_None_group); + radio_VU_Clamp_None_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_VU_Clamp_Normal)); + + radio_VU_Clamp_Extra = gtk_radio_button_new_with_mnemonic (NULL, _("Extra")); + gtk_widget_show (radio_VU_Clamp_Extra); + gtk_box_pack_start (GTK_BOX (hbox38), radio_VU_Clamp_Extra, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_VU_Clamp_Extra), radio_VU_Clamp_None_group); + radio_VU_Clamp_None_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_VU_Clamp_Extra)); + + radio_VU_Clamp_Extra_Preserve = gtk_radio_button_new_with_mnemonic (NULL, _("Extra + Preserve Sign")); + gtk_widget_show (radio_VU_Clamp_Extra_Preserve); + gtk_box_pack_start (GTK_BOX (hbox38), radio_VU_Clamp_Extra_Preserve, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_VU_Clamp_Extra_Preserve), radio_VU_Clamp_None_group); + radio_VU_Clamp_None_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_VU_Clamp_Extra_Preserve)); + + label75 = gtk_label_new (_("Clamp Mode")); + gtk_widget_show (label75); + gtk_frame_set_label_widget (GTK_FRAME (frame29), label75); + gtk_label_set_use_markup (GTK_LABEL (label75), TRUE); + + table7 = gtk_table_new (2, 2, FALSE); + gtk_widget_show (table7); + gtk_box_pack_start (GTK_BOX (vbox54), table7, TRUE, TRUE, 0); + + check_VU_Flush_Zero = gtk_check_button_new_with_mnemonic (_("Flush to Zero")); + gtk_widget_show (check_VU_Flush_Zero); + gtk_table_attach (GTK_TABLE (table7), check_VU_Flush_Zero, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + check_VU_Denormal_Zero = gtk_check_button_new_with_mnemonic (_("Denormals are Zero")); + gtk_widget_show (check_VU_Denormal_Zero); + gtk_table_attach (GTK_TABLE (table7), check_VU_Denormal_Zero, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + Check_Set_OU_Flags = gtk_check_button_new_with_mnemonic (_("Set O & U Flags")); + gtk_widget_show (Check_Set_OU_Flags); + gtk_table_attach (GTK_TABLE (table7), Check_Set_OU_Flags, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_widget_set_sensitive (Check_Set_OU_Flags, FALSE); + + check_Emulate_DaZ = gtk_check_button_new_with_mnemonic (_("Software Emulate DaZ")); + gtk_widget_show (check_Emulate_DaZ); + gtk_table_attach (GTK_TABLE (table7), check_Emulate_DaZ, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_widget_set_sensitive (check_Emulate_DaZ, FALSE); + + label71 = gtk_label_new (_("VU Recs Options")); + gtk_widget_show (label71); + gtk_frame_set_label_widget (GTK_FRAME (frame25), label71); + gtk_label_set_use_markup (GTK_LABEL (label71), TRUE); + + vbox51 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox51); + gtk_box_pack_start (GTK_BOX (hbox33), vbox51, TRUE, TRUE, 0); + + frame30 = gtk_frame_new (NULL); + gtk_widget_show (frame30); + gtk_box_pack_start (GTK_BOX (vbox51), frame30, FALSE, FALSE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame30), GTK_SHADOW_NONE); + + alignment25 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment25); + gtk_container_add (GTK_CONTAINER (frame30), alignment25); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment25), 0, 0, 12, 0); + + label81 = gtk_label_new (_("These options specify how your CPU rounds floating point values.\n\nTry changing the roundmode for EE if your game hangs, it could make it work again.")); + gtk_widget_show (label81); + gtk_container_add (GTK_CONTAINER (alignment25), label81); + gtk_label_set_line_wrap (GTK_LABEL (label81), TRUE); + + label78 = gtk_label_new (_("Round Mode")); + gtk_widget_show (label78); + gtk_frame_set_label_widget (GTK_FRAME (frame30), label78); + gtk_label_set_use_markup (GTK_LABEL (label78), TRUE); + + frame31 = gtk_frame_new (NULL); + gtk_widget_show (frame31); + gtk_box_pack_start (GTK_BOX (vbox51), frame31, FALSE, FALSE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame31), GTK_SHADOW_NONE); + + alignment26 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment26); + gtk_container_add (GTK_CONTAINER (frame31), alignment26); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment26), 0, 0, 12, 0); + + label84 = gtk_label_new (_("These options specify how PCSX2's recompilers will clamp Infinities and NaN (Not a Number) values in the opcode instructions.\n\nNone - No clamping. (Fastest Mode)\nNormal - Clamps the result.\nExtra - Clamps the operands, the result, and anywhere in between.\nExtra + Preserve Sign - Same as \"\"Extra\"\", except preserves NaN's sign when clamping the operands. (Slowest Mode)")); + gtk_widget_show (label84); + gtk_container_add (GTK_CONTAINER (alignment26), label84); + gtk_label_set_line_wrap (GTK_LABEL (label84), TRUE); + + label79 = gtk_label_new (_("Clamp Mode")); + gtk_widget_show (label79); + gtk_frame_set_label_widget (GTK_FRAME (frame31), label79); + gtk_label_set_use_markup (GTK_LABEL (label79), TRUE); + + frame32 = gtk_frame_new (NULL); + gtk_widget_show (frame32); + gtk_box_pack_start (GTK_BOX (vbox51), frame32, FALSE, FALSE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame32), GTK_SHADOW_NONE); + + alignment27 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment27); + gtk_container_add (GTK_CONTAINER (frame32), alignment27); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment27), 0, 0, 12, 0); + + label83 = gtk_label_new (_("Flush to Zero - Your CPU makes Floating Point Underflows become Zero, so it does less work. (Speed Up)\n\nDenormals are Zero - Your CPU makes Floating Point Denormals become Zero, so it does less work. (Speed Up)")); + gtk_widget_show (label83); + gtk_container_add (GTK_CONTAINER (alignment27), label83); + gtk_label_set_line_wrap (GTK_LABEL (label83), TRUE); + + label80 = gtk_label_new (_("Other Options")); + gtk_widget_show (label80); + gtk_frame_set_label_widget (GTK_FRAME (frame32), label80); + gtk_label_set_use_markup (GTK_LABEL (label80), TRUE); + + dialog_action_area4 = GTK_DIALOG (AdvDlg)->action_area; + gtk_widget_show (dialog_action_area4); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area4), GTK_BUTTONBOX_SPREAD); + + AdvDefaultBtn = gtk_button_new_with_mnemonic (_("Defaults")); + gtk_widget_show (AdvDefaultBtn); + gtk_dialog_add_action_widget (GTK_DIALOG (AdvDlg), AdvDefaultBtn, 0); + GTK_WIDGET_SET_FLAGS (AdvDefaultBtn, GTK_CAN_DEFAULT); + + button79 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button79); + gtk_dialog_add_action_widget (GTK_DIALOG (AdvDlg), button79, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (button79, GTK_CAN_DEFAULT); + + button80 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button80); + gtk_dialog_add_action_widget (GTK_DIALOG (AdvDlg), button80, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (button80, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) AdvDefaultBtn, "clicked", + G_CALLBACK (on_Advanced_Defaults), + NULL); + g_signal_connect ((gpointer) button79, "clicked", + G_CALLBACK (on_Advanced_OK), + NULL); + g_signal_connect ((gpointer) button80, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (AdvDlg, AdvDlg, "AdvDlg"); + GLADE_HOOKUP_OBJECT_NO_REF (AdvDlg, dialog_vbox4, "dialog_vbox4"); + GLADE_HOOKUP_OBJECT (AdvDlg, hbox33, "hbox33"); + GLADE_HOOKUP_OBJECT (AdvDlg, vbox52, "vbox52"); + GLADE_HOOKUP_OBJECT (AdvDlg, frame24, "frame24"); + GLADE_HOOKUP_OBJECT (AdvDlg, alignment19, "alignment19"); + GLADE_HOOKUP_OBJECT (AdvDlg, vbox53, "vbox53"); + GLADE_HOOKUP_OBJECT (AdvDlg, frame26, "frame26"); + GLADE_HOOKUP_OBJECT (AdvDlg, alignment21, "alignment21"); + GLADE_HOOKUP_OBJECT (AdvDlg, hbox35, "hbox35"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_EE_Round_Near, "radio_EE_Round_Near"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_EE_Round_Negative, "radio_EE_Round_Negative"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_EE_Round_Positive, "radio_EE_Round_Positive"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_EE_Round_Zero, "radio_EE_Round_Zero"); + GLADE_HOOKUP_OBJECT (AdvDlg, label72, "label72"); + GLADE_HOOKUP_OBJECT (AdvDlg, frame27, "frame27"); + GLADE_HOOKUP_OBJECT (AdvDlg, alignment22, "alignment22"); + GLADE_HOOKUP_OBJECT (AdvDlg, hbox37, "hbox37"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_EE_Clamp_None, "radio_EE_Clamp_None"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_EE_Clamp_Normal, "radio_EE_Clamp_Normal"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_EE_Clamp_Extra_Preserve, "radio_EE_Clamp_Extra_Preserve"); + GLADE_HOOKUP_OBJECT (AdvDlg, label73, "label73"); + GLADE_HOOKUP_OBJECT (AdvDlg, hbox34, "hbox34"); + GLADE_HOOKUP_OBJECT (AdvDlg, check_EE_Flush_Zero, "check_EE_Flush_Zero"); + GLADE_HOOKUP_OBJECT (AdvDlg, check_EE_Denormal_Zero, "check_EE_Denormal_Zero"); + GLADE_HOOKUP_OBJECT (AdvDlg, label70, "label70"); + GLADE_HOOKUP_OBJECT (AdvDlg, frame25, "frame25"); + GLADE_HOOKUP_OBJECT (AdvDlg, alignment20, "alignment20"); + GLADE_HOOKUP_OBJECT (AdvDlg, vbox54, "vbox54"); + GLADE_HOOKUP_OBJECT (AdvDlg, frame28, "frame28"); + GLADE_HOOKUP_OBJECT (AdvDlg, alignment23, "alignment23"); + GLADE_HOOKUP_OBJECT (AdvDlg, hbox36, "hbox36"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_VU_Round_Near, "radio_VU_Round_Near"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_VU_Round_Negative, "radio_VU_Round_Negative"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_VU_Round_Positive, "radio_VU_Round_Positive"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_VU_Round_Zero, "radio_VU_Round_Zero"); + GLADE_HOOKUP_OBJECT (AdvDlg, label74, "label74"); + GLADE_HOOKUP_OBJECT (AdvDlg, frame29, "frame29"); + GLADE_HOOKUP_OBJECT (AdvDlg, alignment24, "alignment24"); + GLADE_HOOKUP_OBJECT (AdvDlg, hbox38, "hbox38"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_VU_Clamp_None, "radio_VU_Clamp_None"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_VU_Clamp_Normal, "radio_VU_Clamp_Normal"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_VU_Clamp_Extra, "radio_VU_Clamp_Extra"); + GLADE_HOOKUP_OBJECT (AdvDlg, radio_VU_Clamp_Extra_Preserve, "radio_VU_Clamp_Extra_Preserve"); + GLADE_HOOKUP_OBJECT (AdvDlg, label75, "label75"); + GLADE_HOOKUP_OBJECT (AdvDlg, table7, "table7"); + GLADE_HOOKUP_OBJECT (AdvDlg, check_VU_Flush_Zero, "check_VU_Flush_Zero"); + GLADE_HOOKUP_OBJECT (AdvDlg, check_VU_Denormal_Zero, "check_VU_Denormal_Zero"); + GLADE_HOOKUP_OBJECT (AdvDlg, Check_Set_OU_Flags, "Check_Set_OU_Flags"); + GLADE_HOOKUP_OBJECT (AdvDlg, check_Emulate_DaZ, "check_Emulate_DaZ"); + GLADE_HOOKUP_OBJECT (AdvDlg, label71, "label71"); + GLADE_HOOKUP_OBJECT (AdvDlg, vbox51, "vbox51"); + GLADE_HOOKUP_OBJECT (AdvDlg, frame30, "frame30"); + GLADE_HOOKUP_OBJECT (AdvDlg, alignment25, "alignment25"); + GLADE_HOOKUP_OBJECT (AdvDlg, label81, "label81"); + GLADE_HOOKUP_OBJECT (AdvDlg, label78, "label78"); + GLADE_HOOKUP_OBJECT (AdvDlg, frame31, "frame31"); + GLADE_HOOKUP_OBJECT (AdvDlg, alignment26, "alignment26"); + GLADE_HOOKUP_OBJECT (AdvDlg, label84, "label84"); + GLADE_HOOKUP_OBJECT (AdvDlg, label79, "label79"); + GLADE_HOOKUP_OBJECT (AdvDlg, frame32, "frame32"); + GLADE_HOOKUP_OBJECT (AdvDlg, alignment27, "alignment27"); + GLADE_HOOKUP_OBJECT (AdvDlg, label83, "label83"); + GLADE_HOOKUP_OBJECT (AdvDlg, label80, "label80"); + GLADE_HOOKUP_OBJECT_NO_REF (AdvDlg, dialog_action_area4, "dialog_action_area4"); + GLADE_HOOKUP_OBJECT (AdvDlg, AdvDefaultBtn, "AdvDefaultBtn"); + GLADE_HOOKUP_OBJECT (AdvDlg, button79, "button79"); + GLADE_HOOKUP_OBJECT (AdvDlg, button80, "button80"); + + return AdvDlg; +} + +GtkWidget* +create_SpeedHacksDlg (void) +{ + GtkWidget *SpeedHacksDlg; + GtkWidget *dialog_vbox3; + GtkWidget *vbox59; + GtkWidget *label88; + GtkWidget *hbox39; + GtkWidget *frame37; + GtkWidget *alignment32; + GtkWidget *vbox61; + GtkWidget *check_default_cycle_rate; + GSList *check_default_cycle_rate_group = NULL; + GtkWidget *label98; + GtkWidget *check_1_5_cycle_rate; + GtkWidget *label93; + GtkWidget *check_2_cycle_rate; + GtkWidget *label94; + GtkWidget *check_3_cycle_rate; + GtkWidget *label95; + GtkWidget *hseparator1; + GtkWidget *label91; + GtkWidget *label90; + GtkWidget *vbox60; + GtkWidget *check_iop_cycle_rate; + GtkWidget *label96; + GtkWidget *check_wait_cycles_sync_hack; + GtkWidget *label97; + GtkWidget *frame36; + GtkWidget *alignment31; + GtkWidget *check_ESC_hack; + GtkWidget *label89; + GtkWidget *dialog_action_area3; + GtkWidget *button99; + GtkWidget *button98; + + SpeedHacksDlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (SpeedHacksDlg), _("PCSX2 Speed Hacks")); + gtk_window_set_type_hint (GTK_WINDOW (SpeedHacksDlg), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox3 = GTK_DIALOG (SpeedHacksDlg)->vbox; + gtk_widget_show (dialog_vbox3); + + vbox59 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox59); + gtk_box_pack_start (GTK_BOX (dialog_vbox3), vbox59, FALSE, FALSE, 0); + + label88 = gtk_label_new (_("These hacks will affect the speed of PCSX2 but possibly compromise compatibility.\nIf you have problems, Disable all of these and try again.")); + gtk_widget_show (label88); + gtk_box_pack_start (GTK_BOX (vbox59), label88, FALSE, FALSE, 0); + + hbox39 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox39); + gtk_box_pack_start (GTK_BOX (vbox59), hbox39, TRUE, TRUE, 0); + + frame37 = gtk_frame_new (NULL); + gtk_widget_show (frame37); + gtk_box_pack_start (GTK_BOX (hbox39), frame37, TRUE, TRUE, 0); + + alignment32 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment32); + gtk_container_add (GTK_CONTAINER (frame37), alignment32); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment32), 0, 0, 12, 0); + + vbox61 = gtk_vbox_new (FALSE, 2); + gtk_widget_show (vbox61); + gtk_container_add (GTK_CONTAINER (alignment32), vbox61); + + check_default_cycle_rate = gtk_radio_button_new_with_mnemonic (NULL, _("Default Cycle Rate")); + gtk_widget_show (check_default_cycle_rate); + gtk_box_pack_start (GTK_BOX (vbox61), check_default_cycle_rate, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (check_default_cycle_rate), check_default_cycle_rate_group); + check_default_cycle_rate_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (check_default_cycle_rate)); + + label98 = gtk_label_new (_("Most compatable option - recommended for everyone with high-end machines.")); + gtk_widget_show (label98); + gtk_box_pack_start (GTK_BOX (vbox61), label98, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (label98), TRUE); + gtk_misc_set_alignment (GTK_MISC (label98), 0.29, 0.5); + + check_1_5_cycle_rate = gtk_radio_button_new_with_mnemonic (NULL, _("Use x1.5 Cycle Rate")); + gtk_widget_show (check_1_5_cycle_rate); + gtk_box_pack_start (GTK_BOX (vbox61), check_1_5_cycle_rate, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (check_1_5_cycle_rate), check_default_cycle_rate_group); + check_default_cycle_rate_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (check_1_5_cycle_rate)); + + label93 = gtk_label_new (_("Moderate speedup, and works well with most games.")); + gtk_widget_show (label93); + gtk_box_pack_start (GTK_BOX (vbox61), label93, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (label93), TRUE); + gtk_misc_set_alignment (GTK_MISC (label93), 0.29, 0.5); + + check_2_cycle_rate = gtk_radio_button_new_with_mnemonic (NULL, _("Use x2 Cycle Rate")); + gtk_widget_show (check_2_cycle_rate); + gtk_box_pack_start (GTK_BOX (vbox61), check_2_cycle_rate, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (check_2_cycle_rate), check_default_cycle_rate_group); + check_default_cycle_rate_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (check_2_cycle_rate)); + + label94 = gtk_label_new (_("Big speedup! Works well with many games.")); + gtk_widget_show (label94); + gtk_box_pack_start (GTK_BOX (vbox61), label94, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (label94), TRUE); + gtk_misc_set_alignment (GTK_MISC (label94), 0.36, 0.5); + + check_3_cycle_rate = gtk_radio_button_new_with_mnemonic (NULL, _("Use x3 Cycle Rate")); + gtk_widget_show (check_3_cycle_rate); + gtk_box_pack_start (GTK_BOX (vbox61), check_3_cycle_rate, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (check_3_cycle_rate), check_default_cycle_rate_group); + check_default_cycle_rate_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (check_3_cycle_rate)); + + label95 = gtk_label_new (_("Big speedup, but causes flickering or missing geometry on many games.")); + gtk_widget_show (label95); + gtk_box_pack_start (GTK_BOX (vbox61), label95, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (label95), TRUE); + gtk_misc_set_alignment (GTK_MISC (label95), 0.24, 0.5); + + hseparator1 = gtk_hseparator_new (); + gtk_widget_show (hseparator1); + gtk_box_pack_start (GTK_BOX (vbox61), hseparator1, FALSE, FALSE, 0); + + label91 = gtk_label_new (_("Important: X2 & X3 sync hacks *will* cause choppy/skippy audio on many FMV movies.")); + gtk_widget_show (label91); + gtk_box_pack_start (GTK_BOX (vbox61), label91, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (label91), TRUE); + + label90 = gtk_label_new (_("frame37")); + gtk_widget_show (label90); + gtk_frame_set_label_widget (GTK_FRAME (frame37), label90); + gtk_label_set_use_markup (GTK_LABEL (label90), TRUE); + + vbox60 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox60); + gtk_box_pack_start (GTK_BOX (hbox39), vbox60, FALSE, FALSE, 0); + + check_iop_cycle_rate = gtk_check_button_new_with_mnemonic (_("Enable IOP x2 Cycle Rate")); + gtk_widget_show (check_iop_cycle_rate); + gtk_box_pack_start (GTK_BOX (vbox60), check_iop_cycle_rate, FALSE, FALSE, 0); + + label96 = gtk_label_new (_("Small speedup, and works well with most games,")); + gtk_widget_show (label96); + gtk_box_pack_start (GTK_BOX (vbox60), label96, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (label96), TRUE); + + check_wait_cycles_sync_hack = gtk_check_button_new_with_mnemonic (_("WaitCycles Sync Hack")); + gtk_widget_show (check_wait_cycles_sync_hack); + gtk_box_pack_start (GTK_BOX (vbox60), check_wait_cycles_sync_hack, FALSE, FALSE, 0); + + label97 = gtk_label_new (_("Small speedup. Works well with most games, but it may cause certain games to crash, or freeze up during bootup or stage changes.")); + gtk_widget_show (label97); + gtk_box_pack_start (GTK_BOX (vbox60), label97, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (label97), TRUE); + + frame36 = gtk_frame_new (NULL); + gtk_widget_show (frame36); + gtk_box_pack_start (GTK_BOX (vbox59), frame36, FALSE, FALSE, 0); + + alignment31 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment31); + gtk_container_add (GTK_CONTAINER (frame36), alignment31); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment31), 0, 0, 12, 0); + + check_ESC_hack = gtk_check_button_new_with_mnemonic (_("Escape Hack - Use Esc key to fully exit PCSX2.")); + gtk_widget_show (check_ESC_hack); + gtk_container_add (GTK_CONTAINER (alignment31), check_ESC_hack); + + label89 = gtk_label_new (_("Miscellaneous")); + gtk_widget_show (label89); + gtk_frame_set_label_widget (GTK_FRAME (frame36), label89); + gtk_label_set_use_markup (GTK_LABEL (label89), TRUE); + + dialog_action_area3 = GTK_DIALOG (SpeedHacksDlg)->action_area; + gtk_widget_show (dialog_action_area3); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area3), GTK_BUTTONBOX_END); + + button99 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button99); + gtk_dialog_add_action_widget (GTK_DIALOG (SpeedHacksDlg), button99, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (button99, GTK_CAN_DEFAULT); + + button98 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button98); + gtk_dialog_add_action_widget (GTK_DIALOG (SpeedHacksDlg), button98, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (button98, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) button99, "clicked", + G_CALLBACK (on_Speed_Hack_OK), + NULL); + g_signal_connect ((gpointer) button98, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (SpeedHacksDlg, SpeedHacksDlg, "SpeedHacksDlg"); + GLADE_HOOKUP_OBJECT_NO_REF (SpeedHacksDlg, dialog_vbox3, "dialog_vbox3"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, vbox59, "vbox59"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, label88, "label88"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, hbox39, "hbox39"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, frame37, "frame37"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, alignment32, "alignment32"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, vbox61, "vbox61"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, check_default_cycle_rate, "check_default_cycle_rate"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, label98, "label98"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, check_1_5_cycle_rate, "check_1_5_cycle_rate"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, label93, "label93"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, check_2_cycle_rate, "check_2_cycle_rate"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, label94, "label94"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, check_3_cycle_rate, "check_3_cycle_rate"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, label95, "label95"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, hseparator1, "hseparator1"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, label91, "label91"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, label90, "label90"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, vbox60, "vbox60"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, check_iop_cycle_rate, "check_iop_cycle_rate"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, label96, "label96"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, check_wait_cycles_sync_hack, "check_wait_cycles_sync_hack"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, label97, "label97"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, frame36, "frame36"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, alignment31, "alignment31"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, check_ESC_hack, "check_ESC_hack"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, label89, "label89"); + GLADE_HOOKUP_OBJECT_NO_REF (SpeedHacksDlg, dialog_action_area3, "dialog_action_area3"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, button99, "button99"); + GLADE_HOOKUP_OBJECT (SpeedHacksDlg, button98, "button98"); + + return SpeedHacksDlg; +} + +GtkWidget* +create_GameFixDlg (void) +{ + GtkWidget *GameFixDlg; + GtkWidget *dialog_vbox1; + GtkWidget *GameSettings; + GtkWidget *alignment5; + GtkWidget *vbox30; + GtkWidget *check_FPU_Clamp; + GtkWidget *check_VU_Branch; + GtkWidget *check_VU_Add_Sub; + GtkWidget *label42; + GtkWidget *dialog_action_area1; + GtkWidget *cancelbutton1; + GtkWidget *button83; + + GameFixDlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (GameFixDlg), _("Game Special Fixes")); + gtk_window_set_type_hint (GTK_WINDOW (GameFixDlg), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox1 = GTK_DIALOG (GameFixDlg)->vbox; + gtk_widget_show (dialog_vbox1); + + GameSettings = gtk_frame_new (NULL); + gtk_widget_show (GameSettings); + gtk_box_pack_start (GTK_BOX (dialog_vbox1), GameSettings, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (GameSettings), GTK_SHADOW_NONE); + + alignment5 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment5); + gtk_container_add (GTK_CONTAINER (GameSettings), alignment5); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment5), 0, 0, 12, 0); + + vbox30 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox30); + gtk_container_add (GTK_CONTAINER (alignment5), vbox30); + + check_FPU_Clamp = gtk_check_button_new_with_mnemonic (_("FPU Clamp Hack - Special fix for Tekken 5 and maybe other games.")); + gtk_widget_show (check_FPU_Clamp); + gtk_box_pack_start (GTK_BOX (vbox30), check_FPU_Clamp, FALSE, FALSE, 0); + + check_VU_Branch = gtk_check_button_new_with_mnemonic (_("VU Branch Hack - Special fix for Magna Carta; Breaks Crash Bandicoot!")); + gtk_widget_show (check_VU_Branch); + gtk_box_pack_start (GTK_BOX (vbox30), check_VU_Branch, FALSE, FALSE, 0); + + check_VU_Add_Sub = gtk_check_button_new_with_mnemonic (_("VU Add / Sub Hack - Special fix for Tri-Ace games!")); + gtk_widget_show (check_VU_Add_Sub); + gtk_box_pack_start (GTK_BOX (vbox30), check_VU_Add_Sub, FALSE, FALSE, 0); + + label42 = gtk_label_new (_("Some games need special settings.\nConfigure them here.")); + gtk_widget_show (label42); + gtk_frame_set_label_widget (GTK_FRAME (GameSettings), label42); + gtk_label_set_use_markup (GTK_LABEL (label42), TRUE); + + dialog_action_area1 = GTK_DIALOG (GameFixDlg)->action_area; + gtk_widget_show (dialog_action_area1); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1), GTK_BUTTONBOX_END); + + cancelbutton1 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (cancelbutton1); + gtk_dialog_add_action_widget (GTK_DIALOG (GameFixDlg), cancelbutton1, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (cancelbutton1, GTK_CAN_DEFAULT); + + button83 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button83); + gtk_dialog_add_action_widget (GTK_DIALOG (GameFixDlg), button83, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (button83, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) cancelbutton1, "clicked", + G_CALLBACK (on_Game_Fix_OK), + NULL); + g_signal_connect ((gpointer) button83, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (GameFixDlg, GameFixDlg, "GameFixDlg"); + GLADE_HOOKUP_OBJECT_NO_REF (GameFixDlg, dialog_vbox1, "dialog_vbox1"); + GLADE_HOOKUP_OBJECT (GameFixDlg, GameSettings, "GameSettings"); + GLADE_HOOKUP_OBJECT (GameFixDlg, alignment5, "alignment5"); + GLADE_HOOKUP_OBJECT (GameFixDlg, vbox30, "vbox30"); + GLADE_HOOKUP_OBJECT (GameFixDlg, check_FPU_Clamp, "check_FPU_Clamp"); + GLADE_HOOKUP_OBJECT (GameFixDlg, check_VU_Branch, "check_VU_Branch"); + GLADE_HOOKUP_OBJECT (GameFixDlg, check_VU_Add_Sub, "check_VU_Add_Sub"); + GLADE_HOOKUP_OBJECT (GameFixDlg, label42, "label42"); + GLADE_HOOKUP_OBJECT_NO_REF (GameFixDlg, dialog_action_area1, "dialog_action_area1"); + GLADE_HOOKUP_OBJECT (GameFixDlg, cancelbutton1, "cancelbutton1"); + GLADE_HOOKUP_OBJECT (GameFixDlg, button83, "button83"); + + return GameFixDlg; +} + +GtkWidget* +create_MemWrite32 (void) +{ + GtkWidget *MemWrite32; + GtkWidget *vbox25; + GtkWidget *hbox18; + GtkWidget *label27; + GtkWidget *label25; + GtkWidget *GtkEntry_Mem; + GtkWidget *hbox19; + GtkWidget *label28; + GtkWidget *label26; + GtkWidget *GtkEntry_Data; + GtkWidget *hbuttonbox22; + GtkWidget *button71; + GtkWidget *button72; + + MemWrite32 = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (MemWrite32), 5); + gtk_window_set_title (GTK_WINDOW (MemWrite32), _("memWrite32")); + + vbox25 = gtk_vbox_new (FALSE, 5); + gtk_widget_show (vbox25); + gtk_container_add (GTK_CONTAINER (MemWrite32), vbox25); + gtk_container_set_border_width (GTK_CONTAINER (vbox25), 5); + + hbox18 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox18); + gtk_box_pack_start (GTK_BOX (vbox25), hbox18, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox18), 5); + + label27 = gtk_label_new (_("Address ")); + gtk_widget_show (label27); + gtk_box_pack_start (GTK_BOX (hbox18), label27, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label27), GTK_JUSTIFY_CENTER); + + label25 = gtk_label_new (_("0x")); + gtk_widget_show (label25); + gtk_box_pack_start (GTK_BOX (hbox18), label25, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label25), GTK_JUSTIFY_CENTER); + + GtkEntry_Mem = gtk_entry_new (); + gtk_widget_show (GtkEntry_Mem); + gtk_box_pack_start (GTK_BOX (hbox18), GtkEntry_Mem, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (GtkEntry_Mem), 8226); + + hbox19 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox19); + gtk_box_pack_start (GTK_BOX (vbox25), hbox19, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox19), 5); + + label28 = gtk_label_new (_("Data ")); + gtk_widget_show (label28); + gtk_box_pack_start (GTK_BOX (hbox19), label28, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label28), GTK_JUSTIFY_CENTER); + + label26 = gtk_label_new (_("0x")); + gtk_widget_show (label26); + gtk_box_pack_start (GTK_BOX (hbox19), label26, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label26), GTK_JUSTIFY_CENTER); + + GtkEntry_Data = gtk_entry_new (); + gtk_widget_show (GtkEntry_Data); + gtk_box_pack_start (GTK_BOX (hbox19), GtkEntry_Data, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (GtkEntry_Data), 8226); + + hbuttonbox22 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox22); + gtk_box_pack_start (GTK_BOX (vbox25), hbuttonbox22, TRUE, TRUE, 0); + gtk_box_set_spacing (GTK_BOX (hbuttonbox22), 30); + + button71 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button71); + gtk_container_add (GTK_CONTAINER (hbuttonbox22), button71); + GTK_WIDGET_SET_FLAGS (button71, GTK_CAN_DEFAULT); + + button72 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button72); + gtk_container_add (GTK_CONTAINER (hbuttonbox22), button72); + GTK_WIDGET_SET_FLAGS (button72, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) button71, "clicked", + G_CALLBACK (OnMemWrite32_Ok), + NULL); + g_signal_connect ((gpointer) button72, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (MemWrite32, MemWrite32, "MemWrite32"); + GLADE_HOOKUP_OBJECT (MemWrite32, vbox25, "vbox25"); + GLADE_HOOKUP_OBJECT (MemWrite32, hbox18, "hbox18"); + GLADE_HOOKUP_OBJECT (MemWrite32, label27, "label27"); + GLADE_HOOKUP_OBJECT (MemWrite32, label25, "label25"); + GLADE_HOOKUP_OBJECT (MemWrite32, GtkEntry_Mem, "GtkEntry_Mem"); + GLADE_HOOKUP_OBJECT (MemWrite32, hbox19, "hbox19"); + GLADE_HOOKUP_OBJECT (MemWrite32, label28, "label28"); + GLADE_HOOKUP_OBJECT (MemWrite32, label26, "label26"); + GLADE_HOOKUP_OBJECT (MemWrite32, GtkEntry_Data, "GtkEntry_Data"); + GLADE_HOOKUP_OBJECT (MemWrite32, hbuttonbox22, "hbuttonbox22"); + GLADE_HOOKUP_OBJECT (MemWrite32, button71, "button71"); + GLADE_HOOKUP_OBJECT (MemWrite32, button72, "button72"); + + return MemWrite32; +} + +GtkWidget* +create_CmdLine (void) +{ + GtkWidget *CmdLine; + GtkWidget *vbox24; + GtkWidget *GtkLabel_Text; + GtkWidget *hbox17; + GtkWidget *GtkEntry_dCMDLINE; + GtkWidget *GtkLabel_Note; + GtkWidget *hbuttonbox20; + GtkWidget *GtkButton_Ok3; + GtkWidget *GtkButton_Cancel2; + GtkTooltips *tooltips; + + tooltips = gtk_tooltips_new (); + + CmdLine = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (CmdLine), 5); + gtk_window_set_title (GTK_WINDOW (CmdLine), _("Program arguments")); + gtk_window_set_modal (GTK_WINDOW (CmdLine), TRUE); + + vbox24 = gtk_vbox_new (FALSE, 5); + gtk_widget_show (vbox24); + gtk_container_add (GTK_CONTAINER (CmdLine), vbox24); + gtk_container_set_border_width (GTK_CONTAINER (vbox24), 5); + + GtkLabel_Text = gtk_label_new (_("Fill in the command line arguments for the opened program:")); + gtk_widget_show (GtkLabel_Text); + gtk_box_pack_start (GTK_BOX (vbox24), GtkLabel_Text, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (GtkLabel_Text), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (GtkLabel_Text), 0.1, 0.5); + + hbox17 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox17); + gtk_box_pack_start (GTK_BOX (vbox24), hbox17, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox17), 5); + + GtkEntry_dCMDLINE = gtk_entry_new (); + gtk_widget_show (GtkEntry_dCMDLINE); + gtk_box_pack_start (GTK_BOX (hbox17), GtkEntry_dCMDLINE, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, GtkEntry_dCMDLINE, _("If you don't know what to write leave it blank"), NULL); + gtk_entry_set_invisible_char (GTK_ENTRY (GtkEntry_dCMDLINE), 8226); + + GtkLabel_Note = gtk_label_new (_("Note: this is intended for developers only.")); + gtk_widget_show (GtkLabel_Note); + gtk_box_pack_start (GTK_BOX (vbox24), GtkLabel_Note, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (GtkLabel_Note), GTK_JUSTIFY_CENTER); + + hbuttonbox20 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox20); + gtk_box_pack_start (GTK_BOX (vbox24), hbuttonbox20, TRUE, TRUE, 0); + gtk_box_set_spacing (GTK_BOX (hbuttonbox20), 30); + + GtkButton_Ok3 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (GtkButton_Ok3); + gtk_container_add (GTK_CONTAINER (hbuttonbox20), GtkButton_Ok3); + GTK_WIDGET_SET_FLAGS (GtkButton_Ok3, GTK_CAN_DEFAULT); + + GtkButton_Cancel2 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (GtkButton_Cancel2); + gtk_container_add (GTK_CONTAINER (hbuttonbox20), GtkButton_Cancel2); + GTK_WIDGET_SET_FLAGS (GtkButton_Cancel2, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) GtkButton_Ok3, "clicked", + G_CALLBACK (OnArguments_Ok), + NULL); + g_signal_connect ((gpointer) GtkButton_Cancel2, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (CmdLine, CmdLine, "CmdLine"); + GLADE_HOOKUP_OBJECT (CmdLine, vbox24, "vbox24"); + GLADE_HOOKUP_OBJECT (CmdLine, GtkLabel_Text, "GtkLabel_Text"); + GLADE_HOOKUP_OBJECT (CmdLine, hbox17, "hbox17"); + GLADE_HOOKUP_OBJECT (CmdLine, GtkEntry_dCMDLINE, "GtkEntry_dCMDLINE"); + GLADE_HOOKUP_OBJECT (CmdLine, GtkLabel_Note, "GtkLabel_Note"); + GLADE_HOOKUP_OBJECT (CmdLine, hbuttonbox20, "hbuttonbox20"); + GLADE_HOOKUP_OBJECT (CmdLine, GtkButton_Ok3, "GtkButton_Ok3"); + GLADE_HOOKUP_OBJECT (CmdLine, GtkButton_Cancel2, "GtkButton_Cancel2"); + GLADE_HOOKUP_OBJECT_NO_REF (CmdLine, tooltips, "tooltips"); + + return CmdLine; +} + +GtkWidget* +create_DumpRDlg (void) +{ + GtkWidget *DumpRDlg; + GtkWidget *vbox21; + GtkWidget *label18; + GtkWidget *hbox13; + GtkWidget *label19; + GtkWidget *GtkEntry_DumpRF; + GtkWidget *hbox14; + GtkWidget *label20; + GtkWidget *GtkEntry_DumpRT; + GtkWidget *label22; + GtkWidget *hbuttonbox18; + GtkWidget *button50; + GtkWidget *button51; + + DumpRDlg = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (DumpRDlg), 5); + gtk_window_set_title (GTK_WINDOW (DumpRDlg), _("Raw Dump")); + + vbox21 = gtk_vbox_new (FALSE, 5); + gtk_widget_show (vbox21); + gtk_container_add (GTK_CONTAINER (DumpRDlg), vbox21); + gtk_container_set_border_width (GTK_CONTAINER (vbox21), 5); + + label18 = gtk_label_new (_("Set Dump Addr (in Hex):")); + gtk_widget_show (label18); + gtk_box_pack_start (GTK_BOX (vbox21), label18, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label18), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (label18), 0.1, 0.5); + + hbox13 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox13); + gtk_box_pack_start (GTK_BOX (vbox21), hbox13, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox13), 5); + + label19 = gtk_label_new (_("From 0x")); + gtk_widget_show (label19); + gtk_box_pack_start (GTK_BOX (hbox13), label19, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label19), GTK_JUSTIFY_CENTER); + gtk_label_set_line_wrap (GTK_LABEL (label19), TRUE); + + GtkEntry_DumpRF = gtk_entry_new (); + gtk_widget_show (GtkEntry_DumpRF); + gtk_box_pack_start (GTK_BOX (hbox13), GtkEntry_DumpRF, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (GtkEntry_DumpRF), 8226); + + hbox14 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox14); + gtk_box_pack_start (GTK_BOX (vbox21), hbox14, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox14), 5); + + label20 = gtk_label_new (_("To 0x")); + gtk_widget_show (label20); + gtk_box_pack_start (GTK_BOX (hbox14), label20, FALSE, FALSE, 0); + + GtkEntry_DumpRT = gtk_entry_new (); + gtk_widget_show (GtkEntry_DumpRT); + gtk_box_pack_start (GTK_BOX (hbox14), GtkEntry_DumpRT, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (GtkEntry_DumpRT), 8226); + + label22 = gtk_label_new (_("Dump File = \"dump.txt\"")); + gtk_widget_show (label22); + gtk_box_pack_start (GTK_BOX (vbox21), label22, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label22), 0.1, 0.5); + + hbuttonbox18 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox18); + gtk_box_pack_start (GTK_BOX (vbox21), hbuttonbox18, TRUE, TRUE, 0); + gtk_box_set_spacing (GTK_BOX (hbuttonbox18), 30); + + button50 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button50); + gtk_container_add (GTK_CONTAINER (hbuttonbox18), button50); + GTK_WIDGET_SET_FLAGS (button50, GTK_CAN_DEFAULT); + + button51 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button51); + gtk_container_add (GTK_CONTAINER (hbuttonbox18), button51); + GTK_WIDGET_SET_FLAGS (button51, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) button50, "clicked", + G_CALLBACK (OnDumpR_Ok), + NULL); + g_signal_connect ((gpointer) button51, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (DumpRDlg, DumpRDlg, "DumpRDlg"); + GLADE_HOOKUP_OBJECT (DumpRDlg, vbox21, "vbox21"); + GLADE_HOOKUP_OBJECT (DumpRDlg, label18, "label18"); + GLADE_HOOKUP_OBJECT (DumpRDlg, hbox13, "hbox13"); + GLADE_HOOKUP_OBJECT (DumpRDlg, label19, "label19"); + GLADE_HOOKUP_OBJECT (DumpRDlg, GtkEntry_DumpRF, "GtkEntry_DumpRF"); + GLADE_HOOKUP_OBJECT (DumpRDlg, hbox14, "hbox14"); + GLADE_HOOKUP_OBJECT (DumpRDlg, label20, "label20"); + GLADE_HOOKUP_OBJECT (DumpRDlg, GtkEntry_DumpRT, "GtkEntry_DumpRT"); + GLADE_HOOKUP_OBJECT (DumpRDlg, label22, "label22"); + GLADE_HOOKUP_OBJECT (DumpRDlg, hbuttonbox18, "hbuttonbox18"); + GLADE_HOOKUP_OBJECT (DumpRDlg, button50, "button50"); + GLADE_HOOKUP_OBJECT (DumpRDlg, button51, "button51"); + + return DumpRDlg; +} + +GtkWidget* +create_DumpCDlg (void) +{ + GtkWidget *DumpCDlg; + GtkWidget *vbox20; + GtkWidget *label15; + GtkWidget *hbox11; + GtkWidget *label16; + GtkWidget *GtkEntry_DumpCF; + GtkWidget *hbox12; + GtkWidget *label17; + GtkWidget *GtkEntry_DumpCT; + GtkWidget *label21; + GtkWidget *hbuttonbox17; + GtkWidget *button48; + GtkWidget *button49; + + DumpCDlg = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (DumpCDlg), 5); + gtk_window_set_title (GTK_WINDOW (DumpCDlg), _("Dump code")); + + vbox20 = gtk_vbox_new (FALSE, 5); + gtk_widget_show (vbox20); + gtk_container_add (GTK_CONTAINER (DumpCDlg), vbox20); + gtk_container_set_border_width (GTK_CONTAINER (vbox20), 5); + + label15 = gtk_label_new (_("Set Dump Addr (in Hex):")); + gtk_widget_show (label15); + gtk_box_pack_start (GTK_BOX (vbox20), label15, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label15), 0.1, 0.5); + + hbox11 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox11); + gtk_box_pack_start (GTK_BOX (vbox20), hbox11, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox11), 5); + + label16 = gtk_label_new (_("From 0x")); + gtk_widget_show (label16); + gtk_box_pack_start (GTK_BOX (hbox11), label16, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label16), GTK_JUSTIFY_CENTER); + gtk_label_set_line_wrap (GTK_LABEL (label16), TRUE); + + GtkEntry_DumpCF = gtk_entry_new (); + gtk_widget_show (GtkEntry_DumpCF); + gtk_box_pack_start (GTK_BOX (hbox11), GtkEntry_DumpCF, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (GtkEntry_DumpCF), 8226); + + hbox12 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox12); + gtk_box_pack_start (GTK_BOX (vbox20), hbox12, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox12), 5); + + label17 = gtk_label_new (_("To 0x")); + gtk_widget_show (label17); + gtk_box_pack_start (GTK_BOX (hbox12), label17, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label17), GTK_JUSTIFY_CENTER); + + GtkEntry_DumpCT = gtk_entry_new (); + gtk_widget_show (GtkEntry_DumpCT); + gtk_box_pack_start (GTK_BOX (hbox12), GtkEntry_DumpCT, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (GtkEntry_DumpCT), 8226); + + label21 = gtk_label_new (_("Dump File = \"dump.txt\"")); + gtk_widget_show (label21); + gtk_box_pack_start (GTK_BOX (vbox20), label21, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label21), 0.1, 0.5); + + hbuttonbox17 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox17); + gtk_box_pack_start (GTK_BOX (vbox20), hbuttonbox17, TRUE, TRUE, 0); + gtk_box_set_spacing (GTK_BOX (hbuttonbox17), 30); + + button48 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button48); + gtk_container_add (GTK_CONTAINER (hbuttonbox17), button48); + GTK_WIDGET_SET_FLAGS (button48, GTK_CAN_DEFAULT); + + button49 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button49); + gtk_container_add (GTK_CONTAINER (hbuttonbox17), button49); + GTK_WIDGET_SET_FLAGS (button49, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) button48, "clicked", + G_CALLBACK (OnDumpC_Ok), + NULL); + g_signal_connect ((gpointer) button49, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (DumpCDlg, DumpCDlg, "DumpCDlg"); + GLADE_HOOKUP_OBJECT (DumpCDlg, vbox20, "vbox20"); + GLADE_HOOKUP_OBJECT (DumpCDlg, label15, "label15"); + GLADE_HOOKUP_OBJECT (DumpCDlg, hbox11, "hbox11"); + GLADE_HOOKUP_OBJECT (DumpCDlg, label16, "label16"); + GLADE_HOOKUP_OBJECT (DumpCDlg, GtkEntry_DumpCF, "GtkEntry_DumpCF"); + GLADE_HOOKUP_OBJECT (DumpCDlg, hbox12, "hbox12"); + GLADE_HOOKUP_OBJECT (DumpCDlg, label17, "label17"); + GLADE_HOOKUP_OBJECT (DumpCDlg, GtkEntry_DumpCT, "GtkEntry_DumpCT"); + GLADE_HOOKUP_OBJECT (DumpCDlg, label21, "label21"); + GLADE_HOOKUP_OBJECT (DumpCDlg, hbuttonbox17, "hbuttonbox17"); + GLADE_HOOKUP_OBJECT (DumpCDlg, button48, "button48"); + GLADE_HOOKUP_OBJECT (DumpCDlg, button49, "button49"); + + return DumpCDlg; +} + +GtkWidget* +create_SetBPCDlg (void) +{ + GtkWidget *SetBPCDlg; + GtkWidget *vbox19; + GtkWidget *label13; + GtkWidget *hbox10; + GtkWidget *label14; + GtkWidget *GtkEntry_BPC; + GtkWidget *hbuttonbox16; + GtkWidget *button46; + GtkWidget *button47; + + SetBPCDlg = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (SetBPCDlg), 5); + gtk_window_set_title (GTK_WINDOW (SetBPCDlg), _("SetBreakPoint Addr")); + + vbox19 = gtk_vbox_new (FALSE, 5); + gtk_widget_show (vbox19); + gtk_container_add (GTK_CONTAINER (SetBPCDlg), vbox19); + gtk_container_set_border_width (GTK_CONTAINER (vbox19), 5); + + label13 = gtk_label_new (_("Set New BP Count (in Hex):")); + gtk_widget_show (label13); + gtk_box_pack_start (GTK_BOX (vbox19), label13, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label13), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (label13), 0.1, 0.5); + + hbox10 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox10); + gtk_box_pack_start (GTK_BOX (vbox19), hbox10, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox10), 5); + + label14 = gtk_label_new (_("0x")); + gtk_widget_show (label14); + gtk_box_pack_start (GTK_BOX (hbox10), label14, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label14), GTK_JUSTIFY_CENTER); + + GtkEntry_BPC = gtk_entry_new (); + gtk_widget_show (GtkEntry_BPC); + gtk_box_pack_start (GTK_BOX (hbox10), GtkEntry_BPC, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (GtkEntry_BPC), 8226); + + hbuttonbox16 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox16); + gtk_box_pack_start (GTK_BOX (vbox19), hbuttonbox16, TRUE, TRUE, 0); + gtk_box_set_spacing (GTK_BOX (hbuttonbox16), 30); + + button46 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button46); + gtk_container_add (GTK_CONTAINER (hbuttonbox16), button46); + GTK_WIDGET_SET_FLAGS (button46, GTK_CAN_DEFAULT); + + button47 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button47); + gtk_container_add (GTK_CONTAINER (hbuttonbox16), button47); + GTK_WIDGET_SET_FLAGS (button47, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) button46, "clicked", + G_CALLBACK (OnSetBPC_Ok), + NULL); + g_signal_connect ((gpointer) button47, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (SetBPCDlg, SetBPCDlg, "SetBPCDlg"); + GLADE_HOOKUP_OBJECT (SetBPCDlg, vbox19, "vbox19"); + GLADE_HOOKUP_OBJECT (SetBPCDlg, label13, "label13"); + GLADE_HOOKUP_OBJECT (SetBPCDlg, hbox10, "hbox10"); + GLADE_HOOKUP_OBJECT (SetBPCDlg, label14, "label14"); + GLADE_HOOKUP_OBJECT (SetBPCDlg, GtkEntry_BPC, "GtkEntry_BPC"); + GLADE_HOOKUP_OBJECT (SetBPCDlg, hbuttonbox16, "hbuttonbox16"); + GLADE_HOOKUP_OBJECT (SetBPCDlg, button46, "button46"); + GLADE_HOOKUP_OBJECT (SetBPCDlg, button47, "button47"); + + return SetBPCDlg; +} + +GtkWidget* +create_SetBPADlg (void) +{ + GtkWidget *SetBPADlg; + GtkWidget *vbox18; + GtkWidget *label11; + GtkWidget *hbox9; + GtkWidget *label12; + GtkWidget *GtkEntry_BPA; + GtkWidget *hbuttonbox15; + GtkWidget *button44; + GtkWidget *button45; + + SetBPADlg = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (SetBPADlg), 5); + gtk_window_set_title (GTK_WINDOW (SetBPADlg), _("SetBreakPoint Addr")); + + vbox18 = gtk_vbox_new (FALSE, 5); + gtk_widget_show (vbox18); + gtk_container_add (GTK_CONTAINER (SetBPADlg), vbox18); + gtk_container_set_border_width (GTK_CONTAINER (vbox18), 5); + + label11 = gtk_label_new (_("Set New BP Address (in Hex):")); + gtk_widget_show (label11); + gtk_box_pack_start (GTK_BOX (vbox18), label11, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label11), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (label11), 0.1, 0.5); + + hbox9 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox9); + gtk_box_pack_start (GTK_BOX (vbox18), hbox9, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox9), 5); + + label12 = gtk_label_new (_("0x")); + gtk_widget_show (label12); + gtk_box_pack_start (GTK_BOX (hbox9), label12, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label12), GTK_JUSTIFY_CENTER); + + GtkEntry_BPA = gtk_entry_new (); + gtk_widget_show (GtkEntry_BPA); + gtk_box_pack_start (GTK_BOX (hbox9), GtkEntry_BPA, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (GtkEntry_BPA), 8226); + + hbuttonbox15 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox15); + gtk_box_pack_start (GTK_BOX (vbox18), hbuttonbox15, TRUE, TRUE, 0); + gtk_box_set_spacing (GTK_BOX (hbuttonbox15), 30); + + button44 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button44); + gtk_container_add (GTK_CONTAINER (hbuttonbox15), button44); + GTK_WIDGET_SET_FLAGS (button44, GTK_CAN_DEFAULT); + + button45 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button45); + gtk_container_add (GTK_CONTAINER (hbuttonbox15), button45); + GTK_WIDGET_SET_FLAGS (button45, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) button44, "clicked", + G_CALLBACK (OnSetBPA_Ok), + NULL); + g_signal_connect ((gpointer) button45, "clicked", + G_CALLBACK (OnSetBPA_Cancel), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (SetBPADlg, SetBPADlg, "SetBPADlg"); + GLADE_HOOKUP_OBJECT (SetBPADlg, vbox18, "vbox18"); + GLADE_HOOKUP_OBJECT (SetBPADlg, label11, "label11"); + GLADE_HOOKUP_OBJECT (SetBPADlg, hbox9, "hbox9"); + GLADE_HOOKUP_OBJECT (SetBPADlg, label12, "label12"); + GLADE_HOOKUP_OBJECT (SetBPADlg, GtkEntry_BPA, "GtkEntry_BPA"); + GLADE_HOOKUP_OBJECT (SetBPADlg, hbuttonbox15, "hbuttonbox15"); + GLADE_HOOKUP_OBJECT (SetBPADlg, button44, "button44"); + GLADE_HOOKUP_OBJECT (SetBPADlg, button45, "button45"); + + return SetBPADlg; +} + +GtkWidget* +create_SetPCDlg (void) +{ + GtkWidget *SetPCDlg; + GtkWidget *vbox17; + GtkWidget *label9; + GtkWidget *hbox8; + GtkWidget *label10; + GtkWidget *GtkEntry_dPC; + GtkWidget *hbuttonbox14; + GtkWidget *button42; + GtkWidget *button43; + + SetPCDlg = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (SetPCDlg), 5); + gtk_window_set_title (GTK_WINDOW (SetPCDlg), _("SetPCDlg")); + + vbox17 = gtk_vbox_new (FALSE, 5); + gtk_widget_show (vbox17); + gtk_container_add (GTK_CONTAINER (SetPCDlg), vbox17); + gtk_container_set_border_width (GTK_CONTAINER (vbox17), 5); + + label9 = gtk_label_new (_("Set New PC Address (in Hex):")); + gtk_widget_show (label9); + gtk_box_pack_start (GTK_BOX (vbox17), label9, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label9), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (label9), 0.1, 0.5); + + hbox8 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox8); + gtk_box_pack_start (GTK_BOX (vbox17), hbox8, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox8), 5); + + label10 = gtk_label_new (_("0x")); + gtk_widget_show (label10); + gtk_box_pack_start (GTK_BOX (hbox8), label10, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label10), GTK_JUSTIFY_CENTER); + + GtkEntry_dPC = gtk_entry_new (); + gtk_widget_show (GtkEntry_dPC); + gtk_box_pack_start (GTK_BOX (hbox8), GtkEntry_dPC, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (GtkEntry_dPC), 8226); + + hbuttonbox14 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox14); + gtk_box_pack_start (GTK_BOX (vbox17), hbuttonbox14, TRUE, TRUE, 0); + gtk_box_set_spacing (GTK_BOX (hbuttonbox14), 30); + + button42 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button42); + gtk_container_add (GTK_CONTAINER (hbuttonbox14), button42); + GTK_WIDGET_SET_FLAGS (button42, GTK_CAN_DEFAULT); + + button43 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button43); + gtk_container_add (GTK_CONTAINER (hbuttonbox14), button43); + GTK_WIDGET_SET_FLAGS (button43, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) button42, "clicked", + G_CALLBACK (OnSetPC_Ok), + NULL); + g_signal_connect ((gpointer) button43, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (SetPCDlg, SetPCDlg, "SetPCDlg"); + GLADE_HOOKUP_OBJECT (SetPCDlg, vbox17, "vbox17"); + GLADE_HOOKUP_OBJECT (SetPCDlg, label9, "label9"); + GLADE_HOOKUP_OBJECT (SetPCDlg, hbox8, "hbox8"); + GLADE_HOOKUP_OBJECT (SetPCDlg, label10, "label10"); + GLADE_HOOKUP_OBJECT (SetPCDlg, GtkEntry_dPC, "GtkEntry_dPC"); + GLADE_HOOKUP_OBJECT (SetPCDlg, hbuttonbox14, "hbuttonbox14"); + GLADE_HOOKUP_OBJECT (SetPCDlg, button42, "button42"); + GLADE_HOOKUP_OBJECT (SetPCDlg, button43, "button43"); + + return SetPCDlg; +} + +GtkWidget* +create_DebugWnd (void) +{ + GtkWidget *DebugWnd; + GtkWidget *vbox16; + GtkWidget *hbox16; + GtkWidget *GtkRadioButton_EE; + GSList *GtkRadioButton_EE_group = NULL; + GtkWidget *GtkRadioButton_IOP; + GtkWidget *hbox6; + GtkWidget *hbox7; + GtkWidget *scrolledwindow1; + GtkWidget *viewport1; + GtkWidget *GtkList_DisView; + GtkWidget *GtkVScrollbar_VList; + GtkWidget *vbox22; + GtkWidget *vbuttonbox2; + GtkWidget *button52; + GtkWidget *button53; + GtkWidget *button65; + GtkWidget *button64; + GtkWidget *vbuttonbox3; + GtkWidget *button58; + GtkWidget *button59; + GtkWidget *button60; + GtkWidget *button61; + GtkWidget *vbuttonbox1; + GtkWidget *button39; + GtkWidget *button40; + GtkWidget *button41; + GtkWidget *vbuttonbox4; + GtkWidget *button68; + + DebugWnd = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (DebugWnd), 5); + gtk_window_set_title (GTK_WINDOW (DebugWnd), _("PCSX2 Debugger")); + + vbox16 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox16); + gtk_container_add (GTK_CONTAINER (DebugWnd), vbox16); + gtk_container_set_border_width (GTK_CONTAINER (vbox16), 5); + + hbox16 = gtk_hbox_new (FALSE, 5); + gtk_widget_show (hbox16); + gtk_box_pack_start (GTK_BOX (vbox16), hbox16, FALSE, TRUE, 0); + + GtkRadioButton_EE = gtk_radio_button_new_with_mnemonic (NULL, _("EE Debug Mode")); + gtk_widget_show (GtkRadioButton_EE); + gtk_box_pack_start (GTK_BOX (hbox16), GtkRadioButton_EE, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (GtkRadioButton_EE), GtkRadioButton_EE_group); + GtkRadioButton_EE_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (GtkRadioButton_EE)); + + GtkRadioButton_IOP = gtk_radio_button_new_with_mnemonic (NULL, _("IOP Debug Mode")); + gtk_widget_show (GtkRadioButton_IOP); + gtk_box_pack_start (GTK_BOX (hbox16), GtkRadioButton_IOP, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (GtkRadioButton_IOP), GtkRadioButton_EE_group); + GtkRadioButton_EE_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (GtkRadioButton_IOP)); + + hbox6 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox6); + gtk_box_pack_start (GTK_BOX (vbox16), hbox6, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox6), 5); + + hbox7 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox7); + gtk_box_pack_start (GTK_BOX (hbox6), hbox7, TRUE, TRUE, 0); + + scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow1); + gtk_box_pack_start (GTK_BOX (hbox7), scrolledwindow1, TRUE, TRUE, 0); + GTK_WIDGET_UNSET_FLAGS (scrolledwindow1, GTK_CAN_FOCUS); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_POLICY_ALWAYS, GTK_POLICY_NEVER); + + viewport1 = gtk_viewport_new (NULL, NULL); + gtk_widget_show (viewport1); + gtk_container_add (GTK_CONTAINER (scrolledwindow1), viewport1); + + GtkList_DisView = gtk_tree_view_new (); + gtk_widget_show (GtkList_DisView); + gtk_container_add (GTK_CONTAINER (viewport1), GtkList_DisView); + GTK_WIDGET_UNSET_FLAGS (GtkList_DisView, GTK_CAN_FOCUS); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (GtkList_DisView), FALSE); + + GtkVScrollbar_VList = gtk_vscrollbar_new (GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 412, 1, 20, 2))); + gtk_widget_show (GtkVScrollbar_VList); + gtk_box_pack_start (GTK_BOX (hbox7), GtkVScrollbar_VList, FALSE, TRUE, 0); + + vbox22 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox22); + gtk_box_pack_start (GTK_BOX (hbox6), vbox22, FALSE, TRUE, 0); + + vbuttonbox2 = gtk_vbutton_box_new (); + gtk_widget_show (vbuttonbox2); + gtk_box_pack_start (GTK_BOX (vbox22), vbuttonbox2, FALSE, TRUE, 0); + + button52 = gtk_button_new_with_mnemonic (_("Step")); + gtk_widget_show (button52); + gtk_container_add (GTK_CONTAINER (vbuttonbox2), button52); + GTK_WIDGET_SET_FLAGS (button52, GTK_CAN_DEFAULT); + + button53 = gtk_button_new_with_mnemonic (_("Skip")); + gtk_widget_show (button53); + gtk_container_add (GTK_CONTAINER (vbuttonbox2), button53); + GTK_WIDGET_SET_FLAGS (button53, GTK_CAN_DEFAULT); + + button65 = gtk_button_new_with_mnemonic (_("Go")); + gtk_widget_show (button65); + gtk_container_add (GTK_CONTAINER (vbuttonbox2), button65); + GTK_WIDGET_SET_FLAGS (button65, GTK_CAN_DEFAULT); + + button64 = gtk_button_new_with_mnemonic (_("Log On/Off")); + gtk_widget_show (button64); + gtk_container_add (GTK_CONTAINER (vbuttonbox2), button64); + GTK_WIDGET_SET_FLAGS (button64, GTK_CAN_DEFAULT); + + vbuttonbox3 = gtk_vbutton_box_new (); + gtk_widget_show (vbuttonbox3); + gtk_box_pack_start (GTK_BOX (vbox22), vbuttonbox3, FALSE, TRUE, 0); + + button58 = gtk_button_new_with_mnemonic (_("Set PC")); + gtk_widget_show (button58); + gtk_container_add (GTK_CONTAINER (vbuttonbox3), button58); + GTK_WIDGET_SET_FLAGS (button58, GTK_CAN_DEFAULT); + + button59 = gtk_button_new_with_mnemonic (_("Set BP Addr")); + gtk_widget_show (button59); + gtk_container_add (GTK_CONTAINER (vbuttonbox3), button59); + GTK_WIDGET_SET_FLAGS (button59, GTK_CAN_DEFAULT); + + button60 = gtk_button_new_with_mnemonic (_("Set BP Count")); + gtk_widget_show (button60); + gtk_container_add (GTK_CONTAINER (vbuttonbox3), button60); + GTK_WIDGET_SET_FLAGS (button60, GTK_CAN_DEFAULT); + + button61 = gtk_button_new_with_mnemonic (_("Clear BPs")); + gtk_widget_show (button61); + gtk_container_add (GTK_CONTAINER (vbuttonbox3), button61); + GTK_WIDGET_SET_FLAGS (button61, GTK_CAN_DEFAULT); + + vbuttonbox1 = gtk_vbutton_box_new (); + gtk_widget_show (vbuttonbox1); + gtk_box_pack_start (GTK_BOX (vbox22), vbuttonbox1, FALSE, TRUE, 0); + + button39 = gtk_button_new_with_mnemonic (_("Dump code")); + gtk_widget_show (button39); + gtk_container_add (GTK_CONTAINER (vbuttonbox1), button39); + GTK_WIDGET_SET_FLAGS (button39, GTK_CAN_DEFAULT); + + button40 = gtk_button_new_with_mnemonic (_("Raw Dump")); + gtk_widget_show (button40); + gtk_container_add (GTK_CONTAINER (vbuttonbox1), button40); + GTK_WIDGET_SET_FLAGS (button40, GTK_CAN_DEFAULT); + + button41 = gtk_button_new_with_mnemonic (_("Close")); + gtk_widget_show (button41); + gtk_container_add (GTK_CONTAINER (vbuttonbox1), button41); + GTK_WIDGET_SET_FLAGS (button41, GTK_CAN_DEFAULT); + + vbuttonbox4 = gtk_vbutton_box_new (); + gtk_widget_show (vbuttonbox4); + gtk_box_pack_start (GTK_BOX (hbox6), vbuttonbox4, FALSE, TRUE, 0); + gtk_box_set_spacing (GTK_BOX (vbuttonbox4), 10); + + button68 = gtk_button_new_with_mnemonic (_("memWrite32")); + gtk_widget_show (button68); + gtk_container_add (GTK_CONTAINER (vbuttonbox4), button68); + GTK_WIDGET_SET_FLAGS (button68, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) GtkRadioButton_EE, "toggled", + G_CALLBACK (OnDebug_EEMode), + NULL); + g_signal_connect ((gpointer) GtkRadioButton_IOP, "toggled", + G_CALLBACK (OnDebug_IOPMode), + NULL); + g_signal_connect ((gpointer) button52, "clicked", + G_CALLBACK (OnDebug_Step), + NULL); + g_signal_connect ((gpointer) button53, "clicked", + G_CALLBACK (OnDebug_Skip), + NULL); + g_signal_connect ((gpointer) button65, "clicked", + G_CALLBACK (OnDebug_Go), + NULL); + g_signal_connect ((gpointer) button64, "clicked", + G_CALLBACK (OnDebug_Log), + NULL); + g_signal_connect ((gpointer) button58, "clicked", + G_CALLBACK (OnDebug_SetPC), + NULL); + g_signal_connect ((gpointer) button59, "clicked", + G_CALLBACK (OnDebug_SetBPA), + NULL); + g_signal_connect ((gpointer) button60, "clicked", + G_CALLBACK (OnDebug_SetBPC), + NULL); + g_signal_connect ((gpointer) button61, "clicked", + G_CALLBACK (OnDebug_ClearBPs), + NULL); + g_signal_connect ((gpointer) button39, "clicked", + G_CALLBACK (OnDebug_DumpCode), + NULL); + g_signal_connect ((gpointer) button40, "clicked", + G_CALLBACK (OnDebug_RawDump), + NULL); + g_signal_connect ((gpointer) button41, "clicked", + G_CALLBACK (OnDebug_Close), + NULL); + g_signal_connect ((gpointer) button68, "clicked", + G_CALLBACK (OnDebug_memWrite32), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (DebugWnd, DebugWnd, "DebugWnd"); + GLADE_HOOKUP_OBJECT (DebugWnd, vbox16, "vbox16"); + GLADE_HOOKUP_OBJECT (DebugWnd, hbox16, "hbox16"); + GLADE_HOOKUP_OBJECT (DebugWnd, GtkRadioButton_EE, "GtkRadioButton_EE"); + GLADE_HOOKUP_OBJECT (DebugWnd, GtkRadioButton_IOP, "GtkRadioButton_IOP"); + GLADE_HOOKUP_OBJECT (DebugWnd, hbox6, "hbox6"); + GLADE_HOOKUP_OBJECT (DebugWnd, hbox7, "hbox7"); + GLADE_HOOKUP_OBJECT (DebugWnd, scrolledwindow1, "scrolledwindow1"); + GLADE_HOOKUP_OBJECT (DebugWnd, viewport1, "viewport1"); + GLADE_HOOKUP_OBJECT (DebugWnd, GtkList_DisView, "GtkList_DisView"); + GLADE_HOOKUP_OBJECT (DebugWnd, GtkVScrollbar_VList, "GtkVScrollbar_VList"); + GLADE_HOOKUP_OBJECT (DebugWnd, vbox22, "vbox22"); + GLADE_HOOKUP_OBJECT (DebugWnd, vbuttonbox2, "vbuttonbox2"); + GLADE_HOOKUP_OBJECT (DebugWnd, button52, "button52"); + GLADE_HOOKUP_OBJECT (DebugWnd, button53, "button53"); + GLADE_HOOKUP_OBJECT (DebugWnd, button65, "button65"); + GLADE_HOOKUP_OBJECT (DebugWnd, button64, "button64"); + GLADE_HOOKUP_OBJECT (DebugWnd, vbuttonbox3, "vbuttonbox3"); + GLADE_HOOKUP_OBJECT (DebugWnd, button58, "button58"); + GLADE_HOOKUP_OBJECT (DebugWnd, button59, "button59"); + GLADE_HOOKUP_OBJECT (DebugWnd, button60, "button60"); + GLADE_HOOKUP_OBJECT (DebugWnd, button61, "button61"); + GLADE_HOOKUP_OBJECT (DebugWnd, vbuttonbox1, "vbuttonbox1"); + GLADE_HOOKUP_OBJECT (DebugWnd, button39, "button39"); + GLADE_HOOKUP_OBJECT (DebugWnd, button40, "button40"); + GLADE_HOOKUP_OBJECT (DebugWnd, button41, "button41"); + GLADE_HOOKUP_OBJECT (DebugWnd, vbuttonbox4, "vbuttonbox4"); + GLADE_HOOKUP_OBJECT (DebugWnd, button68, "button68"); + + return DebugWnd; +} + +GtkWidget* +create_ConfDlg (void) +{ + GtkWidget *ConfDlg; + GtkWidget *vbox12; + GtkWidget *table2; + GtkWidget *GtkLabel_Bios; + GtkWidget *label30; + GtkWidget *hbuttonbox24; + GtkWidget *GtkButton_FWconfigure; + GtkWidget *GtkButton_FWtest; + GtkWidget *GtkButton_FireWireabout; + GtkWidget *hbuttonbox23; + GtkWidget *GtkButton_USBconfigure; + GtkWidget *GtkButton_USBtest; + GtkWidget *GtkButton_USBabout; + GtkWidget *label29; + GtkWidget *hbuttonbox13; + GtkWidget *GtkButton_CDVDconfigure; + GtkWidget *GtkButton_CDVDtest; + GtkWidget *GtkButton_CDVDabout; + GtkWidget *GtkLabel_Cdvdrom; + GtkWidget *label23; + GtkWidget *hbuttonbox21; + GtkWidget *GtkButton_DEV9configure; + GtkWidget *GtkButton_DEV9test; + GtkWidget *GtkButton_DEV9about; + GtkWidget *hbuttonbox12; + GtkWidget *GtkButton_SPU2configure; + GtkWidget *GtkButton_SPU2test; + GtkWidget *GtkButton_SPU2about; + GtkWidget *GtkLabel_Sound; + GtkWidget *GtkLabel_SecondController; + GtkWidget *GtkLabel_FirstController; + GtkWidget *GtkLabel_Graphics; + GtkWidget *hbuttonbox8; + GtkWidget *GtkButton_GSconfigure; + GtkWidget *GtkButton_GStest; + GtkWidget *GtkButton_GSabout; + GtkWidget *hbuttonbox7; + GtkWidget *GtkButton_PAD1configure; + GtkWidget *GtkButton_PAD1test; + GtkWidget *GtkButton_PAD1about; + GtkWidget *hbuttonbox6; + GtkWidget *GtkButton_PAD2configure; + GtkWidget *GtkButton_PAD2test; + GtkWidget *GtkButton_PAD2about; + GtkWidget *GtkCombo_FW; + GtkWidget *GtkCombo_Usb; + GtkWidget *GtkCombo_Cdvd; + GtkWidget *GtkCombo_Dev9; + GtkWidget *GtkCombo_Pad2; + GtkWidget *GtkCombo_Pad1; + GtkWidget *GtkCombo_Spu2; + GtkWidget *GtkCombo_Gs; + GtkWidget *GtkCombo_Bios; + GtkWidget *hbox5; + GtkWidget *hbuttonbox11; + GtkWidget *GtkButton_SelectPluginsDir; + GtkWidget *GtkButton_SelectBiosDir; + GtkWidget *hbuttonbox10; + GtkWidget *GtkButton_Ok1; + GtkWidget *GtkButton_Cancel; + + ConfDlg = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (ConfDlg), 10); + gtk_window_set_title (GTK_WINDOW (ConfDlg), _("Conf")); + gtk_window_set_resizable (GTK_WINDOW (ConfDlg), FALSE); + + vbox12 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox12); + gtk_container_add (GTK_CONTAINER (ConfDlg), vbox12); + + table2 = gtk_table_new (14, 2, FALSE); + gtk_widget_show (table2); + gtk_box_pack_start (GTK_BOX (vbox12), table2, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (table2), 2); + gtk_table_set_col_spacings (GTK_TABLE (table2), 15); + + GtkLabel_Bios = gtk_label_new (_("Bios")); + gtk_widget_show (GtkLabel_Bios); + gtk_table_attach (GTK_TABLE (table2), GtkLabel_Bios, 1, 2, 12, 13, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (GtkLabel_Bios), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (GtkLabel_Bios), 0, 0.5); + + label30 = gtk_label_new (_("FireWire")); + gtk_widget_show (label30); + gtk_table_attach (GTK_TABLE (table2), label30, 1, 2, 9, 10, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (label30), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (label30), 0, 0.5); + + hbuttonbox24 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox24); + gtk_table_attach (GTK_TABLE (table2), hbuttonbox24, 1, 2, 11, 12, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + + GtkButton_FWconfigure = gtk_button_new_with_mnemonic (_("Configure")); + gtk_widget_show (GtkButton_FWconfigure); + gtk_container_add (GTK_CONTAINER (hbuttonbox24), GtkButton_FWconfigure); + GTK_WIDGET_SET_FLAGS (GtkButton_FWconfigure, GTK_CAN_DEFAULT); + + GtkButton_FWtest = gtk_button_new_with_mnemonic (_("Test")); + gtk_widget_show (GtkButton_FWtest); + gtk_container_add (GTK_CONTAINER (hbuttonbox24), GtkButton_FWtest); + GTK_WIDGET_SET_FLAGS (GtkButton_FWtest, GTK_CAN_DEFAULT); + + GtkButton_FireWireabout = gtk_button_new_with_mnemonic (_("About")); + gtk_widget_show (GtkButton_FireWireabout); + gtk_container_add (GTK_CONTAINER (hbuttonbox24), GtkButton_FireWireabout); + GTK_WIDGET_SET_FLAGS (GtkButton_FireWireabout, GTK_CAN_DEFAULT); + + hbuttonbox23 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox23); + gtk_table_attach (GTK_TABLE (table2), hbuttonbox23, 0, 1, 11, 12, + (GtkAttachOptions) (0), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkButton_USBconfigure = gtk_button_new_with_mnemonic (_("Configure")); + gtk_widget_show (GtkButton_USBconfigure); + gtk_container_add (GTK_CONTAINER (hbuttonbox23), GtkButton_USBconfigure); + GTK_WIDGET_SET_FLAGS (GtkButton_USBconfigure, GTK_CAN_DEFAULT); + + GtkButton_USBtest = gtk_button_new_with_mnemonic (_("Test")); + gtk_widget_show (GtkButton_USBtest); + gtk_container_add (GTK_CONTAINER (hbuttonbox23), GtkButton_USBtest); + GTK_WIDGET_SET_FLAGS (GtkButton_USBtest, GTK_CAN_DEFAULT); + + GtkButton_USBabout = gtk_button_new_with_mnemonic (_("About")); + gtk_widget_show (GtkButton_USBabout); + gtk_container_add (GTK_CONTAINER (hbuttonbox23), GtkButton_USBabout); + GTK_WIDGET_SET_FLAGS (GtkButton_USBabout, GTK_CAN_DEFAULT); + + label29 = gtk_label_new (_("Usb")); + gtk_widget_show (label29); + gtk_table_attach (GTK_TABLE (table2), label29, 0, 1, 9, 10, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (label29), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (label29), 0, 0.5); + + hbuttonbox13 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox13); + gtk_table_attach (GTK_TABLE (table2), hbuttonbox13, 1, 2, 8, 9, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + + GtkButton_CDVDconfigure = gtk_button_new_with_mnemonic (_("Configure")); + gtk_widget_show (GtkButton_CDVDconfigure); + gtk_container_add (GTK_CONTAINER (hbuttonbox13), GtkButton_CDVDconfigure); + GTK_WIDGET_SET_FLAGS (GtkButton_CDVDconfigure, GTK_CAN_DEFAULT); + + GtkButton_CDVDtest = gtk_button_new_with_mnemonic (_("Test")); + gtk_widget_show (GtkButton_CDVDtest); + gtk_container_add (GTK_CONTAINER (hbuttonbox13), GtkButton_CDVDtest); + GTK_WIDGET_SET_FLAGS (GtkButton_CDVDtest, GTK_CAN_DEFAULT); + + GtkButton_CDVDabout = gtk_button_new_with_mnemonic (_("About")); + gtk_widget_show (GtkButton_CDVDabout); + gtk_container_add (GTK_CONTAINER (hbuttonbox13), GtkButton_CDVDabout); + GTK_WIDGET_SET_FLAGS (GtkButton_CDVDabout, GTK_CAN_DEFAULT); + + GtkLabel_Cdvdrom = gtk_label_new (_("Cdvdrom")); + gtk_widget_show (GtkLabel_Cdvdrom); + gtk_table_attach (GTK_TABLE (table2), GtkLabel_Cdvdrom, 1, 2, 6, 7, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (GtkLabel_Cdvdrom), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (GtkLabel_Cdvdrom), 0, 0.5); + + label23 = gtk_label_new (_("Dev9")); + gtk_widget_show (label23); + gtk_table_attach (GTK_TABLE (table2), label23, 0, 1, 6, 7, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (label23), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (label23), 0, 0.5); + + hbuttonbox21 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox21); + gtk_table_attach (GTK_TABLE (table2), hbuttonbox21, 0, 1, 8, 9, + (GtkAttachOptions) (0), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkButton_DEV9configure = gtk_button_new_with_mnemonic (_("Configure")); + gtk_widget_show (GtkButton_DEV9configure); + gtk_container_add (GTK_CONTAINER (hbuttonbox21), GtkButton_DEV9configure); + GTK_WIDGET_SET_FLAGS (GtkButton_DEV9configure, GTK_CAN_DEFAULT); + + GtkButton_DEV9test = gtk_button_new_with_mnemonic (_("Test")); + gtk_widget_show (GtkButton_DEV9test); + gtk_container_add (GTK_CONTAINER (hbuttonbox21), GtkButton_DEV9test); + GTK_WIDGET_SET_FLAGS (GtkButton_DEV9test, GTK_CAN_DEFAULT); + + GtkButton_DEV9about = gtk_button_new_with_mnemonic (_("About")); + gtk_widget_show (GtkButton_DEV9about); + gtk_container_add (GTK_CONTAINER (hbuttonbox21), GtkButton_DEV9about); + GTK_WIDGET_SET_FLAGS (GtkButton_DEV9about, GTK_CAN_DEFAULT); + + hbuttonbox12 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox12); + gtk_table_attach (GTK_TABLE (table2), hbuttonbox12, 1, 2, 2, 3, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + + GtkButton_SPU2configure = gtk_button_new_with_mnemonic (_("Configure")); + gtk_widget_show (GtkButton_SPU2configure); + gtk_container_add (GTK_CONTAINER (hbuttonbox12), GtkButton_SPU2configure); + GTK_WIDGET_SET_FLAGS (GtkButton_SPU2configure, GTK_CAN_DEFAULT); + + GtkButton_SPU2test = gtk_button_new_with_mnemonic (_("Test")); + gtk_widget_show (GtkButton_SPU2test); + gtk_container_add (GTK_CONTAINER (hbuttonbox12), GtkButton_SPU2test); + GTK_WIDGET_SET_FLAGS (GtkButton_SPU2test, GTK_CAN_DEFAULT); + + GtkButton_SPU2about = gtk_button_new_with_mnemonic (_("About")); + gtk_widget_show (GtkButton_SPU2about); + gtk_container_add (GTK_CONTAINER (hbuttonbox12), GtkButton_SPU2about); + GTK_WIDGET_SET_FLAGS (GtkButton_SPU2about, GTK_CAN_DEFAULT); + + GtkLabel_Sound = gtk_label_new (_("Sound")); + gtk_widget_show (GtkLabel_Sound); + gtk_table_attach (GTK_TABLE (table2), GtkLabel_Sound, 1, 2, 0, 1, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (GtkLabel_Sound), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (GtkLabel_Sound), 0, 0.5); + + GtkLabel_SecondController = gtk_label_new (_("Second Controller")); + gtk_widget_show (GtkLabel_SecondController); + gtk_table_attach (GTK_TABLE (table2), GtkLabel_SecondController, 1, 2, 3, 4, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (GtkLabel_SecondController), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (GtkLabel_SecondController), 0, 0.5); + + GtkLabel_FirstController = gtk_label_new (_("First Controller")); + gtk_widget_show (GtkLabel_FirstController); + gtk_table_attach (GTK_TABLE (table2), GtkLabel_FirstController, 0, 1, 3, 4, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (GtkLabel_FirstController), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (GtkLabel_FirstController), 0, 0.5); + + GtkLabel_Graphics = gtk_label_new (_("Graphics")); + gtk_widget_show (GtkLabel_Graphics); + gtk_table_attach (GTK_TABLE (table2), GtkLabel_Graphics, 0, 1, 0, 1, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (GtkLabel_Graphics), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (GtkLabel_Graphics), 0, 0.5); + + hbuttonbox8 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox8); + gtk_table_attach (GTK_TABLE (table2), hbuttonbox8, 0, 1, 2, 3, + (GtkAttachOptions) (0), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkButton_GSconfigure = gtk_button_new_with_mnemonic (_("Configure")); + gtk_widget_show (GtkButton_GSconfigure); + gtk_container_add (GTK_CONTAINER (hbuttonbox8), GtkButton_GSconfigure); + GTK_WIDGET_SET_FLAGS (GtkButton_GSconfigure, GTK_CAN_DEFAULT); + + GtkButton_GStest = gtk_button_new_with_mnemonic (_("Test")); + gtk_widget_show (GtkButton_GStest); + gtk_container_add (GTK_CONTAINER (hbuttonbox8), GtkButton_GStest); + GTK_WIDGET_SET_FLAGS (GtkButton_GStest, GTK_CAN_DEFAULT); + + GtkButton_GSabout = gtk_button_new_with_mnemonic (_("About")); + gtk_widget_show (GtkButton_GSabout); + gtk_container_add (GTK_CONTAINER (hbuttonbox8), GtkButton_GSabout); + GTK_WIDGET_SET_FLAGS (GtkButton_GSabout, GTK_CAN_DEFAULT); + + hbuttonbox7 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox7); + gtk_table_attach (GTK_TABLE (table2), hbuttonbox7, 0, 1, 5, 6, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + + GtkButton_PAD1configure = gtk_button_new_with_mnemonic (_("Configure")); + gtk_widget_show (GtkButton_PAD1configure); + gtk_container_add (GTK_CONTAINER (hbuttonbox7), GtkButton_PAD1configure); + GTK_WIDGET_SET_FLAGS (GtkButton_PAD1configure, GTK_CAN_DEFAULT); + + GtkButton_PAD1test = gtk_button_new_with_mnemonic (_("Test")); + gtk_widget_show (GtkButton_PAD1test); + gtk_container_add (GTK_CONTAINER (hbuttonbox7), GtkButton_PAD1test); + GTK_WIDGET_SET_FLAGS (GtkButton_PAD1test, GTK_CAN_DEFAULT); + + GtkButton_PAD1about = gtk_button_new_with_mnemonic (_("About")); + gtk_widget_show (GtkButton_PAD1about); + gtk_container_add (GTK_CONTAINER (hbuttonbox7), GtkButton_PAD1about); + GTK_WIDGET_SET_FLAGS (GtkButton_PAD1about, GTK_CAN_DEFAULT); + + hbuttonbox6 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox6); + gtk_table_attach (GTK_TABLE (table2), hbuttonbox6, 1, 2, 5, 6, + (GtkAttachOptions) (0), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkButton_PAD2configure = gtk_button_new_with_mnemonic (_("Configure")); + gtk_widget_show (GtkButton_PAD2configure); + gtk_container_add (GTK_CONTAINER (hbuttonbox6), GtkButton_PAD2configure); + GTK_WIDGET_SET_FLAGS (GtkButton_PAD2configure, GTK_CAN_DEFAULT); + + GtkButton_PAD2test = gtk_button_new_with_mnemonic (_("Test")); + gtk_widget_show (GtkButton_PAD2test); + gtk_container_add (GTK_CONTAINER (hbuttonbox6), GtkButton_PAD2test); + GTK_WIDGET_SET_FLAGS (GtkButton_PAD2test, GTK_CAN_DEFAULT); + + GtkButton_PAD2about = gtk_button_new_with_mnemonic (_("About")); + gtk_widget_show (GtkButton_PAD2about); + gtk_container_add (GTK_CONTAINER (hbuttonbox6), GtkButton_PAD2about); + GTK_WIDGET_SET_FLAGS (GtkButton_PAD2about, GTK_CAN_DEFAULT); + + GtkCombo_FW = gtk_combo_box_new_text (); + gtk_widget_show (GtkCombo_FW); + gtk_table_attach (GTK_TABLE (table2), GtkCombo_FW, 1, 2, 10, 11, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkCombo_Usb = gtk_combo_box_new_text (); + gtk_widget_show (GtkCombo_Usb); + gtk_table_attach (GTK_TABLE (table2), GtkCombo_Usb, 0, 1, 10, 11, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkCombo_Cdvd = gtk_combo_box_new_text (); + gtk_widget_show (GtkCombo_Cdvd); + gtk_table_attach (GTK_TABLE (table2), GtkCombo_Cdvd, 1, 2, 7, 8, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkCombo_Dev9 = gtk_combo_box_new_text (); + gtk_widget_show (GtkCombo_Dev9); + gtk_table_attach (GTK_TABLE (table2), GtkCombo_Dev9, 0, 1, 7, 8, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkCombo_Pad2 = gtk_combo_box_new_text (); + gtk_widget_show (GtkCombo_Pad2); + gtk_table_attach (GTK_TABLE (table2), GtkCombo_Pad2, 1, 2, 4, 5, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkCombo_Pad1 = gtk_combo_box_new_text (); + gtk_widget_show (GtkCombo_Pad1); + gtk_table_attach (GTK_TABLE (table2), GtkCombo_Pad1, 0, 1, 4, 5, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkCombo_Spu2 = gtk_combo_box_new_text (); + gtk_widget_show (GtkCombo_Spu2); + gtk_table_attach (GTK_TABLE (table2), GtkCombo_Spu2, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkCombo_Gs = gtk_combo_box_new_text (); + gtk_widget_show (GtkCombo_Gs); + gtk_table_attach (GTK_TABLE (table2), GtkCombo_Gs, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + GtkCombo_Bios = gtk_combo_box_new_text (); + gtk_widget_show (GtkCombo_Bios); + gtk_table_attach (GTK_TABLE (table2), GtkCombo_Bios, 1, 2, 13, 14, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + hbox5 = gtk_hbox_new (FALSE, 14); + gtk_widget_show (hbox5); + gtk_box_pack_start (GTK_BOX (vbox12), hbox5, TRUE, TRUE, 0); + + hbuttonbox11 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox11); + gtk_box_pack_start (GTK_BOX (hbox5), hbuttonbox11, FALSE, TRUE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox11), GTK_BUTTONBOX_START); + + GtkButton_SelectPluginsDir = gtk_button_new_with_mnemonic (_("Select Plugins Dir")); + gtk_widget_show (GtkButton_SelectPluginsDir); + gtk_container_add (GTK_CONTAINER (hbuttonbox11), GtkButton_SelectPluginsDir); + GTK_WIDGET_SET_FLAGS (GtkButton_SelectPluginsDir, GTK_CAN_DEFAULT); + + GtkButton_SelectBiosDir = gtk_button_new_with_mnemonic (_("Select Bios Dir")); + gtk_widget_show (GtkButton_SelectBiosDir); + gtk_container_add (GTK_CONTAINER (hbuttonbox11), GtkButton_SelectBiosDir); + GTK_WIDGET_SET_FLAGS (GtkButton_SelectBiosDir, GTK_CAN_DEFAULT); + + hbuttonbox10 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox10); + gtk_box_pack_start (GTK_BOX (hbox5), hbuttonbox10, TRUE, TRUE, 0); + + GtkButton_Ok1 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (GtkButton_Ok1); + gtk_container_add (GTK_CONTAINER (hbuttonbox10), GtkButton_Ok1); + GTK_WIDGET_SET_FLAGS (GtkButton_Ok1, GTK_CAN_DEFAULT); + + GtkButton_Cancel = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (GtkButton_Cancel); + gtk_container_add (GTK_CONTAINER (hbuttonbox10), GtkButton_Cancel); + GTK_WIDGET_SET_FLAGS (GtkButton_Cancel, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) GtkButton_FWconfigure, "clicked", + G_CALLBACK (OnConfConf_FWConf), + NULL); + g_signal_connect ((gpointer) GtkButton_FWtest, "clicked", + G_CALLBACK (OnConfConf_FWTest), + NULL); + g_signal_connect ((gpointer) GtkButton_FireWireabout, "clicked", + G_CALLBACK (OnConfConf_FWAbout), + NULL); + g_signal_connect ((gpointer) GtkButton_USBconfigure, "clicked", + G_CALLBACK (OnConfConf_UsbConf), + NULL); + g_signal_connect ((gpointer) GtkButton_USBtest, "clicked", + G_CALLBACK (OnConfConf_UsbTest), + NULL); + g_signal_connect ((gpointer) GtkButton_USBabout, "clicked", + G_CALLBACK (OnConfConf_UsbAbout), + NULL); + g_signal_connect ((gpointer) GtkButton_CDVDconfigure, "clicked", + G_CALLBACK (OnConfConf_CdvdConf), + NULL); + g_signal_connect ((gpointer) GtkButton_CDVDtest, "clicked", + G_CALLBACK (OnConfConf_CdvdTest), + NULL); + g_signal_connect ((gpointer) GtkButton_CDVDabout, "clicked", + G_CALLBACK (OnConfConf_CdvdAbout), + NULL); + g_signal_connect ((gpointer) GtkButton_DEV9configure, "clicked", + G_CALLBACK (OnConfConf_Dev9Conf), + NULL); + g_signal_connect ((gpointer) GtkButton_DEV9test, "clicked", + G_CALLBACK (OnConfConf_Dev9Test), + NULL); + g_signal_connect ((gpointer) GtkButton_DEV9about, "clicked", + G_CALLBACK (OnConfConf_Dev9About), + NULL); + g_signal_connect ((gpointer) GtkButton_SPU2configure, "clicked", + G_CALLBACK (OnConfConf_Spu2Conf), + NULL); + g_signal_connect ((gpointer) GtkButton_SPU2test, "clicked", + G_CALLBACK (OnConfConf_Spu2Test), + NULL); + g_signal_connect ((gpointer) GtkButton_SPU2about, "clicked", + G_CALLBACK (OnConfConf_Spu2About), + NULL); + g_signal_connect ((gpointer) GtkButton_GSconfigure, "clicked", + G_CALLBACK (OnConfConf_GsConf), + NULL); + g_signal_connect ((gpointer) GtkButton_GStest, "clicked", + G_CALLBACK (OnConfConf_GsTest), + NULL); + g_signal_connect ((gpointer) GtkButton_GSabout, "clicked", + G_CALLBACK (OnConfConf_GsAbout), + NULL); + g_signal_connect ((gpointer) GtkButton_PAD1configure, "clicked", + G_CALLBACK (OnConfConf_Pad1Conf), + NULL); + g_signal_connect ((gpointer) GtkButton_PAD1test, "clicked", + G_CALLBACK (OnConfConf_Pad1Test), + NULL); + g_signal_connect ((gpointer) GtkButton_PAD1about, "clicked", + G_CALLBACK (OnConfConf_Pad1About), + NULL); + g_signal_connect ((gpointer) GtkButton_PAD2configure, "clicked", + G_CALLBACK (OnConfConf_Pad2Conf), + NULL); + g_signal_connect ((gpointer) GtkButton_PAD2test, "clicked", + G_CALLBACK (OnConfConf_Pad2Test), + NULL); + g_signal_connect ((gpointer) GtkButton_PAD2about, "clicked", + G_CALLBACK (OnConfConf_Pad2About), + NULL); + g_signal_connect ((gpointer) GtkButton_SelectPluginsDir, "clicked", + G_CALLBACK (OnConfConf_PluginsPath), + NULL); + g_signal_connect ((gpointer) GtkButton_SelectBiosDir, "clicked", + G_CALLBACK (OnConfConf_BiosPath), + NULL); + g_signal_connect ((gpointer) GtkButton_Ok1, "clicked", + G_CALLBACK (OnConfConf_Ok), + NULL); + g_signal_connect ((gpointer) GtkButton_Cancel, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (ConfDlg, ConfDlg, "ConfDlg"); + GLADE_HOOKUP_OBJECT (ConfDlg, vbox12, "vbox12"); + GLADE_HOOKUP_OBJECT (ConfDlg, table2, "table2"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkLabel_Bios, "GtkLabel_Bios"); + GLADE_HOOKUP_OBJECT (ConfDlg, label30, "label30"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbuttonbox24, "hbuttonbox24"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_FWconfigure, "GtkButton_FWconfigure"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_FWtest, "GtkButton_FWtest"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_FireWireabout, "GtkButton_FireWireabout"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbuttonbox23, "hbuttonbox23"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_USBconfigure, "GtkButton_USBconfigure"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_USBtest, "GtkButton_USBtest"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_USBabout, "GtkButton_USBabout"); + GLADE_HOOKUP_OBJECT (ConfDlg, label29, "label29"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbuttonbox13, "hbuttonbox13"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_CDVDconfigure, "GtkButton_CDVDconfigure"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_CDVDtest, "GtkButton_CDVDtest"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_CDVDabout, "GtkButton_CDVDabout"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkLabel_Cdvdrom, "GtkLabel_Cdvdrom"); + GLADE_HOOKUP_OBJECT (ConfDlg, label23, "label23"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbuttonbox21, "hbuttonbox21"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_DEV9configure, "GtkButton_DEV9configure"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_DEV9test, "GtkButton_DEV9test"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_DEV9about, "GtkButton_DEV9about"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbuttonbox12, "hbuttonbox12"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_SPU2configure, "GtkButton_SPU2configure"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_SPU2test, "GtkButton_SPU2test"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_SPU2about, "GtkButton_SPU2about"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkLabel_Sound, "GtkLabel_Sound"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkLabel_SecondController, "GtkLabel_SecondController"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkLabel_FirstController, "GtkLabel_FirstController"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkLabel_Graphics, "GtkLabel_Graphics"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbuttonbox8, "hbuttonbox8"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_GSconfigure, "GtkButton_GSconfigure"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_GStest, "GtkButton_GStest"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_GSabout, "GtkButton_GSabout"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbuttonbox7, "hbuttonbox7"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_PAD1configure, "GtkButton_PAD1configure"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_PAD1test, "GtkButton_PAD1test"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_PAD1about, "GtkButton_PAD1about"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbuttonbox6, "hbuttonbox6"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_PAD2configure, "GtkButton_PAD2configure"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_PAD2test, "GtkButton_PAD2test"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_PAD2about, "GtkButton_PAD2about"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkCombo_FW, "GtkCombo_FW"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkCombo_Usb, "GtkCombo_Usb"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkCombo_Cdvd, "GtkCombo_Cdvd"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkCombo_Dev9, "GtkCombo_Dev9"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkCombo_Pad2, "GtkCombo_Pad2"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkCombo_Pad1, "GtkCombo_Pad1"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkCombo_Spu2, "GtkCombo_Spu2"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkCombo_Gs, "GtkCombo_Gs"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkCombo_Bios, "GtkCombo_Bios"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbox5, "hbox5"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbuttonbox11, "hbuttonbox11"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_SelectPluginsDir, "GtkButton_SelectPluginsDir"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_SelectBiosDir, "GtkButton_SelectBiosDir"); + GLADE_HOOKUP_OBJECT (ConfDlg, hbuttonbox10, "hbuttonbox10"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_Ok1, "GtkButton_Ok1"); + GLADE_HOOKUP_OBJECT (ConfDlg, GtkButton_Cancel, "GtkButton_Cancel"); + + return ConfDlg; +} + +GtkWidget* +create_AboutDlg (void) +{ + GtkWidget *AboutDlg; + GtkWidget *vbox2; + GtkWidget *vbox4; + GtkWidget *GtkAbout_LabelVersion; + GtkWidget *frame1; + GtkWidget *vbox6; + GtkWidget *GtkAbout_LabelAuthors; + GtkWidget *frame2; + GtkWidget *vbox5; + GtkWidget *GtkAbout_LabelGreets; + GtkWidget *hbuttonbox1; + GtkWidget *GtkButton_Ok; + + AboutDlg = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (AboutDlg), 10); + gtk_window_set_title (GTK_WINDOW (AboutDlg), _("Pcsx About")); + + vbox2 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox2); + gtk_container_add (GTK_CONTAINER (AboutDlg), vbox2); + + vbox4 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox4); + gtk_box_pack_start (GTK_BOX (vbox2), vbox4, TRUE, TRUE, 0); + + GtkAbout_LabelVersion = gtk_label_new (_("PCSX2\n\nVersion x.x")); + gtk_widget_show (GtkAbout_LabelVersion); + gtk_box_pack_start (GTK_BOX (vbox4), GtkAbout_LabelVersion, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (GtkAbout_LabelVersion), GTK_JUSTIFY_CENTER); + + frame1 = gtk_frame_new (NULL); + gtk_widget_show (frame1); + gtk_box_pack_start (GTK_BOX (vbox4), frame1, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (frame1), 5); + + vbox6 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox6); + gtk_container_add (GTK_CONTAINER (frame1), vbox6); + gtk_container_set_border_width (GTK_CONTAINER (vbox6), 5); + + GtkAbout_LabelAuthors = gtk_label_new (_("written by...")); + gtk_widget_show (GtkAbout_LabelAuthors); + gtk_box_pack_start (GTK_BOX (vbox6), GtkAbout_LabelAuthors, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (GtkAbout_LabelAuthors), GTK_JUSTIFY_CENTER); + gtk_label_set_line_wrap (GTK_LABEL (GtkAbout_LabelAuthors), TRUE); + + frame2 = gtk_frame_new (NULL); + gtk_widget_show (frame2); + gtk_box_pack_start (GTK_BOX (vbox2), frame2, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (frame2), 5); + + vbox5 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox5); + gtk_container_add (GTK_CONTAINER (frame2), vbox5); + gtk_container_set_border_width (GTK_CONTAINER (vbox5), 5); + + GtkAbout_LabelGreets = gtk_label_new (_("greets to...")); + gtk_widget_show (GtkAbout_LabelGreets); + gtk_box_pack_start (GTK_BOX (vbox5), GtkAbout_LabelGreets, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (GtkAbout_LabelGreets), GTK_JUSTIFY_CENTER); + gtk_label_set_line_wrap (GTK_LABEL (GtkAbout_LabelGreets), TRUE); + + hbuttonbox1 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox1); + gtk_box_pack_start (GTK_BOX (vbox2), hbuttonbox1, FALSE, FALSE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_EDGE); + + GtkButton_Ok = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (GtkButton_Ok); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), GtkButton_Ok); + GTK_WIDGET_SET_FLAGS (GtkButton_Ok, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) GtkButton_Ok, "clicked", + G_CALLBACK (OnHelpAbout_Ok), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (AboutDlg, AboutDlg, "AboutDlg"); + GLADE_HOOKUP_OBJECT (AboutDlg, vbox2, "vbox2"); + GLADE_HOOKUP_OBJECT (AboutDlg, vbox4, "vbox4"); + GLADE_HOOKUP_OBJECT (AboutDlg, GtkAbout_LabelVersion, "GtkAbout_LabelVersion"); + GLADE_HOOKUP_OBJECT (AboutDlg, frame1, "frame1"); + GLADE_HOOKUP_OBJECT (AboutDlg, vbox6, "vbox6"); + GLADE_HOOKUP_OBJECT (AboutDlg, GtkAbout_LabelAuthors, "GtkAbout_LabelAuthors"); + GLADE_HOOKUP_OBJECT (AboutDlg, frame2, "frame2"); + GLADE_HOOKUP_OBJECT (AboutDlg, vbox5, "vbox5"); + GLADE_HOOKUP_OBJECT (AboutDlg, GtkAbout_LabelGreets, "GtkAbout_LabelGreets"); + GLADE_HOOKUP_OBJECT (AboutDlg, hbuttonbox1, "hbuttonbox1"); + GLADE_HOOKUP_OBJECT (AboutDlg, GtkButton_Ok, "GtkButton_Ok"); + + return AboutDlg; +} + +GtkWidget* +create_MainWindow (void) +{ + GtkWidget *MainWindow; + GtkWidget *vbox1; + GtkWidget *GtkMenuBar_Menu; + GtkWidget *GtkMenuItem_File; + GtkWidget *GtkMenuItem_File_menu; + GtkWidget *run_cd1; + GtkWidget *GtkMenuItem_LoadElf; + GtkWidget *separator2; + GtkWidget *states1; + GtkWidget *states1_menu; + GtkWidget *load1; + GtkWidget *load1_menu; + GtkWidget *slot_1; + GtkWidget *slot_2; + GtkWidget *slot_3_; + GtkWidget *slot_4; + GtkWidget *slot_5; + GtkWidget *other1; + GtkWidget *save1; + GtkWidget *save1_menu; + GtkWidget *slot_6; + GtkWidget *slot_7; + GtkWidget *slot_8; + GtkWidget *slot_9; + GtkWidget *slot_10; + GtkWidget *other2; + GtkWidget *GtkMenuItem_Exit; + GtkWidget *GtkMenuItem_Emulator; + GtkWidget *GtkMenuItem_Emulator_menu; + GtkWidget *GtkMenuItem_Run; + GtkWidget *GtkMenuItem_Reset; + GtkWidget *GtkMenuItem_Arguments; + GtkWidget *GtkMenuItem_Configuration; + GtkWidget *GtkMenuItem_Configuration_menu; + GtkWidget *GtkMenuItem_PluginsBios; + GtkWidget *separator3; + GtkWidget *GtkMenuItem_Graphics; + GtkWidget *GtkMenuItem_Controllers; + GtkWidget *GtkMenuItem_Sound; + GtkWidget *GtkMenuItem_Cdvdrom; + GtkWidget *GtkMenuItem_Dev9; + GtkWidget *GtkMenuItem_USB; + GtkWidget *GtkMenuItem_FW; + GtkWidget *separator4; + GtkWidget *GtkMenuItem_Cpu; + GtkWidget *GtkMenuItem_Game_Fixes; + GtkWidget *GtkMenuItem_Speed_Hacks; + GtkWidget *GtkMenuItem_Advanced; + GtkWidget *GtkMenuItem_Language; + GtkWidget *misc1; + GtkWidget *misc1_menu; + GtkWidget *patch_browser1; + GtkWidget *patch_finder2; + GtkWidget *separator7; + GtkWidget *enable_console1; + GtkWidget *enable_patches1; + GtkWidget *GtkMenuItem_Debug; + GtkWidget *GtkMenuItem_Debug_menu; + GtkWidget *GtkMenuItem_EnterDebugger; + GtkWidget *GtkMenuItem_Logging; + GtkWidget *GtkMenuItem_Help; + GtkWidget *GtkMenuItem_Help_menu; + GtkWidget *GtkMenuItem_About; + GtkWidget *image1; + GtkWidget *status_box; + + MainWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (MainWindow), _("PCSX")); + gtk_window_set_position (GTK_WINDOW (MainWindow), GTK_WIN_POS_CENTER); + + vbox1 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (MainWindow), vbox1); + + GtkMenuBar_Menu = gtk_menu_bar_new (); + gtk_widget_show (GtkMenuBar_Menu); + gtk_box_pack_start (GTK_BOX (vbox1), GtkMenuBar_Menu, FALSE, FALSE, 0); + + GtkMenuItem_File = gtk_menu_item_new_with_mnemonic (_("_File")); + gtk_widget_show (GtkMenuItem_File); + gtk_container_add (GTK_CONTAINER (GtkMenuBar_Menu), GtkMenuItem_File); + + GtkMenuItem_File_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (GtkMenuItem_File), GtkMenuItem_File_menu); + + run_cd1 = gtk_menu_item_new_with_mnemonic (_("_Run CD")); + gtk_widget_show (run_cd1); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_File_menu), run_cd1); + + GtkMenuItem_LoadElf = gtk_menu_item_new_with_mnemonic (_("_Load Elf")); + gtk_widget_show (GtkMenuItem_LoadElf); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_File_menu), GtkMenuItem_LoadElf); + + separator2 = gtk_separator_menu_item_new (); + gtk_widget_show (separator2); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_File_menu), separator2); + gtk_widget_set_sensitive (separator2, FALSE); + + states1 = gtk_menu_item_new_with_mnemonic (_("States")); + gtk_widget_show (states1); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_File_menu), states1); + + states1_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (states1), states1_menu); + + load1 = gtk_menu_item_new_with_mnemonic (_("Load")); + gtk_widget_show (load1); + gtk_container_add (GTK_CONTAINER (states1_menu), load1); + + load1_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (load1), load1_menu); + + slot_1 = gtk_menu_item_new_with_mnemonic (_("Slot 1")); + gtk_widget_show (slot_1); + gtk_container_add (GTK_CONTAINER (load1_menu), slot_1); + + slot_2 = gtk_menu_item_new_with_mnemonic (_("Slot 2")); + gtk_widget_show (slot_2); + gtk_container_add (GTK_CONTAINER (load1_menu), slot_2); + + slot_3_ = gtk_menu_item_new_with_mnemonic (_("Slot 3")); + gtk_widget_show (slot_3_); + gtk_container_add (GTK_CONTAINER (load1_menu), slot_3_); + + slot_4 = gtk_menu_item_new_with_mnemonic (_("Slot 4")); + gtk_widget_show (slot_4); + gtk_container_add (GTK_CONTAINER (load1_menu), slot_4); + + slot_5 = gtk_menu_item_new_with_mnemonic (_("Slot 5")); + gtk_widget_show (slot_5); + gtk_container_add (GTK_CONTAINER (load1_menu), slot_5); + + other1 = gtk_menu_item_new_with_mnemonic (_("Other...")); + gtk_widget_show (other1); + gtk_container_add (GTK_CONTAINER (load1_menu), other1); + + save1 = gtk_menu_item_new_with_mnemonic (_("Save")); + gtk_widget_show (save1); + gtk_container_add (GTK_CONTAINER (states1_menu), save1); + + save1_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (save1), save1_menu); + + slot_6 = gtk_menu_item_new_with_mnemonic (_("Slot 1")); + gtk_widget_show (slot_6); + gtk_container_add (GTK_CONTAINER (save1_menu), slot_6); + + slot_7 = gtk_menu_item_new_with_mnemonic (_("Slot 2")); + gtk_widget_show (slot_7); + gtk_container_add (GTK_CONTAINER (save1_menu), slot_7); + + slot_8 = gtk_menu_item_new_with_mnemonic (_("Slot 3")); + gtk_widget_show (slot_8); + gtk_container_add (GTK_CONTAINER (save1_menu), slot_8); + + slot_9 = gtk_menu_item_new_with_mnemonic (_("Slot 4")); + gtk_widget_show (slot_9); + gtk_container_add (GTK_CONTAINER (save1_menu), slot_9); + + slot_10 = gtk_menu_item_new_with_mnemonic (_("Slot 5")); + gtk_widget_show (slot_10); + gtk_container_add (GTK_CONTAINER (save1_menu), slot_10); + + other2 = gtk_menu_item_new_with_mnemonic (_("Other...")); + gtk_widget_show (other2); + gtk_container_add (GTK_CONTAINER (save1_menu), other2); + + GtkMenuItem_Exit = gtk_menu_item_new_with_mnemonic (_("E_xit")); + gtk_widget_show (GtkMenuItem_Exit); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_File_menu), GtkMenuItem_Exit); + + GtkMenuItem_Emulator = gtk_menu_item_new_with_mnemonic (_("_Run")); + gtk_widget_show (GtkMenuItem_Emulator); + gtk_container_add (GTK_CONTAINER (GtkMenuBar_Menu), GtkMenuItem_Emulator); + + GtkMenuItem_Emulator_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (GtkMenuItem_Emulator), GtkMenuItem_Emulator_menu); + + GtkMenuItem_Run = gtk_menu_item_new_with_mnemonic (_("E_xecute")); + gtk_widget_show (GtkMenuItem_Run); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Emulator_menu), GtkMenuItem_Run); + + GtkMenuItem_Reset = gtk_menu_item_new_with_mnemonic (_("Re_set")); + gtk_widget_show (GtkMenuItem_Reset); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Emulator_menu), GtkMenuItem_Reset); + + GtkMenuItem_Arguments = gtk_menu_item_new_with_mnemonic (_("_Arguments")); + gtk_widget_show (GtkMenuItem_Arguments); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Emulator_menu), GtkMenuItem_Arguments); + + GtkMenuItem_Configuration = gtk_menu_item_new_with_mnemonic (_("_Config")); + gtk_widget_show (GtkMenuItem_Configuration); + gtk_container_add (GTK_CONTAINER (GtkMenuBar_Menu), GtkMenuItem_Configuration); + + GtkMenuItem_Configuration_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (GtkMenuItem_Configuration), GtkMenuItem_Configuration_menu); + + GtkMenuItem_PluginsBios = gtk_menu_item_new_with_mnemonic (_("_Configure")); + gtk_widget_show (GtkMenuItem_PluginsBios); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_PluginsBios); + + separator3 = gtk_separator_menu_item_new (); + gtk_widget_show (separator3); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), separator3); + gtk_widget_set_sensitive (separator3, FALSE); + + GtkMenuItem_Graphics = gtk_menu_item_new_with_mnemonic (_("_Graphics")); + gtk_widget_show (GtkMenuItem_Graphics); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_Graphics); + + GtkMenuItem_Controllers = gtk_menu_item_new_with_mnemonic (_("C_ontrollers")); + gtk_widget_show (GtkMenuItem_Controllers); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_Controllers); + + GtkMenuItem_Sound = gtk_menu_item_new_with_mnemonic (_("_Sound")); + gtk_widget_show (GtkMenuItem_Sound); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_Sound); + + GtkMenuItem_Cdvdrom = gtk_menu_item_new_with_mnemonic (_("_Cdvdrom")); + gtk_widget_show (GtkMenuItem_Cdvdrom); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_Cdvdrom); + + GtkMenuItem_Dev9 = gtk_menu_item_new_with_mnemonic (_("D_ev9")); + gtk_widget_show (GtkMenuItem_Dev9); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_Dev9); + + GtkMenuItem_USB = gtk_menu_item_new_with_mnemonic (_("U_SB")); + gtk_widget_show (GtkMenuItem_USB); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_USB); + + GtkMenuItem_FW = gtk_menu_item_new_with_mnemonic (_("Fire_Wire")); + gtk_widget_show (GtkMenuItem_FW); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_FW); + + separator4 = gtk_separator_menu_item_new (); + gtk_widget_show (separator4); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), separator4); + gtk_widget_set_sensitive (separator4, FALSE); + + GtkMenuItem_Cpu = gtk_menu_item_new_with_mnemonic (_("C_pu")); + gtk_widget_show (GtkMenuItem_Cpu); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_Cpu); + + GtkMenuItem_Game_Fixes = gtk_menu_item_new_with_mnemonic (_("Game Fixes")); + gtk_widget_show (GtkMenuItem_Game_Fixes); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_Game_Fixes); + + GtkMenuItem_Speed_Hacks = gtk_menu_item_new_with_mnemonic (_("Speed Hacks")); + gtk_widget_show (GtkMenuItem_Speed_Hacks); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_Speed_Hacks); + + GtkMenuItem_Advanced = gtk_menu_item_new_with_mnemonic (_("Advanced")); + gtk_widget_show (GtkMenuItem_Advanced); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Configuration_menu), GtkMenuItem_Advanced); + + GtkMenuItem_Language = gtk_menu_item_new_with_mnemonic (_("_Language")); + gtk_widget_show (GtkMenuItem_Language); + gtk_container_add (GTK_CONTAINER (GtkMenuBar_Menu), GtkMenuItem_Language); + + misc1 = gtk_menu_item_new_with_mnemonic (_("_Misc")); + gtk_widget_show (misc1); + gtk_container_add (GTK_CONTAINER (GtkMenuBar_Menu), misc1); + + misc1_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (misc1), misc1_menu); + + patch_browser1 = gtk_menu_item_new_with_mnemonic (_("Patch _Browser")); + gtk_widget_show (patch_browser1); + gtk_container_add (GTK_CONTAINER (misc1_menu), patch_browser1); + + patch_finder2 = gtk_menu_item_new_with_mnemonic (_("Patch _Finder")); + gtk_widget_show (patch_finder2); + gtk_container_add (GTK_CONTAINER (misc1_menu), patch_finder2); + + separator7 = gtk_separator_menu_item_new (); + gtk_widget_show (separator7); + gtk_container_add (GTK_CONTAINER (misc1_menu), separator7); + gtk_widget_set_sensitive (separator7, FALSE); + + enable_console1 = gtk_check_menu_item_new_with_mnemonic (_("Enable _Console")); + gtk_widget_show (enable_console1); + gtk_container_add (GTK_CONTAINER (misc1_menu), enable_console1); + + enable_patches1 = gtk_check_menu_item_new_with_mnemonic (_("Enable _Patches")); + gtk_widget_show (enable_patches1); + gtk_container_add (GTK_CONTAINER (misc1_menu), enable_patches1); + + GtkMenuItem_Debug = gtk_menu_item_new_with_mnemonic (_("_Debug")); + gtk_widget_show (GtkMenuItem_Debug); + gtk_container_add (GTK_CONTAINER (GtkMenuBar_Menu), GtkMenuItem_Debug); + + GtkMenuItem_Debug_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (GtkMenuItem_Debug), GtkMenuItem_Debug_menu); + + GtkMenuItem_EnterDebugger = gtk_menu_item_new_with_mnemonic (_("Enter Debugger ...")); + gtk_widget_show (GtkMenuItem_EnterDebugger); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Debug_menu), GtkMenuItem_EnterDebugger); + + GtkMenuItem_Logging = gtk_menu_item_new_with_mnemonic (_("Logging")); + gtk_widget_show (GtkMenuItem_Logging); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Debug_menu), GtkMenuItem_Logging); + + GtkMenuItem_Help = gtk_menu_item_new_with_mnemonic (_("_Help")); + gtk_widget_show (GtkMenuItem_Help); + gtk_container_add (GTK_CONTAINER (GtkMenuBar_Menu), GtkMenuItem_Help); + + GtkMenuItem_Help_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (GtkMenuItem_Help), GtkMenuItem_Help_menu); + + GtkMenuItem_About = gtk_menu_item_new_with_mnemonic (_("About PCSX2 - Playground...")); + gtk_widget_show (GtkMenuItem_About); + gtk_container_add (GTK_CONTAINER (GtkMenuItem_Help_menu), GtkMenuItem_About); + + image1 = create_pixmap (MainWindow, "pcsxAbout.bmp"); + gtk_widget_show (image1); + gtk_box_pack_start (GTK_BOX (vbox1), image1, TRUE, TRUE, 1); + + status_box = gtk_vbox_new (FALSE, 0); + gtk_widget_show (status_box); + gtk_box_pack_start (GTK_BOX (vbox1), status_box, TRUE, TRUE, 0); + + g_signal_connect ((gpointer) MainWindow, "destroy", + G_CALLBACK (OnDestroy), + NULL); + g_signal_connect ((gpointer) MainWindow, "delete_event", + G_CALLBACK (OnDelete), + NULL); + g_signal_connect ((gpointer) run_cd1, "activate", + G_CALLBACK (OnFile_RunCD), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_LoadElf, "activate", + G_CALLBACK (OnFile_LoadElf), + NULL); + g_signal_connect ((gpointer) slot_1, "activate", + G_CALLBACK (OnStates_Load1), + NULL); + g_signal_connect ((gpointer) slot_2, "activate", + G_CALLBACK (OnStates_Load2), + NULL); + g_signal_connect ((gpointer) slot_3_, "activate", + G_CALLBACK (OnStates_Load3), + NULL); + g_signal_connect ((gpointer) slot_4, "activate", + G_CALLBACK (OnStates_Load4), + NULL); + g_signal_connect ((gpointer) slot_5, "activate", + G_CALLBACK (OnStates_Load5), + NULL); + g_signal_connect ((gpointer) other1, "activate", + G_CALLBACK (OnStates_LoadOther), + NULL); + g_signal_connect ((gpointer) slot_6, "activate", + G_CALLBACK (OnStates_Save1), + NULL); + g_signal_connect ((gpointer) slot_7, "activate", + G_CALLBACK (OnStates_Save2), + NULL); + g_signal_connect ((gpointer) slot_8, "activate", + G_CALLBACK (OnStates_Save3), + NULL); + g_signal_connect ((gpointer) slot_9, "activate", + G_CALLBACK (OnStates_Save4), + NULL); + g_signal_connect ((gpointer) slot_10, "activate", + G_CALLBACK (OnStates_Save5), + NULL); + g_signal_connect ((gpointer) other2, "activate", + G_CALLBACK (OnStates_SaveOther), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Exit, "activate", + G_CALLBACK (OnFile_Exit), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Run, "activate", + G_CALLBACK (OnEmu_Run), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Reset, "activate", + G_CALLBACK (OnEmu_Reset), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Arguments, "activate", + G_CALLBACK (OnEmu_Arguments), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_PluginsBios, "activate", + G_CALLBACK (OnConf_Conf), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Graphics, "activate", + G_CALLBACK (OnConf_Gs), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Controllers, "activate", + G_CALLBACK (OnConf_Pads), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Sound, "activate", + G_CALLBACK (OnConf_Spu2), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Cdvdrom, "activate", + G_CALLBACK (OnConf_Cdvd), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Dev9, "activate", + G_CALLBACK (OnConf_Dev9), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_USB, "activate", + G_CALLBACK (OnConf_Usb), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_FW, "activate", + G_CALLBACK (OnConf_Fw), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Cpu, "activate", + G_CALLBACK (OnConf_Cpu), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Game_Fixes, "activate", + G_CALLBACK (on_Game_Fixes), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Speed_Hacks, "activate", + G_CALLBACK (on_Speed_Hacks), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Advanced, "activate", + G_CALLBACK (on_Advanced), + NULL); + g_signal_connect ((gpointer) patch_browser1, "activate", + G_CALLBACK (on_patch_browser1_activate), + NULL); + g_signal_connect ((gpointer) patch_finder2, "activate", + G_CALLBACK (on_patch_finder2_activate), + NULL); + g_signal_connect ((gpointer) enable_console1, "activate", + G_CALLBACK (on_enable_console1_activate), + NULL); + g_signal_connect ((gpointer) enable_patches1, "activate", + G_CALLBACK (on_enable_patches1_activate), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_EnterDebugger, "activate", + G_CALLBACK (OnDebug_Debugger), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_Logging, "activate", + G_CALLBACK (OnDebug_Logging), + NULL); + g_signal_connect ((gpointer) GtkMenuItem_About, "activate", + G_CALLBACK (OnHelp_About), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (MainWindow, MainWindow, "MainWindow"); + GLADE_HOOKUP_OBJECT (MainWindow, vbox1, "vbox1"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuBar_Menu, "GtkMenuBar_Menu"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_File, "GtkMenuItem_File"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_File_menu, "GtkMenuItem_File_menu"); + GLADE_HOOKUP_OBJECT (MainWindow, run_cd1, "run_cd1"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_LoadElf, "GtkMenuItem_LoadElf"); + GLADE_HOOKUP_OBJECT (MainWindow, separator2, "separator2"); + GLADE_HOOKUP_OBJECT (MainWindow, states1, "states1"); + GLADE_HOOKUP_OBJECT (MainWindow, states1_menu, "states1_menu"); + GLADE_HOOKUP_OBJECT (MainWindow, load1, "load1"); + GLADE_HOOKUP_OBJECT (MainWindow, load1_menu, "load1_menu"); + GLADE_HOOKUP_OBJECT (MainWindow, slot_1, "slot_1"); + GLADE_HOOKUP_OBJECT (MainWindow, slot_2, "slot_2"); + GLADE_HOOKUP_OBJECT (MainWindow, slot_3_, "slot_3_"); + GLADE_HOOKUP_OBJECT (MainWindow, slot_4, "slot_4"); + GLADE_HOOKUP_OBJECT (MainWindow, slot_5, "slot_5"); + GLADE_HOOKUP_OBJECT (MainWindow, other1, "other1"); + GLADE_HOOKUP_OBJECT (MainWindow, save1, "save1"); + GLADE_HOOKUP_OBJECT (MainWindow, save1_menu, "save1_menu"); + GLADE_HOOKUP_OBJECT (MainWindow, slot_6, "slot_6"); + GLADE_HOOKUP_OBJECT (MainWindow, slot_7, "slot_7"); + GLADE_HOOKUP_OBJECT (MainWindow, slot_8, "slot_8"); + GLADE_HOOKUP_OBJECT (MainWindow, slot_9, "slot_9"); + GLADE_HOOKUP_OBJECT (MainWindow, slot_10, "slot_10"); + GLADE_HOOKUP_OBJECT (MainWindow, other2, "other2"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Exit, "GtkMenuItem_Exit"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Emulator, "GtkMenuItem_Emulator"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Emulator_menu, "GtkMenuItem_Emulator_menu"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Run, "GtkMenuItem_Run"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Reset, "GtkMenuItem_Reset"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Arguments, "GtkMenuItem_Arguments"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Configuration, "GtkMenuItem_Configuration"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Configuration_menu, "GtkMenuItem_Configuration_menu"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_PluginsBios, "GtkMenuItem_PluginsBios"); + GLADE_HOOKUP_OBJECT (MainWindow, separator3, "separator3"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Graphics, "GtkMenuItem_Graphics"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Controllers, "GtkMenuItem_Controllers"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Sound, "GtkMenuItem_Sound"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Cdvdrom, "GtkMenuItem_Cdvdrom"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Dev9, "GtkMenuItem_Dev9"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_USB, "GtkMenuItem_USB"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_FW, "GtkMenuItem_FW"); + GLADE_HOOKUP_OBJECT (MainWindow, separator4, "separator4"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Cpu, "GtkMenuItem_Cpu"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Game_Fixes, "GtkMenuItem_Game_Fixes"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Speed_Hacks, "GtkMenuItem_Speed_Hacks"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Advanced, "GtkMenuItem_Advanced"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Language, "GtkMenuItem_Language"); + GLADE_HOOKUP_OBJECT (MainWindow, misc1, "misc1"); + GLADE_HOOKUP_OBJECT (MainWindow, misc1_menu, "misc1_menu"); + GLADE_HOOKUP_OBJECT (MainWindow, patch_browser1, "patch_browser1"); + GLADE_HOOKUP_OBJECT (MainWindow, patch_finder2, "patch_finder2"); + GLADE_HOOKUP_OBJECT (MainWindow, separator7, "separator7"); + GLADE_HOOKUP_OBJECT (MainWindow, enable_console1, "enable_console1"); + GLADE_HOOKUP_OBJECT (MainWindow, enable_patches1, "enable_patches1"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Debug, "GtkMenuItem_Debug"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Debug_menu, "GtkMenuItem_Debug_menu"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_EnterDebugger, "GtkMenuItem_EnterDebugger"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Logging, "GtkMenuItem_Logging"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Help, "GtkMenuItem_Help"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_Help_menu, "GtkMenuItem_Help_menu"); + GLADE_HOOKUP_OBJECT (MainWindow, GtkMenuItem_About, "GtkMenuItem_About"); + GLADE_HOOKUP_OBJECT (MainWindow, image1, "image1"); + GLADE_HOOKUP_OBJECT (MainWindow, status_box, "status_box"); + + return MainWindow; +} + +GtkWidget* +create_PatchBrowserWindow (void) +{ + GtkWidget *PatchBrowserWindow; + GtkWidget *hbox24; + GtkWidget *scrolledwindow2; + GtkWidget *treeview1; + GtkWidget *vbox40; + GtkWidget *button84; + GtkWidget *button85; + GtkWidget *button86; + GtkWidget *button87; + GtkWidget *button88; + GtkWidget *button89; + GtkWidget *button90; + GtkWidget *button91; + + PatchBrowserWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (PatchBrowserWindow), _("Patch List")); + gtk_window_set_modal (GTK_WINDOW (PatchBrowserWindow), TRUE); + gtk_window_set_decorated (GTK_WINDOW (PatchBrowserWindow), FALSE); + gtk_window_set_type_hint (GTK_WINDOW (PatchBrowserWindow), GDK_WINDOW_TYPE_HINT_DIALOG); + + hbox24 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox24); + gtk_container_add (GTK_CONTAINER (PatchBrowserWindow), hbox24); + + scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow2); + gtk_box_pack_start (GTK_BOX (hbox24), scrolledwindow2, TRUE, TRUE, 0); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_SHADOW_IN); + + treeview1 = gtk_tree_view_new (); + gtk_widget_show (treeview1); + gtk_container_add (GTK_CONTAINER (scrolledwindow2), treeview1); + + vbox40 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox40); + gtk_box_pack_start (GTK_BOX (hbox24), vbox40, TRUE, TRUE, 0); + + button84 = gtk_button_new_with_mnemonic (_("Enable / Diable")); + gtk_widget_show (button84); + gtk_box_pack_start (GTK_BOX (vbox40), button84, FALSE, FALSE, 0); + + button85 = gtk_button_new_with_mnemonic (_("Add Patch")); + gtk_widget_show (button85); + gtk_box_pack_start (GTK_BOX (vbox40), button85, FALSE, FALSE, 0); + + button86 = gtk_button_new_with_mnemonic (_("Edit Patch")); + gtk_widget_show (button86); + gtk_box_pack_start (GTK_BOX (vbox40), button86, FALSE, FALSE, 0); + + button87 = gtk_button_new_with_mnemonic (_("Add GS2v3-4")); + gtk_widget_show (button87); + gtk_box_pack_start (GTK_BOX (vbox40), button87, FALSE, FALSE, 0); + + button88 = gtk_button_new_with_mnemonic (_("Add Raw")); + gtk_widget_show (button88); + gtk_box_pack_start (GTK_BOX (vbox40), button88, FALSE, FALSE, 0); + + button89 = gtk_button_new_with_mnemonic (_("Pnach Writer")); + gtk_widget_show (button89); + gtk_box_pack_start (GTK_BOX (vbox40), button89, FALSE, FALSE, 0); + + button90 = gtk_button_new_with_mnemonic (_("Skip Mpeg")); + gtk_widget_show (button90); + gtk_box_pack_start (GTK_BOX (vbox40), button90, FALSE, FALSE, 0); + + button91 = gtk_button_new_from_stock ("gtk-close"); + gtk_widget_show (button91); + gtk_box_pack_start (GTK_BOX (vbox40), button91, FALSE, FALSE, 0); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (PatchBrowserWindow, PatchBrowserWindow, "PatchBrowserWindow"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, hbox24, "hbox24"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, scrolledwindow2, "scrolledwindow2"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, treeview1, "treeview1"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, vbox40, "vbox40"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, button84, "button84"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, button85, "button85"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, button86, "button86"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, button87, "button87"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, button88, "button88"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, button89, "button89"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, button90, "button90"); + GLADE_HOOKUP_OBJECT (PatchBrowserWindow, button91, "button91"); + + return PatchBrowserWindow; +} + +GtkWidget* +create_PatchFinderWindow (void) +{ + GtkWidget *PatchFinderWindow; + GtkWidget *hbox25; + GtkWidget *vbox43; + GtkWidget *frame19; + GtkWidget *alignment14; + GtkWidget *vbox44; + GtkWidget *radiobutton1; + GSList *radiobutton1_group = NULL; + GtkWidget *radiobutton2; + GtkWidget *label55; + GtkWidget *frame20; + GtkWidget *alignment15; + GtkWidget *table6; + GtkWidget *radiobutton3; + GSList *radiobutton3_group = NULL; + GtkWidget *radiobutton4; + GtkWidget *radiobutton5; + GtkWidget *radiobutton6; + GtkWidget *checkbutton1; + GtkWidget *label56; + GtkWidget *frame21; + GtkWidget *alignment16; + GtkWidget *vbox45; + GtkWidget *radiobutton7; + GSList *radiobutton7_group = NULL; + GtkWidget *radiobutton8; + GtkWidget *radiobutton9; + GtkWidget *radiobutton10; + GtkWidget *radiobutton11; + GtkWidget *radiobutton12; + GtkWidget *label57; + GtkWidget *frame22; + GtkWidget *alignment17; + GtkWidget *vbox46; + GtkWidget *radiobutton13; + GSList *radiobutton13_group = NULL; + GtkWidget *radiobutton14; + GtkWidget *entry1; + GtkWidget *label58; + GtkWidget *vbox41; + GtkWidget *vbox42; + GtkWidget *hbox26; + GtkWidget *label59; + GtkWidget *label60; + GtkWidget *label61; + GtkWidget *scrolledwindow3; + GtkWidget *treeview2; + GtkWidget *table5; + GtkWidget *button92; + GtkWidget *button93; + GtkWidget *button94; + GtkWidget *button95; + + PatchFinderWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (PatchFinderWindow), _("Patch Finder")); + gtk_window_set_modal (GTK_WINDOW (PatchFinderWindow), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (PatchFinderWindow), GDK_WINDOW_TYPE_HINT_DIALOG); + + hbox25 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox25); + gtk_container_add (GTK_CONTAINER (PatchFinderWindow), hbox25); + + vbox43 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox43); + gtk_box_pack_start (GTK_BOX (hbox25), vbox43, TRUE, TRUE, 0); + + frame19 = gtk_frame_new (NULL); + gtk_widget_show (frame19); + gtk_box_pack_start (GTK_BOX (vbox43), frame19, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame19), GTK_SHADOW_NONE); + + alignment14 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment14); + gtk_container_add (GTK_CONTAINER (frame19), alignment14); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment14), 0, 0, 12, 0); + + vbox44 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox44); + gtk_container_add (GTK_CONTAINER (alignment14), vbox44); + + radiobutton1 = gtk_radio_button_new_with_mnemonic (NULL, _("EE RAM")); + gtk_widget_show (radiobutton1); + gtk_box_pack_start (GTK_BOX (vbox44), radiobutton1, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton1), radiobutton1_group); + radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton1)); + + radiobutton2 = gtk_radio_button_new_with_mnemonic (NULL, _("IOP RAM")); + gtk_widget_show (radiobutton2); + gtk_box_pack_start (GTK_BOX (vbox44), radiobutton2, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton2), radiobutton1_group); + radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton2)); + + label55 = gtk_label_new (_("Search In")); + gtk_widget_show (label55); + gtk_frame_set_label_widget (GTK_FRAME (frame19), label55); + gtk_label_set_use_markup (GTK_LABEL (label55), TRUE); + + frame20 = gtk_frame_new (NULL); + gtk_widget_show (frame20); + gtk_box_pack_start (GTK_BOX (vbox43), frame20, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame20), GTK_SHADOW_NONE); + + alignment15 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment15); + gtk_container_add (GTK_CONTAINER (frame20), alignment15); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment15), 0, 0, 12, 0); + + table6 = gtk_table_new (3, 2, FALSE); + gtk_widget_show (table6); + gtk_container_add (GTK_CONTAINER (alignment15), table6); + + radiobutton3 = gtk_radio_button_new_with_mnemonic (NULL, _("8 bits")); + gtk_widget_show (radiobutton3); + gtk_table_attach (GTK_TABLE (table6), radiobutton3, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton3), radiobutton3_group); + radiobutton3_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton3)); + + radiobutton4 = gtk_radio_button_new_with_mnemonic (NULL, _("16 bits")); + gtk_widget_show (radiobutton4); + gtk_table_attach (GTK_TABLE (table6), radiobutton4, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton4), radiobutton3_group); + radiobutton3_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton4)); + + radiobutton5 = gtk_radio_button_new_with_mnemonic (NULL, _("32 bits")); + gtk_widget_show (radiobutton5); + gtk_table_attach (GTK_TABLE (table6), radiobutton5, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton5), radiobutton3_group); + radiobutton3_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton5)); + + radiobutton6 = gtk_radio_button_new_with_mnemonic (NULL, _("64 bits")); + gtk_widget_show (radiobutton6); + gtk_table_attach (GTK_TABLE (table6), radiobutton6, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton6), radiobutton3_group); + radiobutton3_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton6)); + + checkbutton1 = gtk_check_button_new_with_mnemonic (_("Unsigned")); + gtk_widget_show (checkbutton1); + gtk_table_attach (GTK_TABLE (table6), checkbutton1, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + label56 = gtk_label_new (_("Values of Size")); + gtk_widget_show (label56); + gtk_frame_set_label_widget (GTK_FRAME (frame20), label56); + gtk_label_set_use_markup (GTK_LABEL (label56), TRUE); + + frame21 = gtk_frame_new (NULL); + gtk_widget_show (frame21); + gtk_box_pack_start (GTK_BOX (vbox43), frame21, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame21), GTK_SHADOW_NONE); + + alignment16 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment16); + gtk_container_add (GTK_CONTAINER (frame21), alignment16); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment16), 0, 0, 12, 0); + + vbox45 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox45); + gtk_container_add (GTK_CONTAINER (alignment16), vbox45); + + radiobutton7 = gtk_radio_button_new_with_mnemonic (NULL, _("Equal")); + gtk_widget_show (radiobutton7); + gtk_box_pack_start (GTK_BOX (vbox45), radiobutton7, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton7), radiobutton7_group); + radiobutton7_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton7)); + + radiobutton8 = gtk_radio_button_new_with_mnemonic (NULL, _("Greater Then")); + gtk_widget_show (radiobutton8); + gtk_box_pack_start (GTK_BOX (vbox45), radiobutton8, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton8), radiobutton7_group); + radiobutton7_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton8)); + + radiobutton9 = gtk_radio_button_new_with_mnemonic (NULL, _("Less Then")); + gtk_widget_show (radiobutton9); + gtk_box_pack_start (GTK_BOX (vbox45), radiobutton9, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton9), radiobutton7_group); + radiobutton7_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton9)); + + radiobutton10 = gtk_radio_button_new_with_mnemonic (NULL, _("Greater Then or Equal")); + gtk_widget_show (radiobutton10); + gtk_box_pack_start (GTK_BOX (vbox45), radiobutton10, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton10), radiobutton7_group); + radiobutton7_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton10)); + + radiobutton11 = gtk_radio_button_new_with_mnemonic (NULL, _("Less Then or Equal")); + gtk_widget_show (radiobutton11); + gtk_box_pack_start (GTK_BOX (vbox45), radiobutton11, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton11), radiobutton7_group); + radiobutton7_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton11)); + + radiobutton12 = gtk_radio_button_new_with_mnemonic (NULL, _("Not Equal")); + gtk_widget_show (radiobutton12); + gtk_box_pack_start (GTK_BOX (vbox45), radiobutton12, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton12), radiobutton7_group); + radiobutton7_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton12)); + + label57 = gtk_label_new (_("Being")); + gtk_widget_show (label57); + gtk_frame_set_label_widget (GTK_FRAME (frame21), label57); + gtk_label_set_use_markup (GTK_LABEL (label57), TRUE); + + frame22 = gtk_frame_new (NULL); + gtk_widget_show (frame22); + gtk_box_pack_start (GTK_BOX (vbox43), frame22, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame22), GTK_SHADOW_NONE); + + alignment17 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment17); + gtk_container_add (GTK_CONTAINER (frame22), alignment17); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment17), 0, 0, 12, 0); + + vbox46 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox46); + gtk_container_add (GTK_CONTAINER (alignment17), vbox46); + + radiobutton13 = gtk_radio_button_new_with_mnemonic (NULL, _("Old Value")); + gtk_widget_show (radiobutton13); + gtk_box_pack_start (GTK_BOX (vbox46), radiobutton13, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton13), radiobutton13_group); + radiobutton13_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton13)); + + radiobutton14 = gtk_radio_button_new_with_mnemonic (NULL, _("Specific Value")); + gtk_widget_show (radiobutton14); + gtk_box_pack_start (GTK_BOX (vbox46), radiobutton14, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton14), radiobutton13_group); + radiobutton13_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton14)); + + entry1 = gtk_entry_new (); + gtk_widget_show (entry1); + gtk_box_pack_start (GTK_BOX (vbox46), entry1, FALSE, FALSE, 0); + + label58 = gtk_label_new (_("Compared To")); + gtk_widget_show (label58); + gtk_frame_set_label_widget (GTK_FRAME (frame22), label58); + gtk_label_set_use_markup (GTK_LABEL (label58), TRUE); + + vbox41 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox41); + gtk_box_pack_start (GTK_BOX (hbox25), vbox41, TRUE, TRUE, 0); + + vbox42 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox42); + gtk_box_pack_start (GTK_BOX (vbox41), vbox42, TRUE, TRUE, 0); + + hbox26 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox26); + gtk_box_pack_start (GTK_BOX (vbox42), hbox26, FALSE, FALSE, 0); + + label59 = gtk_label_new (_("Results:")); + gtk_widget_show (label59); + gtk_box_pack_start (GTK_BOX (hbox26), label59, FALSE, FALSE, 0); + + label60 = gtk_label_new (_("%s")); + gtk_widget_show (label60); + gtk_box_pack_start (GTK_BOX (hbox26), label60, TRUE, TRUE, 0); + gtk_label_set_justify (GTK_LABEL (label60), GTK_JUSTIFY_CENTER); + + label61 = gtk_label_new (_("Ready to Search")); + gtk_widget_show (label61); + gtk_box_pack_start (GTK_BOX (hbox26), label61, TRUE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label61), GTK_JUSTIFY_RIGHT); + + scrolledwindow3 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow3); + gtk_box_pack_start (GTK_BOX (vbox42), scrolledwindow3, TRUE, TRUE, 0); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow3), GTK_SHADOW_IN); + + treeview2 = gtk_tree_view_new (); + gtk_widget_show (treeview2); + gtk_container_add (GTK_CONTAINER (scrolledwindow3), treeview2); + + table5 = gtk_table_new (2, 2, FALSE); + gtk_widget_show (table5); + gtk_box_pack_start (GTK_BOX (vbox41), table5, FALSE, TRUE, 0); + + button92 = gtk_button_new_from_stock ("gtk-clear"); + gtk_widget_show (button92); + gtk_table_attach (GTK_TABLE (table5), button92, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + button93 = gtk_button_new_from_stock ("gtk-find"); + gtk_widget_show (button93); + gtk_table_attach (GTK_TABLE (table5), button93, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + button94 = gtk_button_new_from_stock ("gtk-add"); + gtk_widget_show (button94); + gtk_table_attach (GTK_TABLE (table5), button94, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + button95 = gtk_button_new_from_stock ("gtk-close"); + gtk_widget_show (button95); + gtk_table_attach (GTK_TABLE (table5), button95, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (PatchFinderWindow, PatchFinderWindow, "PatchFinderWindow"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, hbox25, "hbox25"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, vbox43, "vbox43"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, frame19, "frame19"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, alignment14, "alignment14"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, vbox44, "vbox44"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton1, "radiobutton1"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton2, "radiobutton2"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, label55, "label55"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, frame20, "frame20"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, alignment15, "alignment15"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, table6, "table6"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton3, "radiobutton3"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton4, "radiobutton4"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton5, "radiobutton5"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton6, "radiobutton6"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, checkbutton1, "checkbutton1"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, label56, "label56"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, frame21, "frame21"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, alignment16, "alignment16"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, vbox45, "vbox45"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton7, "radiobutton7"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton8, "radiobutton8"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton9, "radiobutton9"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton10, "radiobutton10"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton11, "radiobutton11"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton12, "radiobutton12"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, label57, "label57"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, frame22, "frame22"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, alignment17, "alignment17"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, vbox46, "vbox46"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton13, "radiobutton13"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, radiobutton14, "radiobutton14"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, entry1, "entry1"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, label58, "label58"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, vbox41, "vbox41"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, vbox42, "vbox42"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, hbox26, "hbox26"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, label59, "label59"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, label60, "label60"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, label61, "label61"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, scrolledwindow3, "scrolledwindow3"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, treeview2, "treeview2"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, table5, "table5"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, button92, "button92"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, button93, "button93"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, button94, "button94"); + GLADE_HOOKUP_OBJECT (PatchFinderWindow, button95, "button95"); + + return PatchFinderWindow; +} + +GtkWidget* +create_CpuDlg (void) +{ + GtkWidget *CpuDlg; + GtkWidget *dialog_vbox5; + GtkWidget *hbox27; + GtkWidget *vbox47; + GtkWidget *frame8; + GtkWidget *alignment2; + GtkWidget *vbox28; + GtkWidget *GtkLabel_CpuVendor; + GtkWidget *GtkLabel_Family; + GtkWidget *GtkLabel_CpuSpeed; + GtkWidget *GtkLabel_Features; + GtkWidget *label35; + GtkWidget *GtkCheckButton_EERec; + GtkWidget *frame6; + GtkWidget *vbox26; + GtkWidget *GtkCheckButton_VU0rec; + GtkWidget *GtkCheckButton_VU1rec; + GtkWidget *label32; + GtkWidget *GtkCheckButton_MTGS; + GtkWidget *vbox48; + GtkWidget *frame9; + GtkWidget *alignment4; + GtkWidget *vbox29; + GtkWidget *GtkRadioButton_LimitNormal; + GSList *GtkRadioButton_LimitNormal_group = NULL; + GtkWidget *GtkRadioButton_LimitLimit; + GtkWidget *GtkRadioButton_LimitFS; + GtkWidget *GtkRadioButton_VUSkip; + GtkWidget *label41; + GtkWidget *frame23; + GtkWidget *alignment18; + GtkWidget *vbox49; + GtkWidget *hbox29; + GtkWidget *label66; + GtkObject *CustomFPSLimit_adj; + GtkWidget *CustomFPSLimit; + GtkWidget *hbox30; + GtkWidget *label67; + GtkObject *FrameThreshold_adj; + GtkWidget *FrameThreshold; + GtkWidget *hbox31; + GtkWidget *label68; + GtkObject *FramesBeforeSkipping_adj; + GtkWidget *FramesBeforeSkipping; + GtkWidget *hbox32; + GtkWidget *label69; + GtkObject *FramesToSkip_adj; + GtkWidget *FramesToSkip; + GtkWidget *label63; + GtkWidget *label64; + GtkWidget *label65; + GtkWidget *label62; + GtkWidget *dialog_action_area5; + GtkWidget *button96; + GtkWidget *button97; + + CpuDlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (CpuDlg), _("dialog1")); + gtk_window_set_type_hint (GTK_WINDOW (CpuDlg), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox5 = GTK_DIALOG (CpuDlg)->vbox; + gtk_widget_show (dialog_vbox5); + + hbox27 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox27); + gtk_box_pack_start (GTK_BOX (dialog_vbox5), hbox27, TRUE, TRUE, 0); + + vbox47 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox47); + gtk_box_pack_start (GTK_BOX (hbox27), vbox47, TRUE, TRUE, 0); + + frame8 = gtk_frame_new (NULL); + gtk_widget_show (frame8); + gtk_box_pack_start (GTK_BOX (vbox47), frame8, TRUE, TRUE, 0); + + alignment2 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment2); + gtk_container_add (GTK_CONTAINER (frame8), alignment2); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment2), 0, 0, 12, 0); + + vbox28 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox28); + gtk_container_add (GTK_CONTAINER (alignment2), vbox28); + + GtkLabel_CpuVendor = gtk_label_new (_("CPU vendor")); + gtk_widget_show (GtkLabel_CpuVendor); + gtk_box_pack_start (GTK_BOX (vbox28), GtkLabel_CpuVendor, FALSE, FALSE, 0); + + GtkLabel_Family = gtk_label_new (_("Family")); + gtk_widget_show (GtkLabel_Family); + gtk_box_pack_start (GTK_BOX (vbox28), GtkLabel_Family, FALSE, FALSE, 0); + + GtkLabel_CpuSpeed = gtk_label_new (_("Cpu Speed")); + gtk_widget_show (GtkLabel_CpuSpeed); + gtk_box_pack_start (GTK_BOX (vbox28), GtkLabel_CpuSpeed, FALSE, FALSE, 0); + + GtkLabel_Features = gtk_label_new (_("Features")); + gtk_widget_show (GtkLabel_Features); + gtk_box_pack_start (GTK_BOX (vbox28), GtkLabel_Features, FALSE, FALSE, 0); + + label35 = gtk_label_new (""); + gtk_widget_show (label35); + gtk_frame_set_label_widget (GTK_FRAME (frame8), label35); + gtk_label_set_use_markup (GTK_LABEL (label35), TRUE); + + GtkCheckButton_EERec = gtk_check_button_new_with_mnemonic (_("EERec - EE/IOP recompiler (Need MMX/SSE/SSE2)")); + gtk_widget_show (GtkCheckButton_EERec); + gtk_box_pack_start (GTK_BOX (vbox47), GtkCheckButton_EERec, FALSE, FALSE, 0); + + frame6 = gtk_frame_new (NULL); + gtk_widget_show (frame6); + gtk_box_pack_start (GTK_BOX (vbox47), frame6, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (frame6), 5); + + vbox26 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox26); + gtk_container_add (GTK_CONTAINER (frame6), vbox26); + gtk_container_set_border_width (GTK_CONTAINER (vbox26), 5); + + GtkCheckButton_VU0rec = gtk_check_button_new_with_mnemonic (_("VU0rec - enable recompiler for VU0 unit")); + gtk_widget_show (GtkCheckButton_VU0rec); + gtk_box_pack_start (GTK_BOX (vbox26), GtkCheckButton_VU0rec, FALSE, FALSE, 0); + + GtkCheckButton_VU1rec = gtk_check_button_new_with_mnemonic (_("VU1rec - enable recompiler for VU1 unit")); + gtk_widget_show (GtkCheckButton_VU1rec); + gtk_box_pack_start (GTK_BOX (vbox26), GtkCheckButton_VU1rec, FALSE, FALSE, 0); + + label32 = gtk_label_new (_("VU Recompilers - All options are set by default")); + gtk_widget_show (label32); + gtk_frame_set_label_widget (GTK_FRAME (frame6), label32); + + GtkCheckButton_MTGS = gtk_check_button_new_with_mnemonic (_("Multi threaded GS mode (MTGS)\n (faster on dual core/HT CPUs, requires pcsx2 restart)")); + gtk_widget_show (GtkCheckButton_MTGS); + gtk_box_pack_start (GTK_BOX (vbox47), GtkCheckButton_MTGS, FALSE, FALSE, 0); + + vbox48 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox48); + gtk_box_pack_start (GTK_BOX (hbox27), vbox48, TRUE, TRUE, 0); + + frame9 = gtk_frame_new (NULL); + gtk_widget_show (frame9); + gtk_box_pack_start (GTK_BOX (vbox48), frame9, TRUE, TRUE, 0); + + alignment4 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment4); + gtk_container_add (GTK_CONTAINER (frame9), alignment4); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment4), 0, 0, 12, 0); + + vbox29 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox29); + gtk_container_add (GTK_CONTAINER (alignment4), vbox29); + + GtkRadioButton_LimitNormal = gtk_radio_button_new_with_mnemonic (NULL, _("Normal - All frames are rendered as fast as possible")); + gtk_widget_show (GtkRadioButton_LimitNormal); + gtk_box_pack_start (GTK_BOX (vbox29), GtkRadioButton_LimitNormal, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (GtkRadioButton_LimitNormal), GtkRadioButton_LimitNormal_group); + GtkRadioButton_LimitNormal_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (GtkRadioButton_LimitNormal)); + + GtkRadioButton_LimitLimit = gtk_radio_button_new_with_mnemonic (NULL, _("Limit - Force frames to normal speeds if too fast")); + gtk_widget_show (GtkRadioButton_LimitLimit); + gtk_box_pack_start (GTK_BOX (vbox29), GtkRadioButton_LimitLimit, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (GtkRadioButton_LimitLimit), GtkRadioButton_LimitNormal_group); + GtkRadioButton_LimitNormal_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (GtkRadioButton_LimitLimit)); + + GtkRadioButton_LimitFS = gtk_radio_button_new_with_mnemonic (NULL, _("Frame Skip - In order to achieve normal speeds, \n some frames are skipped (faster).\n Fps displayed counts skipped frames too")); + gtk_widget_show (GtkRadioButton_LimitFS); + gtk_box_pack_start (GTK_BOX (vbox29), GtkRadioButton_LimitFS, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (GtkRadioButton_LimitFS), GtkRadioButton_LimitNormal_group); + GtkRadioButton_LimitNormal_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (GtkRadioButton_LimitFS)); + + GtkRadioButton_VUSkip = gtk_radio_button_new_with_mnemonic (NULL, _("VU Skip - Same as Frame Skip, but tried to skip more. \n Artifacts might be present, but will be faster")); + gtk_widget_show (GtkRadioButton_VUSkip); + gtk_box_pack_start (GTK_BOX (vbox29), GtkRadioButton_VUSkip, FALSE, FALSE, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (GtkRadioButton_VUSkip), GtkRadioButton_LimitNormal_group); + GtkRadioButton_LimitNormal_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (GtkRadioButton_VUSkip)); + + label41 = gtk_label_new (_("Frame Limiting (F4 switches in-game)")); + gtk_widget_show (label41); + gtk_frame_set_label_widget (GTK_FRAME (frame9), label41); + gtk_label_set_use_markup (GTK_LABEL (label41), TRUE); + + frame23 = gtk_frame_new (NULL); + gtk_widget_show (frame23); + gtk_box_pack_start (GTK_BOX (vbox48), frame23, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame23), GTK_SHADOW_NONE); + + alignment18 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment18); + gtk_container_add (GTK_CONTAINER (frame23), alignment18); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment18), 0, 0, 12, 0); + + vbox49 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox49); + gtk_container_add (GTK_CONTAINER (alignment18), vbox49); + + hbox29 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox29); + gtk_box_pack_start (GTK_BOX (vbox49), hbox29, TRUE, TRUE, 0); + + label66 = gtk_label_new (_("Custom FPS Limit (0=auto):")); + gtk_widget_show (label66); + gtk_box_pack_start (GTK_BOX (hbox29), label66, TRUE, TRUE, 0); + + CustomFPSLimit_adj = gtk_adjustment_new (0, 0, 9999, 1, 0, 0); + CustomFPSLimit = gtk_spin_button_new (GTK_ADJUSTMENT (CustomFPSLimit_adj), 1, 0); + gtk_widget_show (CustomFPSLimit); + gtk_box_pack_end (GTK_BOX (hbox29), CustomFPSLimit, FALSE, TRUE, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (CustomFPSLimit), TRUE); + + hbox30 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox30); + gtk_box_pack_start (GTK_BOX (vbox49), hbox30, TRUE, TRUE, 0); + + label67 = gtk_label_new (_("Skip Frames when slower than[1]:")); + gtk_widget_show (label67); + gtk_box_pack_start (GTK_BOX (hbox30), label67, TRUE, TRUE, 0); + + FrameThreshold_adj = gtk_adjustment_new (0, 0, 9999, 1, 0, 0); + FrameThreshold = gtk_spin_button_new (GTK_ADJUSTMENT (FrameThreshold_adj), 1, 0); + gtk_widget_show (FrameThreshold); + gtk_box_pack_end (GTK_BOX (hbox30), FrameThreshold, FALSE, TRUE, 0); + + hbox31 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox31); + gtk_box_pack_start (GTK_BOX (vbox49), hbox31, TRUE, TRUE, 0); + + label68 = gtk_label_new (_("Consecutive Frames before skipping[2]:")); + gtk_widget_show (label68); + gtk_box_pack_start (GTK_BOX (hbox31), label68, TRUE, TRUE, 0); + + FramesBeforeSkipping_adj = gtk_adjustment_new (0, 0, 9999, 1, 0, 0); + FramesBeforeSkipping = gtk_spin_button_new (GTK_ADJUSTMENT (FramesBeforeSkipping_adj), 1, 0); + gtk_widget_show (FramesBeforeSkipping); + gtk_box_pack_end (GTK_BOX (hbox31), FramesBeforeSkipping, FALSE, TRUE, 0); + + hbox32 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox32); + gtk_box_pack_start (GTK_BOX (vbox49), hbox32, TRUE, TRUE, 0); + + label69 = gtk_label_new (_("Consecutive Frames to skip[3]:")); + gtk_widget_show (label69); + gtk_box_pack_start (GTK_BOX (hbox32), label69, TRUE, TRUE, 0); + + FramesToSkip_adj = gtk_adjustment_new (0, 0, 9999, 1, 0, 0); + FramesToSkip = gtk_spin_button_new (GTK_ADJUSTMENT (FramesToSkip_adj), 1, 0); + gtk_widget_show (FramesToSkip); + gtk_box_pack_end (GTK_BOX (hbox32), FramesToSkip, FALSE, TRUE, 0); + + label63 = gtk_label_new (_("[1] Only skips when slower then this number(0 is auto; 9999 always skips).")); + gtk_widget_show (label63); + gtk_box_pack_start (GTK_BOX (vbox49), label63, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (label63), TRUE); + + label64 = gtk_label_new (_("[2] Renders this many frames before skipping (0 is default).")); + gtk_widget_show (label64); + gtk_box_pack_start (GTK_BOX (vbox49), label64, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (label64), TRUE); + + label65 = gtk_label_new (_("[3] Skips this many frames before continuing (0 is default).")); + gtk_widget_show (label65); + gtk_box_pack_start (GTK_BOX (vbox49), label65, FALSE, FALSE, 0); + gtk_label_set_line_wrap (GTK_LABEL (label65), TRUE); + + label62 = gtk_label_new (_("Detailed Settings")); + gtk_widget_show (label62); + gtk_frame_set_label_widget (GTK_FRAME (frame23), label62); + gtk_label_set_use_markup (GTK_LABEL (label62), TRUE); + + dialog_action_area5 = GTK_DIALOG (CpuDlg)->action_area; + gtk_widget_show (dialog_action_area5); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area5), GTK_BUTTONBOX_END); + + button96 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button96); + gtk_dialog_add_action_widget (GTK_DIALOG (CpuDlg), button96, 0); + GTK_WIDGET_SET_FLAGS (button96, GTK_CAN_DEFAULT); + + button97 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button97); + gtk_dialog_add_action_widget (GTK_DIALOG (CpuDlg), button97, 0); + GTK_WIDGET_SET_FLAGS (button97, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) button96, "clicked", + G_CALLBACK (OnCpu_Ok), + NULL); + g_signal_connect ((gpointer) button97, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (CpuDlg, CpuDlg, "CpuDlg"); + GLADE_HOOKUP_OBJECT_NO_REF (CpuDlg, dialog_vbox5, "dialog_vbox5"); + GLADE_HOOKUP_OBJECT (CpuDlg, hbox27, "hbox27"); + GLADE_HOOKUP_OBJECT (CpuDlg, vbox47, "vbox47"); + GLADE_HOOKUP_OBJECT (CpuDlg, frame8, "frame8"); + GLADE_HOOKUP_OBJECT (CpuDlg, alignment2, "alignment2"); + GLADE_HOOKUP_OBJECT (CpuDlg, vbox28, "vbox28"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkLabel_CpuVendor, "GtkLabel_CpuVendor"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkLabel_Family, "GtkLabel_Family"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkLabel_CpuSpeed, "GtkLabel_CpuSpeed"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkLabel_Features, "GtkLabel_Features"); + GLADE_HOOKUP_OBJECT (CpuDlg, label35, "label35"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkCheckButton_EERec, "GtkCheckButton_EERec"); + GLADE_HOOKUP_OBJECT (CpuDlg, frame6, "frame6"); + GLADE_HOOKUP_OBJECT (CpuDlg, vbox26, "vbox26"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkCheckButton_VU0rec, "GtkCheckButton_VU0rec"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkCheckButton_VU1rec, "GtkCheckButton_VU1rec"); + GLADE_HOOKUP_OBJECT (CpuDlg, label32, "label32"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkCheckButton_MTGS, "GtkCheckButton_MTGS"); + GLADE_HOOKUP_OBJECT (CpuDlg, vbox48, "vbox48"); + GLADE_HOOKUP_OBJECT (CpuDlg, frame9, "frame9"); + GLADE_HOOKUP_OBJECT (CpuDlg, alignment4, "alignment4"); + GLADE_HOOKUP_OBJECT (CpuDlg, vbox29, "vbox29"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkRadioButton_LimitNormal, "GtkRadioButton_LimitNormal"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkRadioButton_LimitLimit, "GtkRadioButton_LimitLimit"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkRadioButton_LimitFS, "GtkRadioButton_LimitFS"); + GLADE_HOOKUP_OBJECT (CpuDlg, GtkRadioButton_VUSkip, "GtkRadioButton_VUSkip"); + GLADE_HOOKUP_OBJECT (CpuDlg, label41, "label41"); + GLADE_HOOKUP_OBJECT (CpuDlg, frame23, "frame23"); + GLADE_HOOKUP_OBJECT (CpuDlg, alignment18, "alignment18"); + GLADE_HOOKUP_OBJECT (CpuDlg, vbox49, "vbox49"); + GLADE_HOOKUP_OBJECT (CpuDlg, hbox29, "hbox29"); + GLADE_HOOKUP_OBJECT (CpuDlg, label66, "label66"); + GLADE_HOOKUP_OBJECT (CpuDlg, CustomFPSLimit, "CustomFPSLimit"); + GLADE_HOOKUP_OBJECT (CpuDlg, hbox30, "hbox30"); + GLADE_HOOKUP_OBJECT (CpuDlg, label67, "label67"); + GLADE_HOOKUP_OBJECT (CpuDlg, FrameThreshold, "FrameThreshold"); + GLADE_HOOKUP_OBJECT (CpuDlg, hbox31, "hbox31"); + GLADE_HOOKUP_OBJECT (CpuDlg, label68, "label68"); + GLADE_HOOKUP_OBJECT (CpuDlg, FramesBeforeSkipping, "FramesBeforeSkipping"); + GLADE_HOOKUP_OBJECT (CpuDlg, hbox32, "hbox32"); + GLADE_HOOKUP_OBJECT (CpuDlg, label69, "label69"); + GLADE_HOOKUP_OBJECT (CpuDlg, FramesToSkip, "FramesToSkip"); + GLADE_HOOKUP_OBJECT (CpuDlg, label63, "label63"); + GLADE_HOOKUP_OBJECT (CpuDlg, label64, "label64"); + GLADE_HOOKUP_OBJECT (CpuDlg, label65, "label65"); + GLADE_HOOKUP_OBJECT (CpuDlg, label62, "label62"); + GLADE_HOOKUP_OBJECT_NO_REF (CpuDlg, dialog_action_area5, "dialog_action_area5"); + GLADE_HOOKUP_OBJECT (CpuDlg, button96, "button96"); + GLADE_HOOKUP_OBJECT (CpuDlg, button97, "button97"); + + return CpuDlg; +} + +GtkWidget* +create_Logging (void) +{ + GtkWidget *Logging; + GtkWidget *dialog_vbox6; + GtkWidget *scrolledwindow4; + GtkWidget *viewport2; + GtkWidget *vbox55; + GtkWidget *frame33; + GtkWidget *alignment28; + GtkWidget *vbox56; + GtkWidget *Log0; + GtkWidget *Log1; + GtkWidget *Log2; + GtkWidget *Log3; + GtkWidget *Log4; + GtkWidget *Log5; + GtkWidget *Log6; + GtkWidget *Log7; + GtkWidget *Log8; + GtkWidget *Log9; + GtkWidget *Log10; + GtkWidget *Log11; + GtkWidget *Log12; + GtkWidget *Log13; + GtkWidget *Log14; + GtkWidget *Log15; + GtkWidget *Log16; + GtkWidget *label85; + GtkWidget *frame34; + GtkWidget *alignment29; + GtkWidget *vbox57; + GtkWidget *Log20; + GtkWidget *Log21; + GtkWidget *Log22; + GtkWidget *Log23; + GtkWidget *Log24; + GtkWidget *Log25; + GtkWidget *Log26; + GtkWidget *Log27; + GtkWidget *Log28; + GtkWidget *label86; + GtkWidget *frame35; + GtkWidget *alignment30; + GtkWidget *vbox58; + GtkWidget *Log31; + GtkWidget *Log30; + GtkWidget *label87; + GtkWidget *dialog_action_area6; + GtkWidget *Logging_Ok; + GtkWidget *Logging2Cancel; + + Logging = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (Logging), _("Logging")); + gtk_window_set_default_size (GTK_WINDOW (Logging), 200, 450); + gtk_window_set_type_hint (GTK_WINDOW (Logging), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox6 = GTK_DIALOG (Logging)->vbox; + gtk_widget_show (dialog_vbox6); + + scrolledwindow4 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow4); + gtk_box_pack_start (GTK_BOX (dialog_vbox6), scrolledwindow4, TRUE, TRUE, 0); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow4), GTK_SHADOW_IN); + + viewport2 = gtk_viewport_new (NULL, NULL); + gtk_widget_show (viewport2); + gtk_container_add (GTK_CONTAINER (scrolledwindow4), viewport2); + + vbox55 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox55); + gtk_container_add (GTK_CONTAINER (viewport2), vbox55); + + frame33 = gtk_frame_new (NULL); + gtk_widget_show (frame33); + gtk_box_pack_start (GTK_BOX (vbox55), frame33, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame33), GTK_SHADOW_NONE); + + alignment28 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment28); + gtk_container_add (GTK_CONTAINER (frame33), alignment28); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment28), 0, 0, 12, 0); + + vbox56 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox56); + gtk_container_add (GTK_CONTAINER (alignment28), vbox56); + + Log0 = gtk_check_button_new_with_mnemonic (_("Cpu Log")); + gtk_widget_show (Log0); + gtk_box_pack_start (GTK_BOX (vbox56), Log0, FALSE, FALSE, 0); + + Log1 = gtk_check_button_new_with_mnemonic (_("Mem Log")); + gtk_widget_show (Log1); + gtk_box_pack_start (GTK_BOX (vbox56), Log1, FALSE, FALSE, 0); + + Log2 = gtk_check_button_new_with_mnemonic (_("Hw Log")); + gtk_widget_show (Log2); + gtk_box_pack_start (GTK_BOX (vbox56), Log2, FALSE, FALSE, 0); + + Log3 = gtk_check_button_new_with_mnemonic (_("Dma Log")); + gtk_widget_show (Log3); + gtk_box_pack_start (GTK_BOX (vbox56), Log3, FALSE, FALSE, 0); + + Log4 = gtk_check_button_new_with_mnemonic (_("Bios Log")); + gtk_widget_show (Log4); + gtk_box_pack_start (GTK_BOX (vbox56), Log4, FALSE, FALSE, 0); + + Log5 = gtk_check_button_new_with_mnemonic (_("Elf Log")); + gtk_widget_show (Log5); + gtk_box_pack_start (GTK_BOX (vbox56), Log5, FALSE, FALSE, 0); + + Log6 = gtk_check_button_new_with_mnemonic (_("Fpu Log")); + gtk_widget_show (Log6); + gtk_box_pack_start (GTK_BOX (vbox56), Log6, FALSE, FALSE, 0); + + Log7 = gtk_check_button_new_with_mnemonic (_("MMI Log")); + gtk_widget_show (Log7); + gtk_box_pack_start (GTK_BOX (vbox56), Log7, FALSE, FALSE, 0); + + Log8 = gtk_check_button_new_with_mnemonic (_("VU0 Log")); + gtk_widget_show (Log8); + gtk_box_pack_start (GTK_BOX (vbox56), Log8, FALSE, FALSE, 0); + + Log9 = gtk_check_button_new_with_mnemonic (_("Cop0 Log")); + gtk_widget_show (Log9); + gtk_box_pack_start (GTK_BOX (vbox56), Log9, FALSE, FALSE, 0); + + Log10 = gtk_check_button_new_with_mnemonic (_("Vif Log")); + gtk_widget_show (Log10); + gtk_box_pack_start (GTK_BOX (vbox56), Log10, FALSE, FALSE, 0); + + Log11 = gtk_check_button_new_with_mnemonic (_("SPR Log")); + gtk_widget_show (Log11); + gtk_box_pack_start (GTK_BOX (vbox56), Log11, FALSE, FALSE, 0); + + Log12 = gtk_check_button_new_with_mnemonic (_("GIF Log")); + gtk_widget_show (Log12); + gtk_box_pack_start (GTK_BOX (vbox56), Log12, FALSE, FALSE, 0); + + Log13 = gtk_check_button_new_with_mnemonic (_("Sif Log")); + gtk_widget_show (Log13); + gtk_box_pack_start (GTK_BOX (vbox56), Log13, FALSE, FALSE, 0); + + Log14 = gtk_check_button_new_with_mnemonic (_("IPU Log")); + gtk_widget_show (Log14); + gtk_box_pack_start (GTK_BOX (vbox56), Log14, FALSE, FALSE, 0); + + Log15 = gtk_check_button_new_with_mnemonic (_("VU Micro Log")); + gtk_widget_show (Log15); + gtk_box_pack_start (GTK_BOX (vbox56), Log15, FALSE, FALSE, 0); + + Log16 = gtk_check_button_new_with_mnemonic (_("RPC Log")); + gtk_widget_show (Log16); + gtk_box_pack_start (GTK_BOX (vbox56), Log16, FALSE, FALSE, 0); + + label85 = gtk_label_new (_("EE Logs")); + gtk_widget_show (label85); + gtk_frame_set_label_widget (GTK_FRAME (frame33), label85); + gtk_label_set_use_markup (GTK_LABEL (label85), TRUE); + + frame34 = gtk_frame_new (NULL); + gtk_widget_show (frame34); + gtk_box_pack_start (GTK_BOX (vbox55), frame34, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame34), GTK_SHADOW_NONE); + + alignment29 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment29); + gtk_container_add (GTK_CONTAINER (frame34), alignment29); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment29), 0, 0, 12, 0); + + vbox57 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox57); + gtk_container_add (GTK_CONTAINER (alignment29), vbox57); + + Log20 = gtk_check_button_new_with_mnemonic (_("IOP Log")); + gtk_widget_show (Log20); + gtk_box_pack_start (GTK_BOX (vbox57), Log20, FALSE, FALSE, 0); + + Log21 = gtk_check_button_new_with_mnemonic (_("Mem Log")); + gtk_widget_show (Log21); + gtk_box_pack_start (GTK_BOX (vbox57), Log21, FALSE, FALSE, 0); + + Log22 = gtk_check_button_new_with_mnemonic (_("Hw Log")); + gtk_widget_show (Log22); + gtk_box_pack_start (GTK_BOX (vbox57), Log22, FALSE, FALSE, 0); + + Log23 = gtk_check_button_new_with_mnemonic (_("Bios Log")); + gtk_widget_show (Log23); + gtk_box_pack_start (GTK_BOX (vbox57), Log23, FALSE, FALSE, 0); + + Log24 = gtk_check_button_new_with_mnemonic (_("Dma Log")); + gtk_widget_show (Log24); + gtk_box_pack_start (GTK_BOX (vbox57), Log24, FALSE, FALSE, 0); + + Log25 = gtk_check_button_new_with_mnemonic (_("Pad Log")); + gtk_widget_show (Log25); + gtk_box_pack_start (GTK_BOX (vbox57), Log25, FALSE, FALSE, 0); + + Log26 = gtk_check_button_new_with_mnemonic (_("Gte Log")); + gtk_widget_show (Log26); + gtk_box_pack_start (GTK_BOX (vbox57), Log26, FALSE, FALSE, 0); + + Log27 = gtk_check_button_new_with_mnemonic (_("Cdr Log")); + gtk_widget_show (Log27); + gtk_box_pack_start (GTK_BOX (vbox57), Log27, FALSE, FALSE, 0); + + Log28 = gtk_check_button_new_with_mnemonic (_("GPU Log")); + gtk_widget_show (Log28); + gtk_box_pack_start (GTK_BOX (vbox57), Log28, FALSE, FALSE, 0); + + label86 = gtk_label_new (_("IOP Logs")); + gtk_widget_show (label86); + gtk_frame_set_label_widget (GTK_FRAME (frame34), label86); + gtk_label_set_use_markup (GTK_LABEL (label86), TRUE); + + frame35 = gtk_frame_new (NULL); + gtk_widget_show (frame35); + gtk_box_pack_start (GTK_BOX (vbox55), frame35, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame35), GTK_SHADOW_NONE); + + alignment30 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment30); + gtk_container_add (GTK_CONTAINER (frame35), alignment30); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment30), 0, 0, 12, 0); + + vbox58 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox58); + gtk_container_add (GTK_CONTAINER (alignment30), vbox58); + + Log31 = gtk_check_button_new_with_mnemonic (_("Log to STDOUT")); + gtk_widget_show (Log31); + gtk_box_pack_start (GTK_BOX (vbox58), Log31, FALSE, FALSE, 0); + + Log30 = gtk_check_button_new_with_mnemonic (_("SYMs Log")); + gtk_widget_show (Log30); + gtk_box_pack_start (GTK_BOX (vbox58), Log30, FALSE, FALSE, 0); + + label87 = gtk_label_new (_("Misc")); + gtk_widget_show (label87); + gtk_frame_set_label_widget (GTK_FRAME (frame35), label87); + gtk_label_set_use_markup (GTK_LABEL (label87), TRUE); + + dialog_action_area6 = GTK_DIALOG (Logging)->action_area; + gtk_widget_show (dialog_action_area6); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area6), GTK_BUTTONBOX_END); + + Logging_Ok = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (Logging_Ok); + gtk_dialog_add_action_widget (GTK_DIALOG (Logging), Logging_Ok, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (Logging_Ok, GTK_CAN_DEFAULT); + + Logging2Cancel = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (Logging2Cancel); + gtk_dialog_add_action_widget (GTK_DIALOG (Logging), Logging2Cancel, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (Logging2Cancel, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) Logging_Ok, "clicked", + G_CALLBACK (OnLogging_Ok), + NULL); + g_signal_connect ((gpointer) Logging2Cancel, "clicked", + G_CALLBACK (On_Dialog_Cancelled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (Logging, Logging, "Logging"); + GLADE_HOOKUP_OBJECT_NO_REF (Logging, dialog_vbox6, "dialog_vbox6"); + GLADE_HOOKUP_OBJECT (Logging, scrolledwindow4, "scrolledwindow4"); + GLADE_HOOKUP_OBJECT (Logging, viewport2, "viewport2"); + GLADE_HOOKUP_OBJECT (Logging, vbox55, "vbox55"); + GLADE_HOOKUP_OBJECT (Logging, frame33, "frame33"); + GLADE_HOOKUP_OBJECT (Logging, alignment28, "alignment28"); + GLADE_HOOKUP_OBJECT (Logging, vbox56, "vbox56"); + GLADE_HOOKUP_OBJECT (Logging, Log0, "Log0"); + GLADE_HOOKUP_OBJECT (Logging, Log1, "Log1"); + GLADE_HOOKUP_OBJECT (Logging, Log2, "Log2"); + GLADE_HOOKUP_OBJECT (Logging, Log3, "Log3"); + GLADE_HOOKUP_OBJECT (Logging, Log4, "Log4"); + GLADE_HOOKUP_OBJECT (Logging, Log5, "Log5"); + GLADE_HOOKUP_OBJECT (Logging, Log6, "Log6"); + GLADE_HOOKUP_OBJECT (Logging, Log7, "Log7"); + GLADE_HOOKUP_OBJECT (Logging, Log8, "Log8"); + GLADE_HOOKUP_OBJECT (Logging, Log9, "Log9"); + GLADE_HOOKUP_OBJECT (Logging, Log10, "Log10"); + GLADE_HOOKUP_OBJECT (Logging, Log11, "Log11"); + GLADE_HOOKUP_OBJECT (Logging, Log12, "Log12"); + GLADE_HOOKUP_OBJECT (Logging, Log13, "Log13"); + GLADE_HOOKUP_OBJECT (Logging, Log14, "Log14"); + GLADE_HOOKUP_OBJECT (Logging, Log15, "Log15"); + GLADE_HOOKUP_OBJECT (Logging, Log16, "Log16"); + GLADE_HOOKUP_OBJECT (Logging, label85, "label85"); + GLADE_HOOKUP_OBJECT (Logging, frame34, "frame34"); + GLADE_HOOKUP_OBJECT (Logging, alignment29, "alignment29"); + GLADE_HOOKUP_OBJECT (Logging, vbox57, "vbox57"); + GLADE_HOOKUP_OBJECT (Logging, Log20, "Log20"); + GLADE_HOOKUP_OBJECT (Logging, Log21, "Log21"); + GLADE_HOOKUP_OBJECT (Logging, Log22, "Log22"); + GLADE_HOOKUP_OBJECT (Logging, Log23, "Log23"); + GLADE_HOOKUP_OBJECT (Logging, Log24, "Log24"); + GLADE_HOOKUP_OBJECT (Logging, Log25, "Log25"); + GLADE_HOOKUP_OBJECT (Logging, Log26, "Log26"); + GLADE_HOOKUP_OBJECT (Logging, Log27, "Log27"); + GLADE_HOOKUP_OBJECT (Logging, Log28, "Log28"); + GLADE_HOOKUP_OBJECT (Logging, label86, "label86"); + GLADE_HOOKUP_OBJECT (Logging, frame35, "frame35"); + GLADE_HOOKUP_OBJECT (Logging, alignment30, "alignment30"); + GLADE_HOOKUP_OBJECT (Logging, vbox58, "vbox58"); + GLADE_HOOKUP_OBJECT (Logging, Log31, "Log31"); + GLADE_HOOKUP_OBJECT (Logging, Log30, "Log30"); + GLADE_HOOKUP_OBJECT (Logging, label87, "label87"); + GLADE_HOOKUP_OBJECT_NO_REF (Logging, dialog_action_area6, "dialog_action_area6"); + GLADE_HOOKUP_OBJECT (Logging, Logging_Ok, "Logging_Ok"); + GLADE_HOOKUP_OBJECT (Logging, Logging2Cancel, "Logging2Cancel"); + + return Logging; +} + diff --git a/pcsx2/Linux/interface.h b/pcsx2/Linux/interface.h new file mode 100644 index 0000000000..823fdf6acd --- /dev/null +++ b/pcsx2/Linux/interface.h @@ -0,0 +1,22 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +GtkWidget* create_AdvDlg (void); +GtkWidget* create_SpeedHacksDlg (void); +GtkWidget* create_GameFixDlg (void); +GtkWidget* create_MemWrite32 (void); +GtkWidget* create_CmdLine (void); +GtkWidget* create_DumpRDlg (void); +GtkWidget* create_DumpCDlg (void); +GtkWidget* create_SetBPCDlg (void); +GtkWidget* create_SetBPADlg (void); +GtkWidget* create_SetPCDlg (void); +GtkWidget* create_DebugWnd (void); +GtkWidget* create_ConfDlg (void); +GtkWidget* create_AboutDlg (void); +GtkWidget* create_MainWindow (void); +GtkWidget* create_PatchBrowserWindow (void); +GtkWidget* create_PatchFinderWindow (void); +GtkWidget* create_CpuDlg (void); +GtkWidget* create_Logging (void); diff --git a/pcsx2/Linux/memzero.h b/pcsx2/Linux/memzero.h new file mode 100644 index 0000000000..86fc9c9625 --- /dev/null +++ b/pcsx2/Linux/memzero.h @@ -0,0 +1,182 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef _LNX_MEMZERO_H_ +#define _LNX_MEMZERO_H_ + +// This header contains non-optimized implementation of memzero_ptr and memset8_obj, +// memset16_obj, etc. + +template< u32 data, typename T > +static __forceinline void memset32_obj( T& obj ) +{ + // this function works on 32-bit aligned lengths of data only. + // If the data length is not a factor of 32 bits, the C++ optimizing compiler will + // probably just generate mysteriously broken code in Release builds. ;) + + jASSUME( (sizeof(T) & 0x3) == 0 ); + + u32* dest = (u32*)&obj; + for( int i=sizeof(T)>>2; i; --i, ++dest ) + *dest = data; +} + +template< uint size > +static __forceinline void memzero_ptr( void* dest ) +{ + memset( dest, 0, size ); +} + +template< typename T > +static __forceinline void memzero_obj( T& obj ) +{ + memset( &obj, 0, sizeof( T ) ); +} + +template< u8 data, typename T > +static __forceinline void memset8_obj( T& obj ) +{ + // Aligned sizes use the optimized 32 bit inline memset. Unaligned sizes use memset. + if( (sizeof(T) & 0x3) != 0 ) + memset( &obj, data, sizeof( T ) ); + else + memset32_obj( obj ); +} + +template< u16 data, typename T > +static __forceinline void memset16_obj( T& obj ) +{ + if( (sizeof(T) & 0x3) != 0 ) + _memset_16_unaligned( &obj, data, sizeof( T ) ); + else + memset32_obj( obj ); +} + + +// An optimized memset for 8 bit destination data. +template< u8 data, size_t bytes > +static __forceinline void memset_8( void *dest ) +{ + if( bytes == 0 ) return; + + if( (bytes & 0x3) != 0 ) + { + // unaligned data length. No point in doing an optimized inline version (too complicated!) + // So fall back on the compiler implementation: + + memset( dest, data, bytes ); + return; + } + + // This function only works on 32-bit alignments of data copied. + jASSUME( (bytes & 0x3) == 0 ); + + enum + { + remdat = bytes>>2, + data32 = data + (data<<8) + (data<<16) + (data<<24) + }; + + // macro to execute the x86/32 "stosd" copies. + switch( remdat ) + { + case 1: + *(u32*)dest = data32; + return; + + case 2: + ((u32*)dest)[0] = data32; + ((u32*)dest)[1] = data32; + return; + + case 3: + __asm__ + ( + ".intel_syntax\n" + "cld\n" +// "mov %edi, %0\n" +// "mov %eax, %1\n" + "stosd\n" + "stosd\n" + "stosd\n" + ".att_syntax\n" + : + : "D"(dest), "a"(data32) +// D - edi, a -- eax, c ecx + : + ); + return; + + case 4: + __asm__ + ( + ".intel_syntax\n" + "cld\n" +// "mov %edi, %0\n" +// "mov %eax, %1\n" + "stosd\n" + "stosd\n" + "stosd\n" + "stosd\n" + ".att_syntax\n" + : + : "D"(dest), "a"(data32) + : + + ); + return; + + case 5: + __asm__ + ( + ".intel_syntax\n" + "cld\n" +// "mov %edi, %0\n" +// "mov %eax, %1\n" + "stosd\n" + "stosd\n" + "stosd\n" + "stosd\n" + "stosd\n" + ".att_syntax\n" + : + : "D"(dest), "a"(data32) + : + + ); + return; + + default: + __asm__ + ( + ".intel_syntax\n" + "cld\n" +// "mov ecx, %0\n" +// "mov edi, %1\n" +// "mov eax, %2\n" + "rep stosd\n" + ".att_syntax\n" + : + : "c"(remdat), "D"(dest), "a"(data32) + : + ); + return; + } +} + +#endif diff --git a/pcsx2/Linux/pcsx2.glade b/pcsx2/Linux/pcsx2.glade new file mode 100644 index 0000000000..e34eb5e762 --- /dev/null +++ b/pcsx2/Linux/pcsx2.glade @@ -0,0 +1,8021 @@ + + + + + + + True + Advanced Options + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_SPREAD + + + + True + True + True + Defaults + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + -6 + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + True + False + 0 + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + Nearest + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Negative + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_EE_Round_Near + + + 0 + False + False + + + + + + True + True + Positive + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_EE_Round_Near + + + 0 + False + False + + + + + + True + True + Chop/Zero + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_EE_Round_Near + + + 0 + False + False + + + + + + + + + + True + <b>Round Mode</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + None + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Normal + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_EE_Clamp_None + + + 0 + False + False + + + + + + True + True + Extra + Preserve Sign + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_EE_Clamp_None + + + 0 + False + False + + + + + + + + + + True + <b>Clamp Mode</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + True + Flush to Zero + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Denormals are Zero + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + 0 + True + True + + + + + + + + + + True + <b>EE Recs Options</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + Nearest + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Negative + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_VU_Round_Near + + + 0 + False + False + + + + + + True + True + Positive + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_VU_Round_Near + + + 0 + False + False + + + + + + True + True + Zero + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_VU_Round_Near + + + 0 + False + False + + + + + + + + + + True + <b>Round Mode</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + None + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Normal + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_VU_Clamp_None + + + 0 + False + False + + + + + + True + True + Extra + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_VU_Clamp_None + + + 0 + False + False + + + + + + True + True + Extra + Preserve Sign + True + GTK_RELIEF_NORMAL + True + False + False + True + radio_VU_Clamp_None + + + 0 + False + False + + + + + + + + + + True + <b>Clamp Mode</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + 2 + 2 + False + 0 + 0 + + + + True + True + Flush to Zero + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + True + Denormals are Zero + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 1 + 2 + 0 + 1 + fill + + + + + + + True + False + True + Set O & U Flags + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + False + True + Software Emulate DaZ + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 1 + 2 + 1 + 2 + fill + + + + + + 0 + True + True + + + + + + + + + + True + <b>VU Recs Options</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + These options specify how your CPU rounds floating point values. + +Try changing the roundmode for EE if your game hangs, it could make it work again. + False + False + GTK_JUSTIFY_LEFT + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + + + + + + True + <b>Round Mode</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + False + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + These options specify how PCSX2's recompilers will clamp Infinities and NaN (Not a Number) values in the opcode instructions. + +None - No clamping. (Fastest Mode) +Normal - Clamps the result. +Extra - Clamps the operands, the result, and anywhere in between. +Extra + Preserve Sign - Same as ""Extra"", except preserves NaN's sign when clamping the operands. (Slowest Mode) + False + False + GTK_JUSTIFY_LEFT + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + + + + + + True + <b>Clamp Mode</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + False + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + Flush to Zero - Your CPU makes Floating Point Underflows become Zero, so it does less work. (Speed Up) + +Denormals are Zero - Your CPU makes Floating Point Denormals become Zero, so it does less work. (Speed Up) + False + False + GTK_JUSTIFY_LEFT + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + + + + + + True + <b>Other Options</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + False + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + + + True + PCSX2 Speed Hacks + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + -6 + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + True + These hacks will affect the speed of PCSX2 but possibly compromise compatibility. +If you have problems, Disable all of these and try again. + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + False + 0 + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 2 + + + + True + True + Default Cycle Rate + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + Most compatable option - recommended for everyone with high-end machines. + False + False + GTK_JUSTIFY_LEFT + True + False + 0.289999991655 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + Use x1.5 Cycle Rate + True + GTK_RELIEF_NORMAL + True + False + False + True + check_default_cycle_rate + + + 0 + False + False + + + + + + True + Moderate speedup, and works well with most games. + False + False + GTK_JUSTIFY_LEFT + True + False + 0.289999991655 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + Use x2 Cycle Rate + True + GTK_RELIEF_NORMAL + True + False + False + True + check_default_cycle_rate + + + 0 + False + False + + + + + + True + Big speedup! Works well with many games. + False + False + GTK_JUSTIFY_LEFT + True + False + 0.360000014305 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + Use x3 Cycle Rate + True + GTK_RELIEF_NORMAL + True + False + False + True + check_default_cycle_rate + + + 0 + False + False + + + + + + True + Big speedup, but causes flickering or missing geometry on many games. + False + False + GTK_JUSTIFY_LEFT + True + False + 0.239999994636 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + + + 0 + False + False + + + + + + True + Important: X2 & X3 sync hacks *will* cause choppy/skippy audio on many FMV movies. + False + False + GTK_JUSTIFY_LEFT + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + + True + <b>frame37</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + True + Enable IOP x2 Cycle Rate + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + Small speedup, and works well with most games, + False + False + GTK_JUSTIFY_LEFT + True + False + 0.5 + 0.5 + 0 + 0 + check_iop_cycle_rate + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + WaitCycles Sync Hack + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + Small speedup. Works well with most games, but it may cause certain games to crash, or freeze up during bootup or stage changes. + False + False + GTK_JUSTIFY_LEFT + True + False + 0.5 + 0.5 + 0 + 0 + check_wait_cycles_sync_hack + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + 0 + False + False + + + + + 0 + True + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + True + Escape Hack - Use Esc key to fully exit PCSX2. + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + + + + + True + <b>Miscellaneous</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + False + + + + + 0 + False + False + + + + + + + + True + Game Special Fixes + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + -6 + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + FPU Clamp Hack - Special fix for Tekken 5 and maybe other games. + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + VU Branch Hack - Special fix for Magna Carta; Breaks Crash Bandicoot! + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + VU Add / Sub Hack - Special fix for Tri-Ace games! + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + + + + + True + <b>Some games need special settings. +Configure them here.</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + + + 5 + True + memWrite32 + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + 5 + True + False + 5 + + + + 5 + True + False + 2 + + + + True + Address + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + 0x + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + • + False + + + 0 + True + True + + + + + 0 + False + False + + + + + + 5 + True + False + 2 + + + + True + Data + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + 0x + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + • + False + + + 0 + True + True + + + + + 0 + True + True + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 30 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + True + True + + + + + + + + 5 + True + Program arguments + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + 5 + True + False + 5 + + + + True + Fill in the command line arguments for the opened program: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.10000000149 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + 5 + True + False + 2 + + + + True + If you don't know what to write leave it blank + True + True + True + 0 + + True + • + False + + + 0 + True + True + + + + + 0 + False + False + + + + + + True + Note: this is intended for developers only. + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 30 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + True + True + + + + + + + + 5 + True + Raw Dump + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + 5 + True + False + 5 + + + + True + Set Dump Addr (in Hex): + False + False + GTK_JUSTIFY_CENTER + False + False + 0.10000000149 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + 5 + True + False + 2 + + + + True + From 0x + False + False + GTK_JUSTIFY_CENTER + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + • + False + + + 0 + True + True + + + + + 0 + False + False + + + + + + 5 + True + False + 2 + + + + True + To 0x + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + • + False + + + 0 + True + True + + + + + 0 + True + True + + + + + + True + Dump File = "dump.txt" + False + False + GTK_JUSTIFY_LEFT + False + False + 0.10000000149 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 30 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + True + True + + + + + + + + 5 + True + Dump code + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + 5 + True + False + 5 + + + + True + Set Dump Addr (in Hex): + False + False + GTK_JUSTIFY_LEFT + False + False + 0.10000000149 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + 5 + True + False + 2 + + + + True + From 0x + False + False + GTK_JUSTIFY_CENTER + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + • + False + + + 0 + True + True + + + + + 0 + False + False + + + + + + 5 + True + False + 2 + + + + True + To 0x + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + • + False + + + 0 + True + True + + + + + 0 + True + True + + + + + + True + Dump File = "dump.txt" + False + False + GTK_JUSTIFY_LEFT + False + False + 0.10000000149 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 30 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + True + True + + + + + + + + 5 + True + SetBreakPoint Addr + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + 5 + True + False + 5 + + + + True + Set New BP Count (in Hex): + False + False + GTK_JUSTIFY_CENTER + False + False + 0.10000000149 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + 5 + True + False + 2 + + + + True + 0x + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + • + False + + + 0 + True + True + + + + + 0 + False + False + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 30 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + True + True + + + + + + + + 5 + True + SetBreakPoint Addr + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + 5 + True + False + 5 + + + + True + Set New BP Address (in Hex): + False + False + GTK_JUSTIFY_CENTER + False + False + 0.10000000149 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + 5 + True + False + 2 + + + + True + 0x + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + • + False + + + 0 + True + True + + + + + 0 + False + False + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 30 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + True + True + + + + + + + + 5 + True + SetPCDlg + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + 5 + True + False + 5 + + + + True + Set New PC Address (in Hex): + False + False + GTK_JUSTIFY_CENTER + False + False + 0.10000000149 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + 5 + True + False + 2 + + + + True + 0x + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + • + False + + + 0 + True + True + + + + + 0 + False + False + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 30 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + True + True + + + + + + + + 5 + True + PCSX2 Debugger + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + 5 + True + False + 0 + + + + True + False + 5 + + + + True + True + EE Debug Mode + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 0 + False + False + + + + + + True + True + IOP Debug Mode + True + GTK_RELIEF_NORMAL + True + False + False + True + GtkRadioButton_EE + + + + 0 + False + False + + + + + 0 + False + True + + + + + + 5 + True + False + 0 + + + + True + False + 0 + + + + True + GTK_POLICY_ALWAYS + GTK_POLICY_NEVER + GTK_SHADOW_NONE + GTK_CORNER_TOP_LEFT + + + + True + GTK_SHADOW_IN + + + + True + False + False + False + True + False + False + False + + + + + + + 0 + True + True + + + + + + True + GTK_UPDATE_CONTINUOUS + False + 0 0 412 1 20 2 + + + 0 + False + True + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Step + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Skip + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Go + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Log On/Off + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + False + True + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Set PC + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Set BP Addr + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Set BP Count + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Clear BPs + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + False + True + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Dump code + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Raw Dump + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Close + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + False + True + + + + + 0 + False + True + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 10 + + + + True + True + True + memWrite32 + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + False + True + + + + + 0 + True + True + + + + + + + + 10 + True + Conf + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + False + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + True + False + 0 + + + + 2 + True + 14 + 2 + False + 0 + 15 + + + + True + Bios + False + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 1 + 2 + 12 + 13 + + + + + + + + True + FireWire + False + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 1 + 2 + 9 + 10 + + + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Configure + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Test + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + About + True + GTK_RELIEF_NORMAL + True + + + + + + 1 + 2 + 11 + 12 + + + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Configure + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Test + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + About + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + 1 + 11 + 12 + + + + + + + True + Usb + False + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 9 + 10 + + + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Configure + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Test + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + About + True + GTK_RELIEF_NORMAL + True + + + + + + 1 + 2 + 8 + 9 + + + + + + + + True + Cdvdrom + False + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 1 + 2 + 6 + 7 + + + + + + + + True + Dev9 + False + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 6 + 7 + + + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Configure + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Test + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + About + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + 1 + 8 + 9 + + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Configure + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Test + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + About + True + GTK_RELIEF_NORMAL + True + + + + + + 1 + 2 + 2 + 3 + + + + + + + + True + Sound + False + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 1 + 2 + 0 + 1 + + + + + + + + True + Second Controller + False + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 1 + 2 + 3 + 4 + + + + + + + + True + First Controller + False + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 3 + 4 + + + + + + + + True + Graphics + False + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 0 + 1 + + + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Configure + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Test + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + About + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + 1 + 2 + 3 + + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Configure + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Test + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + About + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + 1 + 5 + 6 + + + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + Configure + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Test + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + About + True + GTK_RELIEF_NORMAL + True + + + + + + 1 + 2 + 5 + 6 + + + + + + + True + + False + True + + + 1 + 2 + 10 + 11 + + + + + + True + + False + True + + + 0 + 1 + 10 + 11 + + + + + + True + + False + True + + + 1 + 2 + 7 + 8 + + + + + + True + + False + True + + + 0 + 1 + 7 + 8 + + + + + + True + + False + True + + + 1 + 2 + 4 + 5 + + + + + + True + + False + True + + + 0 + 1 + 4 + 5 + + + + + + True + + False + True + + + 1 + 2 + 1 + 2 + + + + + + True + + False + True + + + 0 + 1 + 1 + 2 + + + + + + True + + False + True + + + 1 + 2 + 13 + 14 + + + + + 0 + True + True + + + + + + True + False + 14 + + + + True + GTK_BUTTONBOX_START + 0 + + + + True + True + True + Select Plugins Dir + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + Select Bios Dir + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + False + True + + + + + + True + GTK_BUTTONBOX_DEFAULT_STYLE + 0 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + + + 10 + True + Pcsx About + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + True + False + 0 + + + + True + False + 0 + + + + True + PCSX2 + +Version x.x + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + 5 + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 5 + True + False + 0 + + + + True + written by... + False + False + GTK_JUSTIFY_CENTER + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + 0 + False + False + + + + + 0 + True + True + + + + + + 5 + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 5 + True + False + 0 + + + + True + greets to... + False + False + GTK_JUSTIFY_CENTER + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + 0 + False + False + + + + + + True + GTK_BUTTONBOX_EDGE + 0 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + + + + + + 0 + False + False + + + + + + + + True + PCSX + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_CENTER + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + + + + + + True + False + 0 + + + + True + GTK_PACK_DIRECTION_LTR + GTK_PACK_DIRECTION_LTR + + + + True + _File + True + + + + + + + True + _Run CD + True + + + + + + + True + _Load Elf + True + + + + + + + True + + + + + + True + States + True + + + + + + + True + Load + True + + + + + + + True + Slot 1 + True + + + + + + + True + Slot 2 + True + + + + + + + True + Slot 3 + True + + + + + + + True + Slot 4 + True + + + + + + + True + Slot 5 + True + + + + + + + True + Other... + True + + + + + + + + + + + True + Save + True + + + + + + + True + Slot 1 + True + + + + + + + True + Slot 2 + True + + + + + + + True + Slot 3 + True + + + + + + + True + Slot 4 + True + + + + + + + True + Slot 5 + True + + + + + + + True + Other... + True + + + + + + + + + + + + + + + True + E_xit + True + + + + + + + + + + + True + _Run + True + + + + + + + True + E_xecute + True + + + + + + + True + Re_set + True + + + + + + + True + _Arguments + True + + + + + + + + + + + True + _Config + True + + + + + + + True + _Configure + True + + + + + + + True + + + + + + True + _Graphics + True + + + + + + + True + C_ontrollers + True + + + + + + + True + _Sound + True + + + + + + + True + _Cdvdrom + True + + + + + + + True + D_ev9 + True + + + + + + + True + U_SB + True + + + + + + + True + Fire_Wire + True + + + + + + + True + + + + + + True + C_pu + True + + + + + + + True + Game Fixes + True + + + + + + + True + Speed Hacks + True + + + + + + + True + Advanced + True + + + + + + + + + + + True + _Language + True + + + + + + True + _Misc + True + + + + + + + True + Patch _Browser + True + + + + + + + True + Patch _Finder + True + + + + + + + True + + + + + + True + Enable _Console + True + False + + + + + + + True + Enable _Patches + True + False + + + + + + + + + + + True + _Debug + True + + + + + + + True + Enter Debugger ... + True + + + + + + + True + Logging + True + + + + + + + + + + + True + _Help + True + + + + + + + True + About PCSX2 - Playground... + True + + + + + + + + + + 0 + False + False + + + + + + True + pcsxAbout.bmp + 0.5 + 0.5 + 0 + 0 + + + 1 + True + True + + + + + + True + False + 0 + + + + + + + 0 + True + True + + + + + + + + Patch List + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + True + False + False + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + + + + True + False + 0 + + + + True + True + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + False + False + True + False + False + False + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + True + Enable / Diable + True + GTK_RELIEF_NORMAL + True + + + 0 + False + False + + + + + + True + True + Add Patch + True + GTK_RELIEF_NORMAL + True + + + 0 + False + False + + + + + + True + True + Edit Patch + True + GTK_RELIEF_NORMAL + True + + + 0 + False + False + + + + + + True + True + Add GS2v3-4 + True + GTK_RELIEF_NORMAL + True + + + 0 + False + False + + + + + + True + True + Add Raw + True + GTK_RELIEF_NORMAL + True + + + 0 + False + False + + + + + + True + True + Pnach Writer + True + GTK_RELIEF_NORMAL + True + + + 0 + False + False + + + + + + True + True + Skip Mpeg + True + GTK_RELIEF_NORMAL + True + + + 0 + False + False + + + + + + + + + + + + + + True + True + gtk-close + True + GTK_RELIEF_NORMAL + True + + + 0 + False + False + + + + + 0 + True + True + + + + + + + + True + Patch Finder + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + + + + True + False + 0 + + + + True + False + 0 + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + EE RAM + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + IOP RAM + True + GTK_RELIEF_NORMAL + True + False + False + True + radiobutton1 + + + 0 + False + False + + + + + + + + + + True + <b>Search In</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + 3 + 2 + False + 0 + 0 + + + + True + True + 8 bits + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + True + 16 bits + True + GTK_RELIEF_NORMAL + True + False + False + True + radiobutton3 + + + 1 + 2 + 0 + 1 + fill + + + + + + + True + True + 32 bits + True + GTK_RELIEF_NORMAL + True + False + False + True + radiobutton3 + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + True + 64 bits + True + GTK_RELIEF_NORMAL + True + False + False + True + radiobutton3 + + + 1 + 2 + 1 + 2 + fill + + + + + + + True + True + Unsigned + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + 1 + 2 + 3 + fill + + + + + + + + + + + True + <b>Values of Size</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + Equal + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Greater Then + True + GTK_RELIEF_NORMAL + True + False + False + True + radiobutton7 + + + 0 + False + False + + + + + + True + True + Less Then + True + GTK_RELIEF_NORMAL + True + False + False + True + radiobutton7 + + + 0 + False + False + + + + + + True + True + Greater Then or Equal + True + GTK_RELIEF_NORMAL + True + False + False + True + radiobutton7 + + + 0 + False + False + + + + + + True + True + Less Then or Equal + True + GTK_RELIEF_NORMAL + True + False + False + True + radiobutton7 + + + 0 + False + False + + + + + + True + True + Not Equal + True + GTK_RELIEF_NORMAL + True + False + False + True + radiobutton7 + + + 0 + False + False + + + + + + + + + + True + <b>Being</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + Old Value + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Specific Value + True + GTK_RELIEF_NORMAL + True + False + False + True + radiobutton13 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + * + False + + + 0 + False + False + + + + + + + + + + True + <b>Compared To</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + False + 0 + + + + True + False + 0 + + + + True + Results: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + %s + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + True + Ready to Search + False + False + GTK_JUSTIFY_RIGHT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + False + + + + + 0 + False + False + + + + + + True + True + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + False + False + True + False + False + False + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + True + 2 + 2 + False + 0 + 0 + + + + True + True + gtk-clear + True + GTK_RELIEF_NORMAL + True + + + 0 + 1 + 0 + 1 + + + + + + + True + True + gtk-find + True + GTK_RELIEF_NORMAL + True + + + 1 + 2 + 0 + 1 + + + + + + + True + True + gtk-add + True + GTK_RELIEF_NORMAL + True + + + 0 + 1 + 1 + 2 + + + + + + + True + True + gtk-close + True + GTK_RELIEF_NORMAL + True + + + 1 + 2 + 1 + 2 + + + + + + 0 + False + True + + + + + 0 + True + True + + + + + + + + True + dialog1 + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + True + False + 0 + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + CPU vendor + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + Family + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + Cpu Speed + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + Features + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + + True + + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + True + EERec - EE/IOP recompiler (Need MMX/SSE/SSE2) + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + 5 + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 5 + True + False + 0 + + + + True + True + VU0rec - enable recompiler for VU0 unit + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + VU1rec - enable recompiler for VU1 unit + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + + + True + VU Recompilers - All options are set by default + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + True + Multi threaded GS mode (MTGS) + (faster on dual core/HT CPUs, requires pcsx2 restart) + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + Normal - All frames are rendered as fast as possible + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Limit - Force frames to normal speeds if too fast + True + GTK_RELIEF_NORMAL + True + False + False + True + GtkRadioButton_LimitNormal + + + 0 + False + False + + + + + + True + True + Frame Skip - In order to achieve normal speeds, + some frames are skipped (faster). + Fps displayed counts skipped frames too + True + GTK_RELIEF_NORMAL + True + False + False + True + GtkRadioButton_LimitNormal + + + 0 + False + False + + + + + + True + True + VU Skip - Same as Frame Skip, but tried to skip more. + Artifacts might be present, but will be faster + True + GTK_RELIEF_NORMAL + True + False + False + True + GtkRadioButton_LimitNormal + + + 0 + False + False + + + + + + + + + + True + Frame Limiting (F4 switches in-game) + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + False + 0 + + + + True + Custom FPS Limit (0=auto): + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + True + True + 1 + 0 + True + GTK_UPDATE_ALWAYS + False + False + 0 0 9999 1 0 0 + + + 0 + False + True + GTK_PACK_END + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + Skip Frames when slower than[1]: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + True + True + 1 + 0 + False + GTK_UPDATE_ALWAYS + False + False + 0 0 9999 1 0 0 + + + 0 + False + True + GTK_PACK_END + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + Consecutive Frames before skipping[2]: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + True + True + 1 + 0 + False + GTK_UPDATE_ALWAYS + False + False + 0 0 9999 1 0 0 + + + 0 + False + True + GTK_PACK_END + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + Consecutive Frames to skip[3]: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + True + True + 1 + 0 + False + GTK_UPDATE_ALWAYS + False + False + 0 0 9999 1 0 0 + + + 0 + False + True + GTK_PACK_END + + + + + 0 + True + True + + + + + + True + [1] Only skips when slower then this number(0 is auto; 9999 always skips). + False + False + GTK_JUSTIFY_LEFT + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + [2] Renders this many frames before skipping (0 is default). + False + False + GTK_JUSTIFY_LEFT + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + [3] Skips this many frames before continuing (0 is default). + False + False + GTK_JUSTIFY_LEFT + True + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + + True + Detailed Settings + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + + + True + Logging + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 200 + 450 + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + -6 + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + True + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + GTK_SHADOW_IN + + + + True + False + 0 + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + Cpu Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Mem Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Hw Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Dma Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Bios Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Elf Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Fpu Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + MMI Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + VU0 Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Cop0 Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Vif Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + SPR Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + GIF Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Sif Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + IPU Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + VU Micro Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + RPC Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + + + + + True + <b>EE Logs</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + IOP Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Mem Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Hw Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Bios Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Dma Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Pad Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Gte Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Cdr Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + GPU Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + + + + + True + <b>IOP Logs</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_NONE + + + + True + 0.5 + 0.5 + 1 + 1 + 0 + 0 + 12 + 0 + + + + True + False + 0 + + + + True + True + Log to STDOUT + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + SYMs Log + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + + + + + True + <b>Misc</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + True + True + + + + + + + + + 0 + True + True + + + + + + + diff --git a/pcsx2/Linux/support.c b/pcsx2/Linux/support.c new file mode 100644 index 0000000000..8a9e2d9fb5 --- /dev/null +++ b/pcsx2/Linux/support.c @@ -0,0 +1,144 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include + +#include "support.h" + +GtkWidget* +lookup_widget (GtkWidget *widget, + const gchar *widget_name) +{ + GtkWidget *parent, *found_widget; + + for (;;) + { + if (GTK_IS_MENU (widget)) + parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); + else + parent = widget->parent; + if (!parent) + parent = (GtkWidget*) g_object_get_data (G_OBJECT (widget), "GladeParentKey"); + if (parent == NULL) + break; + widget = parent; + } + + found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget), + widget_name); + if (!found_widget) + g_warning ("Widget not found: %s", widget_name); + return found_widget; +} + +static GList *pixmaps_directories = NULL; + +/* Use this function to set the directory containing installed pixmaps. */ +void +add_pixmap_directory (const gchar *directory) +{ + pixmaps_directories = g_list_prepend (pixmaps_directories, + g_strdup (directory)); +} + +/* This is an internally used function to find pixmap files. */ +static gchar* +find_pixmap_file (const gchar *filename) +{ + GList *elem; + + /* We step through each of the pixmaps directory to find it. */ + elem = pixmaps_directories; + while (elem) + { + gchar *pathname = g_strdup_printf ("%s%s%s", (gchar*)elem->data, + G_DIR_SEPARATOR_S, filename); + if (g_file_test (pathname, G_FILE_TEST_EXISTS)) + return pathname; + g_free (pathname); + elem = elem->next; + } + return NULL; +} + +/* This is an internally used function to create pixmaps. */ +GtkWidget* +create_pixmap (GtkWidget *widget, + const gchar *filename) +{ + gchar *pathname = NULL; + GtkWidget *pixmap; + + if (!filename || !filename[0]) + return gtk_image_new (); + + pathname = find_pixmap_file (filename); + + if (!pathname) + { + g_warning (_("Couldn't find pixmap file: %s"), filename); + return gtk_image_new (); + } + + pixmap = gtk_image_new_from_file (pathname); + g_free (pathname); + return pixmap; +} + +/* This is an internally used function to create pixmaps. */ +GdkPixbuf* +create_pixbuf (const gchar *filename) +{ + gchar *pathname = NULL; + GdkPixbuf *pixbuf; + GError *error = NULL; + + if (!filename || !filename[0]) + return NULL; + + pathname = find_pixmap_file (filename); + + if (!pathname) + { + g_warning (_("Couldn't find pixmap file: %s"), filename); + return NULL; + } + + pixbuf = gdk_pixbuf_new_from_file (pathname, &error); + if (!pixbuf) + { + fprintf (stderr, "Failed to load pixbuf file: %s: %s\n", + pathname, error->message); + g_error_free (error); + } + g_free (pathname); + return pixbuf; +} + +/* This is used to set ATK action descriptions. */ +void +glade_set_atk_action_description (AtkAction *action, + const gchar *action_name, + const gchar *description) +{ + gint n_actions, i; + + n_actions = atk_action_get_n_actions (action); + for (i = 0; i < n_actions; i++) + { + if (!strcmp (atk_action_get_name (action, i), action_name)) + atk_action_set_description (action, i, description); + } +} + diff --git a/pcsx2/Linux/support.h b/pcsx2/Linux/support.h new file mode 100644 index 0000000000..160bae001c --- /dev/null +++ b/pcsx2/Linux/support.h @@ -0,0 +1,69 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* + * Standard gettext macros. + */ +#ifdef ENABLE_NLS +# include +# undef _ +# define _(String) dgettext (PACKAGE, String) +# define Q_(String) g_strip_context ((String), gettext (String)) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define textdomain(String) (String) +# define gettext(String) (String) +# define dgettext(Domain,Message) (Message) +# define dcgettext(Domain,Message,Type) (Message) +# define bindtextdomain(Domain,Directory) (Domain) +# define _(String) (String) +# define Q_(String) g_strip_context ((String), (String)) +# define N_(String) (String) +#endif + + +/* + * Public Functions. + */ + +/* + * This function returns a widget in a component created by Glade. + * Call it with the toplevel widget in the component (i.e. a window/dialog), + * or alternatively any widget in the component, and the name of the widget + * you want returned. + */ +GtkWidget* lookup_widget (GtkWidget *widget, + const gchar *widget_name); + + +/* Use this function to set the directory containing installed pixmaps. */ +void add_pixmap_directory (const gchar *directory); + + +/* + * Private Functions. + */ + +/* This is used to create the pixmaps used in the interface. */ +GtkWidget* create_pixmap (GtkWidget *widget, + const gchar *filename); + +/* This is used to create the pixbufs used in the interface. */ +GdkPixbuf* create_pixbuf (const gchar *filename); + +/* This is used to set ATK action descriptions. */ +void glade_set_atk_action_description (AtkAction *action, + const gchar *action_name, + const gchar *description); + diff --git a/pcsx2/MMI.cpp b/pcsx2/MMI.cpp new file mode 100644 index 0000000000..9b44343cb5 --- /dev/null +++ b/pcsx2/MMI.cpp @@ -0,0 +1,1566 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "DebugTools/Debug.h" +#include "R5900.h" +#include "R5900OpcodeTables.h" + +namespace R5900 { +namespace Interpreter { +namespace OpcodeImpl { + + ///////////////////////////////////////////////////////////////// + // Non-MMI Instructions! + // + // Several instructions in the MMI opcode class are actually just regular + // instructions which have been added to "extend" the R5900's instruction + // set. They're here, because if not here they'd be homeless. + // - The Pcsx2 team, doing their part to fight homelessness + + void MADD() { + s64 temp = (s64)((u64)cpuRegs.LO.UL[0] | ((u64)cpuRegs.HI.UL[0] << 32)) + + ((s64)cpuRegs.GPR.r[_Rs_].SL[0] * (s64)cpuRegs.GPR.r[_Rt_].SL[0]); + + cpuRegs.LO.SD[0] = (s32)(temp & 0xffffffff); + cpuRegs.HI.SD[0] = (s32)(temp >> 32); + + if (_Rd_) cpuRegs.GPR.r[_Rd_].SD[0] = cpuRegs.LO.SD[0]; + } + + void MADDU() { + u64 tempu = (u64)((u64)cpuRegs.LO.UL[0] | ((u64)cpuRegs.HI.UL[0] << 32)) + + ((u64)cpuRegs.GPR.r[_Rs_].UL[0] * (u64)cpuRegs.GPR.r[_Rt_].UL[0]); + + cpuRegs.LO.SD[0] = (s32)(tempu & 0xffffffff); + cpuRegs.HI.SD[0] = (s32)(tempu >> 32); + + if (_Rd_) cpuRegs.GPR.r[_Rd_].SD[0] = cpuRegs.LO.SD[0]; + } + + void MADD1() { + s64 temp = (s64)((u64)cpuRegs.LO.UL[2] | ((u64)cpuRegs.HI.UL[2] << 32)) + + ((s64)cpuRegs.GPR.r[_Rs_].SL[0] * (s64)cpuRegs.GPR.r[_Rt_].SL[0]); + + cpuRegs.LO.SD[1] = (s32)(temp & 0xffffffff); + cpuRegs.HI.SD[1] = (s32)(temp >> 32); + + if (_Rd_) cpuRegs.GPR.r[_Rd_].SD[0] = cpuRegs.LO.SD[1]; + } + + void MADDU1() { + u64 tempu = (u64)((u64)cpuRegs.LO.UL[2] | ((u64)cpuRegs.HI.UL[2] << 32)) + + ((u64)cpuRegs.GPR.r[_Rs_].UL[0] * (u64)cpuRegs.GPR.r[_Rt_].UL[0]); + + cpuRegs.LO.SD[1] = (s32)(tempu & 0xffffffff); + cpuRegs.HI.SD[1] = (s32)(tempu >> 32); + + if (_Rd_) cpuRegs.GPR.r[_Rd_].SD[0] = cpuRegs.LO.SD[1]; + } + + void MFHI1() { + if (!_Rd_) return; + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.HI.UD[1]; + } + + void MFLO1() { + if (!_Rd_) return; + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.LO.UD[1]; + } + + void MTHI1() { + cpuRegs.HI.UD[1] = cpuRegs.GPR.r[_Rs_].UD[0]; + } + + void MTLO1() { + cpuRegs.LO.UD[1] = cpuRegs.GPR.r[_Rs_].UD[0]; + } + + void MULT1() { + s64 temp = (s64)cpuRegs.GPR.r[_Rs_].SL[0] * (s64)cpuRegs.GPR.r[_Rt_].SL[0]; + + cpuRegs.LO.UD[1] = (s64)(s32)(temp & 0xffffffff); + cpuRegs.HI.UD[1] = (s64)(s32)(temp >> 32); + + /* Modified a bit . asadr */ + if (_Rd_) cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.LO.UD[1]; + } + + void MULTU1() { + u64 tempu = (u64)cpuRegs.GPR.r[_Rs_].UL[0] * (u64)cpuRegs.GPR.r[_Rt_].UL[0]; + + cpuRegs.LO.UD[1] = (s32)(tempu & 0xffffffff); + cpuRegs.HI.UD[1] = (s32)(tempu >> 32); + + if (_Rd_) cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.LO.UD[1]; + } + + void DIV1() { + if (cpuRegs.GPR.r[_Rt_].SL[0] != 0) { + cpuRegs.LO.SD[1] = cpuRegs.GPR.r[_Rs_].SL[0] / cpuRegs.GPR.r[_Rt_].SL[0]; + cpuRegs.HI.SD[1] = cpuRegs.GPR.r[_Rs_].SL[0] % cpuRegs.GPR.r[_Rt_].SL[0]; + } + } + + void DIVU1() { + if (cpuRegs.GPR.r[_Rt_].UL[0] != 0) { + cpuRegs.LO.UD[1] = cpuRegs.GPR.r[_Rs_].UL[0] / cpuRegs.GPR.r[_Rt_].UL[0]; + cpuRegs.HI.UD[1] = cpuRegs.GPR.r[_Rs_].UL[0] % cpuRegs.GPR.r[_Rt_].UL[0]; + } + } + +namespace MMI { + +//*****************MMI OPCODES********************************* + +__forceinline void _PLZCW(int n) +{ + // This function counts the number of "like" bits in the source register, starting + // with the MSB and working its way down, and returns the result MINUS ONE. + // So 0xff00 would return 7, not 8. + + int c = 0; + s32 i = cpuRegs.GPR.r[_Rs_].SL[n]; + + // Negate the source based on the sign bit. This allows us to use a simple + // unified bit test of the MSB for either condition. + if( i >= 0 ) i = ~i; + + // shift first, compare, then increment. This excludes the sign bit from our final count. + while( i <<= 1, i < 0 ) c++; + + cpuRegs.GPR.r[_Rd_].UL[n] = c; +} + +void PLZCW() { + if (!_Rd_) return; + + _PLZCW (0); + _PLZCW (1); +} + +#define PMFHL_CLAMP(dst, src) \ + if ((int)src > (int)0x00007fff) dst = 0x7fff; \ + else \ + if ((int)src < (int)0xffff8000) dst = 0x8000; \ + else dst = (u16)src; + +void PMFHL() { + if (!_Rd_) return; + + switch (_Sa_) { + case 0x00: // LW + cpuRegs.GPR.r[_Rd_].UL[0] = cpuRegs.LO.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[1] = cpuRegs.HI.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[2] = cpuRegs.LO.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[3] = cpuRegs.HI.UL[2]; + break; + + case 0x01: // UW + cpuRegs.GPR.r[_Rd_].UL[0] = cpuRegs.LO.UL[1]; + cpuRegs.GPR.r[_Rd_].UL[1] = cpuRegs.HI.UL[1]; + cpuRegs.GPR.r[_Rd_].UL[2] = cpuRegs.LO.UL[3]; + cpuRegs.GPR.r[_Rd_].UL[3] = cpuRegs.HI.UL[3]; + break; + + case 0x02: // SLW + { + u64 TempU64 = ((u64)cpuRegs.HI.UL[0] << 32) | (u64)cpuRegs.LO.UL[0]; + if (TempU64 >= 0x000000007fffffffLL) { + cpuRegs.GPR.r[_Rd_].UD[0] = 0x000000007fffffffLL; + } else if (TempU64 <= 0xffffffff80000000LL) { + cpuRegs.GPR.r[_Rd_].UD[0] = 0xffffffff80000000LL; + } else { + cpuRegs.GPR.r[_Rd_].UD[0] = (s64)cpuRegs.LO.SL[0]; + } + + TempU64 = ((u64)cpuRegs.HI.UL[2] << 32) | (u64)cpuRegs.LO.UL[2]; + if (TempU64 >= 0x000000007fffffffLL) { + cpuRegs.GPR.r[_Rd_].UD[1] = 0x000000007fffffffLL; + } else if (TempU64 <= 0xffffffff80000000LL) { + cpuRegs.GPR.r[_Rd_].UD[1] = 0xffffffff80000000LL; + } else { + cpuRegs.GPR.r[_Rd_].UD[1] = (s64)cpuRegs.LO.SL[2]; + } + } + break; + + case 0x03: // LH + cpuRegs.GPR.r[_Rd_].US[0] = cpuRegs.LO.US[0]; + cpuRegs.GPR.r[_Rd_].US[1] = cpuRegs.LO.US[2]; + cpuRegs.GPR.r[_Rd_].US[2] = cpuRegs.HI.US[0]; + cpuRegs.GPR.r[_Rd_].US[3] = cpuRegs.HI.US[2]; + cpuRegs.GPR.r[_Rd_].US[4] = cpuRegs.LO.US[4]; + cpuRegs.GPR.r[_Rd_].US[5] = cpuRegs.LO.US[6]; + cpuRegs.GPR.r[_Rd_].US[6] = cpuRegs.HI.US[4]; + cpuRegs.GPR.r[_Rd_].US[7] = cpuRegs.HI.US[6]; + break; + + case 0x04: // SH + PMFHL_CLAMP(cpuRegs.GPR.r[_Rd_].US[0], cpuRegs.LO.UL[0]); + PMFHL_CLAMP(cpuRegs.GPR.r[_Rd_].US[1], cpuRegs.LO.UL[1]); + PMFHL_CLAMP(cpuRegs.GPR.r[_Rd_].US[2], cpuRegs.HI.UL[0]); + PMFHL_CLAMP(cpuRegs.GPR.r[_Rd_].US[3], cpuRegs.HI.UL[1]); + PMFHL_CLAMP(cpuRegs.GPR.r[_Rd_].US[4], cpuRegs.LO.UL[2]); + PMFHL_CLAMP(cpuRegs.GPR.r[_Rd_].US[5], cpuRegs.LO.UL[3]); + PMFHL_CLAMP(cpuRegs.GPR.r[_Rd_].US[6], cpuRegs.HI.UL[2]); + PMFHL_CLAMP(cpuRegs.GPR.r[_Rd_].US[7], cpuRegs.HI.UL[3]); + break; + } +} + +void PMTHL() { + if (_Sa_ != 0) return; + + cpuRegs.LO.UL[0] = cpuRegs.GPR.r[_Rs_].UL[0]; + cpuRegs.HI.UL[0] = cpuRegs.GPR.r[_Rs_].UL[1]; + cpuRegs.LO.UL[2] = cpuRegs.GPR.r[_Rs_].UL[2]; + cpuRegs.HI.UL[2] = cpuRegs.GPR.r[_Rs_].UL[3]; +} + +__forceinline void _PSLLH(int n) +{ + cpuRegs.GPR.r[_Rd_].US[n] = cpuRegs.GPR.r[_Rt_].US[n] << ( _Sa_ & 0xf ); +} + +void PSLLH() { + if (!_Rd_) return; + + _PSLLH(0); _PSLLH(1); _PSLLH(2); _PSLLH(3); + _PSLLH(4); _PSLLH(5); _PSLLH(6); _PSLLH(7); +} + +__forceinline void _PSRLH(int n) +{ + cpuRegs.GPR.r[_Rd_].US[n] = cpuRegs.GPR.r[_Rt_].US[n] >> ( _Sa_ & 0xf ); +} + +void PSRLH () { + if (!_Rd_) return; + + _PSRLH(0); _PSRLH(1); _PSRLH(2); _PSRLH(3); + _PSRLH(4); _PSRLH(5); _PSRLH(6); _PSRLH(7); +} + +__forceinline void _PSRAH(int n) +{ + cpuRegs.GPR.r[_Rd_].US[n] = cpuRegs.GPR.r[_Rt_].SS[n] >> ( _Sa_ & 0xf ); +} + +void PSRAH() { + if (!_Rd_) return; + + _PSRAH(0); _PSRAH(1); _PSRAH(2); _PSRAH(3); + _PSRAH(4); _PSRAH(5); _PSRAH(6); _PSRAH(7); +} + +__forceinline void _PSLLW(int n) +{ + cpuRegs.GPR.r[_Rd_].UL[n] = cpuRegs.GPR.r[_Rt_].UL[n] << _Sa_; +} + +void PSLLW() { + if (!_Rd_) return; + + _PSLLW(0); _PSLLW(1); _PSLLW(2); _PSLLW(3); +} + +__forceinline void _PSRLW(int n) +{ + cpuRegs.GPR.r[_Rd_].UL[n] = cpuRegs.GPR.r[_Rt_].UL[n] >> _Sa_; +} + +void PSRLW() { + if (!_Rd_) return; + + _PSRLW(0); _PSRLW(1); _PSRLW(2); _PSRLW(3); +} + +__forceinline void _PSRAW(int n) +{ + cpuRegs.GPR.r[_Rd_].UL[n] = cpuRegs.GPR.r[_Rt_].SL[n] >> _Sa_; +} + +void PSRAW() { + if (!_Rd_) return; + + _PSRAW(0); _PSRAW(1); _PSRAW(2); _PSRAW(3); +} + +//*****************END OF MMI OPCODES************************** +//*************************MMI0 OPCODES************************ + +__forceinline void _PADDW(int n) +{ + cpuRegs.GPR.r[_Rd_].UL[n] = cpuRegs.GPR.r[_Rs_].UL[n] + cpuRegs.GPR.r[_Rt_].UL[n]; +} + +void PADDW() { + if (!_Rd_) return; + + _PADDW(0); _PADDW(1); _PADDW(2); _PADDW(3); +} + +__forceinline void _PSUBW(int n) +{ + cpuRegs.GPR.r[_Rd_].UL[n] = cpuRegs.GPR.r[_Rs_].UL[n] - cpuRegs.GPR.r[_Rt_].UL[n]; +} + +void PSUBW() { + if (!_Rd_) return; + + _PSUBW(0); _PSUBW(1); _PSUBW(2); _PSUBW(3); +} + +__forceinline void _PCGTW(int n) +{ + if (cpuRegs.GPR.r[_Rs_].SL[n] > cpuRegs.GPR.r[_Rt_].SL[n]) + cpuRegs.GPR.r[_Rd_].UL[n] = 0xFFFFFFFF; + else + cpuRegs.GPR.r[_Rd_].UL[n] = 0x00000000; +} + +void PCGTW() { + if (!_Rd_) return; + + _PCGTW(0); _PCGTW(1); _PCGTW(2); _PCGTW(3); +} + +__forceinline void _PMAXW(int n) +{ + if (cpuRegs.GPR.r[_Rs_].SL[n] > cpuRegs.GPR.r[_Rt_].SL[n]) + cpuRegs.GPR.r[_Rd_].UL[n] = cpuRegs.GPR.r[_Rs_].UL[n]; + else + cpuRegs.GPR.r[_Rd_].UL[n] = cpuRegs.GPR.r[_Rt_].UL[n]; +} + +void PMAXW() { + if (!_Rd_) return; + + _PMAXW(0); _PMAXW(1); _PMAXW(2); _PMAXW(3); +} + +__forceinline void _PADDH(int n) +{ + cpuRegs.GPR.r[_Rd_].US[n] = cpuRegs.GPR.r[_Rs_].US[n] + cpuRegs.GPR.r[_Rt_].US[n]; +} + +void PADDH() { + if (!_Rd_) return; + + _PADDH(0); _PADDH(1); _PADDH(2); _PADDH(3); + _PADDH(4); _PADDH(5); _PADDH(6); _PADDH(7); +} + +__forceinline void _PSUBH(int n) +{ + cpuRegs.GPR.r[_Rd_].US[n] = cpuRegs.GPR.r[_Rs_].US[n] - cpuRegs.GPR.r[_Rt_].US[n]; +} + +void PSUBH() { + if (!_Rd_) return; + + _PSUBH(0); _PSUBH(1); _PSUBH(2); _PSUBH(3); + _PSUBH(4); _PSUBH(5); _PSUBH(6); _PSUBH(7); +} + +__forceinline void _PCGTH(int n) +{ + if (cpuRegs.GPR.r[_Rs_].SS[n] > cpuRegs.GPR.r[_Rt_].SS[n]) + cpuRegs.GPR.r[_Rd_].US[n] = 0xFFFF; + else + cpuRegs.GPR.r[_Rd_].US[n] = 0x0000; +} + +void PCGTH() { + if (!_Rd_) return; + + _PCGTH(0); _PCGTH(1); _PCGTH(2); _PCGTH(3); + _PCGTH(4); _PCGTH(5); _PCGTH(6); _PCGTH(7); +} + +__forceinline void _PMAXH(int n) +{ + if (cpuRegs.GPR.r[_Rs_].SS[n] > cpuRegs.GPR.r[_Rt_].SS[n]) + cpuRegs.GPR.r[_Rd_].US[n] = cpuRegs.GPR.r[_Rs_].US[n]; + else + cpuRegs.GPR.r[_Rd_].US[n] = cpuRegs.GPR.r[_Rt_].US[n]; +} + +void PMAXH() { + if (!_Rd_) return; + + _PMAXH(0); _PMAXH(1); _PMAXH(2); _PMAXH(3); + _PMAXH(4); _PMAXH(5); _PMAXH(6); _PMAXH(7); +} + +__forceinline void _PADDB(int n) +{ + cpuRegs.GPR.r[_Rd_].SC[n] = cpuRegs.GPR.r[_Rs_].SC[n] + cpuRegs.GPR.r[_Rt_].SC[n]; +} + +void PADDB() { + int i; + if (!_Rd_) return; + + for( i=0; i<16; i++ ) + _PADDB( i ); +} + +__forceinline void _PSUBB(int n) +{ + cpuRegs.GPR.r[_Rd_].SC[n] = cpuRegs.GPR.r[_Rs_].SC[n] - cpuRegs.GPR.r[_Rt_].SC[n]; +} + +void PSUBB() { + int i; + if (!_Rd_) return; + + for( i=0; i<16; i++ ) + _PSUBB( i ); +} + +__forceinline void _PCGTB(int n) +{ + if (cpuRegs.GPR.r[_Rs_].SC[n] > cpuRegs.GPR.r[_Rt_].SC[n]) + cpuRegs.GPR.r[_Rd_].UC[n] = 0xFF; + else + cpuRegs.GPR.r[_Rd_].UC[n] = 0x00; +} + +void PCGTB() { + int i; + if (!_Rd_) return; + + for( i=0; i<16; i++ ) + _PCGTB( i ); +} + +__forceinline void _PADDSW(int n) +{ + s64 sTemp64; + + sTemp64 = (s64)cpuRegs.GPR.r[_Rs_].SL[n] + (s64)cpuRegs.GPR.r[_Rt_].SL[n]; + if (sTemp64 > 0x7FFFFFFF) + cpuRegs.GPR.r[_Rd_].UL[n] = 0x7FFFFFFF; + else if ((sTemp64 < (s32)0x80000000) ) + cpuRegs.GPR.r[_Rd_].UL[n] = 0x80000000LL; + else + cpuRegs.GPR.r[_Rd_].UL[n] = (s32)sTemp64; +} + +void PADDSW() { + if (!_Rd_) return; + + _PADDSW(0); _PADDSW(1); _PADDSW(2); _PADDSW(3); +} + +__forceinline void _PSUBSW(int n) +{ + s64 sTemp64; + + sTemp64 = (s64)cpuRegs.GPR.r[_Rs_].SL[n] - (s64)cpuRegs.GPR.r[_Rt_].SL[n]; + + if (sTemp64 >= 0x7FFFFFFF) + cpuRegs.GPR.r[_Rd_].UL[n] = 0x7FFFFFFF; + else if ((sTemp64 < (s32)0x80000000)) + cpuRegs.GPR.r[_Rd_].UL[n] = 0x80000000; + else + cpuRegs.GPR.r[_Rd_].UL[n] = (s32)sTemp64; +} + +void PSUBSW() { + if (!_Rd_) return; + + _PSUBSW(0); + _PSUBSW(1); + _PSUBSW(2); + _PSUBSW(3); +} + +void PEXTLW() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].UL[0] = Rt.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[1] = Rs.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[2] = Rt.UL[1]; + cpuRegs.GPR.r[_Rd_].UL[3] = Rs.UL[1]; +} + +void PPACW() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].UL[0] = Rt.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[1] = Rt.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[2] = Rs.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[3] = Rs.UL[2]; +} + +__forceinline void _PADDSH(int n) +{ + s32 sTemp32; + sTemp32 = (s32)cpuRegs.GPR.r[_Rs_].SS[n] + (s32)cpuRegs.GPR.r[_Rt_].SS[n]; + + if (sTemp32 > 0x7FFF) + cpuRegs.GPR.r[_Rd_].US[n] = 0x7FFF; + else if ((sTemp32 < (s32)0xffff8000) ) + cpuRegs.GPR.r[_Rd_].US[n] = 0x8000; + else + cpuRegs.GPR.r[_Rd_].US[n] = (s16)sTemp32; +} + +void PADDSH() { + if (!_Rd_) return; + + _PADDSH(0); _PADDSH(1); _PADDSH(2); _PADDSH(3); + _PADDSH(4); _PADDSH(5); _PADDSH(6); _PADDSH(7); +} + +__forceinline void _PSUBSH(int n) +{ + s32 sTemp32; + sTemp32 = (s32)cpuRegs.GPR.r[_Rs_].SS[n] - (s32)cpuRegs.GPR.r[_Rt_].SS[n]; + + if (sTemp32 >= 0x7FFF) + cpuRegs.GPR.r[_Rd_].US[n] = 0x7FFF; + else if ((sTemp32 < (s32)0xffff8000) ) + cpuRegs.GPR.r[_Rd_].US[n] = 0x8000; + else + cpuRegs.GPR.r[_Rd_].US[n] = (s16)sTemp32; +} + +void PSUBSH() { + if (!_Rd_) return; + + _PSUBSH(0); _PSUBSH(1); _PSUBSH(2); _PSUBSH(3); + _PSUBSH(4); _PSUBSH(5); _PSUBSH(6); _PSUBSH(7); +} + +void PEXTLH() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].US[0] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[1] = Rs.US[0]; + cpuRegs.GPR.r[_Rd_].US[2] = Rt.US[1]; + cpuRegs.GPR.r[_Rd_].US[3] = Rs.US[1]; + cpuRegs.GPR.r[_Rd_].US[4] = Rt.US[2]; + cpuRegs.GPR.r[_Rd_].US[5] = Rs.US[2]; + cpuRegs.GPR.r[_Rd_].US[6] = Rt.US[3]; + cpuRegs.GPR.r[_Rd_].US[7] = Rs.US[3]; +} + +void PPACH() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].US[0] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[1] = Rt.US[2]; + cpuRegs.GPR.r[_Rd_].US[2] = Rt.US[4]; + cpuRegs.GPR.r[_Rd_].US[3] = Rt.US[6]; + cpuRegs.GPR.r[_Rd_].US[4] = Rs.US[0]; + cpuRegs.GPR.r[_Rd_].US[5] = Rs.US[2]; + cpuRegs.GPR.r[_Rd_].US[6] = Rs.US[4]; + cpuRegs.GPR.r[_Rd_].US[7] = Rs.US[6]; +} + +__forceinline void _PADDSB(int n) +{ + s16 sTemp16; + sTemp16 = (s16)cpuRegs.GPR.r[_Rs_].SC[n] + (s16)cpuRegs.GPR.r[_Rt_].SC[n]; + + if (sTemp16 > 0x7F) + cpuRegs.GPR.r[_Rd_].UC[n] = 0x7F; + else if ((sTemp16 < 0x180) && (sTemp16 >= 0x100)) + cpuRegs.GPR.r[_Rd_].UC[n] = 0x80; + else + cpuRegs.GPR.r[_Rd_].UC[n] = (s8)sTemp16; +} + +void PADDSB() { + int i; + if (!_Rd_) return; + + for( i=0; i<16; i++ ) + _PADDSB(i); +} + +static __forceinline void _PSUBSB( u8 n ) +{ + s16 sTemp16; + sTemp16 = (s16)cpuRegs.GPR.r[_Rs_].SC[n] - (s16)cpuRegs.GPR.r[_Rt_].SC[n]; + + if (sTemp16 >= 0x7F) + cpuRegs.GPR.r[_Rd_].UC[n] = 0x7F; + else if ((sTemp16 < 0x180) && (sTemp16 >= 0x100)) + cpuRegs.GPR.r[_Rd_].UC[n] = 0x80; + else + cpuRegs.GPR.r[_Rd_].UC[n] = (s8)sTemp16; +} + +void PSUBSB() { + int i; + if (!_Rd_) return; + + for( i=0; i<16; i++ ) + _PSUBSB(i); +} + +void PEXTLB() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].UC[0] = Rt.UC[0]; + cpuRegs.GPR.r[_Rd_].UC[1] = Rs.UC[0]; + cpuRegs.GPR.r[_Rd_].UC[2] = Rt.UC[1]; + cpuRegs.GPR.r[_Rd_].UC[3] = Rs.UC[1]; + + cpuRegs.GPR.r[_Rd_].UC[4] = Rt.UC[2]; + cpuRegs.GPR.r[_Rd_].UC[5] = Rs.UC[2]; + cpuRegs.GPR.r[_Rd_].UC[6] = Rt.UC[3]; + cpuRegs.GPR.r[_Rd_].UC[7] = Rs.UC[3]; + + cpuRegs.GPR.r[_Rd_].UC[8] = Rt.UC[4]; + cpuRegs.GPR.r[_Rd_].UC[9] = Rs.UC[4]; + cpuRegs.GPR.r[_Rd_].UC[10] = Rt.UC[5]; + cpuRegs.GPR.r[_Rd_].UC[11] = Rs.UC[5]; + + cpuRegs.GPR.r[_Rd_].UC[12] = Rt.UC[6]; + cpuRegs.GPR.r[_Rd_].UC[13] = Rs.UC[6]; + cpuRegs.GPR.r[_Rd_].UC[14] = Rt.UC[7]; + cpuRegs.GPR.r[_Rd_].UC[15] = Rs.UC[7]; +} + +void PPACB() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].UC[0] = Rt.UC[0]; + cpuRegs.GPR.r[_Rd_].UC[1] = Rt.UC[2]; + cpuRegs.GPR.r[_Rd_].UC[2] = Rt.UC[4]; + cpuRegs.GPR.r[_Rd_].UC[3] = Rt.UC[6]; + + cpuRegs.GPR.r[_Rd_].UC[4] = Rt.UC[8]; + cpuRegs.GPR.r[_Rd_].UC[5] = Rt.UC[10]; + cpuRegs.GPR.r[_Rd_].UC[6] = Rt.UC[12]; + cpuRegs.GPR.r[_Rd_].UC[7] = Rt.UC[14]; + + cpuRegs.GPR.r[_Rd_].UC[8] = Rs.UC[0]; + cpuRegs.GPR.r[_Rd_].UC[9] = Rs.UC[2]; + cpuRegs.GPR.r[_Rd_].UC[10] = Rs.UC[4]; + cpuRegs.GPR.r[_Rd_].UC[11] = Rs.UC[6]; + + cpuRegs.GPR.r[_Rd_].UC[12] = Rs.UC[8]; + cpuRegs.GPR.r[_Rd_].UC[13] = Rs.UC[10]; + cpuRegs.GPR.r[_Rd_].UC[14] = Rs.UC[12]; + cpuRegs.GPR.r[_Rd_].UC[15] = Rs.UC[14]; +} + +__forceinline void _PEXT5(int n) +{ + cpuRegs.GPR.r[_Rd_].UL[n] = \ + ((cpuRegs.GPR.r[_Rt_].UL[n] & 0x0000001F) << 3) | \ + ((cpuRegs.GPR.r[_Rt_].UL[n] & 0x000003E0) << 6) | \ + ((cpuRegs.GPR.r[_Rt_].UL[n] & 0x00007C00) << 9) | \ + ((cpuRegs.GPR.r[_Rt_].UL[n] & 0x00008000) << 16); +} + +void PEXT5() { + if (!_Rd_) return; + + _PEXT5(0); _PEXT5(1); _PEXT5(2); _PEXT5(3); +} + +__forceinline void _PPAC5(int n) +{ + cpuRegs.GPR.r[_Rd_].UL[n] = \ + ((cpuRegs.GPR.r[_Rt_].UL[n] >> 3) & 0x0000001F) | \ + ((cpuRegs.GPR.r[_Rt_].UL[n] >> 6) & 0x000003E0) | \ + ((cpuRegs.GPR.r[_Rt_].UL[n] >> 9) & 0x00007C00) | \ + ((cpuRegs.GPR.r[_Rt_].UL[n] >> 16) & 0x00008000); +} + +void PPAC5() { + if (!_Rd_) return; + + _PPAC5(0); _PPAC5(1); _PPAC5(2); _PPAC5(3); +} + +//***END OF MMI0 OPCODES****************************************** +//**********MMI1 OPCODES************************************** + +__forceinline void _PABSW(int n) +{ + cpuRegs.GPR.r[_Rd_].UL[n] = abs(cpuRegs.GPR.r[_Rt_].SL[n]); +} + +void PABSW() { + if (!_Rd_) return; + + _PABSW(0); _PABSW(1); _PABSW(2); _PABSW(3); +} + +__forceinline void _PCEQW(int n) +{ + if (cpuRegs.GPR.r[_Rs_].UL[n] == cpuRegs.GPR.r[_Rt_].UL[n]) + cpuRegs.GPR.r[_Rd_].UL[n] = 0xFFFFFFFF; + else + cpuRegs.GPR.r[_Rd_].UL[n] = 0x00000000; +} + +void PCEQW() { + if (!_Rd_) return; + + _PCEQW(0); _PCEQW(1); _PCEQW(2); _PCEQW(3); +} + +static __forceinline void _PMINW( u8 n ) +{ + if (cpuRegs.GPR.r[_Rs_].SL[n] < cpuRegs.GPR.r[_Rt_].SL[n]) + cpuRegs.GPR.r[_Rd_].SL[n] = cpuRegs.GPR.r[_Rs_].SL[n]; + else + cpuRegs.GPR.r[_Rd_].SL[n] = cpuRegs.GPR.r[_Rt_].SL[n]; +} + +void PMINW() { + if (!_Rd_) return; + + _PMINW(0); _PMINW(1); _PMINW(2); _PMINW(3); +} + +void PADSBH() { + if (!_Rd_) return; + + _PSUBH(0); _PSUBH(1); _PSUBH(2); _PSUBH(3); + _PADDH(4); _PADDH(5); _PADDH(6); _PADDH(7); +} + +__forceinline void _PABSH(int n) +{ + cpuRegs.GPR.r[_Rd_].US[n] = abs(cpuRegs.GPR.r[_Rt_].SS[n]); +} + +void PABSH() { + if (!_Rd_) return; + + _PABSH(0); _PABSH(1); _PABSH(2); _PABSH(3); + _PABSH(4); _PABSH(5); _PABSH(6); _PABSH(7); +} + +static __forceinline void _PCEQH( u8 n ) +{ + if (cpuRegs.GPR.r[_Rs_].US[n] == cpuRegs.GPR.r[_Rt_].US[n]) + cpuRegs.GPR.r[_Rd_].US[n] = 0xFFFF; + else + cpuRegs.GPR.r[_Rd_].US[n] = 0x0000; +} + +void PCEQH() { + if (!_Rd_) return; + + _PCEQH(0); _PCEQH(1); _PCEQH(2); _PCEQH(3); + _PCEQH(4); _PCEQH(5); _PCEQH(6); _PCEQH(7); +} + +static __forceinline void _PMINH( u8 n ) +{ + if (cpuRegs.GPR.r[_Rs_].SS[n] < cpuRegs.GPR.r[_Rt_].SS[n]) + cpuRegs.GPR.r[_Rd_].US[n] = cpuRegs.GPR.r[_Rs_].US[n]; + else + cpuRegs.GPR.r[_Rd_].US[n] = cpuRegs.GPR.r[_Rt_].US[n]; +} + +void PMINH() { + if (!_Rd_) return; + + _PMINH(0); _PMINH(1); _PMINH(2); _PMINH(3); + _PMINH(4); _PMINH(5); _PMINH(6); _PMINH(7); +} + +__forceinline void _PCEQB(int n) +{ + if (cpuRegs.GPR.r[_Rs_].UC[n] == cpuRegs.GPR.r[_Rt_].UC[n]) + cpuRegs.GPR.r[_Rd_].UC[n] = 0xFF; + else + cpuRegs.GPR.r[_Rd_].UC[n] = 0x00; +} + +void PCEQB() { + int i; + if (!_Rd_) return; + + for( i=0; i<16; i++ ) + _PCEQB(i); +} + +__forceinline void _PADDUW(int n) +{ + s64 tmp; + tmp = (s64)cpuRegs.GPR.r[_Rs_].UL[n] + (s64)cpuRegs.GPR.r[_Rt_].UL[n]; + + if (tmp > 0xffffffff) + cpuRegs.GPR.r[_Rd_].UL[n] = 0xffffffff; + else + cpuRegs.GPR.r[_Rd_].UL[n] = (u32)tmp; +} + +void PADDUW () { + if (!_Rd_) return; + + _PADDUW(0); _PADDUW(1); _PADDUW(2); _PADDUW(3); +} + +__forceinline void _PSUBUW(int n) +{ + s64 sTemp64; + sTemp64 = (s64)cpuRegs.GPR.r[_Rs_].UL[n] - (s64)cpuRegs.GPR.r[_Rt_].UL[n]; + + if (sTemp64 <= 0x0) + cpuRegs.GPR.r[_Rd_].UL[n] = 0x0; + else + cpuRegs.GPR.r[_Rd_].UL[n] = (u32)sTemp64; +} + +void PSUBUW() { + if (!_Rd_) return; + + _PSUBUW(0); _PSUBUW(1); _PSUBUW(2); _PSUBUW(3); +} + +void PEXTUW() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].UL[0] = Rt.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[1] = Rs.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[2] = Rt.UL[3]; + cpuRegs.GPR.r[_Rd_].UL[3] = Rs.UL[3]; +} + +__forceinline void _PADDUH(int n) +{ + s32 sTemp32; + sTemp32 = (s32)cpuRegs.GPR.r[_Rs_].US[n] + (s32)cpuRegs.GPR.r[_Rt_].US[n]; + + if (sTemp32 > 0xFFFF) + cpuRegs.GPR.r[_Rd_].US[n] = 0xFFFF; + else + cpuRegs.GPR.r[_Rd_].US[n] = (u16)sTemp32; +} + +void PADDUH() { + if (!_Rd_) return; + + _PADDUH(0); _PADDUH(1); _PADDUH(2); _PADDUH(3); + _PADDUH(4); _PADDUH(5); _PADDUH(6); _PADDUH(7); +} + +__forceinline void _PSUBUH(int n) +{ + s32 sTemp32; + sTemp32 = (s32)cpuRegs.GPR.r[_Rs_].US[n] - (s32)cpuRegs.GPR.r[_Rt_].US[n]; + + if (sTemp32 <= 0x0) + cpuRegs.GPR.r[_Rd_].US[n] = 0x0; + else + cpuRegs.GPR.r[_Rd_].US[n] = (u16)sTemp32; +} + +void PSUBUH() { + if (!_Rd_) return; + + _PSUBUH(0); _PSUBUH(1); _PSUBUH(2); _PSUBUH(3); + _PSUBUH(4); _PSUBUH(5); _PSUBUH(6); _PSUBUH(7); +} + +void PEXTUH() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].US[0] = Rt.US[4]; + cpuRegs.GPR.r[_Rd_].US[1] = Rs.US[4]; + cpuRegs.GPR.r[_Rd_].US[2] = Rt.US[5]; + cpuRegs.GPR.r[_Rd_].US[3] = Rs.US[5]; + + cpuRegs.GPR.r[_Rd_].US[4] = Rt.US[6]; + cpuRegs.GPR.r[_Rd_].US[5] = Rs.US[6]; + cpuRegs.GPR.r[_Rd_].US[6] = Rt.US[7]; + cpuRegs.GPR.r[_Rd_].US[7] = Rs.US[7]; +} + +__forceinline void _PADDUB(int n) +{ + u16 Temp16; + Temp16 = (u16)cpuRegs.GPR.r[_Rs_].UC[n] + (u16)cpuRegs.GPR.r[_Rt_].UC[n]; + + if (Temp16 > 0xFF) + cpuRegs.GPR.r[_Rd_].UC[n] = 0xFF; + else + cpuRegs.GPR.r[_Rd_].UC[n] = (u8)Temp16; +} + +void PADDUB() { + int i; + if (!_Rd_) return; + + for( i=0; i<16; i++ ) + _PADDUB(i); +} + +__forceinline void _PSUBUB(int n) { + s16 sTemp16; + sTemp16 = (s16)cpuRegs.GPR.r[_Rs_].UC[n] - (s16)cpuRegs.GPR.r[_Rt_].UC[n]; + + if (sTemp16 <= 0x0) + cpuRegs.GPR.r[_Rd_].UC[n] = 0x0; + else + cpuRegs.GPR.r[_Rd_].UC[n] = (u8)sTemp16; +} + +void PSUBUB() { + int i; + if (!_Rd_) return; + + for( i=0; i<16; i++ ) + _PSUBUB(i); +} + +void PEXTUB() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].UC[0] = Rt.UC[8]; + cpuRegs.GPR.r[_Rd_].UC[1] = Rs.UC[8]; + cpuRegs.GPR.r[_Rd_].UC[2] = Rt.UC[9]; + cpuRegs.GPR.r[_Rd_].UC[3] = Rs.UC[9]; + cpuRegs.GPR.r[_Rd_].UC[4] = Rt.UC[10]; + cpuRegs.GPR.r[_Rd_].UC[5] = Rs.UC[10]; + cpuRegs.GPR.r[_Rd_].UC[6] = Rt.UC[11]; + cpuRegs.GPR.r[_Rd_].UC[7] = Rs.UC[11]; + cpuRegs.GPR.r[_Rd_].UC[8] = Rt.UC[12]; + cpuRegs.GPR.r[_Rd_].UC[9] = Rs.UC[12]; + cpuRegs.GPR.r[_Rd_].UC[10] = Rt.UC[13]; + cpuRegs.GPR.r[_Rd_].UC[11] = Rs.UC[13]; + cpuRegs.GPR.r[_Rd_].UC[12] = Rt.UC[14]; + cpuRegs.GPR.r[_Rd_].UC[13] = Rs.UC[14]; + cpuRegs.GPR.r[_Rd_].UC[14] = Rt.UC[15]; + cpuRegs.GPR.r[_Rd_].UC[15] = Rs.UC[15]; +} + +//int saZero = 0; +void QFSRV() { // JayteeMaster: changed a bit to avoid screw up + GPR_reg Rd; + if (!_Rd_) return; + + if (cpuRegs.sa == 0) { + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rt_].UD[0]; + cpuRegs.GPR.r[_Rd_].UD[1] = cpuRegs.GPR.r[_Rt_].UD[1]; + //saZero++; + //if( saZero >= 388800 ) + //Console::WriteLn( "SA Is Zero, Bitch: %d zeros and counting.", params saZero ); + } else { + //Console::WriteLn( "SA Properly Valued at: %d (after %d zeros)", params cpuRegs.sa, saZero ); + //saZero = 0; + if (cpuRegs.sa < 64) { + /* + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rt_].UD[0] >> cpuRegs.sa; + cpuRegs.GPR.r[_Rd_].UD[1] = cpuRegs.GPR.r[_Rt_].UD[1] >> cpuRegs.sa; + cpuRegs.GPR.r[_Rd_].UD[0]|= cpuRegs.GPR.r[_Rt_].UD[1] << (64 - cpuRegs.sa); + cpuRegs.GPR.r[_Rd_].UD[1]|= cpuRegs.GPR.r[_Rs_].UD[0] << (64 - cpuRegs.sa); + */ + Rd.UD[0] = cpuRegs.GPR.r[_Rt_].UD[0] >> cpuRegs.sa; + Rd.UD[1] = cpuRegs.GPR.r[_Rt_].UD[1] >> cpuRegs.sa; + Rd.UD[0]|= cpuRegs.GPR.r[_Rt_].UD[1] << (64 - cpuRegs.sa); + Rd.UD[1]|= cpuRegs.GPR.r[_Rs_].UD[0] << (64 - cpuRegs.sa); + cpuRegs.GPR.r[_Rd_] = Rd; + } else { + /* + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rt_].UD[1] >> (cpuRegs.sa - 64); + cpuRegs.GPR.r[_Rd_].UD[1] = cpuRegs.GPR.r[_Rs_].UD[0] >> (cpuRegs.sa - 64); + cpuRegs.GPR.r[_Rd_].UD[0]|= cpuRegs.GPR.r[_Rs_].UD[0] << (128 - cpuRegs.sa); + cpuRegs.GPR.r[_Rd_].UD[1]|= cpuRegs.GPR.r[_Rs_].UD[1] << (128 - cpuRegs.sa); + */ + Rd.UD[0] = cpuRegs.GPR.r[_Rt_].UD[1] >> (cpuRegs.sa - 64); + Rd.UD[1] = cpuRegs.GPR.r[_Rs_].UD[0] >> (cpuRegs.sa - 64); + Rd.UD[0]|= cpuRegs.GPR.r[_Rs_].UD[0] << (128 - cpuRegs.sa); + Rd.UD[1]|= cpuRegs.GPR.r[_Rs_].UD[1] << (128 - cpuRegs.sa); + cpuRegs.GPR.r[_Rd_] = Rd; + } + } +} + +//********END OF MMI1 OPCODES*********************************** + +//*********MMI2 OPCODES*************************************** + +__forceinline void _PMADDW(int dd, int ss) +{ + s64 temp = (s64)((s64)cpuRegs.LO.SL[ss] | ((s64)cpuRegs.HI.SL[ss] << 32)) + + ((s64)cpuRegs.GPR.r[_Rs_].SL[ss] * (s64)cpuRegs.GPR.r[_Rt_].SL[ss]); + + cpuRegs.LO.SD[dd] = (s32)(temp & 0xffffffff); + cpuRegs.HI.SD[dd] = (s32)(temp >> 32); + + if (_Rd_) cpuRegs.GPR.r[_Rd_].SD[dd] = temp; +} + +void PMADDW() { + _PMADDW(0, 0); + _PMADDW(1, 2); +} + +void PSLLVW() { + if (!_Rd_) return; + + cpuRegs.GPR.r[_Rd_].UD[0] = (s32)(cpuRegs.GPR.r[_Rt_].UL[0] << + (cpuRegs.GPR.r[_Rs_].UL[0] & 0x1F)); + cpuRegs.GPR.r[_Rd_].UD[1] = (s32)(cpuRegs.GPR.r[_Rt_].UL[2] << + (cpuRegs.GPR.r[_Rs_].UL[2] & 0x1F)); +} + +void PSRLVW() { + if (!_Rd_) return; + + cpuRegs.GPR.r[_Rd_].UD[0] = (cpuRegs.GPR.r[_Rt_].UL[0] >> + (cpuRegs.GPR.r[_Rs_].UL[0] & 0x1F)); + cpuRegs.GPR.r[_Rd_].UD[1] = (cpuRegs.GPR.r[_Rt_].UL[2] >> + (cpuRegs.GPR.r[_Rs_].UL[2] & 0x1F)); +} + +__forceinline void _PMSUBW(int dd, int ss) +{ + s64 temp = (s64)((s64)cpuRegs.LO.SL[ss] | ((s64)cpuRegs.HI.SL[ss] << 32)) - + ((s64)cpuRegs.GPR.r[_Rs_].SL[ss] * (s64)cpuRegs.GPR.r[_Rt_].SL[ss]); + + cpuRegs.LO.SD[dd] = (s32)(temp & 0xffffffff); + cpuRegs.HI.SD[dd] = (s32)(temp >> 32); + + if (_Rd_) cpuRegs.GPR.r[_Rd_].SD[dd] = temp; +} + +void PMSUBW() { + _PMSUBW(0, 0); + _PMSUBW(1, 2); +} + +void PMFHI() { + if (!_Rd_) return; + + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.HI.UD[0]; + cpuRegs.GPR.r[_Rd_].UD[1] = cpuRegs.HI.UD[1]; +} + +void PMFLO() { + if (!_Rd_) return; + + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.LO.UD[0]; + cpuRegs.GPR.r[_Rd_].UD[1] = cpuRegs.LO.UD[1]; +} + +void PINTH() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].US[0] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[1] = Rs.US[4]; + cpuRegs.GPR.r[_Rd_].US[2] = Rt.US[1]; + cpuRegs.GPR.r[_Rd_].US[3] = Rs.US[5]; + cpuRegs.GPR.r[_Rd_].US[4] = Rt.US[2]; + cpuRegs.GPR.r[_Rd_].US[5] = Rs.US[6]; + cpuRegs.GPR.r[_Rd_].US[6] = Rt.US[3]; + cpuRegs.GPR.r[_Rd_].US[7] = Rs.US[7]; +} + +__forceinline void _PMULTW(int dd, int ss) +{ + s64 temp = (s64)cpuRegs.GPR.r[_Rs_].SL[ss] * (s64)cpuRegs.GPR.r[_Rt_].SL[ss]; + + cpuRegs.LO.UD[dd] = (s32)(temp & 0xffffffff); + cpuRegs.HI.UD[dd] = (s32)(temp >> 32); + + if (_Rd_) cpuRegs.GPR.r[_Rd_].SD[dd] = temp; +} + +void PMULTW() { + _PMULTW(0, 0); + _PMULTW(1, 2); +} + +__forceinline void _PDIVW(int dd, int ss) +{ + if (cpuRegs.GPR.r[_Rt_].UL[ss] != 0) + { + cpuRegs.LO.SD[dd] = cpuRegs.GPR.r[_Rs_].SL[ss] / cpuRegs.GPR.r[_Rt_].SL[ss]; + cpuRegs.HI.SD[dd] = cpuRegs.GPR.r[_Rs_].SL[ss] % cpuRegs.GPR.r[_Rt_].SL[ss]; + } +} + +void PDIVW() { + _PDIVW(0, 0); + _PDIVW(1, 2); +} + +void PCPYLD() { + if (!_Rd_) return; + + // note: first _Rs_, since the other way when _Rd_ equals + // _Rs_ or _Rt_ this would screw up + cpuRegs.GPR.r[_Rd_].UD[1] = cpuRegs.GPR.r[_Rs_].UD[0]; + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rt_].UD[0]; +} + +void PMADDH() { // JayteeMaster: changed a bit to avoid screw up + s32 temp; + + temp = cpuRegs.LO.UL[0] + (s32)cpuRegs.GPR.r[_Rs_].SS[0] * (s32)cpuRegs.GPR.r[_Rt_].SS[0]; + cpuRegs.LO.UL[0] = temp; + /* if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[0] = temp; */ + + temp = cpuRegs.LO.UL[1] + (s32)cpuRegs.GPR.r[_Rs_].SS[1] * (s32)cpuRegs.GPR.r[_Rt_].SS[1]; + cpuRegs.LO.UL[1] = temp; + + temp = cpuRegs.HI.UL[0] + (s32)cpuRegs.GPR.r[_Rs_].SS[2] * (s32)cpuRegs.GPR.r[_Rt_].SS[2]; + cpuRegs.HI.UL[0] = temp; + /* if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[1] = temp; */ + + temp = cpuRegs.HI.UL[1] + (s32)cpuRegs.GPR.r[_Rs_].SS[3] * (s32)cpuRegs.GPR.r[_Rt_].SS[3]; + cpuRegs.HI.UL[1] = temp; + + temp = cpuRegs.LO.UL[2] + (s32)cpuRegs.GPR.r[_Rs_].SS[4] * (s32)cpuRegs.GPR.r[_Rt_].SS[4]; + cpuRegs.LO.UL[2] = temp; + /* if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[2] = temp; */ + + temp = cpuRegs.LO.UL[3] + (s32)cpuRegs.GPR.r[_Rs_].SS[5] * (s32)cpuRegs.GPR.r[_Rt_].SS[5]; + cpuRegs.LO.UL[3] = temp; + + temp = cpuRegs.HI.UL[2] + (s32)cpuRegs.GPR.r[_Rs_].SS[6] * (s32)cpuRegs.GPR.r[_Rt_].SS[6]; + cpuRegs.HI.UL[2] = temp; + /* if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[3] = temp; */ + + temp = cpuRegs.HI.UL[3] + (s32)cpuRegs.GPR.r[_Rs_].SS[7] * (s32)cpuRegs.GPR.r[_Rt_].SS[7]; + cpuRegs.HI.UL[3] = temp; + + if (_Rd_) { + cpuRegs.GPR.r[_Rd_].UL[0] = cpuRegs.LO.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[1] = cpuRegs.HI.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[2] = cpuRegs.LO.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[3] = cpuRegs.HI.UL[2]; + } + +} + +// JayteeMaster: changed a bit to avoid screw up +__forceinline void _PHMADH_LO(int dd, int n) +{ + s32 temp = (s32)cpuRegs.GPR.r[_Rs_].SS[n+1] * (s32)cpuRegs.GPR.r[_Rt_].SS[n+1] + \ + (s32)cpuRegs.GPR.r[_Rs_].SS[n] * (s32)cpuRegs.GPR.r[_Rt_].SS[n]; + + cpuRegs.LO.UL[dd] = temp; +} + +__forceinline void _PHMADH_HI(int dd, int n) +{ + s32 temp = (s32)cpuRegs.GPR.r[_Rs_].SS[n+1] * (s32)cpuRegs.GPR.r[_Rt_].SS[n+1] + \ + (s32)cpuRegs.GPR.r[_Rs_].SS[n] * (s32)cpuRegs.GPR.r[_Rt_].SS[n]; + + cpuRegs.HI.UL[dd] = temp; +} + +void PHMADH() { // JayteeMaster: changed a bit to avoid screw up. Also used 0,2,4,6 instead of 0,1,2,3 + _PHMADH_LO(0, 0); + _PHMADH_HI(0, 2); + _PHMADH_LO(2, 4); + _PHMADH_HI(2, 6); + if (_Rd_) { + cpuRegs.GPR.r[_Rd_].UL[0] = cpuRegs.LO.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[1] = cpuRegs.HI.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[2] = cpuRegs.LO.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[3] = cpuRegs.HI.UL[2]; + } +} + +void PAND() { + if (!_Rd_) return; + + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] & cpuRegs.GPR.r[_Rt_].UD[0]; + cpuRegs.GPR.r[_Rd_].UD[1] = cpuRegs.GPR.r[_Rs_].UD[1] & cpuRegs.GPR.r[_Rt_].UD[1]; +} + +void PXOR() { + if (!_Rd_) return; + + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] ^ cpuRegs.GPR.r[_Rt_].UD[0]; + cpuRegs.GPR.r[_Rd_].UD[1] = cpuRegs.GPR.r[_Rs_].UD[1] ^ cpuRegs.GPR.r[_Rt_].UD[1]; +} + +void PMSUBH() { // JayteeMaster: changed a bit to avoid screw up + s32 temp; + + temp = cpuRegs.LO.UL[0] - (s32)cpuRegs.GPR.r[_Rs_].SS[0] * (s32)cpuRegs.GPR.r[_Rt_].SS[0]; + cpuRegs.LO.UL[0] = temp; + /*if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[0] = temp;*/ + + temp = cpuRegs.LO.UL[1] - (s32)cpuRegs.GPR.r[_Rs_].SS[1] * (s32)cpuRegs.GPR.r[_Rt_].SS[1]; + cpuRegs.LO.UL[1] = temp; + + temp = cpuRegs.HI.UL[0] - (s32)cpuRegs.GPR.r[_Rs_].SS[2] * (s32)cpuRegs.GPR.r[_Rt_].SS[2]; + cpuRegs.HI.UL[0] = temp; + /*if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[1] = temp;*/ + + temp = cpuRegs.HI.UL[1] - (s32)cpuRegs.GPR.r[_Rs_].SS[3] * (s32)cpuRegs.GPR.r[_Rt_].SS[3]; + cpuRegs.HI.UL[1] = temp; + + temp = cpuRegs.LO.UL[2] - (s32)cpuRegs.GPR.r[_Rs_].SS[4] * (s32)cpuRegs.GPR.r[_Rt_].SS[4]; + cpuRegs.LO.UL[2] = temp; + /*if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[2] = temp;*/ + + temp = cpuRegs.LO.UL[3] - (s32)cpuRegs.GPR.r[_Rs_].SS[5] * (s32)cpuRegs.GPR.r[_Rt_].SS[5]; + cpuRegs.LO.UL[3] = temp; + + temp = cpuRegs.HI.UL[2] - (s32)cpuRegs.GPR.r[_Rs_].SS[6] * (s32)cpuRegs.GPR.r[_Rt_].SS[6]; + cpuRegs.HI.UL[2] = temp; + /*if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[3] = temp;*/ + + temp = cpuRegs.HI.UL[3] - (s32)cpuRegs.GPR.r[_Rs_].SS[7] * (s32)cpuRegs.GPR.r[_Rt_].SS[7]; + cpuRegs.HI.UL[3] = temp; + + if (_Rd_) { + cpuRegs.GPR.r[_Rd_].UL[0] = cpuRegs.LO.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[1] = cpuRegs.HI.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[2] = cpuRegs.LO.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[3] = cpuRegs.HI.UL[2]; + } +} + +// JayteeMaster: changed a bit to avoid screw up +__forceinline void _PHMSBH_LO(int dd, int n, int rdd) +{ + s32 temp = (s32)cpuRegs.GPR.r[_Rs_].SS[n+1] * (s32)cpuRegs.GPR.r[_Rt_].SS[n+1] - \ + (s32)cpuRegs.GPR.r[_Rs_].SS[n] * (s32)cpuRegs.GPR.r[_Rt_].SS[n]; + + cpuRegs.LO.UL[dd] = temp; +} +__forceinline void _PHMSBH_HI(int dd, int n, int rdd) +{ + s32 temp = (s32)cpuRegs.GPR.r[_Rs_].SS[n+1] * (s32)cpuRegs.GPR.r[_Rt_].SS[n+1] - \ + (s32)cpuRegs.GPR.r[_Rs_].SS[n] * (s32)cpuRegs.GPR.r[_Rt_].SS[n]; + + cpuRegs.HI.UL[dd] = temp; +} + +void PHMSBH() { // JayteeMaster: changed a bit to avoid screw up + _PHMSBH_LO(0, 0, 0); + _PHMSBH_HI(0, 2, 1); + _PHMSBH_LO(2, 4, 2); + _PHMSBH_HI(2, 6, 3); + if (_Rd_) { + cpuRegs.GPR.r[_Rd_].UL[0] = cpuRegs.LO.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[1] = cpuRegs.HI.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[2] = cpuRegs.LO.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[3] = cpuRegs.HI.UL[2]; + } +} + +void PEXEH() { + GPR_reg Rt; + + if (!_Rd_) return; + + Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].US[0] = Rt.US[2]; + cpuRegs.GPR.r[_Rd_].US[1] = Rt.US[1]; + cpuRegs.GPR.r[_Rd_].US[2] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[3] = Rt.US[3]; + cpuRegs.GPR.r[_Rd_].US[4] = Rt.US[6]; + cpuRegs.GPR.r[_Rd_].US[5] = Rt.US[5]; + cpuRegs.GPR.r[_Rd_].US[6] = Rt.US[4]; + cpuRegs.GPR.r[_Rd_].US[7] = Rt.US[7]; +} + +void PREVH () { + GPR_reg Rt; + + if (!_Rd_) return; + + Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].US[0] = Rt.US[3]; + cpuRegs.GPR.r[_Rd_].US[1] = Rt.US[2]; + cpuRegs.GPR.r[_Rd_].US[2] = Rt.US[1]; + cpuRegs.GPR.r[_Rd_].US[3] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[4] = Rt.US[7]; + cpuRegs.GPR.r[_Rd_].US[5] = Rt.US[6]; + cpuRegs.GPR.r[_Rd_].US[6] = Rt.US[5]; + cpuRegs.GPR.r[_Rd_].US[7] = Rt.US[4]; +} + +void PMULTH() { // JayteeMaster: changed a bit to avoid screw up + s32 temp; + + temp = (s32)cpuRegs.GPR.r[_Rs_].SS[0] * (s32)cpuRegs.GPR.r[_Rt_].SS[0]; + cpuRegs.LO.UL[0] = temp; + /*if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[0] = temp;*/ + + temp = (s32)cpuRegs.GPR.r[_Rs_].SS[1] * (s32)cpuRegs.GPR.r[_Rt_].SS[1]; + cpuRegs.LO.UL[1] = temp; + + temp = (s32)cpuRegs.GPR.r[_Rs_].SS[2] * (s32)cpuRegs.GPR.r[_Rt_].SS[2]; + cpuRegs.HI.UL[0] = temp; + /*if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[1] = temp;*/ + + temp = (s32)cpuRegs.GPR.r[_Rs_].SS[3] * (s32)cpuRegs.GPR.r[_Rt_].SS[3]; + cpuRegs.HI.UL[1] = temp; + + temp = (s32)cpuRegs.GPR.r[_Rs_].SS[4] * (s32)cpuRegs.GPR.r[_Rt_].SS[4]; + cpuRegs.LO.UL[2] = temp; + /*if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[2] = temp;*/ + + temp = (s32)cpuRegs.GPR.r[_Rs_].SS[5] * (s32)cpuRegs.GPR.r[_Rt_].SS[5]; + cpuRegs.LO.UL[3] = temp; + + temp = (s32)cpuRegs.GPR.r[_Rs_].SS[6] * (s32)cpuRegs.GPR.r[_Rt_].SS[6]; + cpuRegs.HI.UL[2] = temp; + /*if (_Rd_) cpuRegs.GPR.r[_Rd_].UL[3] = temp;*/ + + temp = (s32)cpuRegs.GPR.r[_Rs_].SS[7] * (s32)cpuRegs.GPR.r[_Rt_].SS[7]; + cpuRegs.HI.UL[3] = temp; + + if (_Rd_) { + cpuRegs.GPR.r[_Rd_].UL[0] = cpuRegs.LO.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[1] = cpuRegs.HI.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[2] = cpuRegs.LO.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[3] = cpuRegs.HI.UL[2]; + } +} + +__forceinline void _PDIVBW(int n) +{ + cpuRegs.LO.UL[n] = (s32)(cpuRegs.GPR.r[_Rs_].SL[n] / cpuRegs.GPR.r[_Rt_].SS[0]); + cpuRegs.HI.UL[n] = (s16)(cpuRegs.GPR.r[_Rs_].SL[n] % cpuRegs.GPR.r[_Rt_].SS[0]); +} + +void PDIVBW() { + if (cpuRegs.GPR.r[_Rt_].US[0] == 0) return; + + _PDIVBW(0); _PDIVBW(1); _PDIVBW(2); _PDIVBW(3); +} + +void PEXEW() { + GPR_reg Rt; + + if (!_Rd_) return; + + Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].UL[0] = Rt.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[1] = Rt.UL[1]; + cpuRegs.GPR.r[_Rd_].UL[2] = Rt.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[3] = Rt.UL[3]; +} + +void PROT3W() { + GPR_reg Rt; + + if (!_Rd_) return; + + Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].UL[0] = Rt.UL[1]; + cpuRegs.GPR.r[_Rd_].UL[1] = Rt.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[2] = Rt.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[3] = Rt.UL[3]; +} + +//*****END OF MMI2 OPCODES*********************************** + +//*************************MMI3 OPCODES************************ + +__forceinline void _PMADDUW(int dd, int ss) +{ + u64 tempu = (u64)((u64)cpuRegs.LO.UL[ss] | ((u64)cpuRegs.HI.UL[ss] << 32)) + \ + ((u64)cpuRegs.GPR.r[_Rs_].UL[ss] * (u64)cpuRegs.GPR.r[_Rt_].UL[ss]); + + cpuRegs.LO.SD[dd] = (s32)(tempu & 0xffffffff); + cpuRegs.HI.SD[dd] = (s32)(tempu >> 32); + + if (_Rd_) cpuRegs.GPR.r[_Rd_].UD[dd] = tempu; +} + +void PMADDUW() { + _PMADDUW(0, 0); + _PMADDUW(1, 2); +} + +void PSRAVW() { + if (!_Rd_) return; + + cpuRegs.GPR.r[_Rd_].UD[0] = (cpuRegs.GPR.r[_Rt_].SL[0] >> + (cpuRegs.GPR.r[_Rs_].UL[0] & 0x1F)); + cpuRegs.GPR.r[_Rd_].UD[1] = (cpuRegs.GPR.r[_Rt_].SL[2] >> + (cpuRegs.GPR.r[_Rs_].UL[2] & 0x1F)); +} + +void PMTHI() { + cpuRegs.HI.UD[0] = cpuRegs.GPR.r[_Rs_].UD[0]; + cpuRegs.HI.UD[1] = cpuRegs.GPR.r[_Rs_].UD[1]; +} + +void PMTLO() { + cpuRegs.LO.UD[0] = cpuRegs.GPR.r[_Rs_].UD[0]; + cpuRegs.LO.UD[1] = cpuRegs.GPR.r[_Rs_].UD[1]; +} + +void PINTEH() { + GPR_reg Rs, Rt; + + if (!_Rd_) return; + + Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].US[0] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[1] = Rs.US[0]; + cpuRegs.GPR.r[_Rd_].US[2] = Rt.US[2]; + cpuRegs.GPR.r[_Rd_].US[3] = Rs.US[2]; + cpuRegs.GPR.r[_Rd_].US[4] = Rt.US[4]; + cpuRegs.GPR.r[_Rd_].US[5] = Rs.US[4]; + cpuRegs.GPR.r[_Rd_].US[6] = Rt.US[6]; + cpuRegs.GPR.r[_Rd_].US[7] = Rs.US[6]; +} + +__forceinline void _PMULTUW(int dd, int ss) +{ + u64 tempu = (u64)cpuRegs.GPR.r[_Rs_].UL[ss] * (u64)cpuRegs.GPR.r[_Rt_].UL[ss]; + + cpuRegs.LO.UD[dd] = (s32)(tempu & 0xffffffff); + cpuRegs.HI.UD[dd] = (s32)(tempu >> 32); + + if (_Rd_) cpuRegs.GPR.r[_Rd_].UD[dd] = tempu; +} + + +void PMULTUW() { + _PMULTUW(0, 0); + _PMULTUW(1, 2); +} + +__forceinline void _PDIVUW(int dd, int ss) +{ + if (cpuRegs.GPR.r[_Rt_].UL[ss] != 0) { + cpuRegs.LO.UD[dd] = (u64)cpuRegs.GPR.r[_Rs_].UL[ss] / (u64)cpuRegs.GPR.r[_Rt_].UL[ss]; + cpuRegs.HI.UD[dd] = (u64)cpuRegs.GPR.r[_Rs_].UL[ss] % (u64)cpuRegs.GPR.r[_Rt_].UL[ss]; + } +} + +void PDIVUW() { + _PDIVUW(0, 0); + _PDIVUW(1, 2); +} + +void PCPYUD() { + if (!_Rd_) return; + + // note: first _Rs_, since the other way when _Rd_ equals + // _Rs_ or _Rt_ this would screw up + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[1]; + cpuRegs.GPR.r[_Rd_].UD[1] = cpuRegs.GPR.r[_Rt_].UD[1]; +} + +void POR() { + if (!_Rd_) return; + + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] | cpuRegs.GPR.r[_Rt_].UD[0]; + cpuRegs.GPR.r[_Rd_].UD[1] = cpuRegs.GPR.r[_Rs_].UD[1] | cpuRegs.GPR.r[_Rt_].UD[1]; +} + +void PNOR () { + if (!_Rd_) return; + + cpuRegs.GPR.r[_Rd_].UD[0] = ~(cpuRegs.GPR.r[_Rs_].UD[0] | cpuRegs.GPR.r[_Rt_].UD[0]); + cpuRegs.GPR.r[_Rd_].UD[1] = ~(cpuRegs.GPR.r[_Rs_].UD[1] | cpuRegs.GPR.r[_Rt_].UD[1]); +} + +void PEXCH() { + GPR_reg Rt; + + if (!_Rd_) return; + + Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].US[0] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[1] = Rt.US[2]; + cpuRegs.GPR.r[_Rd_].US[2] = Rt.US[1]; + cpuRegs.GPR.r[_Rd_].US[3] = Rt.US[3]; + cpuRegs.GPR.r[_Rd_].US[4] = Rt.US[4]; + cpuRegs.GPR.r[_Rd_].US[5] = Rt.US[6]; + cpuRegs.GPR.r[_Rd_].US[6] = Rt.US[5]; + cpuRegs.GPR.r[_Rd_].US[7] = Rt.US[7]; +} + +void PCPYH() { + GPR_reg Rt; + + if (!_Rd_) return; + + Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].US[0] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[1] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[2] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[3] = Rt.US[0]; + cpuRegs.GPR.r[_Rd_].US[4] = Rt.US[4]; + cpuRegs.GPR.r[_Rd_].US[5] = Rt.US[4]; + cpuRegs.GPR.r[_Rd_].US[6] = Rt.US[4]; + cpuRegs.GPR.r[_Rd_].US[7] = Rt.US[4]; +} + +void PEXCW() { + GPR_reg Rt; + + if (!_Rd_) return; + + Rt = cpuRegs.GPR.r[_Rt_]; + cpuRegs.GPR.r[_Rd_].UL[0] = Rt.UL[0]; + cpuRegs.GPR.r[_Rd_].UL[1] = Rt.UL[2]; + cpuRegs.GPR.r[_Rd_].UL[2] = Rt.UL[1]; + cpuRegs.GPR.r[_Rd_].UL[3] = Rt.UL[3]; +} + +//**********************END OF MMI3 OPCODES******************** + +// obs: +// QFSRV not verified + +}}} } // end namespace R5900::Interpreter::OpcodeImpl::MMI \ No newline at end of file diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp new file mode 100644 index 0000000000..d3f20c27f3 --- /dev/null +++ b/pcsx2/MTGS.cpp @@ -0,0 +1,1049 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include +#include + +#include "Common.h" +#include "VU.h" +#include "GS.h" +#include "iR5900.h" +#include "VifDma.h" + +#include "SamplProf.h" + +// Uncomment this to enable profiling of the GS RingBufferCopy function. +//#define PCSX2_GSRING_SAMPLING_STATS + +#ifdef PCSX2_GSRING_TX_STATS +#include +#endif + +using namespace Threading; +using namespace std; + +#ifdef DEBUG +#define MTGS_LOG Console::WriteLn +#else +#define MTGS_LOG 0&& +#endif + +// forces the compiler to treat a non-volatile value as volatile. +// This allows us to declare the vars as non-volatile and only use +// them as volatile when appropriate (more optimized). + +#define volatize(x) (*reinterpret_cast(&(x))) + +///////////////////////////////////////////////////////////////////////////// +// BEGIN -- MTGS GIFtag Parse Implementation +// +// The MTGS needs a dummy "GS plugin" for processing SIGNAL, FINISH, and LABEL +// commands. These commands trigger gsIRQs, which need to be handled accurately +// in synch with the EE (which can be running several frames ahead of the MTGS) +// +// Yeah, it's a lot of work, but the performance gains are huge, even on HT cpus. + +// unpack the registers +// registers are stored as a sequence of 4 bit values in the +// upper 64 bits of the GIFTAG. That sucks for us, so we unpack +// them into an 8 bit array. +__forceinline void GIFPath::PrepRegs() +{ + if( tag.nreg == 0 ) + { + u32 tempreg = tag.regs[0]; + for(u32 i=0; i<16; ++i, tempreg >>= 4) + { + if( i == 8 ) tempreg = tag.regs[1]; + assert( (tempreg&0xf) < 0x64 ); + regs[i] = tempreg & 0xf; + } + } + else + { + u32 tempreg = tag.regs[0]; + for(u32 i=0; i>= 4) + { + assert( (tempreg&0xf) < 0x64 ); + regs[i] = tempreg & 0xf; + } + } +} + +void GIFPath::SetTag(const void* mem) +{ + tag = *((GIFTAG*)mem); + curreg = 0; + + PrepRegs(); +} + +u32 GIFPath::GetReg() +{ + return regs[curreg]; +} + +static void _mtgsFreezeGIF( SaveState& state, GIFPath (&paths)[3] ) +{ + for(int i=0; i<3; i++ ) + { + state.Freeze( paths[i].tag ); + state.Freeze( paths[i].curreg ); + } + + for(int i=0; i<3; i++ ) + { + state.Freeze( paths[i].regs ); + } +} + +void SaveState::mtgsFreeze() +{ + if( mtgsThread != NULL ) + { + mtgsThread->Freeze( *this ); + } + else + { + // save some zero'd dummy info... + // This isn't ideal, and it could lead to problems in very rare + // circumstances, but most of the time should be perfectly fine. + + GIFPath path[3]; + memzero_obj( path ); + _mtgsFreezeGIF( *this, path ); + } +} + + +static void RegHandlerSIGNAL(const u32* data) +{ + MTGS_LOG("MTGS SIGNAL data %x_%x CSRw %x\n",data[0], data[1], CSRw); + + GSSIGLBLID->SIGID = (GSSIGLBLID->SIGID&~data[1])|(data[0]&data[1]); + + if ((CSRw & 0x1)) + GSCSRr |= 1; // signal + + if (!(GSIMR&0x100) ) + gsIrq(); +} + +static void RegHandlerFINISH(const u32* data) +{ + MTGS_LOG("MTGS FINISH data %x_%x CSRw %x\n",data[0], data[1], CSRw); + + if ((CSRw & 0x2)) + GSCSRr |= 2; // finish + + if (!(GSIMR&0x200) ) + gsIrq(); + +} + +static void RegHandlerLABEL(const u32* data) +{ + GSSIGLBLID->LBLID = (GSSIGLBLID->LBLID&~data[1])|(data[0]&data[1]); +} + +// END -- MTGS GIFtag Parse Implementation +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// MTGS Threaded Class Implementation + +mtgsThreadObject* mtgsThread = NULL; + +#ifdef RINGBUF_DEBUG_STACK +#include +std::list ringposStack; +#endif + +#ifdef _DEBUG +// debug variable used to check for bad code bits where copies are started +// but never closed, or closed without having been started. (GSRingBufCopy calls +// should always be followed by a call to GSRINGBUF_DONECOPY) +static int copyLock = 0; +#endif + +typedef void (*GIFRegHandler)(const u32* data); +static GIFRegHandler s_GSHandlers[3] = { RegHandlerSIGNAL, RegHandlerFINISH, RegHandlerLABEL }; + +mtgsThreadObject::mtgsThreadObject() : + m_RingPos( 0 ) +, m_WritePos( 0 ) + +, m_post_InitDone() +, m_lock_RingRestart() + +, m_CopyCommandTally( 0 ) +, m_CopyDataTally( 0 ) +, m_RingBufferIsBusy( 0 ) +, m_packet_size( 0 ) +, m_packet_ringpos( 0 ) + +#ifdef RINGBUF_DEBUG_STACK +, m_lock_Stack() +#endif +, m_RingBuffer( m_RingBufferSize + (Ps2MemSize::GSregs/sizeof(u128)) ) +, m_gsMem( (u8*)m_RingBuffer.GetPtr( m_RingBufferSize ) ) +{ + memzero_obj( m_path ); + + // Wait for the thread to finish initialization (it runs GSinit, which can take + // some time since it's creating a new window and all), and then check for errors. + + m_post_event.Post(); // tell MTGS we're done here + m_post_InitDone.Wait(); // and wait for MTGS to be done there! + + if( m_returncode != 0 ) // means the thread failed to init the GS plugin + throw Exception::PluginFailure( "GS", "The GS plugin failed to open/initialize." ); +} + +mtgsThreadObject::~mtgsThreadObject() +{ + Console::WriteLn( "MTGS > Closing GS thread..." ); + SetEvent(); + + // rest of the cleanup will be handled by the inherited object destructors... +} + +void mtgsThreadObject::Reset() +{ + // MTGS Reset process: + // * clear the ringbuffer. + // * Signal a reset. + // * clear the path and byRegs structs (used by GIFtagDummy) + + AtomicExchange( m_RingPos, m_WritePos ); + + MTGS_LOG( "MTGS > Sending Reset...\n" ); + SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 ); + SendSimplePacket( GS_RINGTYPE_FRAMESKIP, 0, 0, 0 ); + + memzero_obj( m_path ); +} + +// Processes a GIFtag & packet, and throws out some gsIRQs as needed. +// Used to keep interrupts in sync with the EE, while the GS itself +// runs potentially several frames behind. +// size - size of the packet in simd128's +__forceinline u32 mtgsThreadObject::_gifTransferDummy( GIF_PATH pathidx, const u8* pMem, u32 size ) +{ + GIFPath& path = m_path[pathidx]; + +#ifdef PCSX2_GSRING_SAMPLING_STATS + static uptr profStartPtr = 0; + static uptr profEndPtr = 0; + if( profStartPtr == 0 ) + { + __asm + { + __beginfunc: + mov profStartPtr, offset __beginfunc; + mov profEndPtr, offset __endfunc; + } + ProfilerRegisterSource( "GSRingBufCopy", (void*)profStartPtr, profEndPtr - profStartPtr ); + } +#endif + + while(size > 0) + { + bool eop = false; + + if(path.tag.nloop == 0) + { + path.SetTag( pMem ); + + pMem += sizeof(GIFTAG); + --size; + + if(pathidx == 2 && path.tag.eop) + Path3transfer = 0; + + if( pathidx == 0 ) + { + // hack: if too much data for VU1, just ignore. + + // The GIF is evil : if nreg is 0, it's really 16. Otherwise it's the value in nreg. + const int numregs = ((path.tag.nreg-1)&15)+1; + + if((path.tag.nloop * numregs) > (size * ((path.tag.flg == 1) ? 2 : 1))) + { + path.tag.nloop = 0; + return ++size; + } + } + + + /*f(path.tag.pre) + { + assert(path.tag.flg != GIF_FLG_IMAGE); // kingdom hearts, ffxii, tales of abyss + + if((path.tag.flg & 2) == 0) + { + // Primitive handler... Nothing for the Dummy to do here. + + //GIFReg r; + //r.i64 = path.tag.PRIM; + //(this->*m_fpGIFRegHandlers[GIF_A_D_REG_PRIM])(&r); + } + }*/ + + if(path.tag.eop) + { + eop = true; + } + else if(path.tag.nloop == 0) + { + if(pathidx == 0 && g_FFXHack) + continue; + + eop = true; + } + } + + if(path.tag.nloop > 0) + { + switch(path.tag.flg) + { + case GIF_FLG_PACKED: + + while(size > 0) + { + if( path.GetReg() == 0xe ) + { + const int handler = pMem[8]; + if(handler >= 0x60 && handler < 0x63) + s_GSHandlers[handler&0x3]((const u32*)pMem); + } + size--; + pMem += 16; // 128 bits! //sizeof(GIFPackedReg); + + if((++path.curreg & 0xf) == path.tag.nreg) + { + path.curreg = 0; + path.tag.nloop--; + + if(path.tag.nloop == 0) + break; + } + } + break; + + case GIF_FLG_REGLIST: + + size *= 2; + + while(size > 0) + { + const int handler = path.GetReg(); + if(handler >= 0x60 && handler < 0x63) + s_GSHandlers[handler&0x3]((const u32*)pMem); + + size--; + pMem += 8; //sizeof(GIFReg); -- 64 bits! + + if((++path.curreg & 0xf) == path.tag.nreg) + { + path.curreg = 0; + path.tag.nloop--; + + if(path.tag.nloop == 0) + { + break; + } + } + } + + if(size & 1) pMem += 8; //sizeof(GIFReg); + size /= 2; + + break; + + case GIF_FLG_IMAGE2: // hmmm + assert(0); + path.tag.nloop = 0; + + break; + + case GIF_FLG_IMAGE: + { + int len = (int)min(size, path.tag.nloop); + + pMem += len * 16; + path.tag.nloop -= len; + size -= len; + } + break; + + jNO_DEFAULT; + + } + } + + if(eop && ((int)size <= 0 || pathidx == 0)) + { + break; + } + } + + if(pathidx == 0) + { + if(!path.tag.eop && path.tag.nloop > 0) + { + path.tag.nloop = 0; + DevCon::Write( "path1 hack! " ); + + // This means that the giftag data got screwly somewhere + // along the way (often means curreg was in a bad state or something) + } + } +#ifdef PCSX2_GSRING_SAMPLING_STATS + __asm + { + __endfunc: + nop; + } +#endif + return size; +} + +struct PacketTagType +{ + u32 command; + u32 data[3]; +}; + +int mtgsThreadObject::Callback() +{ + Console::WriteLn("MTGS > Thread Started, Opening GS Plugin..."); + + // Wait for the MTGS to initialize structures. + m_post_event.Wait(); + + memcpy_aligned( m_gsMem, PS2MEM_GS, sizeof(m_gsMem) ); + GSsetBaseMem( m_gsMem ); + + m_returncode = GSopen((void *)&pDsp, "PCSX2", 1); + GSCSRr = 0x551B400F; // 0x55190000 + m_post_InitDone.Post(); + if (m_returncode != 0) { return m_returncode; } // error msg will be issued to the user by Plugins.c + Console::WriteLn("MTGS > GSopen Finished."); + +#ifdef RINGBUF_DEBUG_STACK + PacketTagType prevCmd; +#endif + + while( !m_sigterm ) + { + m_post_event.Wait(); + //if( m_sigterm ) break; + + AtomicExchange( m_RingBufferIsBusy, 1 ); + + // note: m_RingPos is intentionally not volatile, because it should only + // ever be modified by this thread. + while( m_RingPos != volatize(m_WritePos)) + { + assert( m_RingPos < m_RingBufferSize ); + + const PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_RingPos]; + u32 ringposinc = 1; + +#ifdef RINGBUF_DEBUG_STACK + // pop a ringpos off the stack. It should match this one! + + m_lock_Stack.Lock(); + uptr stackpos = ringposStack.back(); + if( stackpos != m_RingPos ) + { + Console::Error( "MTGS Ringbuffer Critical Failure ---> %x to %x (prevCmd: %x)\n", params stackpos, m_RingPos, prevCmd.command ); + } + assert( stackpos == m_RingPos ); + prevCmd = tag; + ringposStack.pop_back(); + m_lock_Stack.Unlock(); +#endif + + switch( tag.command ) + { + case GS_RINGTYPE_RESTART: + AtomicExchange(m_RingPos, 0); + + // stall for a bit to let the MainThread have time to update the g_pGSWritePos. + m_lock_RingRestart.Lock(); + m_lock_RingRestart.Unlock(); + continue; + + case GS_RINGTYPE_P1: + { + const int qsize = tag.data[0]; + const u128* data = m_RingBuffer.GetPtr( m_RingPos+1 ); + + // make sure that tag>>16 is the MAX size readable + //GSgifTransfer1(((u32*)data) - 0x1000 + 4*qsize, 0x4000-qsize*16); + GSgifTransfer1((u32*)(data - 0x400 + qsize), 0x4000-qsize*16); + ringposinc += qsize; + } + break; + + case GS_RINGTYPE_P2: + { + const int qsize = tag.data[0]; + const u128* data = m_RingBuffer.GetPtr( m_RingPos+1 ); + GSgifTransfer2((u32*)data, qsize); + ringposinc += qsize; + } + break; + + case GS_RINGTYPE_P3: + { + const int qsize = tag.data[0]; + const u128* data = m_RingBuffer.GetPtr( m_RingPos+1 ); + GSgifTransfer3((u32*)data, qsize); + ringposinc += qsize; + } + break; + + case GS_RINGTYPE_VSYNC: + { + GSvsync(tag.data[0]); + + gsFrameSkip( !tag.data[1] ); + + if( PAD1update != NULL ) PAD1update(0); + if( PAD2update != NULL ) PAD2update(1); + } + break; + + case GS_RINGTYPE_FRAMESKIP: + _gs_ResetFrameskip(); + break; + + case GS_RINGTYPE_MEMWRITE8: + m_gsMem[tag.data[0]] = (u8)tag.data[1]; + break; + case GS_RINGTYPE_MEMWRITE16: + *(u16*)(m_gsMem+tag.data[0]) = (u16)tag.data[1]; + break; + case GS_RINGTYPE_MEMWRITE32: + *(u32*)(m_gsMem+tag.data[0]) = tag.data[1]; + break; + case GS_RINGTYPE_MEMWRITE64: + *(u64*)(m_gsMem+tag.data[0]) = *(u64*)&tag.data[1]; + break; + + case GS_RINGTYPE_FREEZE: + { + freezeData* data = (freezeData*)(*(uptr*)&tag.data[1]); + int mode = tag.data[0]; + GSfreeze( mode, data ); + break; + } + + case GS_RINGTYPE_RECORD: + { + int record = tag.data[0]; + if( GSsetupRecording != NULL ) GSsetupRecording(record, NULL); + if( SPU2setupRecording != NULL ) SPU2setupRecording(record, NULL); + break; + } + + case GS_RINGTYPE_RESET: + MTGS_LOG( "MTGS > Receiving Reset...\n" ); + if( GSreset != NULL ) GSreset(); + break; + + case GS_RINGTYPE_SOFTRESET: + { + int mask = tag.data[0]; + MTGS_LOG( "MTGS > Receiving GIF Soft Reset (mask: %d)\n", mask ); + GSgifSoftReset( mask ); + break; + } + + case GS_RINGTYPE_WRITECSR: + GSwriteCSR( tag.data[0] ); + break; + + case GS_RINGTYPE_MODECHANGE: + _gs_ChangeTimings( tag.data[0], tag.data[1] ); + break; + + case GS_RINGTYPE_STARTTIME: + m_iSlowStart += tag.data[0]; + break; + +#ifdef PCSX2_DEVBUILD + default: + Console::Error("GSThreadProc, bad packet (%x) at m_RingPos: %x, m_WritePos: %x", params tag.command, m_RingPos, m_WritePos); + assert(0); + m_RingPos = m_WritePos; + continue; +#else + // Optimized performance in non-Dev builds. + jNO_DEFAULT; +#endif + } + + uint newringpos = m_RingPos + ringposinc; + assert( newringpos <= m_RingBufferSize ); + newringpos &= m_RingBufferMask; + AtomicExchange( m_RingPos, newringpos ); + } + AtomicExchange( m_RingBufferIsBusy, 0 ); + } + + GSclose(); + return 0; +} + +// Waits for the GS to empty out the entire ring buffer contents. +// Used primarily for plugin startup/shutdown. +void mtgsThreadObject::WaitGS() +{ + // Freeze registers because some kernel code likes to destroy them + FreezeXMMRegs(1); + FreezeMMXRegs(1); + SetEvent(); + while( volatize(m_RingPos) != volatize(m_WritePos) ) + { + Timeslice(); + //SpinWait(); + } + FreezeXMMRegs(0); + FreezeMMXRegs(0); +} + +// Sets the gsEvent flag and releases a timeslice. +// For use in loops that wait on the GS thread to do certain things. +void mtgsThreadObject::SetEvent() +{ + m_post_event.Post(); + m_CopyCommandTally = 0; + m_CopyDataTally = 0; +} + +void mtgsThreadObject::PrepEventWait() +{ + // Freeze registers because some kernel code likes to destroy them + FreezeXMMRegs(1); + FreezeMMXRegs(1); + //Console::Notice( "MTGS Stall! EE waits for nothing! ... except your GPU sometimes." ); + SetEvent(); + Timeslice(); +} + +void mtgsThreadObject::PostEventWait() const +{ + FreezeMMXRegs(0); + FreezeXMMRegs(0); +} + +u8* mtgsThreadObject::GetDataPacketPtr() const +{ + return (u8*)m_RingBuffer.GetPtr( m_packet_ringpos ); +} + +// Closes the data packet send command, and initiates the gs thread (if needed). +void mtgsThreadObject::SendDataPacket() +{ + // make sure a previous copy block has been started somewhere. + jASSUME( m_packet_size != 0 ); + + uint temp = m_packet_ringpos + m_packet_size; + jASSUME( temp <= m_RingBufferSize ); + temp &= m_RingBufferMask; + +#ifdef _DEBUG + if( m_packet_ringpos + m_packet_size < m_RingBufferSize ) + { + uint readpos = volatize(m_RingPos); + if( readpos != m_WritePos ) + { + // The writepos should never leapfrog the readpos + // since that indicates a bad write. + if( m_packet_ringpos < readpos ) + assert( temp < readpos ); + } + + // Updating the writepos should never make it equal the readpos, since + // that would stop the buffer prematurely (and indicates bad code in the + // ringbuffer manager) + assert( readpos != temp ); + } +#endif + + AtomicExchange( m_WritePos, temp ); + + m_packet_size = 0; + + if( m_RingBufferIsBusy ) return; + + // The ringbuffer is current in a resting state, so if enough copies have + // queued up then go ahead and initiate the GS thread.. + + // Optimization notes: What we're doing here is initiating a "burst" mode on + // the thread, which improves its cache hit performance and makes it more friendly + // to other threads in Pcsx2 and such. Primary is the Command Tally, and then a + // secondary data size threshold for games that do lots of texture swizzling. + + // 16 was the best value I found so far. + // tested values: + // 24 - very slow on HT machines (+5% drop in fps) + // 8 - roughly 2% slower on HT machines. + + m_CopyDataTally += m_packet_size; + if( ( m_CopyDataTally > 0x8000 ) || ( ++m_CopyCommandTally > 16 ) ) + { + FreezeXMMRegs(1); + FreezeMMXRegs(1); + //Console::Status( "MTGS Kick! DataSize : 0x%5.8x, CommandTally : %d", m_CopyDataTally, m_CopyCommandTally ); + SetEvent(); + FreezeMMXRegs(0); + FreezeXMMRegs(0); + } +} + +int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u64* srcdata, u32 size ) +{ + return PrepDataPacket( pathidx, (u8*)srcdata, size ); +} + +int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 size ) +{ + return PrepDataPacket( pathidx, (u8*)srcdata, size ); +} + +#ifdef PCSX2_GSRING_TX_STATS +static u32 ringtx_s=0; +static u32 ringtx_s_ulg=0; +static u32 ringtx_s_min=0xFFFFFFFF; +static u32 ringtx_s_max=0; +static u32 ringtx_c=0; +static u32 ringtx_inf[32][32]; +static u32 ringtx_inf_s[32]; +#endif + +#ifdef PCSX2_GSRING_SAMPLING_STATS +static u32 GSRingBufCopySz = 0; +#endif + +// returns the amount of giftag data not processed (in simd128 values). +// Return value is used by VU1 XGKICK to hack-fix data packets which are too +// large for VU1 memory. +// Parameters: +// size - size of the packet data, in smd128's +int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size ) +{ +#ifdef PCSX2_GSRING_TX_STATS + ringtx_s+=size; + ringtx_s_ulg+=size&0x7F; + ringtx_s_min=min(ringtx_s_min,size); + ringtx_s_max=max(ringtx_s_max,size); + ringtx_c++; + unsigned long tx_sz; + if (_BitScanReverse(&tx_sz,size)) + { + unsigned long tx_algn; + _BitScanForward(&tx_algn,size); + ringtx_inf[tx_sz][tx_algn]++; + ringtx_inf_s[tx_sz]+=size; + } + if (ringtx_s>=128*1024*1024) + { + Console::Status("GSRingBufCopy:128MB in %d tx -> b/tx: AVG = %.2f , max = %d, min = %d",ringtx_c,ringtx_s/(float)ringtx_c,ringtx_s_max,ringtx_s_min); + for (int i=0;i<32;i++) + { + u32 total_bucket=0; + u32 bucket_subitems=0; + for (int j=0;j<32;j++) + { + if (ringtx_inf[i][j]) + { + total_bucket+=ringtx_inf[i][j]; + bucket_subitems++; + Console::Notice("GSRingBufCopy :tx [%d,%d] algn %d : count= %d [%.2f%%]",1<= readpos) + + PrepEventWait(); + while( true ) + { + uint readpos = volatize(m_RingPos); + if( writepos >= readpos ) break; + if( writepos+size < readpos ) break; + SpinWait(); + } + PostEventWait(); + } + } + else if( writepos + size > m_RingBufferSize ) + { + // If the incoming packet doesn't fit, then start over from + // the start of the ring buffer (it's a lot easier than trying + // to wrap the packet around the end of the buffer). + + // We have to be careful not to leapfrog our read-position. If it's + // greater than the current write position then we need to stall + // until it loops around to the beginning of the buffer + + PrepEventWait(); + while( true ) + { + uint readpos = volatize(m_RingPos); + + // is the buffer empty? + if( readpos == writepos ) break; + + // Also: Wait for the readpos to go past the start of the buffer + // Otherwise it'll stop dead in its tracks when we set the new write + // position below (bad!) + if( readpos < writepos && readpos != 0 ) break; + + SpinWait(); + } + + m_lock_RingRestart.Lock(); + SendSimplePacket( GS_RINGTYPE_RESTART, 0, 0, 0 ); + writepos = 0; + AtomicExchange( m_WritePos, writepos ); + m_lock_RingRestart.Unlock(); + SetEvent(); + + // stall until the read position is past the end of our incoming block, + // or until it reaches the current write position (signals an empty buffer). + while( true ) + { + uint readpos = volatize(m_RingPos); + + if( readpos == m_WritePos ) break; + if( writepos+size < readpos ) break; + + SpinWait(); + } + PostEventWait(); + } + else // always true - if( writepos + size == MTGS_RINGBUFFEREND ) + { + // Yay. Perfect fit. What are the odds? + //SysPrintf( "MTGS > Perfect Fit!\n"); + + PrepEventWait(); + while( true ) + { + uint readpos = volatize(m_RingPos); + + // stop waiting if the buffer is empty! + if( writepos == readpos ) break; + + // Copy is ready so long as readpos is less than writepos and *not* + // equal to the base of the ringbuffer (otherwise the buffer will stop + // when the writepos is wrapped around to zero later-on in SendDataPacket) + if( readpos < writepos && readpos != 0 ) break; + + SpinWait(); + } + PostEventWait(); + } + +#ifdef RINGBUF_DEBUG_STACK + m_lock_Stack.Lock(); + ringposStack.push_front( writepos ); + m_lock_Stack.Unlock(); +#endif + + // Command qword: Low word is the command, and the high word is the packet + // length in SIMDs (128 bits). + + PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos]; + tag.command = pathidx+1; + tag.data[0] = m_packet_size; + m_packet_ringpos = m_WritePos + 1; + + return m_packet_size; +} + +__forceinline uint mtgsThreadObject::_PrepForSimplePacket() +{ +#ifdef RINGBUF_DEBUG_STACK + m_lock_Stack.Lock(); + ringposStack.push_front( m_WritePos ); + m_lock_Stack.Unlock(); +#endif + + uint future_writepos = m_WritePos+1; + jASSUME( future_writepos <= m_RingBufferSize ); + + future_writepos &= m_RingBufferMask; + + if( future_writepos == volatize(m_RingPos) ) + { + PrepEventWait(); + do + { + SpinWait(); + } while( future_writepos == volatize(m_RingPos) ); + PostEventWait(); + } + + return future_writepos; +} + +__forceinline void mtgsThreadObject::_FinishSimplePacket( uint future_writepos ) +{ + assert( future_writepos != volatize(m_RingPos) ); + AtomicExchange( m_WritePos, future_writepos ); +} + +void mtgsThreadObject::SendSimplePacket( GS_RINGTYPE type, int data0, int data1, int data2 ) +{ + const uint thefuture = _PrepForSimplePacket(); + PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos]; + + tag.command = type; + tag.data[0] = data0; + tag.data[1] = data1; + tag.data[2] = data2; + + _FinishSimplePacket( thefuture ); +} + +void mtgsThreadObject::SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 ) +{ + const uint thefuture = _PrepForSimplePacket(); + PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos]; + + tag.command = type; + tag.data[0] = data0; + *(uptr*)&tag.data[1] = (uptr)data1; + + _FinishSimplePacket( thefuture ); +} + +// Waits for the GS to empty out the entire ring buffer contents. +// Used primarily for plugin startup/shutdown. +void mtgsWaitGS() +{ + if( mtgsThread == NULL ) return; + mtgsThread->WaitGS(); +} + +bool mtgsOpen() +{ + // Check the config flag since our thread object has yet to be created + if( !CHECK_MULTIGS ) return false; + + // better not be a thread already running, yo! + assert( mtgsThread == NULL ); + + try + { + mtgsThread = new mtgsThreadObject(); + } + catch( Exception::ThreadCreationError& ) + { + Console::Error( "MTGS > Thread creation failed!" ); + mtgsThread = NULL; + return false; + } + return true; +} + + +void mtgsThreadObject::GIFSoftReset( int mask ) +{ + if(mask & 1) memzero_obj(m_path[0]); + if(mask & 2) memzero_obj(m_path[1]); + if(mask & 4) memzero_obj(m_path[2]); + + if( GSgifSoftReset == NULL ) return; + + MTGS_LOG( "MTGS > Sending GIF Soft Reset (mask: %d)\n", mask ); + mtgsThread->SendSimplePacket( GS_RINGTYPE_SOFTRESET, mask, 0, 0 ); +} + +void mtgsThreadObject::Freeze( SaveState& state ) +{ + _mtgsFreezeGIF( state, this->m_path ); +} + +// this function is needed because of recompiled calls from iGS.cpp +// (currently used in GCC only) +void mtgsRingBufSimplePacket( s32 command, u32 data0, u32 data1, u32 data2 ) +{ + mtgsThread->SendSimplePacket( (GS_RINGTYPE)command, data0, data1, data2 ); +} diff --git a/pcsx2/Makefile.am b/pcsx2/Makefile.am new file mode 100644 index 0000000000..3fe3e0f970 --- /dev/null +++ b/pcsx2/Makefile.am @@ -0,0 +1,23 @@ +AUTOMAKE_OPTIONS = foreign +INCLUDES = -I@srcdir@/x86/ -I@srcdir@/common/ +noinst_LIBRARIES = libpcsx2.a + +libpcsx2_a_SOURCES = \ +CDVD.cpp CDVDiso.cpp CDVDisodrv.cpp COP0.cpp COP2.cpp Cache.cpp CdRom.cpp Console.cpp \ +Counters.cpp Decode_XA.cpp Elfheader.cpp FPU.cpp FPU2.cpp FiFo.cpp Gif.cpp GS.cpp Hw.cpp Interpreter.cpp \ +IopBios.cpp IopCounters.cpp IopDma.cpp IopHw.cpp IopMem.cpp IopSio2.cpp MMI.cpp MTGS.cpp \ +Memory.cpp Misc.cpp Patch.cpp PathUtils.cpp Plugins.cpp PrecompiledHeader.cpp R3000A.cpp \ +R3000AInterpreter.cpp R3000AOpcodeTables.cpp R5900.cpp R5900OpcodeImpl.cpp R5900OpcodeTables.cpp \ +SPR.cpp SaveState.cpp Sif.cpp Sio.cpp SourceLog.cpp Stats.cpp System.cpp ThreadTools.cpp \ +VU0.cpp VU0micro.cpp VU0microInterp.cpp VU1micro.cpp VU1microInterp.cpp VUflags.cpp VUmicroMem.cpp VUops.cpp \ +Vif.cpp VifDma.cpp vssprintf.cpp vtlb.cpp xmlpatchloader.cpp AlignedMalloc.cpp + + +libpcsx2_a_SOURCES += \ +CDVD.h CDVDiso.h CDVDisodrv.h CDVDlib.h COP0.h Cache.h CdRom.h Common.h Counters.h Decode_XA.h EEregs.h \ +Elfheader.h Exceptions.h GS.h Hw.h IopBios.h IopBios2.h IopCounters.h IopDma.h IopHw.h IopMem.h IopSio2.h Memcpyfast.h \ +Memory.h Misc.h Patch.h Paths.h Plugins.h PrecompiledHeader.h PsxCommon.h R3000A.h R5900.h R5900OpcodeTables.h \ +SPR.h SamplProf.h SaveState.h Sif.h Sifcmd.h Sio.h SafeArray.h Stats.h StringUtils.h System.h Threading.h \ +VU.h VUflags.h VUmicro.h VUops.h Vif.h VifDma.h cheatscpp.h vtlb.h + +SUBDIRS = x86 . DebugTools IPU RDebug tinyxml Linux \ No newline at end of file diff --git a/pcsx2/Mdec.cpp b/pcsx2/Mdec.cpp new file mode 100644 index 0000000000..af7733e015 --- /dev/null +++ b/pcsx2/Mdec.cpp @@ -0,0 +1,420 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +/* This code was based on the FPSE v0.08 Mdec decoder*/ + +#include +#include + +#include "Misc.h" +#include "PsxCommon.h" +#include "Mdec.h" + +int iq_y[DCTSIZE2],iq_uv[DCTSIZE2]; + +static void idct1(int *block) +{ + int i, val; + + val = RANGE(DESCALE(block[0], PASS1_BITS+3)); + + for(i=0;i>16)*(bcr&0xffff); + + if (cmd==0x40000001) { + u8 *p = (u8*)PSXM(adr); + iqtab_init(iq_y,p); + iqtab_init(iq_uv,p+64); + } + else if ((cmd&0xf5ff0000)==0x30000000) { + mdec.rl = (u16*)PSXM(adr); + } + + HW_DMA0_CHCR &= ~0x01000000; + psxDmaInterrupt(0); +} + +void psxDma1(u32 adr, u32 bcr, u32 chcr) { + int blk[DCTSIZE2*6]; + unsigned short *image; + int size; + + CDR_LOG("DMA1 %lx %lx %lx (cmd = %lx)\n", adr, bcr, chcr, mdec.command); + + if (chcr!=0x01000200) return; + + size = (bcr>>16)*(bcr&0xffff); + image = (u16*)PSXM(adr); + if (mdec.command&0x08000000) { + for (;size>0;size-=(16*16)/2,image+=(16*16)) { + mdec.rl = rl2blk(blk,mdec.rl); + yuv2rgb15(blk,image); + } + } else { + for (;size>0;size-=(24*16)/2,image+=(24*16)) { + mdec.rl = rl2blk(blk,mdec.rl); + yuv2rgb24(blk,(u8 *)image); + } + } + + HW_DMA1_CHCR &= ~0x01000000; + psxDmaInterrupt(1); +} + +static int zscan[DCTSIZE2] = { + 0 ,1 ,8 ,16,9 ,2 ,3 ,10, + 17,24,32,25,18,11,4 ,5 , + 12,19,26,33,40,48,41,34, + 27,20,13,6 ,7 ,14,21,28, + 35,42,49,56,57,50,43,36, + 29,22,15,23,30,37,44,51, + 58,59,52,45,38,31,39,46, + 53,60,61,54,47,55,62,63 +}; + +static int aanscales[DCTSIZE2] = { + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 +}; + +void iqtab_init(int *iqtab,unsigned char *iq_y) +{ + int i; + + for(i=0;i>(CONST_BITS14-IFAST_SCALE_BITS); + } +} + +unsigned short* rl2blk(int *blk,unsigned short *mdec_rl) { + int i,k,q_scale,rl; + int *iqtab; + + memset (blk, 0, 6*DCTSIZE2*4); + iqtab = iq_uv; + for(i=0;i<6;i++) { // decode blocks (Cr,Cb,Y1,Y2,Y3,Y4) + if (i>1) iqtab = iq_y; + + // zigzag transformation + rl = *mdec_rl++; + q_scale = RUNOF(rl); + blk[0] = iqtab[0]*VALOF(rl); + for(k = 0;;) { + rl = *mdec_rl++; + if (rl==NOP) break; + k += RUNOF(rl)+1; // skip level zero-coefficients + if (k > 63) break; + blk[zscan[k]] = (VALOF(rl) * iqtab[k] * q_scale) / 8; // / 16; + } + + idct(blk,k+1); + blk+=DCTSIZE2; + } + return mdec_rl; +} + +unsigned char roundtbl[256*3]; + +void round_init(void) { + int i; + for(i=0;i<256;i++) { + roundtbl[i]=0; + roundtbl[i+256]=i; + roundtbl[i+512]=255; + } +} + +void yuv2rgb15(int *blk,unsigned short *image) { + int x,y; + int *Yblk = blk+DCTSIZE2*2; + int Cb,Cr,R,G,B; + int *Cbblk = blk; + int *Crblk = blk+DCTSIZE2; + + if (!(Config.Mdec&0x1)) + for (y=0;y<16;y+=2,Crblk+=4,Cbblk+=4,Yblk+=8,image+=24) { + if (y==8) Yblk+=DCTSIZE2; + for (x=0;x<4;x++,image+=2,Crblk++,Cbblk++,Yblk+=2) { + Cr = *Crblk; + Cb = *Cbblk; + R = MULR(Cr); + G = MULG(Cb) + MULG2(Cr); + B = MULB(Cb); + + RGB15(0, Yblk[0]); + RGB15(1, Yblk[1]); + RGB15(16, Yblk[8]); + RGB15(17, Yblk[9]); + + Cr = *(Crblk+4); + Cb = *(Cbblk+4); + R = MULR(Cr); + G = MULG(Cb) + MULG2(Cr); + B = MULB(Cb); + + RGB15(8, Yblk[DCTSIZE2+0]); + RGB15(9, Yblk[DCTSIZE2+1]); + RGB15(24, Yblk[DCTSIZE2+8]); + RGB15(25, Yblk[DCTSIZE2+9]); + } + } else + for (y=0;y<16;y+=2,Yblk+=8,image+=24) { + if (y==8) Yblk+=DCTSIZE2; + for (x=0;x<4;x++,image+=2,Yblk+=2) { + RGB15BW(0, Yblk[0]); + RGB15BW(1, Yblk[1]); + RGB15BW(16, Yblk[8]); + RGB15BW(17, Yblk[9]); + + RGB15BW(8, Yblk[DCTSIZE2+0]); + RGB15BW(9, Yblk[DCTSIZE2+1]); + RGB15BW(24, Yblk[DCTSIZE2+8]); + RGB15BW(25, Yblk[DCTSIZE2+9]); + } + } +} + +void yuv2rgb24(int *blk,unsigned char *image) { + int x,y; + int *Yblk = blk+DCTSIZE2*2; + int Cb,Cr,R,G,B; + int *Cbblk = blk; + int *Crblk = blk+DCTSIZE2; + + if (!(Config.Mdec&0x1)) + for (y=0;y<16;y+=2,Crblk+=4,Cbblk+=4,Yblk+=8,image+=24*3) { + if (y==8) Yblk+=DCTSIZE2; + for (x=0;x<4;x++,image+=6,Crblk++,Cbblk++,Yblk+=2) { + Cr = *Crblk; + Cb = *Cbblk; + R = MULR(Cr); + G = MULG(Cb) + MULG2(Cr); + B = MULB(Cb); + + RGB24(0, Yblk[0]); + RGB24(1*3, Yblk[1]); + RGB24(16*3, Yblk[8]); + RGB24(17*3, Yblk[9]); + + Cr = *(Crblk+4); + Cb = *(Cbblk+4); + R = MULR(Cr); + G = MULG(Cb) + MULG2(Cr); + B = MULB(Cb); + + RGB24(8*3, Yblk[DCTSIZE2+0]); + RGB24(9*3, Yblk[DCTSIZE2+1]); + RGB24(24*3, Yblk[DCTSIZE2+8]); + RGB24(25*3, Yblk[DCTSIZE2+9]); + } + } else + for (y=0;y<16;y+=2,Yblk+=8,image+=24*3) { + if (y==8) Yblk+=DCTSIZE2; + for (x=0;x<4;x++,image+=6,Yblk+=2) { + RGB24BW(0, Yblk[0]); + RGB24BW(1*3, Yblk[1]); + RGB24BW(16*3, Yblk[8]); + RGB24BW(17*3, Yblk[9]); + + RGB24BW(8*3, Yblk[DCTSIZE2+0]); + RGB24BW(9*3, Yblk[DCTSIZE2+1]); + RGB24BW(24*3, Yblk[DCTSIZE2+8]); + RGB24BW(25*3, Yblk[DCTSIZE2+9]); + } + } +} + +int SaveState::mdecFreeze() { + Freeze(mdec); + Freeze(iq_y); + Freeze(iq_uv); + + return 0; + +} + + + + diff --git a/pcsx2/Mdec.h b/pcsx2/Mdec.h new file mode 100644 index 0000000000..ca9f4dfefd --- /dev/null +++ b/pcsx2/Mdec.h @@ -0,0 +1,110 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __MDEC_H__ +#define __MDEC_H__ + + +// mdec status: +#define MDEC_BUSY 0x20000000 +#define MDEC_DREQ 0x18000000 +#define MDEC_FIFO 0xc0000000 +#define MDEC_RGB24 0x02000000 +#define MDEC_STP 0x00800000 + + +#define CONST_BITS 8 +#define PASS1_BITS 2 +#define CONST_BITS14 14 +#define IFAST_SCALE_BITS 2 + +#define FIX_1_082392200 (277) +#define FIX_1_414213562 (362) +#define FIX_1_847759065 (473) +#define FIX_2_613125930 (669) + +#define MULTIPLY(var,const) (DESCALE((var) * (const), CONST_BITS)) + +#define DEQUANTIZE(coef,quantval) (coef) + +#define DESCALE(x,n) ((x)>>(n)) +#define RANGE(n) (n) + +#define DCTSIZE 8 +#define DCTSIZE2 64 + +#define RUNOF(a) ((a)>>10) +#define VALOF(a) (((int)(a)<<(32-10))>>(32-10)) +#define NOP 0xfe00 + +#define FIXED + +#ifdef FIXED +#define MULR(a) ((((int)0x0000059B) * (a)) >> 10) +#define MULG(a) ((((int)0xFFFFFEA1) * (a)) >> 10) +#define MULG2(a) ((((int)0xFFFFFD25) * (a)) >> 10) +#define MULB(a) ((((int)0x00000716) * (a)) >> 10) +#else +#define MULR(a) ((int)((float)1.40200 * (a))) +#define MULG(a) ((int)((float)-0.3437 * (a))) +#define MULG2(a) ((int)((float)-0.7143 * (a))) +#define MULB(a) ((int)((float)1.77200 * (a))) +#endif + +#define MAKERGB15(r,g,b) ( (((r)>>3)<<10)|(((g)>>3)<<5)|((b)>>3) ) +#define ROUND(c) roundtbl[((c)+128+256)]//&0x3ff] + +#define RGB15(n, Y) \ + image[n] = MAKERGB15(ROUND(Y + R),ROUND(Y + G),ROUND(Y + B)); + +#define RGB15BW(n, Y) \ + image[n] = MAKERGB15(ROUND(Y),ROUND(Y),ROUND(Y)); + +#define RGB24(n, Y) \ + image[n+2] = ROUND(Y + R); \ + image[n+1] = ROUND(Y + G); \ + image[n+0] = ROUND(Y + B); + +#define RGB24BW(n, Y) \ + image[n+2] = ROUND(Y); \ + image[n+1] = ROUND(Y); \ + image[n+0] = ROUND(Y); + +void mdecInit(); +void mdecWrite0(u32 data); +void mdecWrite1(u32 data); +u32 mdecRead0(); +u32 mdecRead1(); +void psxDma0(u32 madr, u32 bcr, u32 chcr); +void psxDma1(u32 madr, u32 bcr, u32 chcr); +int mdecFreeze(gzFile f, int Mode); + +unsigned short* rl2blk(int *blk,unsigned short *mdec_rl); +void iqtab_init(int *iqtab,unsigned char *iq_y); +void round_init(void); +void yuv2rgb24(int *blk,unsigned char *image); +void yuv2rgb15(int *blk,unsigned short *image); + +struct { + unsigned long command; + unsigned long status; + unsigned short *rl; + int rlsize; +} mdec; + +#endif /* __MDEC_H__ */ diff --git a/pcsx2/MemcpyFast.h b/pcsx2/MemcpyFast.h new file mode 100644 index 0000000000..c6bc23247f --- /dev/null +++ b/pcsx2/MemcpyFast.h @@ -0,0 +1,59 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __MEMCPY_FAST_H__ +#define __MEMCPY_FAST_H__ + +//#include "Misc.h" + +void _memset16_unaligned( void* dest, u16 data, size_t size ); + +#if defined(_WIN32) && !defined(__x86_64__) + + // The new simplified memcpy_amd_ is now faster than memcpy_raz_. + // memcpy_amd_ also does mmx register saving, negating the need for freezeregs (code cleanup!) + // Additionally, using one single memcpy implementation keeps the code cache cleaner. + + //extern void __fastcall memcpy_raz_udst(void *dest, const void *src, size_t bytes); + //extern void __fastcall memcpy_raz_usrc(void *dest, const void *src, size_t bytes); + //extern void __fastcall memcpy_raz_(void *dest, const void *src, size_t bytes); + extern void __fastcall memcpy_amd_(void *dest, const void *src, size_t bytes); + +# include "windows/memzero.h" +# define memcpy_fast memcpy_amd_ +# define memcpy_aligned memcpy_amd_ + +#else + + // for now linux uses the GCC memcpy/memset implementations. + #define memcpy_fast memcpy + #define memcpy_raz_ memcpy + #define memcpy_raz_u memcpy + + #define memcpy_aligned memcpy + #define memcpy_raz_u memcpy + + #include "Linux/memzero.h" + +#endif + + +extern u8 memcmp_mmx(const void* src1, const void* src2, int cmpsize); +extern void memxor_mmx(void* dst, const void* src1, int cmpsize); + +#endif diff --git a/pcsx2/Memory.cpp b/pcsx2/Memory.cpp new file mode 100644 index 0000000000..89f9a65b43 --- /dev/null +++ b/pcsx2/Memory.cpp @@ -0,0 +1,904 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +/* + +RAM +--- +0x00100000-0x01ffffff this is the physical address for the ram.its cached there +0x20100000-0x21ffffff uncached +0x30100000-0x31ffffff uncached & acceleretade +0xa0000000-0xa1ffffff MIRROR might...??? +0x80000000-0x81ffffff MIRROR might... ???? + +scratch pad +---------- +0x70000000-0x70003fff scratch pad + +BIOS +---- +0x1FC00000 - 0x1FFFFFFF un-cached +0x9FC00000 - 0x9FFFFFFF cached +0xBFC00000 - 0xBFFFFFFF un-cached +*/ + +#include "PrecompiledHeader.h" + +#pragma warning(disable:4799) // No EMMS at end of function + +#include + +#include "Common.h" +#include "iR5900.h" + +#include "PsxCommon.h" +#include "VUmicro.h" +#include "GS.h" +#include "vtlb.h" +#include "IPU/IPU.h" + + +#ifdef ENABLECACHE +#include "Cache.h" +#endif + +#ifdef __LINUX__ +#include +#endif + +//#define FULLTLB +int MemMode = 0; // 0 is Kernel Mode, 1 is Supervisor Mode, 2 is User Mode + +void memSetKernelMode() { + //Do something here + MemMode = 0; +} + +void memSetSupervisorMode() { +} + +void memSetUserMode() { + +} + +u16 ba0R16(u32 mem) +{ + //MEM_LOG("ba00000 Memory read16 address %x\n", mem); + + if (mem == 0x1a000006) { + static int ba6; + ba6++; + if (ba6 == 3) ba6 = 0; + return ba6; + } + return 0; +} + + +// Attempts to load a BIOS rom file, by trying multiple combinations of base filename +// and extension. The bios specified in Config.Bios is used as the base. +void loadBiosRom( const char *ext, u8 *dest, long maxSize ) +{ + string Bios1; + string Bios; + long filesize; + + Path::Combine( Bios, Config.BiosDir, Config.Bios ); + + // Try first a basic extension concatenation (normally results in something like name.bin.rom1) + ssprintf(Bios1, "%hs.%s", &Bios, ext); + if( (filesize=Path::getFileSize( Bios1 ) ) <= 0 ) + { + // Try the name properly extensioned next (name.rom1) + Path::ReplaceExtension( Bios1, Bios, ext ); + if( (filesize=Path::getFileSize( Bios1 ) ) <= 0 ) + { + // Try for the old-style method (rom1.bin) + Path::Combine( Bios1, Config.BiosDir, ext ); + Bios1 += ".bin"; + if( (filesize=Path::getFileSize( Bios1 ) ) <= 0 ) + { + Console::Error( "\n\n\n" + "**************\n" + "%s NOT FOUND\n" + "**************\n\n\n", params ext + ); + return; + } + } + } + + // if we made it this far, we have a successful file found: + + FILE *fp = fopen(Bios1.c_str(), "rb"); + fread(dest, 1, std::min( maxSize, filesize ), fp); + fclose(fp); +} + +u32 psMPWC[(Ps2MemSize::Base/32)>>12]; +std::vector psMPWVA[Ps2MemSize::Base>>12]; + +u8 *psM = NULL; //32mb Main Ram +u8 *psR = NULL; //4mb rom area +u8 *psR1 = NULL; //256kb rom1 area (actually 196kb, but can't mask this) +u8 *psR2 = NULL; // 0x00080000 +u8 *psER = NULL; // 0x001C0000 +u8 *psS = NULL; //0.015 mb, scratch pad + +#define CHECK_MEM(mem) //MyMemCheck(mem) + +void MyMemCheck(u32 mem) +{ + if( mem == 0x1c02f2a0 ) + SysPrintf("yo\n"); +} + +///////////////////////////// +// REGULAR MEM START +///////////////////////////// +vtlbHandler tlb_fallback_0; +vtlbHandler tlb_fallback_1; +vtlbHandler tlb_fallback_2; +vtlbHandler tlb_fallback_3; +vtlbHandler tlb_fallback_4; +vtlbHandler tlb_fallback_5; +vtlbHandler tlb_fallback_6; +vtlbHandler tlb_fallback_7; +vtlbHandler tlb_fallback_8; + +vtlbHandler vu0_micro_mem[2]; // 0 - dynarec, 1 - interpreter +vtlbHandler vu1_micro_mem[2]; // 0 - dynarec, 1 - interpreter + +vtlbHandler hw_by_page[0x10]; + +// Used to remap the VUmicro memory according to the VU0/VU1 dynarec setting. +// (the VU memory operations are different for recs vs. interpreters) +void memMapVUmicro() +{ + vtlb_MapHandler(vu0_micro_mem[CHECK_VU0REC ? 0 : 1],0x11000000,0x00004000); + vtlb_MapHandler(vu1_micro_mem[CHECK_VU1REC ? 0 : 1],0x11008000,0x00004000); + + vtlb_MapBlock(VU0.Mem,0x11004000,0x00004000,0x1000); + vtlb_MapBlock(VU1.Mem,0x1100c000,0x00004000); +} + +void memMapPhy() +{ + //Main mem + vtlb_MapBlock(psM,0x00000000,Ps2MemSize::Base);//mirrored on first 256 mb ? + + //Rom + vtlb_MapBlock(psR,0x1fc00000,Ps2MemSize::Rom);//Writable ? + //Rom 1 + vtlb_MapBlock(psR1,0x1e000000,Ps2MemSize::Rom1);//Writable ? + //Rom 2 ? + vtlb_MapBlock(psR2,0x1e400000,Ps2MemSize::Rom2);//Writable ? + //EEProm ? + vtlb_MapBlock(psER,0x1e040000,Ps2MemSize::ERom);//Writable ? + + //IOP mem + vtlb_MapBlock(psxM,0x1c000000,0x00800000); + + //These fallback to mem* stuff ... + vtlb_MapHandler(tlb_fallback_1,0x10000000,0x10000); + vtlb_MapHandler(tlb_fallback_6,0x12000000,0x10000); + vtlb_MapHandler(tlb_fallback_7,0x14000000,0x10000); + vtlb_MapHandler(tlb_fallback_4,0x18000000,0x10000); + vtlb_MapHandler(tlb_fallback_5,0x1a000000,0x10000); + vtlb_MapHandler(tlb_fallback_8,0x1f000000,0x10000); + vtlb_MapHandler(tlb_fallback_3,0x1f400000,0x10000); + vtlb_MapHandler(tlb_fallback_2,0x1f800000,0x10000); + vtlb_MapHandler(tlb_fallback_8,0x1f900000,0x10000); + + // map specific optimized page handlers for HW accesses + vtlb_MapHandler(hw_by_page[0x0], 0x10000000, 0x01000); + vtlb_MapHandler(hw_by_page[0x1], 0x10001000, 0x01000); + vtlb_MapHandler(hw_by_page[0x2], 0x10002000, 0x01000); + vtlb_MapHandler(hw_by_page[0x3], 0x10003000, 0x01000); + vtlb_MapHandler(hw_by_page[0xb], 0x1000b000, 0x01000); + vtlb_MapHandler(hw_by_page[0xe], 0x1000e000, 0x01000); + vtlb_MapHandler(hw_by_page[0xf], 0x1000f000, 0x01000); +} + +//Why is this required ? +void memMapKernelMem() +{ + //lower 512 mb: direct map + //vtlb_VMap(0x00000000,0x00000000,0x20000000); + //0x8* mirror + vtlb_VMap(0x80000000,0x00000000,0x20000000); + //0xa* mirror + vtlb_VMap(0xA0000000,0x00000000,0x20000000); +} + +//what do do with these ? +void memMapSupervisorMem() +{ +} + +void memMapUserMem() +{ +} + +template +mem8_t __fastcall _ext_memRead8 (u32 mem) +{ + switch (p) + { + case 1: // hwm + return hwRead8(mem); + case 2: // psh + return psxHwRead8(mem); + case 3: // psh4 + return psxHw4Read8(mem); + case 6: // gsm + return gsRead8(mem); + case 7: // dev9 + { + mem8_t retval = DEV9read8(mem & ~0xa4000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0xa4000000, retval); + return retval; + } + } + + MEM_LOG("Unknown Memory Read8 from address %8.8x\n", mem); + cpuTlbMissR(mem, cpuRegs.branch); + return 0; +} + +template +mem16_t __fastcall _ext_memRead16(u32 mem) +{ + switch (p) + { + case 1: // hwm + return hwRead16(mem); + case 2: // psh + return psxHwRead16(mem); + case 4: // b80 + MEM_LOG("b800000 Memory read16 address %x\n", mem); + return 0; + case 5: // ba0 + return ba0R16(mem); + case 6: // gsm + return gsRead16(mem); + + case 7: // dev9 + { + mem16_t retval = DEV9read16(mem & ~0xa4000000); + SysPrintf("DEV9 read16 %8.8lx: %4.4lx\n", mem & ~0xa4000000, retval); + return retval; + } + + case 8: // spu2 + return SPU2read(mem); + } + MEM_LOG("Unknown Memory read16 from address %8.8x\n", mem); + cpuTlbMissR(mem, cpuRegs.branch); + return 0; +} + +template +mem32_t __fastcall _ext_memRead32(u32 mem) +{ + switch (p) + { + case 1: // hwm + return hwRead32_page_other(mem); + case 2: // psh + return psxHwRead32(mem); + case 6: // gsm + return gsRead32(mem); + case 7: // dev9 + { + mem32_t retval = DEV9read32(mem & ~0xa4000000); + SysPrintf("DEV9 read32 %8.8lx: %8.8lx\n", mem & ~0xa4000000, retval); + return retval; + } + } + + MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); + cpuTlbMissR(mem, cpuRegs.branch); + return 0; +} + +template +void __fastcall _ext_memRead64(u32 mem, mem64_t *out) +{ + switch (p) + { + case 1: // hwm + *out = hwRead64(mem); return; + case 6: // gsm + *out = gsRead64(mem); return; + } + + MEM_LOG("Unknown Memory read64 from address %8.8x\n", mem); + cpuTlbMissR(mem, cpuRegs.branch); +} + +template +void __fastcall _ext_memRead128(u32 mem, mem128_t *out) +{ + switch (p) + { + case 1: // hwm + hwRead128(mem & ~0xa0000000, out); return; + case 6: // gsm + out[0] = gsRead64(mem ); + out[1] = gsRead64(mem+8); return; + } + + MEM_LOG("Unknown Memory read128 from address %8.8x\n", mem); + cpuTlbMissR(mem, cpuRegs.branch); +} + +template +void __fastcall _ext_memWrite8 (u32 mem, u8 value) +{ + switch (p) { + case 1: // hwm + hwWrite8(mem, value); + return; + case 2: // psh + psxHwWrite8(mem, value); return; + case 3: // psh4 + psxHw4Write8(mem, value); return; + case 6: // gsm + gsWrite8(mem, value); return; + case 7: // dev9 + DEV9write8(mem & ~0xa4000000, value); + SysPrintf("DEV9 write8 %8.8lx: %2.2lx\n", mem & ~0xa4000000, value); + return; + } + + MEM_LOG("Unknown Memory write8 to address %x with data %2.2x\n", mem, value); + cpuTlbMissW(mem, cpuRegs.branch); +} +template +void __fastcall _ext_memWrite16(u32 mem, u16 value) +{ + switch (p) { + case 1: // hwm + hwWrite16(mem, value); + return; + case 2: // psh + psxHwWrite16(mem, value); return; + case 5: // ba0 + MEM_LOG("ba00000 Memory write16 to address %x with data %x\n", mem, value); + return; + case 6: // gsm + gsWrite16(mem, value); return; + case 7: // dev9 + DEV9write16(mem & ~0xa4000000, value); + SysPrintf("DEV9 write16 %8.8lx: %4.4lx\n", mem & ~0xa4000000, value); + return; + case 8: // spu2 + SPU2write(mem, value); return; + } + MEM_LOG("Unknown Memory write16 to address %x with data %4.4x\n", mem, value); + cpuTlbMissW(mem, cpuRegs.branch); +} +template +void __fastcall _ext_memWrite32(u32 mem, u32 value) +{ + switch (p) { + case 1: // hwm + hwWrite32_page_other(mem, value); return; + case 2: // psh + psxHwWrite32(mem, value); return; + case 6: // gsm + gsWrite32(mem, value); return; + case 7: // dev9 + DEV9write32(mem & ~0xa4000000, value); + SysPrintf("DEV9 write32 %8.8lx: %8.8lx\n", mem & ~0xa4000000, value); + return; + } + MEM_LOG("Unknown Memory write32 to address %x with data %8.8x\n", mem, value); + cpuTlbMissW(mem, cpuRegs.branch); +} +template +void __fastcall _ext_memWrite64(u32 mem, const u64* value) +{ + + switch (p) { + case 1: // hwm + hwWrite64(mem & ~0xa0000000, *value); + return; + case 6: // gsm + gsWrite64(mem & ~0xa0000000, *value); return; + } + + MEM_LOG("Unknown Memory write64 to address %x with data %8.8x_%8.8x\n", mem, (u32)(*value>>32), (u32)*value); + cpuTlbMissW(mem, cpuRegs.branch); +} +template +void __fastcall _ext_memWrite128(u32 mem, const u64 *value) +{ + switch (p) { + case 1: // hwm + hwWrite128(mem & ~0xa0000000, value); + return; + case 6: // gsm + mem &= ~0xa0000000; + gsWrite64(mem, value[0]); + gsWrite64(mem+8, value[1]); return; + } + + MEM_LOG("Unknown Memory write128 to address %x with data %8.8x_%8.8x_%8.8x_%8.8x\n", mem, ((u32*)value)[3], ((u32*)value)[2], ((u32*)value)[1], ((u32*)value)[0]); + cpuTlbMissW(mem, cpuRegs.branch); +} + +#define vtlb_RegisterHandlerTempl1(nam,t) vtlb_RegisterHandler(nam##Read8,nam##Read16,nam##Read32,nam##Read64,nam##Read128, \ + nam##Write8,nam##Write16,nam##Write32,nam##Write64,nam##Write128) + +#define vtlb_RegisterHandlerTempl2(nam,t,rec) vtlb_RegisterHandler(nam##Read8,nam##Read16,nam##Read32,nam##Read64,nam##Read128, \ + nam##Write8,nam##Write16,nam##Write32,nam##Write64,nam##Write128) + +typedef void __fastcall ClearFunc_t( u32 addr, u32 qwc ); + +template +static __forceinline void ClearVuFunc( u32 addr, u32 size ) +{ + if( dynarec ) + { + if( vunum==0 ) + VU0micro::recClear(addr,size); + else + VU1micro::recClear(addr,size); + } + else + { + if( vunum==0 ) + VU0micro::intClear(addr,size); + else + VU1micro::intClear(addr,size); + } +} + +template +mem8_t __fastcall vuMicroRead8(u32 addr) +{ + addr&=(vunum==0)?0xfff:0x3fff; + VURegs* vu=(vunum==0)?&VU0:&VU1; + + return vu->Micro[addr]; +} + +template +mem16_t __fastcall vuMicroRead16(u32 addr) +{ + addr&=(vunum==0)?0xfff:0x3fff; + VURegs* vu=(vunum==0)?&VU0:&VU1; + + return *(u16*)&vu->Micro[addr]; +} + +template +mem32_t __fastcall vuMicroRead32(u32 addr) +{ + addr&=(vunum==0)?0xfff:0x3fff; + VURegs* vu=(vunum==0)?&VU0:&VU1; + + return *(u32*)&vu->Micro[addr]; +} + +template +void __fastcall vuMicroRead64(u32 addr,mem64_t* data) +{ + addr&=(vunum==0)?0xfff:0x3fff; + VURegs* vu=(vunum==0)?&VU0:&VU1; + + *data=*(u64*)&vu->Micro[addr]; +} + +template +void __fastcall vuMicroRead128(u32 addr,mem128_t* data) +{ + addr&=(vunum==0)?0xfff:0x3fff; + VURegs* vu=(vunum==0)?&VU0:&VU1; + + data[0]=*(u64*)&vu->Micro[addr]; + data[1]=*(u64*)&vu->Micro[addr+8]; +} + +// [TODO] : Profile this code and see how often the VUs get written, and how +// often it changes the values being written (invoking a cpuClear). + +template +void __fastcall vuMicroWrite8(u32 addr,mem8_t data) +{ + addr &= (vunum==0) ? 0xfff : 0x3fff; + VURegs& vu = (vunum==0) ? VU0 : VU1; + + if (vu.Micro[addr]!=data) + { + ClearVuFunc(addr&(~7), 8); // Clear before writing new data (clearing 8 bytes because an instruction is 8 bytes) (cottonvibes) + vu.Micro[addr]=data; + } +} + +template +void __fastcall vuMicroWrite16(u32 addr,mem16_t data) +{ + addr &= (vunum==0) ? 0xfff : 0x3fff; + VURegs& vu = (vunum==0) ? VU0 : VU1; + + if (*(u16*)&vu.Micro[addr]!=data) + { + ClearVuFunc(addr&(~7), 8); + *(u16*)&vu.Micro[addr]=data; + } +} + +template +void __fastcall vuMicroWrite32(u32 addr,mem32_t data) +{ + addr &= (vunum==0) ? 0xfff : 0x3fff; + VURegs& vu = (vunum==0) ? VU0 : VU1; + + if (*(u32*)&vu.Micro[addr]!=data) + { + ClearVuFunc(addr&(~7), 8); + *(u32*)&vu.Micro[addr]=data; + } +} + +template +void __fastcall vuMicroWrite64(u32 addr,const mem64_t* data) +{ + addr &= (vunum==0) ? 0xfff : 0x3fff; + VURegs& vu = (vunum==0) ? VU0 : VU1; + + if (*(u64*)&vu.Micro[addr]!=data[0]) + { + ClearVuFunc(addr&(~7), 8); + *(u64*)&vu.Micro[addr]=data[0]; + } +} + +template +void __fastcall vuMicroWrite128(u32 addr,const mem128_t* data) +{ + addr &= (vunum==0) ? 0xfff : 0x3fff; + VURegs& vu = (vunum==0) ? VU0 : VU1; + + if (*(u64*)&vu.Micro[addr]!=data[0] || *(u64*)&vu.Micro[addr+8]!=data[1]) + { + ClearVuFunc(addr&(~7), 16); + *(u64*)&vu.Micro[addr]=data[0]; + *(u64*)&vu.Micro[addr+8]=data[1]; + } +} + +void memSetPageAddr(u32 vaddr, u32 paddr) +{ + //SysPrintf("memSetPageAddr: %8.8x -> %8.8x\n", vaddr, paddr); + + vtlb_VMap(vaddr,paddr,0x1000); + +} + +void memClearPageAddr(u32 vaddr) +{ + //SysPrintf("memClearPageAddr: %8.8x\n", vaddr); + + vtlb_VMapUnmap(vaddr,0x1000); // -> whut ? + +#ifdef FULLTLB + memLUTRK[vaddr >> 12] = 0; + memLUTWK[vaddr >> 12] = 0; +#endif +} + +/////////////////////////////////////////////////////////////////////////// +// PS2 Memory Init / Reset / Shutdown + +static const uint m_allMemSize = + Ps2MemSize::Rom + Ps2MemSize::Rom1 + Ps2MemSize::Rom2 + Ps2MemSize::ERom + + Ps2MemSize::Base + Ps2MemSize::Hardware + Ps2MemSize::Scratch; + +static u8* m_psAllMem = NULL; + +void memAlloc() +{ + if( m_psAllMem == NULL ) + m_psAllMem = vtlb_malloc( m_allMemSize, 4096, 0x2400000 ); + + if( m_psAllMem == NULL) + throw Exception::OutOfMemory( "memAlloc > failed to allocate PS2's base ram/rom/scratchpad." ); + + u8* curpos = m_psAllMem; + psM = curpos; curpos += Ps2MemSize::Base; + psR = curpos; curpos += Ps2MemSize::Rom; + psR1 = curpos; curpos += Ps2MemSize::Rom1; + psR2 = curpos; curpos += Ps2MemSize::Rom2; + psER = curpos; curpos += Ps2MemSize::ERom; + psH = curpos; curpos += Ps2MemSize::Hardware; + psS = curpos; //curpos += Ps2MemSize::Scratch; +} + +void memShutdown() +{ + vtlb_free( m_psAllMem, m_allMemSize ); + m_psAllMem = NULL; + psM = psR = psR1 = psR2 = psER = psS = psH = NULL; + vtlb_Term(); +} + +// Resets memory mappings, unmaps TLBs, reloads bios roms, etc. +void memReset() +{ + // VTLB Protection Preparations. + +#ifdef _WIN32 + DWORD OldProtect; + // make sure can write + VirtualProtect(m_psAllMem, m_allMemSize, PAGE_READWRITE, &OldProtect); +#else + mprotect(m_psAllMem, m_allMemSize, PROT_READ|PROT_WRITE); +#endif + + // Note!! Ideally the vtlb should only be initialized once, and then subsequent + // resets of the system hardware would only clear vtlb mappings, but since the + // rest of the emu is not really set up to support a "soft" reset of that sort + // we opt for the hard/safe version. + + memzero_ptr( m_psAllMem ); +#ifdef ENABLECACHE + memset(pCache,0,sizeof(_cacheS)*64); +#endif + + vtlb_Init(); + + tlb_fallback_0=vtlb_RegisterHandlerTempl1(_ext_mem,0); + tlb_fallback_2=vtlb_RegisterHandlerTempl1(_ext_mem,2); + tlb_fallback_3=vtlb_RegisterHandlerTempl1(_ext_mem,3); + tlb_fallback_4=vtlb_RegisterHandlerTempl1(_ext_mem,4); + tlb_fallback_5=vtlb_RegisterHandlerTempl1(_ext_mem,5); + tlb_fallback_6=vtlb_RegisterHandlerTempl1(_ext_mem,6); + tlb_fallback_7=vtlb_RegisterHandlerTempl1(_ext_mem,7); + tlb_fallback_8=vtlb_RegisterHandlerTempl1(_ext_mem,8); + + // Dynarec versions of VUs + vu0_micro_mem[0] = vtlb_RegisterHandlerTempl2(vuMicro,0,true); + vu1_micro_mem[0] = vtlb_RegisterHandlerTempl2(vuMicro,1,true); + + // Interpreter versions of VUs + vu0_micro_mem[1] = vtlb_RegisterHandlerTempl2(vuMicro,0,false); + vu1_micro_mem[1] = vtlb_RegisterHandlerTempl2(vuMicro,1,false); + + ////////////////////////////////////////////////////// + // psHw Optimized Mappings + // The HW Registers have been split into pages to improve optimization. + // Anything not explicitly mapped into one of the hw_by_page handlers will be handled + // by the default/generic tlb_fallback_1 handler. + + tlb_fallback_1 = vtlb_RegisterHandler( + _ext_memRead8<1>, _ext_memRead16<1>, hwRead32_page_other, _ext_memRead64<1>, _ext_memRead128<1>, + _ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_other, _ext_memWrite64<1>, _ext_memWrite128<1> + ); + + hw_by_page[0x0] = vtlb_RegisterHandler( + _ext_memRead8<1>, _ext_memRead16<1>, hwRead32_page_00, _ext_memRead64<1>, _ext_memRead128<1>, + _ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_00, _ext_memWrite64<1>, _ext_memWrite128<1> + ); + + hw_by_page[0x1] = vtlb_RegisterHandler( + _ext_memRead8<1>, _ext_memRead16<1>, hwRead32_page_01, _ext_memRead64<1>, _ext_memRead128<1>, + _ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_01, _ext_memWrite64<1>, _ext_memWrite128<1> + ); + + hw_by_page[0x2] = vtlb_RegisterHandler( + _ext_memRead8<1>, _ext_memRead16<1>, hwRead32_page_02, _ext_memRead64<1>, _ext_memRead128<1>, + _ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_02, _ext_memWrite64<1>, _ext_memWrite128<1> + ); + + hw_by_page[0x3] = vtlb_RegisterHandler( + _ext_memRead8<1>, _ext_memRead16<1>, hwRead32_page_other, _ext_memRead64<1>, _ext_memRead128<1>, + _ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_03, _ext_memWrite64<1>, _ext_memWrite128<1> + ); + + hw_by_page[0xb] = vtlb_RegisterHandler( + _ext_memRead8<1>, _ext_memRead16<1>, hwRead32_page_other, _ext_memRead64<1>, _ext_memRead128<1>, + _ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_0B, _ext_memWrite64<1>, _ext_memWrite128<1> + ); + + hw_by_page[0xe] = vtlb_RegisterHandler( + _ext_memRead8<1>, _ext_memRead16<1>, hwRead32_page_other, _ext_memRead64<1>, _ext_memRead128<1>, + _ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_0E, _ext_memWrite64<1>, _ext_memWrite128<1> + ); + + hw_by_page[0xf] = vtlb_RegisterHandler( + _ext_memRead8<1>, _ext_memRead16<1>, hwRead32_page_0F, _ext_memRead64<1>, _ext_memRead128<1>, + _ext_memWrite8<1>, _ext_memWrite16<1>, hwWrite32_page_0F, _ext_memWrite64<1>, _ext_memWrite128<1> + ); + + //vtlb_Reset(); + + // reset memLUT (?) + //vtlb_VMap(0x00000000,0x00000000,0x20000000); + //vtlb_VMapUnmap(0x20000000,0x60000000); + + memMapPhy(); + memMapVUmicro(); + memMapKernelMem(); + memMapSupervisorMem(); + memMapUserMem(); + memSetKernelMode(); + + vtlb_VMap(0x00000000,0x00000000,0x20000000); + vtlb_VMapUnmap(0x20000000,0x60000000); + + string Bios; + FILE *fp; + + Path::Combine( Bios, Config.BiosDir, Config.Bios ); + + long filesize; + if( ( filesize = Path::getFileSize( Bios ) ) <= 0 ) + { + //Console::Error("Unable to load bios: '%s', PCSX2 can't run without that", params Bios); + throw Exception::FileNotFound( Bios, + "The specified Bios file was not found. A bios is required for Pcsx2 to run.\n\nFile not found" ); + } + + fp = fopen(Bios.c_str(), "rb"); + fread(PS2MEM_ROM, 1, std::min( (long)Ps2MemSize::Rom, filesize ), fp); + fclose(fp); + + BiosVersion = GetBiosVersion(); + Console::Status("Bios Version %d.%d", params BiosVersion >> 8, BiosVersion & 0xff); + + //injectIRX("host.irx"); //not fully tested; still buggy + + loadBiosRom("rom1", PS2MEM_ROM1, Ps2MemSize::Rom1); + loadBiosRom("rom2", PS2MEM_ROM2, Ps2MemSize::Rom2); + loadBiosRom("erom", PS2MEM_EROM, Ps2MemSize::ERom); +} + +int mmap_GetRamPageInfo(void* ptr) +{ + u32 offset=((u8*)ptr-psM); + if (offset>=Ps2MemSize::Base) + return -1; //not in ram, no tracking done ... + offset>>=12; + return (psMPWC[(offset/32)]&(1<<(offset&31)))?1:0; +} + +void mmap_MarkCountedRamPage(void* ptr,u32 vaddr) +{ +#ifdef _WIN32 + DWORD old; + VirtualProtect(ptr,1,PAGE_READONLY,&old); +#else + // fixed? mprotect needs input and size to be aligned to 4096 bytes pagesize. + // 'ptr' should be aligned properly, but a size of 1 was invalid. (air) + mprotect(ptr, getpagesize(), PROT_READ); +#endif + + u32 offset=((u8*)ptr-psM); + offset>>=12; + + for (u32 i=0;i>12);i++) + { + psMPWVA[i].clear(); + } +#ifdef _WIN32 + DWORD old; + VirtualProtect(psM,Ps2MemSize::Base,PAGE_READWRITE,&old); +#else + mprotect(psM,Ps2MemSize::Base, PROT_READ|PROT_WRITE); +#endif +} + +#ifdef _WIN32 +int SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps) +{ + const _EXCEPTION_RECORD& ExceptionRecord = *eps->ExceptionRecord; + //const _CONTEXT& ContextRecord = *eps->ContextRecord; + + if (ExceptionRecord.ExceptionCode != EXCEPTION_ACCESS_VIOLATION) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // get bad virtual address + u32 offset = (u8*)ExceptionRecord.ExceptionInformation[1]-psM; + + if (offset>=Ps2MemSize::Base) + return EXCEPTION_CONTINUE_SEARCH; + + DWORD old; + VirtualProtect(&psM[offset],1,PAGE_READWRITE,&old); + + offset>>=12; + psMPWC[(offset/32)]|=(1<<(offset&31)); + + for (u32 i=0;iClear(psMPWVA[offset][i],0x1000); + } + psMPWVA[offset].clear(); + + return EXCEPTION_CONTINUE_EXECUTION; +} + +#else +#include "errno.h" + +void InstallLinuxExceptionHandler() +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = &SysPageFaultExceptionFilter; + sigaction(SIGSEGV, &sa, NULL); +} + +void ReleaseLinuxExceptionHandler() +{ + // Code this later. +} +// Linux implementation of SIGSEGV handler. Bind it using sigaction(). +// This is my shot in the dark. Probably needs some work. Good luck! (air) +void SysPageFaultExceptionFilter( int signal, siginfo_t *info, void * ) +{ + int err; + u32 pagesize = getpagesize(); + + //DevCon::Error("SysPageFaultExceptionFilter!"); + // get bad virtual address + u32 offset = (u8*)info->si_addr - psM; + uptr pageoffset = ( offset / pagesize ) * pagesize; + + DevCon::Status( "Protected memory cleanup. Offset 0x%x", params offset ); + + if (offset>=Ps2MemSize::Base) + { + // Bad mojo! Completly invalid address. + // Instigate a crash or abort emulation or something. + assert( false ); + } + + err = mprotect( &psM[pageoffset], pagesize, PROT_READ | PROT_WRITE ); + if (err) DevCon::Error("SysPageFaultExceptionFilter: %s", params strerror(errno)); + + offset>>=12; + psMPWC[(offset/32)]|=(1<<(offset&31)); + + for (u32 i=0;iClear(psMPWVA[offset][i],0x1000); + } + psMPWVA[offset].clear(); +} +#endif diff --git a/pcsx2/Memory.h b/pcsx2/Memory.h new file mode 100644 index 0000000000..a9c908c7eb --- /dev/null +++ b/pcsx2/Memory.h @@ -0,0 +1,303 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +////////// +// Rewritten by zerofrog to add os virtual memory +////////// + +#ifndef __MEMORY_H__ +#define __MEMORY_H__ + +#ifdef __LINUX__ +#include +#endif + +//#define ENABLECACHE + +namespace Ps2MemSize +{ + static const uint Base = 0x02000000; // 32 MB main memory! + static const uint Rom = 0x00400000; // 4 MB main rom + static const uint Rom1 = 0x00040000; // DVD player + static const uint Rom2 = 0x00080000; // Chinese rom extension (?) + static const uint ERom = 0x001C0000; // DVD player extensions (?) + static const uint Hardware = 0x00010000; + static const uint Scratch = 0x00004000; + + static const uint IopRam = 0x00200000; // 2MB main ram on the IOP. + static const uint IopHardware = 0x00010000; + + static const uint GSregs = 0x00002000; // 8k for the GS registers and stuff. +} + +extern u8 *psM; //32mb Main Ram +extern u8 *psR; //4mb rom area +extern u8 *psR1; //256kb rom1 area (actually 196kb, but can't mask this) +extern u8 *psR2; // 0x00080000 +extern u8 *psER; // 0x001C0000 +extern u8 *psS; //0.015 mb, scratch pad + +#define PS2MEM_BASE psM +#define PS2MEM_HW psH +#define PS2MEM_ROM psR +#define PS2MEM_ROM1 psR1 +#define PS2MEM_ROM2 psR2 +#define PS2MEM_EROM psER +#define PS2MEM_SCRATCH psS + +extern u8 g_RealGSMem[0x2000]; +#define PS2MEM_GS g_RealGSMem + +//#define _PSM(mem) (memLUTR[(mem) >> 12] == 0 ? NULL : (void*)(memLUTR[(mem) >> 12] + ((mem) & 0xfff))) +#define PSM(mem) (vtlb_GetPhyPtr(mem&0x1fffffff)) //pcsx2 is a competition.The one with most hacks wins :D + +#define psMs8(mem) (*(s8 *)&PS2MEM_BASE[(mem) & 0x1ffffff]) +#define psMs16(mem) (*(s16*)&PS2MEM_BASE[(mem) & 0x1ffffff]) +#define psMs32(mem) (*(s32*)&PS2MEM_BASE[(mem) & 0x1ffffff]) +#define psMs64(mem) (*(s64*)&PS2MEM_BASE[(mem) & 0x1ffffff]) +#define psMu8(mem) (*(u8 *)&PS2MEM_BASE[(mem) & 0x1ffffff]) +#define psMu16(mem) (*(u16*)&PS2MEM_BASE[(mem) & 0x1ffffff]) +#define psMu32(mem) (*(u32*)&PS2MEM_BASE[(mem) & 0x1ffffff]) +#define psMu64(mem) (*(u64*)&PS2MEM_BASE[(mem) & 0x1ffffff]) + +#define psRs8(mem) (*(s8 *)&PS2MEM_ROM[(mem) & 0x3fffff]) +#define psRs16(mem) (*(s16*)&PS2MEM_ROM[(mem) & 0x3fffff]) +#define psRs32(mem) (*(s32*)&PS2MEM_ROM[(mem) & 0x3fffff]) +#define psRs64(mem) (*(s64*)&PS2MEM_ROM[(mem) & 0x3fffff]) +#define psRu8(mem) (*(u8 *)&PS2MEM_ROM[(mem) & 0x3fffff]) +#define psRu16(mem) (*(u16*)&PS2MEM_ROM[(mem) & 0x3fffff]) +#define psRu32(mem) (*(u32*)&PS2MEM_ROM[(mem) & 0x3fffff]) +#define psRu64(mem) (*(u64*)&PS2MEM_ROM[(mem) & 0x3fffff]) + +#define psR1s8(mem) (*(s8 *)&PS2MEM_ROM1[(mem) & 0x3ffff]) +#define psR1s16(mem) (*(s16*)&PS2MEM_ROM1[(mem) & 0x3ffff]) +#define psR1s32(mem) (*(s32*)&PS2MEM_ROM1[(mem) & 0x3ffff]) +#define psR1s64(mem) (*(s64*)&PS2MEM_ROM1[(mem) & 0x3ffff]) +#define psR1u8(mem) (*(u8 *)&PS2MEM_ROM1[(mem) & 0x3ffff]) +#define psR1u16(mem) (*(u16*)&PS2MEM_ROM1[(mem) & 0x3ffff]) +#define psR1u32(mem) (*(u32*)&PS2MEM_ROM1[(mem) & 0x3ffff]) +#define psR1u64(mem) (*(u64*)&PS2MEM_ROM1[(mem) & 0x3ffff]) + +#define psR2s8(mem) (*(s8 *)&PS2MEM_ROM2[(mem) & 0x3ffff]) +#define psR2s16(mem) (*(s16*)&PS2MEM_ROM2[(mem) & 0x3ffff]) +#define psR2s32(mem) (*(s32*)&PS2MEM_ROM2[(mem) & 0x3ffff]) +#define psR2s64(mem) (*(s64*)&PS2MEM_ROM2[(mem) & 0x3ffff]) +#define psR2u8(mem) (*(u8 *)&PS2MEM_ROM2[(mem) & 0x3ffff]) +#define psR2u16(mem) (*(u16*)&PS2MEM_ROM2[(mem) & 0x3ffff]) +#define psR2u32(mem) (*(u32*)&PS2MEM_ROM2[(mem) & 0x3ffff]) +#define psR2u64(mem) (*(u64*)&PS2MEM_ROM2[(mem) & 0x3ffff]) + +#define psERs8(mem) (*(s8 *)&PS2MEM_EROM[(mem) & 0x3ffff]) +#define psERs16(mem) (*(s16*)&PS2MEM_EROM[(mem) & 0x3ffff]) +#define psERs32(mem) (*(s32*)&PS2MEM_EROM[(mem) & 0x3ffff]) +#define psERs64(mem) (*(s64*)&PS2MEM_EROM[(mem) & 0x3ffff]) +#define psERu8(mem) (*(u8 *)&PS2MEM_EROM[(mem) & 0x3ffff]) +#define psERu16(mem) (*(u16*)&PS2MEM_EROM[(mem) & 0x3ffff]) +#define psERu32(mem) (*(u32*)&PS2MEM_EROM[(mem) & 0x3ffff]) +#define psERu64(mem) (*(u64*)&PS2MEM_EROM[(mem) & 0x3ffff]) + +#define psSs8(mem) (*(s8 *)&PS2MEM_SCRATCH[(mem) & 0x3fff]) +#define psSs16(mem) (*(s16*)&PS2MEM_SCRATCH[(mem) & 0x3fff]) +#define psSs32(mem) (*(s32*)&PS2MEM_SCRATCH[(mem) & 0x3fff]) +#define psSs64(mem) (*(s64*)&PS2MEM_SCRATCH[(mem) & 0x3fff]) +#define psSu8(mem) (*(u8 *)&PS2MEM_SCRATCH[(mem) & 0x3fff]) +#define psSu16(mem) (*(u16*)&PS2MEM_SCRATCH[(mem) & 0x3fff]) +#define psSu32(mem) (*(u32*)&PS2MEM_SCRATCH[(mem) & 0x3fff]) +#define psSu64(mem) (*(u64*)&PS2MEM_SCRATCH[(mem) & 0x3fff]) + +#define PSMs8(mem) (*(s8 *)PSM(mem)) +#define PSMs16(mem) (*(s16*)PSM(mem)) +#define PSMs32(mem) (*(s32*)PSM(mem)) +#define PSMs64(mem) (*(s64*)PSM(mem)) +#define PSMu8(mem) (*(u8 *)PSM(mem)) +#define PSMu16(mem) (*(u16*)PSM(mem)) +#define PSMu32(mem) (*(u32*)PSM(mem)) +#define PSMu64(mem) (*(u64*)PSM(mem)) + +extern void memAlloc(); +extern void memReset(); // clears PS2 ram and loads the bios. Throws Exception::FileNotFound on error. +extern void memShutdown(); +extern void memSetKernelMode(); +extern void memSetSupervisorMode(); +extern void memSetUserMode(); +extern void memSetPageAddr(u32 vaddr, u32 paddr); +extern void memClearPageAddr(u32 vaddr); + +extern void memMapVUmicro(); + +#ifdef __LINUX__ +void SysPageFaultExceptionFilter( int signal, siginfo_t *info, void * ); +void __fastcall InstallLinuxExceptionHandler(); +void __fastcall ReleaseLinuxExceptionHandler(); +#else +int SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps); +#endif + +#include "vtlb.h" + +int mmap_GetRamPageInfo(void* ptr); +void mmap_MarkCountedRamPage(void* ptr,u32 vaddr); +void mmap_ResetBlockTracking(); + +extern void __fastcall memRead8(u32 mem, u8 *out); +extern void __fastcall memRead16(u32 mem, u16 *out); +extern void __fastcall memRead32(u32 mem, u32 *out); + +#define memRead64 vtlb_memRead64 +#define memRead128 vtlb_memRead128 + +#define memWrite8 vtlb_memWrite8 +#define memWrite16 vtlb_memWrite16 +#define memWrite32 vtlb_memWrite32 +#define memWrite64 vtlb_memWrite64 +#define memWrite128 vtlb_memWrite128 + +#define _eeReadConstMem8 0&& +#define _eeReadConstMem16 0&& +#define _eeReadConstMem32 0&& +#define _eeReadConstMem128 0&& +#define _eeWriteConstMem8 0&& +#define _eeWriteConstMem16 0&& +#define _eeWriteConstMem32 0&& +#define _eeWriteConstMem64 0&& +#define _eeWriteConstMem128 0&& +#define _eeMoveMMREGtoR 0&& + +// extra ops +#define _eeWriteConstMem16OP 0&& +#define _eeWriteConstMem32OP 0&& + +#define recMemConstRead8 0&& +#define recMemConstRead16 0&& +#define recMemConstRead32 0&& +#define recMemConstRead64 0&& +#define recMemConstRead128 0&& + +#define recMemConstWrite8 0&& +#define recMemConstWrite16 0&& +#define recMemConstWrite32 0&& +#define recMemConstWrite64 0&& +#define recMemConstWrite128 0&& + +extern void loadBiosRom( const char *ext, u8 *dest, long maxSize ); +extern u16 ba0R16(u32 mem); + +////////////////////////////////////////////////////////////////////////// +// The rest of this header contains the old VM version of the Memory.h API. +// Left in for references purposes. + +#ifdef PCSX2_VIRTUAL_MEM + +#define PS2MEM_BASE_ 0x15000000 +#define PS2MEM_PSX_ (PS2MEM_BASE_+0x1c000000) + +#ifdef _WIN32 +struct PSMEMORYMAP +{ + uptr* aPFNs, *aVFNs; +}; +#endif + +#define TRANSFORM_ADDR(memaddr) ( ((u32)(memaddr)>=0x40000000) ? ((memaddr)&~0xa0000000) : (memaddr) ) + +//new memory model +#define PS2MEM_BASE ((u8*)PS2MEM_BASE_) +#define PS2MEM_HW ((u8*)((u32)PS2MEM_BASE+0x10000000)) +#define PS2MEM_ROM ((u8*)((u32)PS2MEM_BASE+0x1fc00000)) +#define PS2MEM_ROM1 ((u8*)((u32)PS2MEM_BASE+0x1e000000)) +#define PS2MEM_ROM2 ((u8*)((u32)PS2MEM_BASE+0x1e400000)) +#define PS2MEM_EROM ((u8*)((u32)PS2MEM_BASE+0x1e040000)) +#define PS2MEM_PSX ((u8*)PS2MEM_PSX_) +#define PS2MEM_SCRATCH ((u8*)((u32)PS2MEM_BASE+0x50000000)) +#define PS2MEM_VU0MICRO ((u8*)((u32)PS2MEM_BASE+0x11000000)) +#define PS2MEM_VU0MEM ((u8*)((u32)PS2MEM_BASE+0x11004000)) +#define PS2MEM_VU1MICRO ((u8*)((u32)PS2MEM_BASE+0x11008000)) +#define PS2MEM_VU1MEM ((u8*)((u32)PS2MEM_BASE+0x1100c000)) + +// function for mapping memory +#define PS2MEM_PSXHW ((u8*)((u32)PS2MEM_BASE+0x1f800000)) +//#define PS2MEM_PSXHW2 ((u8*)((u32)PS2MEM_BASE+0x1fa00000)) +#define PS2MEM_PSXHW4 ((u8*)((u32)PS2MEM_BASE+0x1f400000)) +#define PS2MEM_GS ((u8*)((u32)PS2MEM_BASE+0x12000000)) +#define PS2MEM_DEV9 ((u8*)((u32)PS2MEM_BASE+0x14000000)) +#define PS2MEM_SPU2 ((u8*)((u32)PS2MEM_BASE+0x1f900000)) +#define PS2MEM_SPU2_ ((u8*)((u32)PS2MEM_BASE+0x1f000000)) // ? +#define PS2MEM_B80 ((u8*)((u32)PS2MEM_BASE+0x18000000)) +#define PS2MEM_BA0 ((u8*)((u32)PS2MEM_BASE+0x1a000000)) + +#define PSM(mem) (PS2MEM_BASE + TRANSFORM_ADDR(mem)) + +int __fastcall memRead8(u32 mem, u8 *out); +int __fastcall memRead8RS(u32 mem, u64 *out); +int __fastcall memRead8RU(u32 mem, u64 *out); +int __fastcall memRead16(u32 mem, u16 *out); +int __fastcall memRead16RS(u32 mem, u64 *out); +int __fastcall memRead16RU(u32 mem, u64 *out); +int __fastcall memRead32(u32 mem, u32 *out); +int __fastcall memRead32RS(u32 mem, u64 *out); +int __fastcall memRead32RU(u32 mem, u64 *out); +int __fastcall memRead64(u32 mem, u64 *out); +int __fastcall memRead128(u32 mem, u64 *out); +void __fastcall memWrite8 (u32 mem, u8 value); +void __fastcall memWrite16(u32 mem, u16 value); +void __fastcall memWrite32(u32 mem, u32 value); +void __fastcall memWrite64(u32 mem, const u64 *value); +void __fastcall memWrite128(u32 mem, const u64 *value); + +// recMemConstRead8, recMemConstRead16, recMemConstRead32 return 1 if a call was made, 0 otherwise +u8 recMemRead8(); +u16 recMemRead16(); +u32 recMemRead32(); +void recMemRead64(u64 *out); +void recMemRead128(u64 *out); + +void recMemWrite8(); +void recMemWrite16(); +void recMemWrite32(); +void recMemWrite64(); +void recMemWrite128(); + +void _eeReadConstMem8(int mmreg, u32 mem, int sign); +void _eeReadConstMem16(int mmreg, u32 mem, int sign); +void _eeReadConstMem32(int mmreg, u32 mem); +void _eeReadConstMem128(int mmreg, u32 mem); +void _eeWriteConstMem8(u32 mem, int mmreg); +void _eeWriteConstMem16(u32 mem, int mmreg); +void _eeWriteConstMem32(u32 mem, int mmreg); +void _eeWriteConstMem64(u32 mem, int mmreg); +void _eeWriteConstMem128(u32 mem, int mmreg); +void _eeMoveMMREGtoR(int to, int mmreg); + +// extra ops +void _eeWriteConstMem16OP(u32 mem, int mmreg, int op); +void _eeWriteConstMem32OP(u32 mem, int mmreg, int op); + +int recMemConstRead8(u32 x86reg, u32 mem, u32 sign); +int recMemConstRead16(u32 x86reg, u32 mem, u32 sign); +int recMemConstRead32(u32 x86reg, u32 mem); +void recMemConstRead64(u32 mem, int mmreg); +void recMemConstRead128(u32 mem, int xmmreg); + +int recMemConstWrite8(u32 mem, int mmreg); +int recMemConstWrite16(u32 mem, int mmreg); +int recMemConstWrite32(u32 mem, int mmreg); +int recMemConstWrite64(u32 mem, int mmreg); +int recMemConstWrite128(u32 mem, int xmmreg); +#endif + +#endif diff --git a/pcsx2/MemoryVM.cpp b/pcsx2/MemoryVM.cpp new file mode 100644 index 0000000000..9f67252b1a --- /dev/null +++ b/pcsx2/MemoryVM.cpp @@ -0,0 +1,2140 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// Virtual memory model for Pcsx2. +// This module is left in primarily as a reference for the implementation of constant +// propagation. + +#include "PrecompiledHeader.h" +#include "Common.h" +#include "iR5900.h" + +#include "PsxCommon.h" +#include "VUmicro.h" +#include "GS.h" +#include "vtlb.h" +#include "IPU/IPU.h" + +#ifdef PCSX2_VIRTUAL_MEM +#include "iR3000A.h" // VM handles both Iop and EE memory from here. >_< +#include "Counters.h" +#endif + +#pragma warning(disable:4799) // No EMMS at end of function + +#ifdef ENABLECACHE +#include "Cache.h" +#endif + +#ifdef __LINUX__ +#include +#endif + +///////////////////////////// +// VIRTUAL MEM START +///////////////////////////// +#ifdef PCSX2_VIRTUAL_MEM + +class vm_alloc_failed_exception : public std::runtime_error +{ +public: + void* requested_addr; + int requested_size; + void* returned_addr; + + explicit vm_alloc_failed_exception( void* reqadr, uint reqsize, void* retadr ) : + std::runtime_error( "virtual memory allocation failure." ) + , requested_addr( reqadr ) + , requested_size( reqsize ) + , returned_addr( retadr ) + {} +}; + +PSMEMORYBLOCK s_psM = {0}, s_psHw = {0}, s_psS = {0}, s_psxM = {0}, s_psVuMem = {0}; + +static void PHYSICAL_ALLOC( void* ptr, uint size, PSMEMORYBLOCK& block) +{ + if(SysPhysicalAlloc(size, &block) == -1 ) + throw vm_alloc_failed_exception( ptr, size, NULL ); + if(SysVirtualPhyAlloc(ptr, size, &block) == -1) + throw vm_alloc_failed_exception( ptr, size, NULL ); +} + +static void PHYSICAL_FREE( void* ptr, uint size, PSMEMORYBLOCK& block) +{ + SysVirtualFree(ptr, size); + SysPhysicalFree(&block); +} + + +#ifdef _WIN32 // windows implementation of vm + +static PSMEMORYMAP initMemoryMap(uptr* aPFNs, uptr* aVFNs) +{ + PSMEMORYMAP m; + m.aPFNs = aPFNs; + m.aVFNs = aVFNs; + return m; +} + +// only do vm hack for release +#ifndef PCSX2_DEVBUILD +#define VM_HACK +#endif + +// virtual memory blocks +PSMEMORYMAP *memLUT = NULL; + +static void VIRTUAL_ALLOC( void* base, uint size, uint Protection) +{ + LPVOID lpMemReserved = VirtualAlloc( base, size, MEM_RESERVE|MEM_COMMIT, Protection ); + if( base != lpMemReserved ) + throw vm_alloc_failed_exception( base, size, lpMemReserved ); +} + +static void ReserveExtraMem( void* base, uint size ) +{ + void* pExtraMem = VirtualAlloc(base, size, MEM_RESERVE|MEM_PHYSICAL, PAGE_READWRITE); + if( pExtraMem != base ) + throw vm_alloc_failed_exception( base, size, pExtraMem); +} + +void memAlloc() +{ + LPVOID pExtraMem = NULL; + + // release the previous reserved mem + VirtualFree(PS2MEM_BASE, 0, MEM_RELEASE); + + try + { + // allocate all virtual memory + PHYSICAL_ALLOC(PS2MEM_BASE, Ps2MemSize::Base, s_psM); + VIRTUAL_ALLOC(PS2MEM_ROM, Ps2MemSize::Rom, PAGE_READONLY); + VIRTUAL_ALLOC(PS2MEM_ROM1, Ps2MemSize::Rom1, PAGE_READONLY); + VIRTUAL_ALLOC(PS2MEM_ROM2, Ps2MemSize::Rom2, PAGE_READONLY); + VIRTUAL_ALLOC(PS2MEM_EROM, Ps2MemSize::ERom, PAGE_READONLY); + PHYSICAL_ALLOC(PS2MEM_SCRATCH, Ps2MemSize::Scratch, s_psS); + PHYSICAL_ALLOC(PS2MEM_HW, Ps2MemSize::Hardware, s_psHw); + PHYSICAL_ALLOC(PS2MEM_PSX, Ps2MemSize::IopRam, s_psxM); + PHYSICAL_ALLOC(PS2MEM_VU0MICRO, 0x00010000, s_psVuMem); + + VIRTUAL_ALLOC(PS2MEM_PSXHW, Ps2MemSize::IopHardware, PAGE_READWRITE); + //VIRTUAL_ALLOC(PS2MEM_PSXHW2, 0x00010000, PAGE_READWRITE); + VIRTUAL_ALLOC(PS2MEM_PSXHW4, 0x00010000, PAGE_NOACCESS); + VIRTUAL_ALLOC(PS2MEM_GS, 0x00002000, PAGE_READWRITE); + VIRTUAL_ALLOC(PS2MEM_DEV9, 0x00010000, PAGE_NOACCESS); + VIRTUAL_ALLOC(PS2MEM_SPU2, 0x00010000, PAGE_NOACCESS); + VIRTUAL_ALLOC(PS2MEM_SPU2_, 0x00010000, PAGE_NOACCESS); + + VIRTUAL_ALLOC(PS2MEM_B80, 0x00010000, PAGE_READWRITE); + VIRTUAL_ALLOC(PS2MEM_BA0, 0x00010000, PAGE_READWRITE); + + // reserve the left over 224Mb, don't map + ReserveExtraMem( PS2MEM_BASE+Ps2MemSize::Base, 0x0e000000 ); + + // reserve left over psx mem + ReserveExtraMem( PS2MEM_PSX+Ps2MemSize::IopRam, 0x00600000 ); + + // reserve gs mem + ReserveExtraMem( PS2MEM_BASE+0x20000000, 0x10000000 ); + + // special addrs mmap + VIRTUAL_ALLOC(PS2MEM_BASE+0x5fff0000, 0x10000, PAGE_READWRITE); + + // alloc virtual mappings + if( memLUT == NULL ) + memLUT = (PSMEMORYMAP*)_aligned_malloc(0x100000 * sizeof(PSMEMORYMAP), 16); + if( memLUT == NULL ) + throw Exception::OutOfMemory( "memAlloc VM > failed to allocated memory for LUT." ); + } + catch( vm_alloc_failed_exception& ex ) + { + Console::Error( "Virtual Memory Error > Cannot reserve %dk memory block at 0x%8.8x", params + ex.requested_size / 1024, ex.requested_addr ); + + Console::Error( "\tError code: %d \tReturned address: 0x%8.8x", params + GetLastError(), ex.returned_addr); + + memShutdown(); + } + catch( std::exception& ) + { + memShutdown(); + } +} + +void memShutdown() +{ + // Free up the "extra mem" reservations + VirtualFree(PS2MEM_BASE+Ps2MemSize::Base, 0, MEM_RELEASE); + VirtualFree(PS2MEM_PSX+Ps2MemSize::IopRam, 0, MEM_RELEASE); + VirtualFree(PS2MEM_BASE+0x20000000, 0, MEM_RELEASE); // GS reservation + + PHYSICAL_FREE(PS2MEM_BASE, Ps2MemSize::Base, s_psM); + SysMunmap(PS2MEM_ROM, Ps2MemSize::Rom); + SysMunmap(PS2MEM_ROM1, Ps2MemSize::Rom1); + SysMunmap(PS2MEM_ROM2, Ps2MemSize::Rom2); + SysMunmap(PS2MEM_EROM, Ps2MemSize::ERom); + PHYSICAL_FREE(PS2MEM_SCRATCH, Ps2MemSize::Scratch, s_psS); + PHYSICAL_FREE(PS2MEM_HW, Ps2MemSize::Hardware, s_psHw); + PHYSICAL_FREE(PS2MEM_PSX, Ps2MemSize::IopRam, s_psxM); + PHYSICAL_FREE(PS2MEM_VU0MICRO, 0x00010000, s_psVuMem); + + SysMunmap(PS2MEM_VU0MICRO, 0x00010000); // allocate for all VUs + + SysMunmap(PS2MEM_PSXHW, Ps2MemSize::IopHardware); + //SysMunmap(PS2MEM_PSXHW2, 0x00010000); + SysMunmap(PS2MEM_PSXHW4, 0x00010000); + SysMunmap(PS2MEM_GS, 0x00002000); + SysMunmap(PS2MEM_DEV9, 0x00010000); + SysMunmap(PS2MEM_SPU2, 0x00010000); + SysMunmap(PS2MEM_SPU2_, 0x00010000); + + SysMunmap(PS2MEM_B80, 0x00010000); + SysMunmap(PS2MEM_BA0, 0x00010000); + + // Special Addrs.. ? + SysMunmap(PS2MEM_BASE+0x5fff0000, 0x10000); + + VirtualFree(PS2MEM_VU0MICRO, 0, MEM_RELEASE); + + safe_aligned_free( memLUT ); + + // reserve mem + VirtualAlloc(PS2MEM_BASE, 0x40000000, MEM_RESERVE, PAGE_NOACCESS); +} + +//NOTE: A lot of the code reading depends on the registers being less than 8 +// MOV8 88/8A +// MOV16 6689 +// MOV32 89/8B +// SSEMtoR64 120f +// SSERtoM64 130f +// SSEMtoR128 280f +// SSERtoM128 290f + +#define SKIP_WRITE() { \ + switch(code&0xff) { \ + case 0x88: \ + if( !(code&0x8000) ) goto DefaultHandler; \ + ContextRecord->Eip += 6; \ + break; \ + case 0x66: \ + assert( code&0x800000 ); \ + assert( (code&0xffff) == 0x8966 ); \ + ContextRecord->Eip += 7; \ + break; \ + case 0x89: \ + assert( code&0x8000 ); \ + ContextRecord->Eip += 6; \ + break; \ + case 0x0f: /* 130f, 230f*/ \ + assert( (code&0xffff) == 0x290f || (code&0xffff) == 0x130f ); \ + assert( code&0x800000 ); \ + ContextRecord->Eip += 7; \ + break; \ + default: \ + goto DefaultHandler; \ +} \ +} \ + +#define SKIP_READ() { \ + switch(code&0xff) { \ + case 0x8A: \ + if( !(code&0x8000) ) goto DefaultHandler; \ + ContextRecord->Eip += 6; \ + rd = (code>>(8+3))&7; \ + break; \ + case 0x66: \ + if( (code&0x07000000) == 0x05000000 ) ContextRecord->Eip += 8; /* 8 for mem reads*/ \ + else ContextRecord->Eip += 4 + ((code&0x1f000000) == 0x0c000000) + !!(code&0x40000000); \ + rd = (code>>(24+3))&7; \ + break; \ + case 0x8B: \ + if( !(code&0x8000) ) goto DefaultHandler; \ + ContextRecord->Eip += 6; \ + rd = (code>>(8+3))&7; \ + break; \ + case 0x0f: { \ + assert( (code&0xffff)==0x120f || (code&0xffff)==0x280f || (code&0xffff) == 0xb60f || (code&0xffff) == 0xb70f ); \ + if( !(code&0x800000) ) goto DefaultHandler; \ + ContextRecord->Eip += 7; \ + rd = (code>>(16+3))&7; \ + break; } \ + default: \ + goto DefaultHandler; \ +} \ +} \ + +int SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps) +{ + struct _EXCEPTION_RECORD* ExceptionRecord = eps->ExceptionRecord; + struct _CONTEXT* ContextRecord = eps->ContextRecord; + + u32 addr; + + C_ASSERT(sizeof(ContextRecord->Eax) == 4); + + // If the exception is not a page fault, exit. + if (ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // get bad virtual address + addr = (u32)ExceptionRecord->ExceptionInformation[1]; + + if( (unsigned)(addr-(u32)PS2MEM_BASE) < 0x60000000) { + PSMEMORYMAP* pmap; + + pmap = &memLUT[(addr-(u32)PS2MEM_BASE)>>12]; + + if( !pmap->aPFNs ) { + // NOTE: this is a hack because the address is truncated and there's no way + // to tell what it's upper bits are (due to OS limitations). + pmap += 0x80000; + if( !pmap->aPFNs ) { + pmap += 0x20000; + if( !pmap->aPFNs ) goto OtherException; + } + //else addr += 0x20000000; + } + + { + //LPVOID pnewaddr; not used + uptr curvaddr = pmap->aVFNs[0]; + + if( curvaddr ) { + // delete the current mapping + SysMapUserPhysicalPages((void*)curvaddr, 1, NULL, 0); + } + + assert( pmap->aPFNs[0] != 0 ); + + pmap->aVFNs[0] = curvaddr = addr&~0xfff; + if( SysMapUserPhysicalPages((void*)curvaddr, 1, pmap->aPFNs, 0) ) + return EXCEPTION_CONTINUE_EXECUTION; + + // try allocing the virtual mem + //pnewaddr = <- not used + /* use here the size of allocation granularity and force rounding down, + because in reserve mode the address is rounded up/down to the nearest + multiple of this granularity; if you did it not this way, in some cases + the same address would be used twice, so the api fails */ + VirtualAlloc((void*)(curvaddr&~0xffff), 0x10000, MEM_RESERVE|MEM_PHYSICAL, PAGE_READWRITE); + + if( SysMapUserPhysicalPages((void*)curvaddr, 1, pmap->aPFNs, 0) ) + return EXCEPTION_CONTINUE_EXECUTION; + + Console::Error("Virtual Memory Error > page 0x%x cannot be found %d (p:%x,v:%x)", params + addr-(u32)PS2MEM_BASE, GetLastError(), pmap->aPFNs[0], curvaddr); + } + } + // check if vumem + else if( (addr&0xffff4000) == 0x11000000 ) { + // vu0mem + SysMapUserPhysicalPages((void*)s_psVuMem.aVFNs[1], 1, NULL, 0); + + s_psVuMem.aVFNs[1] = addr&~0xfff; + SysMapUserPhysicalPages((void*)addr, 1, s_psVuMem.aPFNs, 1); + + //SysPrintf("Exception: vumem\n"); + return EXCEPTION_CONTINUE_EXECUTION; + } +OtherException: + +#ifdef VM_HACK + { + u32 code = *(u32*)ExceptionRecord->ExceptionAddress; + u32 rd = 0; + + if( ExceptionRecord->ExceptionInformation[0] ) { + //SKIP_WRITE(); + // shouldn't be writing + } + else { + SysPrintf("vmhack "); + SKIP_READ(); + //((u32*)&ContextRecord->Eax)[rd] = 0; + return EXCEPTION_CONTINUE_EXECUTION; // TODO: verify this!!! + } + } +DefaultHandler: +#endif + + return EXCEPTION_CONTINUE_SEARCH; +} + +#else // linux implementation + +#define VIRTUAL_ALLOC(base, size, Protection) { \ + void* lpMemReserved = mmap( base, size, Protection, MAP_PRIVATE|MAP_ANONYMOUS ); \ + if( lpMemReserved == NULL || base != lpMemReserved ) \ +{ \ + SysPrintf("Cannot reserve memory at 0x%8.8x(%x).\n", base, lpMemReserved); \ + perror("err"); \ + goto eCleanupAndExit; \ +} \ +} \ + +#define VIRTUAL_FREE(ptr, size) munmap(ptr, size) + +uptr *memLUT = NULL; + +void memAlloc() +{ + int i; + LPVOID pExtraMem = NULL; + + // release the previous reserved mem + munmap(PS2MEM_BASE, 0x40000000); + + // allocate all virtual memory + PHYSICAL_ALLOC(PS2MEM_BASE, Ps2MemSize::Base, s_psM); + VIRTUAL_ALLOC(PS2MEM_ROM, Ps2MemSize::Rom, PROT_READ); + VIRTUAL_ALLOC(PS2MEM_ROM1, Ps2MemSize::Rom1, PROT_READ); + VIRTUAL_ALLOC(PS2MEM_ROM2, Ps2MemSize::Rom2, PROT_READ); + VIRTUAL_ALLOC(PS2MEM_EROM, Ps2MemSize::ERom, PROT_READ); + PHYSICAL_ALLOC(PS2MEM_SCRATCH, 0x00010000, s_psS); + PHYSICAL_ALLOC(PS2MEM_HW, Ps2MemSize::Hardware, s_psHw); + PHYSICAL_ALLOC(PS2MEM_PSX, Ps2MemSize::IopRam, s_psxM); + PHYSICAL_ALLOC(PS2MEM_VU0MICRO, 0x00010000, s_psVuMem); + + VIRTUAL_ALLOC(PS2MEM_PSXHW, Ps2MemSize::IopHardware, PROT_READ|PROT_WRITE); + VIRTUAL_ALLOC(PS2MEM_PSXHW4, 0x00010000, PROT_NONE); + VIRTUAL_ALLOC(PS2MEM_GS, 0x00002000, PROT_READ|PROT_WRITE); + VIRTUAL_ALLOC(PS2MEM_DEV9, 0x00010000, PROT_NONE); + VIRTUAL_ALLOC(PS2MEM_SPU2, 0x00010000, PROT_NONE); + VIRTUAL_ALLOC(PS2MEM_SPU2_, 0x00010000, PROT_NONE); + + VIRTUAL_ALLOC(PS2MEM_B80, 0x00010000, PROT_READ|PROT_WRITE); + VIRTUAL_ALLOC(PS2MEM_BA0, 0x00010000, PROT_READ|PROT_WRITE); + + // special addrs mmap + VIRTUAL_ALLOC(PS2MEM_BASE+0x5fff0000, 0x10000, PROT_READ|PROT_WRITE); + +eCleanupAndExit: + memShutdown(); + return -1; +} + +void memShutdown() +{ + VIRTUAL_FREE(PS2MEM_BASE, 0x40000000); + VIRTUAL_FREE(PS2MEM_PSX, 0x00800000); + + PHYSICAL_FREE(PS2MEM_BASE, Ps2MemSize::Base, s_psM); + VIRTUAL_FREE(PS2MEM_ROM, Ps2MemSize::Rom); + VIRTUAL_FREE(PS2MEM_ROM1, Ps2MemSize::Rom1); + VIRTUAL_FREE(PS2MEM_ROM2, Ps2MemSize::Rom2); + VIRTUAL_FREE(PS2MEM_EROM, Ps2MemSize::ERom); + PHYSICAL_FREE(PS2MEM_SCRATCH, 0x00010000, s_psS); + PHYSICAL_FREE(PS2MEM_HW, Ps2MemSize::Hardware, s_psHw); + PHYSICAL_FREE(PS2MEM_PSX, Ps2MemSize::IopRam, s_psxM); + PHYSICAL_FREE(PS2MEM_VU0MICRO, 0x00010000, s_psVuMem); + + VIRTUAL_FREE(PS2MEM_VU0MICRO, 0x00010000); // allocate for all VUs + + VIRTUAL_FREE(PS2MEM_PSXHW, Ps2MemSize::IopHardware); + VIRTUAL_FREE(PS2MEM_PSXHW4, 0x00010000); + VIRTUAL_FREE(PS2MEM_GS, 0x00002000); + VIRTUAL_FREE(PS2MEM_DEV9, 0x00010000); + VIRTUAL_FREE(PS2MEM_SPU2, 0x00010000); + VIRTUAL_FREE(PS2MEM_SPU2_, 0x00010000); + + VIRTUAL_FREE(PS2MEM_B80, 0x00010000); + VIRTUAL_FREE(PS2MEM_BA0, 0x00010000); + + VirtualFree(PS2MEM_VU0MICRO, 0, MEM_RELEASE); + + safe_aligned_free(memLUT); + + // reserve mem + if( mmap(PS2MEM_BASE, 0x40000000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) != PS2MEM_BASE ) { + SysPrintf("failed to reserve mem\n"); + } +} + +#endif // _WIN32 + +void vm_Reset() +{ + jASSUME( memLUT != NULL ); + + memzero_ptr(memLUT); + for (int i=0; i<0x02000; i++) memLUT[i + 0x00000] = initMemoryMap(&s_psM.aPFNs[i], &s_psM.aVFNs[i]); + for (int i=2; i<0x00010; i++) memLUT[i + 0x10000] = initMemoryMap(&s_psHw.aPFNs[i], &s_psHw.aVFNs[i]); + for (int i=0; i<0x00800; i++) memLUT[i + 0x1c000] = initMemoryMap(&s_psxM.aPFNs[(i & 0x1ff)], &s_psxM.aVFNs[(i & 0x1ff)]); + for (int i=0; i<0x00004; i++) + { + memLUT[i + 0x11000] = initMemoryMap(&s_psVuMem.aPFNs[0], &s_psVuMem.aVFNs[0]); + memLUT[i + 0x11004] = initMemoryMap(&s_psVuMem.aPFNs[1], &s_psVuMem.aVFNs[1]); + memLUT[i + 0x11008] = initMemoryMap(&s_psVuMem.aPFNs[4+i], &s_psVuMem.aVFNs[4+i]); + memLUT[i + 0x1100c] = initMemoryMap(&s_psVuMem.aPFNs[8+i], &s_psVuMem.aVFNs[8+i]); + + // Yay! Scratchpad mapping! We love the scratchpad. + memLUT[i + 0x50000] = initMemoryMap(&s_psS.aPFNs[i], &s_psS.aVFNs[i]); + } + + // map to other modes + memcpy(memLUT+0x80000, memLUT, 0x20000*sizeof(PSMEMORYMAP)); + memcpy(memLUT+0xa0000, memLUT, 0x20000*sizeof(PSMEMORYMAP)); +} + +// Some games read/write between different addrs but same physical memory +// this causes major slowdowns because it goes into the exception handler, so use this (zerofrog) +u32 VM_RETRANSLATE(u32 mem) +{ + u8* p, *pbase; + if( (mem&0xffff0000) == 0x50000000 ) // reserved scratch pad mem + return PS2MEM_BASE_+mem; + + p = (u8*)dmaGetAddrBase(mem); + +#ifdef _WIN32 + // do manual LUT since IPU/SPR seems to use addrs 0x3000xxxx quite often + if( memLUT[ (p-PS2MEM_BASE)>>12 ].aPFNs == NULL ) { + return PS2MEM_BASE_+mem; + } + + pbase = (u8*)memLUT[ (p-PS2MEM_BASE)>>12 ].aVFNs[0]; + if( pbase != NULL ) + p = pbase + ((u32)p&0xfff); +#endif + + return (u32)p; +} + +void memSetPageAddr(u32 vaddr, u32 paddr) { + + PSMEMORYMAP* pmap; + + if( vaddr == paddr ) + return; + + if( (vaddr>>28) != 1 && (vaddr>>28) != 9 && (vaddr>>28) != 11 ) { +#ifdef _WIN32 + pmap = &memLUT[vaddr >> 12]; + + if( pmap->aPFNs != NULL && (pmap->aPFNs != memLUT[paddr>>12].aPFNs || + pmap->aVFNs[0] != TRANSFORM_ADDR(vaddr)+(u32)PS2MEM_BASE) ) { + + SysMapUserPhysicalPages((void*)pmap->aVFNs[0], 1, NULL, 0); + pmap->aVFNs[0] = 0; + } + + *pmap = memLUT[paddr >> 12]; +#else + memLUT[vaddr>>12] = memLUT[paddr>>12]; +#endif + } +} + +void memClearPageAddr(u32 vaddr) { + // SysPrintf("memClearPageAddr: %8.8x\n", vaddr); + + if ((vaddr & 0xffffc000) == 0x70000000) return; + +#ifdef _WIN32 + // if( vaddr >= 0x20000000 && vaddr < 0x80000000 ) { + // Cpu->Clear(vaddr&~0xfff, 0x1000/4); + // if( memLUT[vaddr>>12].aVFNs != NULL ) { + // SysMapUserPhysicalPages((void*)memLUT[vaddr>>12].aVFNs[0], 1, NULL, 0 ); + // memLUT[vaddr>>12].aVFNs = NULL; + // memLUT[vaddr>>12].aPFNs = NULL; + // } + // } +#else + if( memLUT[vaddr>>12] != NULL ) { + SysVirtualFree(memLUT[vaddr>>12], 0x1000); + memLUT[vaddr>>12] = 0; + } +#endif +} + +u8 recMemRead8() +{ + register u32 mem; + __asm mov mem, ecx // already anded with ~0xa0000000 + + switch( (mem&~0xffff) ) { +case 0x1f400000: return psxHw4Read8(mem); +case 0x10000000: return hwRead8(mem); +case 0x1f800000: return psxHwRead8(mem); +case 0x12000000: return *(PS2MEM_BASE+(mem&~0xc00)); +case 0x14000000: + { + u32 ret = DEV9read8(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, ret); + return ret; + } + +default: + return *(u8*)(PS2MEM_BASE+mem); + } + MEM_LOG("Unknown Memory read32 from address %8.8x\n", mem); + + cpuTlbMissR(mem, cpuRegs.branch); + + return 0; +} + +void _eeReadConstMem8(int mmreg, u32 mem, int sign) +{ + assert( !IS_XMMREG(mmreg)); + + if( IS_MMXREG(mmreg) ) { + SetMMXstate(); + MOVDMtoMMX(mmreg&0xf, mem-3); + assert(0); + } + else { + if( sign ) MOVSX32M8toR(mmreg, mem); + else MOVZX32M8toR(mmreg, mem); + } +} + +void _eeReadConstMem16(int mmreg, u32 mem, int sign) +{ + assert( !IS_XMMREG(mmreg)); + + if( IS_MMXREG(mmreg) ) { + SetMMXstate(); + MOVDMtoMMX(mmreg&0xf, mem-2); + assert(0); + } + else { + if( sign ) MOVSX32M16toR(mmreg, mem); + else MOVZX32M16toR(mmreg, mem); + } +} + +void _eeReadConstMem32(int mmreg, u32 mem) +{ + if( IS_XMMREG(mmreg) ) SSEX_MOVD_M32_to_XMM(mmreg&0xf, mem); + else if( IS_MMXREG(mmreg) ) { + SetMMXstate(); + MOVDMtoMMX(mmreg&0xf, mem); + } + else MOV32MtoR(mmreg, mem); +} + +void _eeReadConstMem128(int mmreg, u32 mem) +{ + if( IS_MMXREG(mmreg) ) { + SetMMXstate(); + MOVQMtoR((mmreg>>4)&0xf, mem+8); + MOVQMtoR(mmreg&0xf, mem); + } + else SSEX_MOVDQA_M128_to_XMM( mmreg&0xf, mem); +} + +void _eeWriteConstMem8(u32 mem, int mmreg) +{ + assert( !IS_XMMREG(mmreg) && !IS_MMXREG(mmreg) ); + if( IS_EECONSTREG(mmreg) ) MOV8ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + else if( IS_PSXCONSTREG(mmreg) ) MOV8ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); + else MOV8RtoM(mem, mmreg); +} + +void _eeWriteConstMem16(u32 mem, int mmreg) +{ + assert( !IS_XMMREG(mmreg) && !IS_MMXREG(mmreg) ); + if( IS_EECONSTREG(mmreg) ) MOV16ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + else if( IS_PSXCONSTREG(mmreg) ) MOV16ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); + else MOV16RtoM(mem, mmreg); +} + +// op - 0 for AND, 1 for OR +void _eeWriteConstMem16OP(u32 mem, int mmreg, int op) +{ + assert( !IS_XMMREG(mmreg) && !IS_MMXREG(mmreg) ); + switch(op) { +case 0: // AND operation + if( IS_EECONSTREG(mmreg) ) AND16ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + else if( IS_PSXCONSTREG(mmreg) ) AND16ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); + else AND16RtoM(mem, mmreg); + break; +case 1: // OR operation + if( IS_EECONSTREG(mmreg) ) OR16ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + else if( IS_PSXCONSTREG(mmreg) ) OR16ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); + else OR16RtoM(mem, mmreg); + break; + + jNO_DEFAULT + } +} + +void _eeWriteConstMem32(u32 mem, int mmreg) +{ + if( IS_XMMREG(mmreg) ) SSE2_MOVD_XMM_to_M32(mem, mmreg&0xf); + else if( IS_MMXREG(mmreg) ) { + SetMMXstate(); + MOVDMMXtoM(mem, mmreg&0xf); + } + else if( IS_EECONSTREG(mmreg) ) MOV32ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + else if( IS_PSXCONSTREG(mmreg) ) MOV32ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); + else MOV32RtoM(mem, mmreg); +} + +void _eeWriteConstMem32OP(u32 mem, int mmreg, int op) +{ + switch(op) { +case 0: // and + if( IS_XMMREG(mmreg) ) { + _deleteEEreg((mmreg>>16)&0x1f, 1); + SSE2_PAND_M128_to_XMM(mmreg&0xf, mem); + SSE2_MOVD_XMM_to_M32(mem, mmreg&0xf); + } + else if( IS_MMXREG(mmreg) ) { + _deleteEEreg((mmreg>>16)&0x1f, 1); + SetMMXstate(); + PANDMtoR(mmreg&0xf, mem); + MOVDMMXtoM(mem, mmreg&0xf); + } + else if( IS_EECONSTREG(mmreg) ) { + AND32ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + } + else if( IS_PSXCONSTREG(mmreg) ) { + AND32ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); + } + else { + AND32RtoM(mem, mmreg&0xf); + } + break; + +case 1: // or + if( IS_XMMREG(mmreg) ) { + _deleteEEreg((mmreg>>16)&0x1f, 1); + SSE2_POR_M128_to_XMM(mmreg&0xf, mem); + SSE2_MOVD_XMM_to_M32(mem, mmreg&0xf); + } + else if( IS_MMXREG(mmreg) ) { + _deleteEEreg((mmreg>>16)&0x1f, 1); + SetMMXstate(); + PORMtoR(mmreg&0xf, mem); + MOVDMMXtoM(mem, mmreg&0xf); + } + else if( IS_EECONSTREG(mmreg) ) { + OR32ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + } + else if( IS_PSXCONSTREG(mmreg) ) { + OR32ItoM(mem, g_psxConstRegs[((mmreg>>16)&0x1f)]); + } + else { + OR32RtoM(mem, mmreg&0xf); + } + break; + +case 2: // not and + if( mmreg & MEM_XMMTAG ) { + _deleteEEreg(mmreg>>16, 1); + SSEX_PANDN_M128_to_XMM(mmreg&0xf, mem); + SSEX_MOVD_XMM_to_M32(mem, mmreg&0xf); + } + else if( mmreg & MEM_MMXTAG ) { + _deleteEEreg(mmreg>>16, 1); + PANDNMtoR(mmreg&0xf, mem); + MOVDMMXtoM(mem, mmreg&0xf); + } + else if( IS_EECONSTREG(mmreg) ) { + AND32ItoM(mem, ~g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + } + else if( IS_PSXCONSTREG(mmreg) ) { + AND32ItoM(mem, ~g_psxConstRegs[((mmreg>>16)&0x1f)]); + } + else { + NOT32R(mmreg&0xf); + AND32RtoM(mem, mmreg&0xf); + } + break; + +default: assert(0); + } +} + +void _eeWriteConstMem64(u32 mem, int mmreg) +{ + if( IS_XMMREG(mmreg) ) SSE_MOVLPS_XMM_to_M64(mem, mmreg&0xf); + else if( IS_MMXREG(mmreg) ) { + SetMMXstate(); + MOVQRtoM(mem, mmreg&0xf); + } + else if( IS_EECONSTREG(mmreg) ) { + MOV32ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + MOV32ItoM(mem+4, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[1]); + } + else assert(0); +} + +void _eeWriteConstMem128(u32 mem, int mmreg) +{ + if( IS_MMXREG(mmreg) ) { + SetMMXstate(); + MOVQRtoM(mem, mmreg&0xf); + MOVQRtoM(mem+8, (mmreg>>4)&0xf); + } + else if( IS_EECONSTREG(mmreg) ) { + SetMMXstate(); + MOV32ItoM(mem, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + MOV32ItoM(mem+4, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[1]); + MOVQRtoM(mem+8, mmreg&0xf); + } + else SSEX_MOVDQA_XMM_to_M128(mem, mmreg&0xf); +} + +void _eeMoveMMREGtoR(x86IntRegType to, int mmreg) +{ + if( IS_XMMREG(mmreg) ) SSE2_MOVD_XMM_to_R(to, mmreg&0xf); + else if( IS_MMXREG(mmreg) ) { + SetMMXstate(); + MOVD32MMXtoR(to, mmreg&0xf); + } + else if( IS_EECONSTREG(mmreg) ) MOV32ItoR(to, g_cpuConstRegs[((mmreg>>16)&0x1f)].UL[0]); + else if( IS_PSXCONSTREG(mmreg) ) MOV32ItoR(to, g_psxConstRegs[((mmreg>>16)&0x1f)]); + else if( mmreg != to ) MOV32RtoR(to, mmreg); +} + +int recMemConstRead8(u32 x86reg, u32 mem, u32 sign) +{ + mem = TRANSFORM_ADDR(mem); + + switch( mem>>16 ) { +case 0x1f40: return psxHw4ConstRead8(x86reg, mem, sign); +case 0x1000: return hwConstRead8(x86reg, mem, sign); +case 0x1f80: return psxHwConstRead8(x86reg, mem, sign); +case 0x1200: return gsConstRead8(x86reg, mem, sign); +case 0x1400: + { + iFlushCall(0); + PUSH32I(mem & ~0x04000000); + CALLFunc((u32)DEV9read8); + if( sign ) MOVSX32R8toR(EAX, EAX); + else MOVZX32R8toR(EAX, EAX); + return 1; + } + +default: + _eeReadConstMem8(x86reg, VM_RETRANSLATE(mem), sign); + return 0; + } +} + +u16 recMemRead16() { + + register u32 mem; + __asm mov mem, ecx // already anded with ~0xa0000000 + + switch( mem>>16 ) { + case 0x1000: return hwRead16(mem); + case 0x1f80: return psxHwRead16(mem); + case 0x1200: return *(u16*)(PS2MEM_BASE+(mem&~0xc00)); + case 0x1800: return 0; + case 0x1a00: return ba0R16(mem); + case 0x1f90: + case 0x1f00: + return SPU2read(mem); + case 0x1400: + { + u32 ret = DEV9read16(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, ret); + return ret; + } + + default: + return *(u16*)(PS2MEM_BASE+mem); + } + MEM_LOG("Unknown Memory read16 from address %8.8x\n", mem); + cpuTlbMissR(mem, cpuRegs.branch); + return 0; +} + +int recMemConstRead16(u32 x86reg, u32 mem, u32 sign) +{ + mem = TRANSFORM_ADDR(mem); + + switch( mem>>16 ) { +case 0x1000: return hwConstRead16(x86reg, mem, sign); +case 0x1f80: return psxHwConstRead16(x86reg, mem, sign); +case 0x1200: return gsConstRead16(x86reg, mem, sign); +case 0x1800: + if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf); + else XOR32RtoR(x86reg, x86reg); + return 0; +case 0x1a00: + iFlushCall(0); + PUSH32I(mem); + CALLFunc((u32)ba0R16); + ADD32ItoR(ESP, 4); + if( sign ) MOVSX32R16toR(EAX, EAX); + else MOVZX32R16toR(EAX, EAX); + return 1; + +case 0x1f90: +case 0x1f00: + iFlushCall(0); + PUSH32I(mem); + CALLFunc((u32)SPU2read); + if( sign ) MOVSX32R16toR(EAX, EAX); + else MOVZX32R16toR(EAX, EAX); + return 1; + +case 0x1400: + iFlushCall(0); + PUSH32I(mem & ~0x04000000); + CALLFunc((u32)DEV9read16); + if( sign ) MOVSX32R16toR(EAX, EAX); + else MOVZX32R16toR(EAX, EAX); + return 1; + +default: + _eeReadConstMem16(x86reg, VM_RETRANSLATE(mem), sign); + return 0; + } +} + +__declspec(naked) +u32 recMemRead32() { + // ecx is address - already anded with ~0xa0000000 + __asm { + + mov edx, ecx + shr edx, 16 + cmp dx, 0x1000 + je hwread + cmp dx, 0x1f80 + je psxhwread + cmp dx, 0x1200 + je gsread + cmp dx, 0x1400 + je devread + + // default read + mov eax, dword ptr [ecx+PS2MEM_BASE_] + ret + } + +hwread: + { + __asm { + cmp ecx, 0x10002000 + jb counterread + + cmp ecx, 0x1000f260 + je hwsifpresetread + cmp ecx, 0x1000f240 + je hwsifsyncread + cmp ecx, 0x1000f440 + je hwmch_drd + cmp ecx, 0x1000f430 + je hwmch_ricm + + cmp ecx, 0x10003000 + jb hwdefread2 + mov eax, dword ptr [ecx+PS2MEM_BASE_] + ret + + // ipu +hwdefread2: + push ecx + call ipuRead32 + add esp, 4 + ret + + // sif +hwsifpresetread: + xor eax, eax + ret +hwsifsyncread: + mov eax, 0x1000F240 + mov eax, dword ptr [eax+PS2MEM_BASE_] + or eax, 0xF0000102 + ret + } + +counterread: + { + static u32 mem, index; + + // counters + __asm mov mem, ecx + index = (mem>>11)&3; + + if( (mem&0x7ff) == 0 ) { + __asm { + push index + call rcntRcount + add esp, 4 + and eax, 0xffff + ret + } + } + + index = (u32)&counters[index] + ((mem>>2)&0xc); + + __asm { + mov eax, index + mov eax, dword ptr [eax] + movzx eax, ax + ret + } + } + +hwmch_drd: // MCH_DRD + __asm { + mov eax, dword ptr [ecx+PS2MEM_BASE_-0x10] + shr eax, 6 + test eax, 0xf + jz mch_drd_2 +hwmch_ricm: + xor eax, eax + ret + +mch_drd_2: + shr eax, 10 + and eax, 0xfff + cmp eax, 0x21 // INIT + je mch_drd_init + cmp eax, 0x23 // CNFGA + je mch_drd_cnfga + cmp eax, 0x24 // CNFGB + je mch_drd_cnfgb + cmp eax, 0x40 // DEVID + je mch_drd_devid + xor eax, eax + ret + +mch_drd_init: + mov edx, rdram_devices + xor eax, eax + cmp edx, rdram_sdevid + setg al + add rdram_sdevid, eax + imul eax, 0x1f + ret + +mch_drd_cnfga: + mov eax, 0x0D0D + ret + +mch_drd_cnfgb: + mov eax, 0x0090 + ret + +mch_drd_devid: + mov eax, dword ptr [ecx+PS2MEM_BASE_-0x10] + and eax, 0x1f + ret + } + } + +psxhwread: + __asm { + push ecx + call psxHwRead32 + add esp, 4 + ret + } + +gsread: + __asm { + and ecx, 0xfffff3ff + mov eax, dword ptr [ecx+PS2MEM_BASE_] + ret + } + +devread: + __asm { + and ecx, 0xfbffffff + push ecx + call DEV9read32 + add esp, 4 + ret + } +} + +int recMemConstRead32(u32 x86reg, u32 mem) +{ + mem = TRANSFORM_ADDR(mem); + + switch( (mem&~0xffff) ) { +case 0x10000000: return hwConstRead32(x86reg, mem); +case 0x1f800000: return psxHwConstRead32(x86reg, mem); +case 0x12000000: return gsConstRead32(x86reg, mem); +case 0x14000000: + iFlushCall(0); + PUSH32I(mem & ~0x04000000); + CALLFunc((u32)DEV9read32); + return 1; + +default: + _eeReadConstMem32(x86reg, VM_RETRANSLATE(mem)); + return 0; + } +} + +void recMemRead64(u64 *out) +{ + register u32 mem; + __asm mov mem, ecx // already anded with ~0xa0000000 + + switch( (mem&0xffff0000) ) { +case 0x10000000: *out = hwRead64(mem); return; +case 0x11000000: *out = *(u64*)(PS2MEM_BASE+mem); return; +case 0x12000000: *out = *(u64*)(PS2MEM_BASE+(mem&~0xc00)); return; + +default: + //assert(0); + *out = *(u64*)(PS2MEM_BASE+mem); + return; + } + + MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); + cpuTlbMissR(mem, cpuRegs.branch); +} + +void recMemConstRead64(u32 mem, int mmreg) +{ + mem = TRANSFORM_ADDR(mem); + + switch( (mem&0xffff0000) ) { +case 0x10000000: hwConstRead64(mem, mmreg); return; +case 0x12000000: gsConstRead64(mem, mmreg); return; +default: + if( IS_XMMREG(mmreg) ) SSE_MOVLPS_M64_to_XMM(mmreg&0xff, VM_RETRANSLATE(mem)); + else { + MOVQMtoR(mmreg, VM_RETRANSLATE(mem)); + SetMMXstate(); + } + return; + } +} + +void recMemRead128(u64 *out) { + + register u32 mem; + __asm mov mem, ecx // already anded with ~0xa0000000 + + switch( (mem&0xffff0000) ) { + case 0x10000000: + hwRead128(mem, out); + return; + case 0x12000000: + out[0] = *(u64*)(PS2MEM_BASE+(mem&~0xc00)); + out[1] = *(u64*)(PS2MEM_BASE+(mem&~0xc00)+8); + return; + case 0x11000000: + out[0] = *(u64*)(PS2MEM_BASE+mem); + out[1] = *(u64*)(PS2MEM_BASE+mem+8); + return; + default: + //assert(0); + out[0] = *(u64*)(PS2MEM_BASE+mem); + out[1] = *(u64*)(PS2MEM_BASE+mem+8); + return; + } + + MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); + cpuTlbMissR(mem, cpuRegs.branch); +} + +void recMemConstRead128(u32 mem, int xmmreg) +{ + mem = TRANSFORM_ADDR(mem); + + switch( (mem&0xffff0000) ) { +case 0x10000000: hwConstRead128(mem, xmmreg); return; +case 0x12000000: gsConstRead128(mem, xmmreg); return; +default: + _eeReadConstMem128(xmmreg, VM_RETRANSLATE(mem)); + return; + } +} + +void errwrite() +{ + int i, bit, tempeax; + __asm mov i, ecx + __asm mov tempeax, eax + __asm mov bit, edx + SysPrintf("Error write%d at %x\n", bit, i); + assert(0); + __asm mov eax, tempeax + __asm mov ecx, i +} + +void recMemWrite8() +{ + register u32 mem; + register u8 value; + __asm mov mem, ecx // already anded with ~0xa0000000 + __asm mov value, al + + switch( mem>>16 ) { +case 0x1f40: psxHw4Write8(mem, value); return; +case 0x1000: hwWrite8(mem, value); return; +case 0x1f80: psxHwWrite8(mem, value); return; +case 0x1200: gsWrite8(mem, value); return; +case 0x1400: + DEV9write8(mem & ~0x04000000, value); + SysPrintf("DEV9 write8 %8.8lx: %2.2lx\n", mem & ~0x04000000, value); + return; + +#ifdef _DEBUG +case 0x1100: assert(0); +#endif +default: + // vus, bad addrs, etc + *(u8*)(PS2MEM_BASE+mem) = value; + return; + } + MEM_LOG("Unknown Memory write8 to address %x with data %2.2x\n", mem, value); + cpuTlbMissW(mem, cpuRegs.branch); +} + +int recMemConstWrite8(u32 mem, int mmreg) +{ + mem = TRANSFORM_ADDR(mem); + + switch( mem>>16 ) { +case 0x1f40: psxHw4ConstWrite8(mem, mmreg); return 0; +case 0x1000: hwConstWrite8(mem, mmreg); return 0; +case 0x1f80: psxHwConstWrite8(mem, mmreg); return 0; +case 0x1200: gsConstWrite8(mem, mmreg); return 0; +case 0x1400: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(mem & ~0x04000000); + CALLFunc((u32)DEV9write8); + return 0; + +case 0x1100: + _eeWriteConstMem8(PS2MEM_BASE_+mem, mmreg); + + if( mem < 0x11004000 ) { + PUSH32I(1); + PUSH32I(mem&0x3ff8); + CALLFunc((u32)CpuVU0->Clear); + ADD32ItoR(ESP, 8); + } + else if( mem >= 0x11008000 && mem < 0x1100c000 ) { + PUSH32I(1); + PUSH32I(mem&0x3ff8); + CALLFunc((u32)CpuVU1->Clear); + ADD32ItoR(ESP, 8); + } + return 0; + +default: + _eeWriteConstMem8(PS2MEM_BASE_+mem, mmreg); + return 1; + } +} + +void recMemWrite16() { + + register u32 mem; + register u16 value; + __asm mov mem, ecx // already anded with ~0xa0000000 + __asm mov value, ax + + switch( mem>>16 ) { + case 0x1000: hwWrite16(mem, value); return; + case 0x1600: + //HACK: DEV9 VM crash fix + return; + case 0x1f80: psxHwWrite16(mem, value); return; + case 0x1200: gsWrite16(mem, value); return; + case 0x1f90: + case 0x1f00: SPU2write(mem, value); return; + case 0x1400: + DEV9write16(mem & ~0x04000000, value); + SysPrintf("DEV9 write16 %8.8lx: %4.4lx\n", mem & ~0x04000000, value); + return; + +#ifdef _DEBUG + case 0x1100: assert(0); +#endif + default: + // vus, bad addrs, etc + *(u16*)(PS2MEM_BASE+mem) = value; + return; + } + MEM_LOG("Unknown Memory write16 to address %x with data %4.4x\n", mem, value); + cpuTlbMissW(mem, cpuRegs.branch); +} + +int recMemConstWrite16(u32 mem, int mmreg) +{ + mem = TRANSFORM_ADDR(mem); + + switch( mem>>16 ) { +case 0x1000: hwConstWrite16(mem, mmreg); return 0; +case 0x1600: + //HACK: DEV9 VM crash fix + return 0; +case 0x1f80: psxHwConstWrite16(mem, mmreg); return 0; +case 0x1200: gsConstWrite16(mem, mmreg); return 0; +case 0x1f90: +case 0x1f00: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(mem); + CALLFunc((u32)SPU2write); + return 0; +case 0x1400: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(mem & ~0x04000000); + CALLFunc((u32)DEV9write16); + return 0; + +case 0x1100: + _eeWriteConstMem16(PS2MEM_BASE_+mem, mmreg); + + if( mem < 0x11004000 ) { + PUSH32I(1); + PUSH32I(mem&0x3ff8); + CALLFunc((u32)CpuVU0->Clear); + ADD32ItoR(ESP, 8); + } + else if( mem >= 0x11008000 && mem < 0x1100c000 ) { + PUSH32I(1); + PUSH32I(mem&0x3ff8); + CALLFunc((u32)CpuVU1->Clear); + ADD32ItoR(ESP, 8); + } + return 0; + +default: + _eeWriteConstMem16(PS2MEM_BASE_+mem, mmreg); + return 1; + } +} + +C_ASSERT( sizeof(BASEBLOCK) == 8 ); + +__declspec(naked) +void recMemWrite32() +{ + // ecx is address - already anded with ~0xa0000000 + __asm { + + mov edx, ecx + shr edx, 16 + cmp dx, 0x1000 + je hwwrite + cmp dx, 0x1f80 + je psxwrite + cmp dx, 0x1200 + je gswrite + cmp dx, 0x1400 + je devwrite + cmp dx, 0x1100 + je vuwrite + } + + __asm { + // default write + mov dword ptr [ecx+PS2MEM_BASE_], eax + ret + +hwwrite: + push eax + push ecx + call hwWrite32 + add esp, 8 + ret +psxwrite: + push eax + push ecx + call psxHwWrite32 + add esp, 8 + ret +gswrite: + push eax + push ecx + call gsWrite32 + add esp, 8 + ret +devwrite: + and ecx, 0xfbffffff + push eax + push ecx + call DEV9write32 + add esp, 8 + ret +vuwrite: + // default write + mov dword ptr [ecx+PS2MEM_BASE_], eax + + cmp ecx, 0x11004000 + jge vu1write + and ecx, 0x3ff8 + // clear vu0mem + mov eax, CpuVU0 + push 1 + push ecx + call [eax]CpuVU0.Clear + add esp, 8 + ret + +vu1write: + cmp ecx, 0x11008000 + jl vuend + cmp ecx, 0x1100c000 + jge vuend + // clear vu1mem + and ecx, 0x3ff8 + mov eax, CpuVU1 + push 1 + push ecx + call [eax]CpuVU1.Clear + add esp, 8 +vuend: + ret + } +} + +int recMemConstWrite32(u32 mem, int mmreg) +{ + mem = TRANSFORM_ADDR(mem); + + switch( mem&0xffff0000 ) { +case 0x10000000: hwConstWrite32(mem, mmreg); return 0; +case 0x1f800000: psxHwConstWrite32(mem, mmreg); return 0; +case 0x12000000: gsConstWrite32(mem, mmreg); return 0; +case 0x1f900000: +case 0x1f000000: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(mem); + CALLFunc((u32)SPU2write); + return 0; +case 0x14000000: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(mem & ~0x04000000); + CALLFunc((u32)DEV9write32); + return 0; + +case 0x11000000: + _eeWriteConstMem32(PS2MEM_BASE_+mem, mmreg); + + if( mem < 0x11004000 ) { + PUSH32I(1); + PUSH32I(mem&0x3ff8); + CALLFunc((u32)CpuVU0->Clear); + ADD32ItoR(ESP, 8); + } + else if( mem >= 0x11008000 && mem < 0x1100c000 ) { + PUSH32I(1); + PUSH32I(mem&0x3ff8); + CALLFunc((u32)CpuVU1->Clear); + ADD32ItoR(ESP, 8); + } + return 0; + +default: + _eeWriteConstMem32(PS2MEM_BASE_+mem, mmreg); + return 1; + } +} + +__declspec(naked) void recMemWrite64() +{ + __asm { + mov edx, ecx + shr edx, 16 + cmp dx, 0x1000 + je hwwrite + cmp dx, 0x1200 + je gswrite + cmp dx, 0x1100 + je vuwrite + } + + __asm { + // default write + mov edx, 64 + call errwrite + +hwwrite: + push dword ptr [eax+4] + push dword ptr [eax] + push ecx + call hwWrite64 + add esp, 12 + ret + +gswrite: + push dword ptr [eax+4] + push dword ptr [eax] + push ecx + call gsWrite64 + add esp, 12 + ret + +vuwrite: + mov ebx, dword ptr [eax] + mov edx, dword ptr [eax+4] + mov dword ptr [ecx+PS2MEM_BASE_], ebx + mov dword ptr [ecx+PS2MEM_BASE_+4], edx + + cmp ecx, 0x11004000 + jge vu1write + and ecx, 0x3ff8 + // clear vu0mem + mov eax, CpuVU0 + push 2 + push ecx + call [eax]CpuVU0.Clear + add esp, 8 + ret + +vu1write: + cmp ecx, 0x11008000 + jl vuend + cmp ecx, 0x1100c000 + jge vuend + // clear vu1mem + and ecx, 0x3ff8 + mov eax, CpuVU0 + push 2 + push ecx + call [eax]CpuVU1.Clear + add esp, 8 +vuend: + ret + } +} + +int recMemConstWrite64(u32 mem, int mmreg) +{ + mem = TRANSFORM_ADDR(mem); + + switch( (mem>>16) ) { +case 0x1000: hwConstWrite64(mem, mmreg); return 0; +case 0x1200: gsConstWrite64(mem, mmreg); return 0; + +case 0x1100: + _eeWriteConstMem64(PS2MEM_BASE_+mem, mmreg); + + if( mem < 0x11004000 ) { + PUSH32I(2); + PUSH32I(mem&0x3ff8); + CALLFunc((u32)CpuVU0->Clear); + ADD32ItoR(ESP, 8); + } + else if( mem >= 0x11008000 && mem < 0x1100c000 ) { + PUSH32I(2); + PUSH32I(mem&0x3ff8); + CALLFunc((u32)CpuVU1->Clear); + ADD32ItoR(ESP, 8); + } + return 0; + +default: + _eeWriteConstMem64(PS2MEM_BASE_+mem, mmreg); + return 1; + } +} + +__declspec(naked) +void recMemWrite128() +{ + __asm { + + mov edx, ecx + shr edx, 16 + cmp dx, 0x1000 + je hwwrite + cmp dx, 0x1200 + je gswrite + cmp dx, 0x1100 + je vuwrite + } + + __asm { + mov edx, 128 + call errwrite + +hwwrite: + + push eax + push ecx + call hwWrite128 + add esp, 8 + ret + +vuwrite: + mov ebx, dword ptr [eax] + mov edx, dword ptr [eax+4] + mov edi, dword ptr [eax+8] + mov eax, dword ptr [eax+12] + mov dword ptr [ecx+PS2MEM_BASE_], ebx + mov dword ptr [ecx+PS2MEM_BASE_+4], edx + mov dword ptr [ecx+PS2MEM_BASE_+8], edi + mov dword ptr [ecx+PS2MEM_BASE_+12], eax + + cmp ecx, 0x11004000 + jge vu1write + and ecx, 0x3ff8 + // clear vu0mem + mov eax, CpuVU0 + push 4 + push ecx + call [eax]CpuVU0.Clear + add esp, 8 + ret + +vu1write: + cmp ecx, 0x11008000 + jl vuend + cmp ecx, 0x1100c000 + jge vuend + // clear vu1mem + and ecx, 0x3ff8 + mov eax, CpuVU1 + push 4 + push ecx + call [eax]CpuVU1.Clear + add esp, 8 +vuend: + + // default write + //movaps xmm7, qword ptr [eax] + + // removes possible exceptions and saves on remapping memory + // *might* be faster for certain games, no way to tell + // cmp ecx, 0x20000000 + // jb Write128 + // + // // look for better mapping + // mov edx, ecx + // shr edx, 12 + // shl edx, 3 + // add edx, memLUT + // mov edx, dword ptr [edx + 4] + // cmp edx, 0 + // je Write128 + // mov edx, dword ptr [edx] + // cmp edx, 0 + // je Write128 + // and ecx, 0xfff + // movaps qword ptr [ecx+edx], xmm7 + // jmp CheckOverwrite + //Write128: + //movaps qword ptr [ecx+PS2MEM_BASE_], xmm7 + ret + +gswrite: + sub esp, 8 + movlps xmm7, qword ptr [eax] + movlps qword ptr [esp], xmm7 + push ecx + call gsWrite64 + + // call again for upper 8 bytes + movlps xmm7, qword ptr [eax+8] + movlps qword ptr [esp+4], xmm7 + add [esp], 8 + call gsWrite64 + add esp, 12 + ret + } +} + +int recMemConstWrite128(u32 mem, int mmreg) +{ + mem = TRANSFORM_ADDR(mem); + + switch( (mem&0xffff0000) ) { +case 0x10000000: hwConstWrite128(mem, mmreg); return 0; +case 0x12000000: gsConstWrite128(mem, mmreg); return 0; + +case 0x11000000: + _eeWriteConstMem128(PS2MEM_BASE_+mem, mmreg); + + if( mem < 0x11004000 ) { + PUSH32I(4); + PUSH32I(mem&0x3ff8); + CALLFunc((u32)CpuVU0->Clear); + ADD32ItoR(ESP, 8); + } + else if( mem >= 0x11008000 && mem < 0x1100c000 ) { + PUSH32I(4); + PUSH32I(mem&0x3ff8); + CALLFunc((u32)CpuVU1->Clear); + ADD32ItoR(ESP, 8); + } + return 0; + +default: + _eeWriteConstMem128(PS2MEM_BASE_+mem, mmreg); + return 1; + } +} + +int __fastcall memRead8 (u32 mem, u8 *out) { + + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x1f400000: *out = psxHw4Read8(mem); return 0; + case 0x10000000: *out = hwRead8(mem); return 0; + case 0x1f800000: *out = psxHwRead8(mem); return 0; + case 0x12000000: *out = gsRead8(mem); return 0; + case 0x14000000: + *out = DEV9read8(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); + return 0; + + default: + *out = *(u8*)(PS2MEM_BASE+mem); + return 0; + } + +#ifdef MEM_LOG + MEM_LOG("Unknown Memory read32 from address %8.8x\n", mem); +#endif + cpuTlbMissR(mem, cpuRegs.branch); + + return -1; +} + +int __fastcall memRead8RS (u32 mem, u64 *out) +{ + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { +case 0x1f400000: *out = (s8)psxHw4Read8(mem); return 0; +case 0x10000000: *out = (s8)hwRead8(mem); return 0; +case 0x1f800000: *out = (s8)psxHwRead8(mem); return 0; +case 0x12000000: *out = (s8)gsRead8(mem); return 0; +case 0x14000000: + *out = (s8)DEV9read8(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); + return 0; + +default: + *out = *(s8*)(PS2MEM_BASE+mem); + return 0; + } + MEM_LOG("Unknown Memory read32 from address %8.8x\n", mem); + cpuTlbMissR(mem, cpuRegs.branch); + + return -1; +} + +int __fastcall memRead8RU (u32 mem, u64 *out) +{ + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { +case 0x1f400000: *out = (u8)psxHw4Read8(mem); return 0; +case 0x10000000: *out = (u8)hwRead8(mem); return 0; +case 0x1f800000: *out = (u8)psxHwRead8(mem); return 0; +case 0x12000000: *out = (u8)gsRead8(mem); return 0; +case 0x14000000: + *out = (u8)DEV9read8(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); + return 0; + +default: + *out = *(u8*)(PS2MEM_BASE+mem); + return 0; + } + MEM_LOG("Unknown Memory read32 from address %8.8x\n", mem); + cpuTlbMissR(mem, cpuRegs.branch); + + return -1; +} + +int __fastcall memRead16(u32 mem, u16 *out) { + + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x10000000: *out = hwRead16(mem); return 0; + case 0x1f800000: *out = psxHwRead16(mem); return 0; + case 0x12000000: *out = gsRead16(mem); return 0; + case 0x18000000: *out = 0; return 0; + case 0x1a000000: *out = ba0R16(mem); return 0; + case 0x1f900000: + case 0x1f000000: + *out = SPU2read(mem); return 0; + break; + case 0x14000000: + *out = DEV9read16(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); + return 0; + + default: + *out = *(u16*)(PS2MEM_BASE+mem); + return 0; + } + MEM_LOG("Unknown Memory read16 from address %8.8x\n", mem); + cpuTlbMissR(mem, cpuRegs.branch); + return -1; +} + +int __fastcall memRead16RS(u32 mem, u64 *out) { + + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x10000000: *out = (s16)hwRead16(mem); return 0; + case 0x1f800000: *out = (s16)psxHwRead16(mem); return 0; + case 0x12000000: *out = (s16)gsRead16(mem); return 0; + case 0x18000000: *out = 0; return 0; + case 0x1a000000: *out = (s16)ba0R16(mem); return 0; + case 0x1f900000: + case 0x1f000000: + *out = (s16)SPU2read(mem); return 0; + break; + case 0x14000000: + *out = (s16)DEV9read16(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); + return 0; + + default: + *out = *(s16*)(PS2MEM_BASE+mem); + return 0; + } + MEM_LOG("Unknown Memory read16 from address %8.8x\n", mem); + cpuTlbMissR(mem, cpuRegs.branch); + return -1; +} + +int __fastcall memRead16RU(u32 mem, u64 *out) { + + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x10000000: *out = (u16)hwRead16(mem ); return 0; + case 0x1f800000: *out = (u16)psxHwRead16(mem ); return 0; + case 0x12000000: *out = (u16)gsRead16(mem); return 0; + case 0x18000000: *out = 0; return 0; + case 0x1a000000: *out = (u16)ba0R16(mem); return 0; + case 0x1f900000: + case 0x1f000000: + *out = (u16)SPU2read(mem ); return 0; + break; + case 0x14000000: + *out = (u16)DEV9read16(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); + return 0; + + default: + *out = *(u16*)(PS2MEM_BASE+mem); + return 0; + } + MEM_LOG("Unknown Memory read16 from address %8.8x\n", mem); + cpuTlbMissR(mem, cpuRegs.branch); + return -1; +} + +int __fastcall memRead32(u32 mem, u32 *out) { + + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x10000000: *out = hwRead32(mem); return 0; + case 0x1f800000: *out = psxHwRead32(mem); return 0; + case 0x12000000: *out = gsRead32(mem); return 0; + case 0x14000000: + *out = (u32)DEV9read32(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); + return 0; + + default: + *out = *(u32*)(PS2MEM_BASE+mem); + return 0; + } + + MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); + cpuTlbMissR(mem, cpuRegs.branch); + + return -1; +} + +int __fastcall memRead32RS(u32 mem, u64 *out) +{ + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { +case 0x10000000: *out = (s32)hwRead32(mem); return 0; +case 0x1f800000: *out = (s32)psxHwRead32(mem); return 0; +case 0x12000000: *out = (s32)gsRead32(mem); return 0; +case 0x14000000: + *out = (s32)DEV9read32(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); + return 0; + +default: + *out = *(s32*)(PS2MEM_BASE+mem); + return 0; + } + + MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); + cpuTlbMissR(mem, cpuRegs.branch); + + return -1; +} + +int __fastcall memRead32RU(u32 mem, u64 *out) +{ + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { +case 0x10000000: *out = (u32)hwRead32(mem); return 0; +case 0x1f800000: *out = (u32)psxHwRead32(mem); return 0; +case 0x12000000: *out = (u32)gsRead32(mem); return 0; +case 0x14000000: + *out = (u32)DEV9read32(mem & ~0x04000000); + SysPrintf("DEV9 read8 %8.8lx: %2.2lx\n", mem & ~0x04000000, *out); + return 0; + +default: + *out = *(u32*)(PS2MEM_BASE+mem); + return 0; + } + + MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); + cpuTlbMissR(mem, cpuRegs.branch); + + return -1; +} + +int __fastcall memRead64(u32 mem, u64 *out) { + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x10000000: *out = hwRead64(mem); return 0; + case 0x12000000: *out = gsRead64(mem); return 0; + + default: + *out = *(u64*)(PS2MEM_BASE+mem); + return 0; + } + + MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); + cpuTlbMissR(mem, cpuRegs.branch); + + return -1; +} + +int __fastcall memRead128(u32 mem, u64 *out) { + + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x10000000: + hwRead128(mem, out); + return 0; + case 0x12000000: + out[0] = gsRead64(mem); + out[1] = gsRead64(mem + 8); + return 0; + + default: + out[0] = *(u64*)(PS2MEM_BASE+mem); + out[1] = *(u64*)(PS2MEM_BASE+mem+8); + return 0; + } + + MEM_LOG("Unknown Memory read32 from address %8.8x (Status=%8.8x)\n", mem, cpuRegs.CP0.n.Status.val); + cpuTlbMissR(mem, cpuRegs.branch); + + return -1; +} + +void __fastcall memWrite8 (u32 mem, u8 value) { + + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x1f400000: psxHw4Write8(mem, value); return; + case 0x10000000: hwWrite8(mem, value); return; + case 0x1f800000: psxHwWrite8(mem, value); return; + case 0x12000000: gsWrite8(mem, value); return; + case 0x14000000: + DEV9write8(mem & ~0x04000000, value); + SysPrintf("DEV9 write8 %8.8lx: %2.2lx\n", mem & ~0x04000000, value); + return; + + default: + *(u8*)(PS2MEM_BASE+mem) = value; + + if (CHECK_EEREC) { + REC_CLEARM(mem&~3); + } + return; + } + MEM_LOG("Unknown Memory write8 to address %x with data %2.2x\n", mem, value); + cpuTlbMissW(mem, cpuRegs.branch); +} + +void __fastcall memWrite16(u32 mem, u16 value) { + + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x10000000: hwWrite16(mem, value); return; + case 0x1f800000: psxHwWrite16(mem, value); return; + case 0x12000000: gsWrite16(mem, value); return; + case 0x1f900000: + case 0x1f000000: SPU2write(mem, value); return; + case 0x14000000: + DEV9write16(mem & ~0x04000000, value); + SysPrintf("DEV9 write16 %8.8lx: %4.4lx\n", mem & ~0x04000000, value); + return; + + default: + *(u16*)(PS2MEM_BASE+mem) = value; + if (CHECK_EEREC) { + REC_CLEARM(mem&~3); + } + return; + } + MEM_LOG("Unknown Memory write16 to address %x with data %4.4x\n", mem, value); + cpuTlbMissW(mem, cpuRegs.branch); +} + +void __fastcall memWrite32(u32 mem, u32 value) +{ + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { +case 0x10000000: hwWrite32(mem, value); return; +case 0x1f800000: psxHwWrite32(mem, value); return; +case 0x12000000: gsWrite32(mem, value); return; +case 0x1f900000: +case 0x1f000000: SPU2write(mem, value); return; +case 0x14000000: + DEV9write32(mem & ~0x4000000, value); + SysPrintf("DEV9 write32 %8.8lx: %8.8lx\n", mem & ~0x4000000, value); + return; + +default: + *(u32*)(PS2MEM_BASE+mem) = value; + + if (CHECK_EEREC) { + REC_CLEARM(mem); + } + return; + } + MEM_LOG("Unknown Memory write32 to address %x with data %8.8x\n", mem, value); + cpuTlbMissW(mem, cpuRegs.branch); +} + +void __fastcall memWrite64(u32 mem, const u64* value) { + + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x10000000: hwWrite64(mem, *value); return; + case 0x12000000: gsWrite64(mem, *value); return; + + default: + *(u64*)(PS2MEM_BASE+mem) = *value; + + if (CHECK_EEREC) { + REC_CLEARM(mem); + REC_CLEARM(mem+4); + } + return; + } + MEM_LOG("Unknown Memory write64 to address %x with data %8.8x_%8.8x\n", mem, (u32)((*value)>>32), (u32)value); + cpuTlbMissW(mem, cpuRegs.branch); +} + +void __fastcall memWrite128(u32 mem, const u64 *value) { + + mem = TRANSFORM_ADDR(mem); + switch( (mem&~0xffff) ) { + case 0x10000000: hwWrite128(mem, value); return; + case 0x12000000: + gsWrite64(mem, value[0]); + gsWrite64(mem + 8, value[1]); + return; + + default: + *(u64*)(PS2MEM_BASE+mem) = value[0]; + *(u64*)(PS2MEM_BASE+mem+8) = value[1]; + + if (CHECK_EEREC) { + REC_CLEARM(mem); + REC_CLEARM(mem+4); + REC_CLEARM(mem+8); + REC_CLEARM(mem+12); + } + return; + } + MEM_LOG("Unknown Memory write128 to address %x with data %8.8x_%8.8x_%8.8x_%8.8x\n", mem, ((u32*)value)[3], ((u32*)value)[2], ((u32*)value)[1], ((u32*)value)[0]); + cpuTlbMissW(mem, cpuRegs.branch); +} + +// Resets memory mappings, unmaps TLBs, reloads bios roms, etc. +void memReset() +{ +#ifdef _WIN32 + DWORD OldProtect; + // make sure can write + VirtualProtect(PS2MEM_ROM, Ps2MemSize::Rom, PAGE_READWRITE, &OldProtect); + VirtualProtect(PS2MEM_ROM1, Ps2MemSize::Rom1, PAGE_READWRITE, &OldProtect); + VirtualProtect(PS2MEM_ROM2, Ps2MemSize::Rom2, PAGE_READWRITE, &OldProtect); + VirtualProtect(PS2MEM_EROM, Ps2MemSize::ERom, PAGE_READWRITE, &OldProtect); +#else + mprotect(PS2EMEM_ROM, Ps2MemSize::Rom, PROT_READ|PROT_WRITE); + mprotect(PS2EMEM_ROM1, Ps2MemSize::Rom1, PROT_READ|PROT_WRITE); + mprotect(PS2EMEM_ROM2, Ps2MemSize::Rom2, PROT_READ|PROT_WRITE); + mprotect(PS2EMEM_EROM, Ps2MemSize::ERom, PROT_READ|PROT_WRITE); +#endif + + memzero_ptr(PS2MEM_BASE); + memzero_ptr(PS2MEM_SCRATCH); + vm_Reset(); + + string Bios; + FILE *fp; + + Path::Combine( Bios, Config.BiosDir, Config.Bios ); + + long filesize; + if( ( filesize = Path::getFileSize( Bios ) ) <= 0 ) + { + //Console::Error("Unable to load bios: '%s', PCSX2 can't run without that", params Bios); + throw Exception::FileNotFound( Bios, + "The specified Bios file was not found. A bios is required for Pcsx2 to run.\n\nFile not found" ); + } + + fp = fopen(Bios.c_str(), "rb"); + fread(PS2MEM_ROM, 1, std::min( (long)Ps2MemSize::Rom, filesize ), fp); + fclose(fp); + + BiosVersion = GetBiosVersion(); + Console::Status("Bios Version %d.%d", params BiosVersion >> 8, BiosVersion & 0xff); + + //injectIRX("host.irx"); //not fully tested; still buggy + + loadBiosRom("rom1", PS2MEM_ROM1, Ps2MemSize::Rom1); + loadBiosRom("rom2", PS2MEM_ROM2, Ps2MemSize::Rom2); + loadBiosRom("erom", PS2MEM_EROM, Ps2MemSize::ERom); + +#ifdef _WIN32 + VirtualProtect(PS2MEM_ROM, Ps2MemSize::Rom, PAGE_READONLY, &OldProtect); + VirtualProtect(PS2MEM_ROM1, Ps2MemSize::Rom1, PAGE_READONLY, &OldProtect); + VirtualProtect(PS2MEM_ROM2, Ps2MemSize::Rom2, PAGE_READONLY, &OldProtect); + VirtualProtect(PS2MEM_EROM, Ps2MemSize::ERom, PAGE_READONLY, &OldProtect); +#else + mprotect(PS2EMEM_ROM, Ps2MemSize::Rom, PROT_READ); + mprotect(PS2EMEM_ROM1, Ps2MemSize::Rom1, PROT_READ); + mprotect(PS2EMEM_ROM2, Ps2MemSize::Rom2, PROT_READ); + mprotect(PS2EMEM_EROM, Ps2MemSize::ERom, PROT_READ); +#endif +} + +#endif diff --git a/pcsx2/Misc.cpp b/pcsx2/Misc.cpp new file mode 100644 index 0000000000..20ed57f84c --- /dev/null +++ b/pcsx2/Misc.cpp @@ -0,0 +1,823 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#ifdef _WIN32 +#include "RDebug/deci2.h" +#else +#include +#endif + +#include + +#include "Common.h" +#include "PsxCommon.h" +#include "SaveState.h" + +#include "CDVDisodrv.h" +#include "VUmicro.h" +#include "VU.h" +#include "iCore.h" +#include "iVUzerorec.h" +#include "BaseblockEx.h" // for debuild block dumping (which may or may not work anymore?) + +#include "GS.h" +#include "COP0.h" +#include "Cache.h" + +#include "Paths.h" + +using namespace std; +using namespace R5900; + +PcsxConfig Config; +u32 BiosVersion; +char CdromId[12]; +static int g_Pcsx2Recording = 0; // true 1 if recording video and sound + +const char *LabelAuthors = { N_( + "PCSX2, a PS2 emulator\n\n" + "originally written by:\n" + "saqib, refraction, zerofrog,\n" + "shadow, linuzappz, florin,\n" + "nachbrenner, auMatt, loser, \n" + "alexey silinov, goldfinger\n" + "\n" + "Playground Mod Devs:\n" + "Arcum42, drkIIRaziel, Cottonvibes, \n" + "Jake.Stine, Rama\n\n" + "Playground Testing:\n" + "Krakatos\n" + "\n" + "Webmasters: CKemu, Falcon4ever") +}; + +const char *LabelGreets = { N_( + "Greets to: Bobbi, Keith, CpUMasteR, Nave, Snake785\n\n" + "Special thanks to: Gigaherz, Gabest, Sjeep, Dreamtime, F|RES, BGnome, MrBrown, \n" + "Seta-San, Skarmeth, blackd_wd, _Demo_\n" + "\n" + "Credits: Hiryu && Sjeep for their libcdvd (iso parsing and filesystem driver code)\n" + "\n" + "Some betatester/support dudes: Belmont, bositman, ChaosCode, CKemu, crushtest," + "Falcon4ever, GeneralPlot, jegHegy, parotaku, Prafull, Razorblade, Rudy_X, Seta-san") +}; + +static struct { + const char *name; + u32 size; +} ioprps[]={ + {"IOPRP14", 43845}, + {"IOPRP142", 48109}, + {"IOPRP143", 58317}, + {"IOPRP144", 58525}, + {"IOPRP15", 82741}, + {"IOPRP151", 82917}, + {"IOPRP153", 82949}, + {"IOPRP16", 91909}, + {"IOPRP165", 98901}, + {"IOPRP20", 109809}, + {"IOPRP202", 110993}, + {"IOPRP205", 119797}, + {"IOPRP21", 126857}, + {"IOPRP211", 129577}, + {"IOPRP213", 129577}, + {"IOPRP214", 140945}, + {"IOPRP22", 199257}, + {"IOPRP221", 196937}, + {"IOPRP222", 198233}, + {"IOPRP224", 201065}, + {"IOPRP23", 230329}, + {"IOPRP234", 247641}, + {"IOPRP24", 251065}, + {"IOPRP241", 251049}, + {"IOPRP242", 252409}, + {"IOPRP243", 253201}, + {"IOPRP250", 264897}, + {"IOPRP252", 265233}, + {"IOPRP253", 267217}, + {"IOPRP254", 264449}, + {"IOPRP255", 264449}, + {"IOPRP260", 248945}, + {"IOPRP270", 249121}, + {"IOPRP271", 266817}, + {"IOPRP280", 269889}, + {"IOPRP300", 275345}, + {"DNAS280", 272753}, + {"DNAS270", 251729}, + {"DNAS271", 268977}, + {"DNAS300", 278641}, + {"DNAS280", 272705}, + {"DNAS255", 264945}, + {NULL, 0} +}; + +void GetRPCVersion(char *ioprp, char *rpcver){ + char *p=ioprp; + int i; + struct TocEntry te; + + if (p && (CDVD_findfile(p+strlen("cdromN:"), &te) != -1)){ + for (i=0; ioprps[i].size>0; i++) + if (te.fileSize==ioprps[i].size) + break; + if (ioprps[i].size>0) + p=(char *)ioprps[i].name; + } + // fixme - Is p really supposed to be set in the middle of an if statement? + if (p && (p=strstr(p, "IOPRP")+strlen("IOPRP"))){ + for (i=0;(i<4) && p && (*p>='0') && (*p<='9');i++, p++) rpcver[i]=*p; + for ( ; i<4 ;i++ ) rpcver[i]='0'; + } +} + +u32 GetBiosVersion() { + unsigned int fileOffset=0; + s8 *ROMVER; + char vermaj[8]; + char vermin[8]; + struct romdir *rd; + u32 version; + int i; + + for (i=0; i<512*1024; i++) { + rd = (struct romdir*)&psRu8(i); + if (strncmp(rd->fileName, "RESET", 5) == 0) + break; /* found romdir */ + } + if (i == 512*1024) return -1; + + while(strlen(rd->fileName) > 0){ + if (strcmp(rd->fileName, "ROMVER") == 0){ // found romver + ROMVER = &psRs8(fileOffset); + + strncpy(vermaj, (char *)(ROMVER+ 0), 2); vermaj[2] = 0; + strncpy(vermin, (char *)(ROMVER+ 2), 2); vermin[2] = 0; + version = strtol(vermaj, (char**)NULL, 0) << 8; + version|= strtol(vermin, (char**)NULL, 0); + + return version; + } + + if ((rd->fileSize % 0x10)==0) + fileOffset += rd->fileSize; + else + fileOffset += (rd->fileSize + 0x10) & 0xfffffff0; + + rd++; + } + + return -1; +} + +//2002-09-22 (Florin) +int IsBIOS(char *filename, char *description) +{ + string Bios; + char ROMVER[14+1], zone[12+1]; + FILE *fp; + unsigned int fileOffset=0, found=FALSE; + struct romdir rd; + + Path::Combine( Bios, Config.BiosDir, filename ); + + int biosFileSize = Path::getFileSize( Bios ); + if( biosFileSize <= 0) return FALSE; + + fp = fopen(Bios.c_str(), "rb"); + if (fp == NULL) return FALSE; + + while ((ftell(fp)<512*1024) && (fread(&rd, DIRENTRY_SIZE, 1, fp)==1)) + if (strcmp(rd.fileName, "RESET") == 0) + break; /* found romdir */ + + if ((strcmp(rd.fileName, "RESET") != 0) || (rd.fileSize == 0)) { + fclose(fp); + return FALSE; //Unable to locate ROMDIR structure in file or a ioprpXXX.img + } + + while(strlen(rd.fileName) > 0){ + if (strcmp(rd.fileName, "ROMVER") == 0){ // found romver + unsigned int filepos=ftell(fp); + fseek(fp, fileOffset, SEEK_SET); + if (fread(&ROMVER, 14, 1, fp) == 0) break; + fseek(fp, filepos, SEEK_SET);//go back + + switch(ROMVER[4]){ + case 'T':sprintf(zone, "T10K "); break; + case 'X':sprintf(zone, "Test ");break; + case 'J':sprintf(zone, "Japan "); break; + case 'A':sprintf(zone, "USA "); break; + case 'E':sprintf(zone, "Europe"); break; + case 'H':sprintf(zone, "HK "); break; + case 'P':sprintf(zone, "Free "); break; + case 'C':sprintf(zone, "China "); break; + default: sprintf(zone, "%c ",ROMVER[4]); break;//shoudn't show + } + sprintf(description, "%s vXX.XX(XX/XX/XXXX) %s", zone, + ROMVER[5]=='C'?"Console":ROMVER[5]=='D'?"Devel":""); + strncpy(description+ 8, ROMVER+ 0, 2);//ver major + strncpy(description+11, ROMVER+ 2, 2);//ver minor + strncpy(description+14, ROMVER+12, 2);//day + strncpy(description+17, ROMVER+10, 2);//month + strncpy(description+20, ROMVER+ 6, 4);//year + found = TRUE; + } + + if ((rd.fileSize % 0x10)==0) + fileOffset += rd.fileSize; + else + fileOffset += (rd.fileSize + 0x10) & 0xfffffff0; + + if (fread(&rd, DIRENTRY_SIZE, 1, fp)==0) break; + } + fileOffset-=((rd.fileSize + 0x10) & 0xfffffff0) - rd.fileSize; + + fclose(fp); + + if (found) + { + char percent[6]; + + if ( biosFileSize < (int)fileOffset) + { + sprintf(percent, " %d%%", biosFileSize*100/(int)fileOffset); + strcat(description, percent);//we force users to have correct bioses, + //not that lame scph10000 of 513KB ;-) + } + return TRUE; + } + + return FALSE; //fail quietly +} + +// LOAD STUFF + +// fixme - Is there any reason why we shouldn't delete this define, and replace the array lengths +// with the actual numbers? +#define ISODCL(from, to) (to - from + 1) + +struct iso_directory_record { + char length [ISODCL (1, 1)]; /* length[1]; 711 */ + char ext_attr_length [ISODCL (2, 2)]; /* ext_attr_length[1]; 711 */ + char extent [ISODCL (3, 10)]; /* extent[8]; 733 */ + char size [ISODCL (11, 18)]; /* size[8]; 733 */ + char date [ISODCL (19, 25)]; /* date[7]; 7 by 711 */ + char flags [ISODCL (26, 26)]; /* flags[1]; */ + char file_unit_size [ISODCL (27, 27)]; /* file_unit_size[1]; 711 */ + char interleave [ISODCL (28, 28)]; /* interleave[1]; 711 */ + char volume_sequence_number [ISODCL (29, 32)]; /* volume_sequence_number[3]; 723 */ + unsigned char name_len [ISODCL (33, 33)]; /* name_len[1]; 711 */ + char name [1]; +}; + +int LoadCdrom() { + return 0; +} + +int CheckCdrom() { + u8 *buf; + + if (CDVDreadTrack(16, CDVD_MODE_2352) == -1) + return -1; + buf = CDVDgetBuffer(); + if (buf == NULL) + return -1; + + strncpy(CdromId, (char*)buf+52, 10); + + return 0; +} + +int GetPS2ElfName(char *name){ + int f; + char buffer[g_MaxPath];//if a file is longer...it should be shorter :D + char *pos; + TocEntry tocEntry; + + CDVDFS_init(); + + // check if the file exists + if (CDVD_findfile("SYSTEM.CNF;1", &tocEntry) != TRUE){ + Console::Error("Boot Error > SYSTEM.CNF not found"); + return 0;//could not find; not a PS/PS2 cdvd + } + + f=CDVDFS_open("SYSTEM.CNF;1", 1); + CDVDFS_read(f, buffer, g_MaxPath); + CDVDFS_close(f); + + buffer[tocEntry.fileSize]='\0'; + + pos=strstr(buffer, "BOOT2"); + if (pos==NULL){ + pos=strstr(buffer, "BOOT"); + if (pos==NULL) { + Console::Error("Boot Error > This is not a PS2 game!"); + return 0; + } + return 1; + } + pos+=strlen("BOOT2"); + while (pos && *pos && pos<=&buffer[255] + && (*pos<'A' || (*pos>'Z' && *pos<'a') || *pos>'z')) + pos++; + if (!pos || *pos==0) + return 0; + + sscanf(pos, "%s", name); + + if (strncmp("cdrom0:\\", name, 8) == 0) { + strncpy(CdromId, name+8, 11); CdromId[11] = 0; + } + +#ifdef PCSX2_DEVBUILD + FILE *fp; + int i; + + // inifile_read(CdromId); + fp = fopen("System.map", "r"); + if( fp == NULL ) return 2; + + u32 addr; + + Console::WriteLn("Loading System.map..."); + while (!feof(fp)) { + fseek(fp, 8, SEEK_CUR); + buffer[0] = '0'; buffer[1] = 'x'; + for (i=2; i<10; i++) buffer[i] = fgetc(fp); buffer[i] = 0; + addr = strtoul(buffer, (char**)NULL, 0); + fseek(fp, 3, SEEK_CUR); + for (i=0; iFreeze( g_nLeftGSFrames ); +} + +extern uptr pDsp; +void LoadGSState(const string& file) +{ + int ret; + gzLoadingState* f; + + Console::Status( "Loading GS State..." ); + + try + { + f = new gzLoadingState( file ); + } + catch( Exception::FileNotFound& ) + { + // file not found? try prefixing with sstates folder: + if( !Path::isRooted( file ) ) + { + string strfile; + Path::Combine( strfile, SSTATES_DIR, file ); + f = new gzLoadingState( strfile.c_str() ); + + // If this load attempt fails, then let the exception bubble up to + // the caller to deal with... + } + } + + // Always set gsIrq callback -- GS States are always exclusionary of MTGS mode + GSirqCallback( gsIrq ); + + ret = GSopen(&pDsp, "PCSX2", 0); + if (ret != 0) + { + delete f; + throw Exception::PluginFailure( "GS" ); + } + + ret = PAD1open((void *)&pDsp); + + f->Freeze(g_nLeftGSFrames); + f->gsFreeze(); + + f->FreezePlugin( "GS", gsSafeFreeze ); + + RunGSState( *f ); + + delete( f ); + + GSclose(); + PAD1close(); +} + +#endif + +struct LangDef { + char id[8]; + char name[64]; +}; + +LangDef sLangs[] = { + { "ar_AR", N_("Arabic") }, + { "bg_BG", N_("Bulgarian") }, + { "ca_CA", N_("Catalan") }, + { "cz_CZ", N_("Czech") }, + { "du_DU", N_("Dutch") }, + { "de_DE", N_("German") }, + { "el_EL", N_("Greek") }, + { "en_US", N_("English") }, + { "fr_FR", N_("French") }, + { "hb_HB" , N_("Hebrew") }, + { "hu_HU", N_("Hungarian") }, + { "it_IT", N_("Italian") }, + { "ja_JA", N_("Japanese") }, + { "pe_PE", N_("Persian") }, + { "po_PO", N_("Portuguese") }, + { "po_BR", N_("Portuguese BR") }, + { "pl_PL" , N_("Polish") }, + { "ro_RO", N_("Romanian") }, + { "ru_RU", N_("Russian") }, + { "es_ES", N_("Spanish") }, + { "sh_SH" , N_("S-Chinese") }, + { "sw_SW", N_("Swedish") }, + { "tc_TC", N_("T-Chinese") }, + { "tr_TR", N_("Turkish") }, + { "", "" }, +}; + + +char *ParseLang(char *id) { + int i=0; + + while (sLangs[i].id[0] != 0) { + if (!strcmp(id, sLangs[i].id)) + return _(sLangs[i].name); + i++; + } + + return id; +} + +#define NUM_STATES 10 +int StatesC = 0; + +extern char strgametitle[256]; + +char* mystrlwr( char* string ) +{ + assert( string != NULL ); + while ( 0 != ( *string++ = (char)tolower( *string ) ) ); + return string; +} + +static void GetGSStateFilename( string& dest ) +{ + string gsText; + ssprintf( gsText, "/%8.8X.%d.gs", ElfCRC, StatesC); + Path::Combine( dest, SSTATES_DIR, gsText ); +} + +void ProcessFKeys(int fkey, int shift) +{ + string Text; + + assert(fkey >= 1 && fkey <= 12 ); + + switch(fkey) { + case 1: + try + { + SaveState::GetFilename( Text, StatesC ); + gzSavingState( Text ).FreezeAll(); + } + catch( std::exception& ex ) + { + // 99% of the time this is a file permission error and the + // cpu state is intact so just display a passive msg to console. + + Console::Error( _( "Error > Could not save state to slot %d" ), params StatesC ); + Console::Error( ex.what() ); + } + break; + + case 2: + if( shift ) + StatesC = (StatesC+NUM_STATES-1) % NUM_STATES; + else + StatesC = (StatesC+1) % NUM_STATES; + + Console::Notice( _( " > Selected savestate slot %d" ), params StatesC); + + if( GSchangeSaveState != NULL ) { + SaveState::GetFilename(Text, StatesC); + GSchangeSaveState(StatesC, Text.c_str()); + } + break; + + case 3: + try + { + SaveState::GetFilename( Text, StatesC ); + gzLoadingState joe( Text ); // throws exception on version mismatch + cpuReset(); + SysResetExecutionState(); + joe.FreezeAll(); + } + catch( Exception::StateLoadError_Recoverable& ) + { + // At this point the cpu hasn't been reset, so we can return + // control to the user safely... (and silently) + } + catch( Exception::FileNotFound& ) + { + Console::Notice( _("Saveslot %d cannot be loaded; slot does not exist (file not found)"), params StatesC ); + } + catch( Exception::RuntimeError& ex ) + { + // This is the bad one. Chances are the cpu has been reset, so emulation has + // to be aborted. Sorry user! We'll give you some info for your trouble: + + Console::Error( _("An error occured while trying to load saveslot %d"), params StatesC ); + Console::Error( ex.cMessage() ); + Msgbox::Alert( + "Pcsx2 encountered an error while trying to load the savestate\n" + "and emulation had to be aborted." ); + + ClosePlugins(); + + throw Exception::CpuStateShutdown( + "Saveslot load failed; PS2 emulated state had to be shut down." ); // let the GUI handle the error "gracefully" + } + break; + + case 4: + { + const char* limitMsg; + u32 newOptions; + // cycle + if( shift ) { + // previous + newOptions = (Config.Options&~PCSX2_FRAMELIMIT_MASK)|(((Config.Options&PCSX2_FRAMELIMIT_MASK)+PCSX2_FRAMELIMIT_VUSKIP)&PCSX2_FRAMELIMIT_MASK); + } + else { + // next + newOptions = (Config.Options&~PCSX2_FRAMELIMIT_MASK)|(((Config.Options&PCSX2_FRAMELIMIT_MASK)+PCSX2_FRAMELIMIT_LIMIT)&PCSX2_FRAMELIMIT_MASK); + } + + gsResetFrameSkip(); + + switch(newOptions & PCSX2_FRAMELIMIT_MASK) { + case PCSX2_FRAMELIMIT_NORMAL: + limitMsg = "None/Normal"; + break; + case PCSX2_FRAMELIMIT_LIMIT: + limitMsg = "Limit"; + break; + case PCSX2_FRAMELIMIT_SKIP: + case PCSX2_FRAMELIMIT_VUSKIP: + if( GSsetFrameSkip == NULL ) + { + newOptions &= ~PCSX2_FRAMELIMIT_MASK; + Console::Notice("Notice: GS Plugin does not support frameskipping."); + limitMsg = "None/Normal"; + } + else + { + // When enabling Skipping we have to make sure Skipper (GS) and Limiter (EE) + // are properly synchronized. + gsDynamicSkipEnable(); + limitMsg = ((newOptions & PCSX2_FRAMELIMIT_MASK) == PCSX2_FRAMELIMIT_SKIP) ? "Skip" : "VUSkip"; + } + + break; + } + Threading::AtomicExchange( Config.Options, newOptions ); + + Console::Notice("Frame Limit Mode Changed: %s", params limitMsg ); + + // [Air]: Do we really want to save runtime changes to frameskipping? + //SaveConfig(); + } + break; + + // note: VK_F5-VK_F7 are reserved for GS + case 8: + GSmakeSnapshot("snap/"); + break; + +#ifdef PCSX2_DEVBUILD + case 10: + { + int num; + FILE* f; + BASEBLOCKEX** ppblocks = GetAllBaseBlocks(&num, 0); + + f = fopen("perflog.txt", "w"); + while(num-- > 0 ) { + if( ppblocks[0]->visited > 0 ) { + fprintf(f, "%u %u %u %u\n", ppblocks[0]->startpc, (u32)(ppblocks[0]->ltime.QuadPart / ppblocks[0]->visited), ppblocks[0]->visited, ppblocks[0]->size); + } + ppblocks[0]->visited = 0; + ppblocks[0]->ltime.QuadPart = 0; + ppblocks++; + } + fclose(f); + Console::Status( "perflog.txt written" ); + break; + } + + case 11: + if( mtgsThread != NULL ) { + Console::Notice( "Cannot make gsstates in MTGS mode" ); + } + else { + if( strgametitle[0] != 0 ) { + // only take the first two words + char name[256], *tok; + string gsText; + + tok = strtok(strgametitle, " "); + sprintf(name, "%s_", mystrlwr(tok)); + tok = strtok(NULL, " "); + if( tok != NULL ) strcat(name, tok); + + ssprintf( gsText, "%s.%d.gs", name, StatesC); + Path::Combine( Text, SSTATES_DIR, gsText ); + } + else + GetGSStateFilename( Text ); + + SaveGSState(Text); + } + break; +#endif + + case 12: + if( shift ) { +#ifdef PCSX2_DEVBUILD + iDumpRegisters(cpuRegs.pc, 0); + Console::Notice("hardware registers dumped EE:%x, IOP:%x\n", params cpuRegs.pc, psxRegs.pc); +#endif + } + else { + g_Pcsx2Recording ^= 1; + if( mtgsThread != NULL ) { + mtgsThread->SendSimplePacket(GS_RINGTYPE_RECORD, g_Pcsx2Recording, 0, 0); + } + else { + if( GSsetupRecording != NULL ) GSsetupRecording(g_Pcsx2Recording, NULL); + } + if( SPU2setupRecording != NULL ) SPU2setupRecording(g_Pcsx2Recording, NULL); + } + break; + } +} + +void injectIRX(const char *filename) +{ + string path; + char name[260], *p, *q; + struct romdir *rd; + int iROMDIR=-1, iIOPBTCONF=-1, iBLANK=-1, i, filesize; + FILE *fp; + + strcpy(name, filename); + for (i=0; name[i] && name[i]!='.' && i<10; i++) name[i]=toupper(name[i]);name[i]=0; + + //phase 1: find ROMDIR in bios + for (p=(char*)PS2MEM_ROM; p<(char*)PS2MEM_ROM+0x80000; p++) + if (strncmp(p, "RESET", 5)==0) + break; + rd=(struct romdir*)p; + + for (i=0; rd[i].fileName[0]; i++)if (strncmp(rd[i].fileName, name, strlen(name))==0)break; + if (rd[i].fileName[0])return;//already in;) + + //phase 2: make room in IOPBTCONF & ROMDIR + for (i=0; rd[i].fileName[0]; i++)if (strncmp(rd[i].fileName, "ROMDIR", 6)==0)iROMDIR=i; + for (i=0; rd[i].fileName[0]; i++)if (strncmp(rd[i].fileName, "IOPBTCONF", 9)==0)iIOPBTCONF=i; + + for (i=0; rd[i].fileName[0]; i++)if (rd[i].fileName[0]=='-')break; iBLANK=i; + rd[iBLANK].fileSize-=DIRENTRY_SIZE+DIRENTRY_SIZE; + p=(char*)PS2MEM_ROM;for (i=0; iq){*((u64*)p)=*((u64*)p-4);*((u64*)p+1)=*((u64*)p-3);p-=DIRENTRY_SIZE;} + *((u64*)p)=*((u64*)p+1)=0;p-=DIRENTRY_SIZE;rd[iIOPBTCONF].fileSize+=DIRENTRY_SIZE; + + q=(char*)PS2MEM_ROM;for (i=0; i<=iROMDIR; i++) q+=(rd[i].fileSize+0xF)&(~0xF); + while (p >q){*((u64*)p)=*((u64*)p-2);*((u64*)p+1)=*((u64*)p-1);p-=DIRENTRY_SIZE;} + *((u64*)p)=*((u64*)p+1)=0;p-=DIRENTRY_SIZE;rd[iROMDIR].fileSize+=DIRENTRY_SIZE; + + //phase 3: add the name to the end of IOPBTCONF + p=(char*)PS2MEM_ROM;for (i=0; i +#include +#endif + +#undef _ +#define _(String) dgettext (PACKAGE, String) +#ifdef gettext_noop +# define N_(String) gettext_noop (String) +#else +# define N_(String) (String) +#endif + +#else + +#define _(msgid) msgid +#define N_(msgid) msgid + +#endif // ENABLE_NLS + +// [TODO] : Move the config options mess from Misc.h into "config.h" or someting more sensible. + +///////////////////////////////////////////////////////////////////////// +// Session Configuration Override Flags +// +// a handful of flags that can override user configurations for the current application session +// only. This allows us to do things like force-disable recompilers if the memory allocations +// for them fail. +struct SessionOverrideFlags +{ + bool ForceDisableEErec:1; + bool ForceDisableVU0rec:1; + bool ForceDisableVU1rec:1; +}; + +extern SessionOverrideFlags g_Session; + +////////////////////////////////////////////////////////////////////////// +// Pcsx2 User Configuration Options! + +//#define PCSX2_MEGAVU // Use Mega VU recs instead of Zero VU Recs +#define PCSX2_GSMULTITHREAD 1 // uses multi-threaded gs +#define PCSX2_EEREC 0x10 +#define PCSX2_VU0REC 0x20 +#define PCSX2_VU1REC 0x40 +#define PCSX2_FRAMELIMIT_MASK 0xc00 +#define PCSX2_FRAMELIMIT_NORMAL 0x000 +#define PCSX2_FRAMELIMIT_LIMIT 0x400 +#define PCSX2_FRAMELIMIT_SKIP 0x800 +#define PCSX2_FRAMELIMIT_VUSKIP 0xc00 + +#define CHECK_MULTIGS (Config.Options&PCSX2_GSMULTITHREAD) +#define CHECK_EEREC (!g_Session.ForceDisableEErec && Config.Options&PCSX2_EEREC) +//------------ SPEED/MISC HACKS!!! --------------- +#define CHECK_EE_CYCLERATE (Config.Hacks & 0x03) +#define CHECK_IOP_CYCLERATE (Config.Hacks & (1<<3)) +#define CHECK_WAITCYCLE_HACK (Config.Hacks & (1<<4)) +#define CHECK_ESCAPE_HACK (Config.Hacks & 0x400) +//------------ SPECIAL GAME FIXES!!! --------------- +#define CHECK_VUADDSUBHACK (Config.GameFixes & 0x1) // Special Fix for Tri-ace games, they use an encryption algorithm that requires VU addi opcode to be bit-accurate. +#define CHECK_FPUCLAMPHACK (Config.GameFixes & 0x4) // Special Fix for Tekken 5, different clamping for FPU (sets NaN to zero; doesn't clamp infinities) +#define CHECK_VUBRANCHHACK (Config.GameFixes & 0x8) // Special Fix for Magna Carta (note: Breaks Crash Bandicoot) +//------------ Advanced Options!!! --------------- +#define CHECK_VU_OVERFLOW (Config.vuOptions & 0x1) +#define CHECK_VU_EXTRA_OVERFLOW (Config.vuOptions & 0x2) // If enabled, Operands are clamped before being used in the VU recs +#define CHECK_VU_SIGN_OVERFLOW (Config.vuOptions & 0x4) +#define CHECK_VU_UNDERFLOW (Config.vuOptions & 0x8) +#define CHECK_VU_EXTRA_FLAGS 0 // Always disabled now // Sets correct flags in the VU recs +#define CHECK_FPU_OVERFLOW (Config.eeOptions & 0x1) +#define CHECK_FPU_EXTRA_OVERFLOW (Config.eeOptions & 0x2) // If enabled, Operands are checked for infinities before being used in the FPU recs +#define CHECK_FPU_EXTRA_FLAGS 1 // Always enabled now // Sets D/I flags on FPU instructions +#define DEFAULT_eeOptions 0x01 +#define DEFAULT_vuOptions 0x01 +//------------ DEFAULT sseMXCSR VALUES!!! --------------- +#define DEFAULT_sseMXCSR 0xffc0 //FPU rounding, DaZ, FtZ, "chop" +#define DEFAULT_sseVUMXCSR 0x7f80 //VU rounding, "chop" + +#define CHECK_FRAMELIMIT (Config.Options&PCSX2_FRAMELIMIT_MASK) + +#define CHECK_VU0REC (!g_Session.ForceDisableVU0rec && Config.Options&PCSX2_VU0REC) +#define CHECK_VU1REC (!g_Session.ForceDisableVU1rec && (Config.Options&PCSX2_VU1REC)) + + +struct PcsxConfig +{ + char Bios[g_MaxPath]; + char GS[g_MaxPath]; + char PAD1[g_MaxPath]; + char PAD2[g_MaxPath]; + char SPU2[g_MaxPath]; + char CDVD[g_MaxPath]; + char DEV9[g_MaxPath]; + char USB[g_MaxPath]; + char FW[g_MaxPath]; + char Mcd1[g_MaxPath]; + char Mcd2[g_MaxPath]; + char PluginsDir[g_MaxPath]; + char BiosDir[g_MaxPath]; + char Lang[g_MaxPath]; + + u32 Options; // PCSX2_X options + + bool PsxOut; + bool Profiler; // Displays profiling info to console + bool cdvdPrint; // Prints cdvd reads to console + bool closeGSonEsc; // closes the GS (and saves its state) on escape automatically. + + int PsxType; + int Cdda; + int Mdec; + int Patch; + int ThPriority; + int CustomFps; + int Hacks; + int GameFixes; + int CustomFrameSkip; + int CustomConsecutiveFrames; + int CustomConsecutiveSkip; + u32 sseMXCSR; + u32 sseVUMXCSR; + u32 eeOptions; + u32 vuOptions; +}; + +extern PcsxConfig Config; +extern u32 BiosVersion; +extern char CdromId[12]; +extern uptr pDsp; // what the hell is this unused piece of crap passed to every plugin for? (air) + +int LoadCdrom(); +int CheckCdrom(); +int GetPS2ElfName(char*); + +extern const char *LabelAuthors; +extern const char *LabelGreets; + +void SaveGSState(const string& file); +void LoadGSState(const string& file); + +char *ParseLang(char *id); +void ProcessFKeys(int fkey, int shift); // processes fkey related commands value 1-12 + +#define DIRENTRY_SIZE 16 + +#if defined(_MSC_VER) +#pragma pack(1) +#endif + +struct romdir{ + char fileName[10]; + u16 extInfoSize; + u32 fileSize; +#if defined(_MSC_VER) +}; //+22 +#else +} __attribute__((packed)); +#endif + +u32 GetBiosVersion(); +int IsBIOS(char *filename, char *description); + +extern u32 g_sseVUMXCSR, g_sseMXCSR; + +void SetCPUState(u32 sseMXCSR, u32 sseVUMXCSR); + +// when using mmx/xmm regs, use; 0 is load +// freezes no matter the state +extern void FreezeXMMRegs_(int save); +extern void FreezeMMXRegs_(int save); +extern bool g_EEFreezeRegs; +extern u8 g_globalMMXSaved; +extern u8 g_globalXMMSaved; + +// these macros check to see if needs freezing +#define FreezeXMMRegs(save) if( g_EEFreezeRegs ) { FreezeXMMRegs_(save); } +#define FreezeMMXRegs(save) if( g_EEFreezeRegs ) { FreezeMMXRegs_(save); } + +#ifdef _MSC_VER +#pragma pack() +#endif + +void injectIRX(const char *filename); + +extern void InitCPUTicks(); +extern u64 GetTickFrequency(); +extern u64 GetCPUTicks(); + + +#endif /* __MISC_H__ */ + diff --git a/pcsx2/Patch.cpp b/pcsx2/Patch.cpp new file mode 100644 index 0000000000..b7527fa9e0 --- /dev/null +++ b/pcsx2/Patch.cpp @@ -0,0 +1,642 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// +// Includes +// +#include "PrecompiledHeader.h" + +#include "PsxCommon.h" +#include "Paths.h" +#include "Patch.h" +#include "VU.h" + +#ifdef _WIN32 +#include "windows/cheats/cheats.h" +#else +#include +#include +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4996) //ignore the stricmp deprecated warning +#endif + +IniPatch patch[ MAX_PATCH ]; + +u32 SkipCount=0; +u32 IterationCount=0; +u32 IterationIncrement=0; +u32 ValueIncrement=0; +u32 PrevCheatType=0; +u32 PrevCheataddr = 0; +u32 LastType = 0; + +int g_ZeroGSOptions=0; +int patchnumber; + +char strgametitle[256]= {0}; + +// +// Variables +// +PatchTextTable commands[] = +{ + { "comment", 1, patchFunc_comment }, + { "gametitle", 2, patchFunc_gametitle }, + { "patch", 3, patchFunc_patch }, + { "fastmemory", 4, patchFunc_fastmemory }, // enable for faster but bugger mem (mvc2 is faster) + { "roundmode", 5, patchFunc_roundmode }, // changes rounding mode for floating point + // syntax: roundmode=X,Y + // possible values for X,Y: NEAR, DOWN, UP, CHOP + // X - EE rounding mode (default is NEAR) + // Y - VU rounding mode (default is CHOP) + { "zerogs", 6, patchFunc_zerogs }, // zerogs=hex + { "path3hack", 7, patchFunc_path3hack }, + { "vunanmode",8, patchFunc_vunanmode }, + { "ffxhack",9, patchFunc_ffxhack}, + { "xkickdelay",10, patchFunc_xkickdelay}, + { "", 0, NULL } +}; + +PatchTextTable dataType[] = +{ + { "byte", 1, NULL }, + { "short", 2, NULL }, + { "word", 3, NULL }, + { "double", 4, NULL }, + { "extended", 5, NULL }, + { "", 0, NULL } +}; + +PatchTextTable cpuCore[] = +{ + { "EE", 1, NULL }, + { "IOP", 2, NULL }, + { "", 0, NULL } +}; + +// +// Function Implementations +// + +int PatchTableExecute( char * text1, char * text2, PatchTextTable * Table ) +{ + int i = 0; + + while ( Table[ i ].text[ 0 ] ) + { + if ( !strcmp( Table[ i ].text, text1 ) ) + { + if ( Table[ i ].func ) + { + Table[ i ].func( text1, text2 ); + } + break; + } + i++; + } + + return Table[ i ].code; +} + +void _applypatch(int place, IniPatch *p) { + u8 u8Val=0; + u16 u16Val=0; + u32 u32Val=0; + u32 i; + + if (p->placetopatch != place) return; + if (p->enabled == 0) return; + + switch (p->cpu) { + case CPU_EE: + switch (p->type) { + case BYTE_T: + memWrite8(p->addr, (u8)p->data); + break; + case SHORT_T: + memWrite16(p->addr, (u16)p->data); + break; + case WORD_T: + memWrite32(p->addr, (u32)p->data); + break; + case DOUBLE_T: + memWrite64(p->addr, &p->data); + break; + case EXTENDED_T: + if (SkipCount > 0){ + SkipCount--; + } + else switch (PrevCheatType) { + case 0x3040: // vvvvvvvv 00000000 Inc + memRead32(PrevCheataddr,&u32Val); + memWrite32(PrevCheataddr, u32Val+(p->addr)); + PrevCheatType = 0; + break; + case 0x3050: // vvvvvvvv 00000000 Dec + memRead32(PrevCheataddr,&u32Val); + memWrite32(PrevCheataddr, u32Val-(p->addr)); + PrevCheatType = 0; + break; + case 0x4000: // vvvvvvvv iiiiiiii + for(i=0;iaddr+((u32)p->data*i))); + PrevCheatType = 0; + break; + case 0x5000: // dddddddd iiiiiiii + for(i=0;idata)+i,u8Val); + } + PrevCheatType = 0; + break; + case 0x6000: // 000Xnnnn iiiiiiii + // Get Number of pointers + if (IterationCount == 0) + IterationCount = (u32)p->addr&0x0000FFFF; + + // Read first pointer + LastType = ((u32)p->addr&0x000F0000)/0x10000; + memRead32(PrevCheataddr,&u32Val); + PrevCheataddr = u32Val+(u32)p->data; + IterationCount--; + + // Check if needed to read another pointer + if (IterationCount == 0){ + PrevCheatType = 0; + if (LastType==0x0) + memWrite8(PrevCheataddr,IterationIncrement&0xFF); + if (LastType==0x1) + memWrite16(PrevCheataddr,IterationIncrement&0xFFFF); + if (LastType==0x2) + memWrite32(PrevCheataddr,IterationIncrement); + } + else + PrevCheatType = 0x6001; + case 0x6001: // 000Xnnnn iiiiiiii + // Read first pointer + memRead32(PrevCheataddr,&u32Val); + PrevCheataddr =u32Val+(u32)p->addr; + IterationCount--; + + // Check if needed to read another pointer + if (IterationCount == 0){ + PrevCheatType = 0; + if (LastType==0x0) + memWrite8(PrevCheataddr,IterationIncrement&0xFF); + if (LastType==0x1) + memWrite16(PrevCheataddr,IterationIncrement&0xFFFF); + if (LastType==0x2) + memWrite32(PrevCheataddr,IterationIncrement); + } + else + { + memRead32(PrevCheataddr,&u32Val); + PrevCheataddr =u32Val+(u32)p->data; + IterationCount--; + if (IterationCount == 0){ + PrevCheatType = 0; + if (LastType==0x0) + memWrite8(PrevCheataddr,IterationIncrement&0xFF); + if (LastType==0x1) + memWrite16(PrevCheataddr,IterationIncrement&0xFFFF); + if (LastType==0x2) + memWrite32(PrevCheataddr,IterationIncrement); + } + } + default: + if ((p->addr&0xF0000000) == 0x00000000){ // 0aaaaaaa 0000000vv + memWrite8(p->addr&0x0FFFFFFF, (u8)p->data&0x000000FF); + PrevCheatType = 0; + } + else if ((p->addr&0xF0000000) == 0x10000000){ // 0aaaaaaa 0000vvvv + memWrite16(p->addr&0x0FFFFFFF, (u16)p->data&0x0000FFFF); + PrevCheatType = 0; + } + else if ((p->addr&0xF0000000) == 0x20000000){ // 0aaaaaaa vvvvvvvv + memWrite32(p->addr&0x0FFFFFFF, (u32)p->data); + PrevCheatType = 0; + } + else if ((p->addr&0xFFFF0000) == 0x30000000){ // 300000vv 0aaaaaaa Inc + memRead8((u32)p->data,&u8Val); + memWrite8((u32)p->data, u8Val+(p->addr&0x000000FF)); + PrevCheatType = 0; + } + else if ((p->addr&0xFFFF0000) == 0x30100000){ // 301000vv 0aaaaaaa Dec + memRead8((u32)p->data,&u8Val); + memWrite8((u32)p->data, u8Val-(p->addr&0x000000FF)); + PrevCheatType = 0; + } + else if ((p->addr&0xFFFF0000) == 0x30200000){ // 3020vvvv 0aaaaaaa Inc + memRead16((u32)p->data,&u16Val); + memWrite16((u32)p->data, u16Val+(p->addr&0x0000FFFF)); + PrevCheatType = 0; + } + else if ((p->addr&0xFFFF0000) == 0x30300000){ // 3030vvvv 0aaaaaaa Dec + memRead16((u32)p->data,&u16Val); + memWrite16((u32)p->data, u16Val-(p->addr&0x0000FFFF)); + PrevCheatType = 0; + } + else if ((p->addr&0xFFFF0000) == 0x30400000){ // 30400000 0aaaaaaa Inc + Another line + PrevCheatType= 0x3040; + PrevCheataddr= (u32)p->data; + } + else if ((p->addr&0xFFFF0000) == 0x30500000){ // 30500000 0aaaaaaa Inc + Another line + PrevCheatType= 0x3050; + PrevCheataddr= (u32)p->data; + } + else if ((p->addr&0xF0000000) == 0x40000000){ // 4aaaaaaa nnnnssss + Another line + IterationCount=((u32)p->data&0xFFFF0000)/0x10000; + IterationIncrement=((u32)p->data&0x0000FFFF)*4; + PrevCheataddr=(u32)p->addr&0x0FFFFFFF; + PrevCheatType= 0x4000; + } + else if ((p->addr&0xF0000000) == 0x50000000){ // 5sssssss nnnnnnnn + Another line + PrevCheataddr = (u32)p->addr&0x0FFFFFFF; + IterationCount=((u32)p->data); + PrevCheatType= 0x5000; + } + else if ((p->addr&0xF0000000) == 0x60000000){ // 6aaaaaaa 000000vv + Another line/s + PrevCheataddr = (u32)p->addr&0x0FFFFFFF; + IterationIncrement=((u32)p->data); + IterationCount=0; + PrevCheatType= 0x6000; + } + else if ((p->addr&0xF0000000) == 0x70000000) { + if ((p->data&0x00F00000) == 0x00000000){ // 7aaaaaaa 000000vv + memRead8((u32)p->addr&0x0FFFFFFF,&u8Val); + memWrite8((u32)p->addr&0x0FFFFFFF,(u8)(u8Val|(p->data&0x000000FF))); + }else if ((p->data&0x00F00000) == 0x00100000){ // 7aaaaaaa 0010vvvv + memRead16((u32)p->addr&0x0FFFFFFF,&u16Val); + memWrite16((u32)p->addr&0x0FFFFFFF,(u16)(u16Val|(p->data&0x0000FFFF))); + }else if ((p->data&0x00F00000) == 0x00200000){ // 7aaaaaaa 002000vv + memRead8((u32)p->addr&0x0FFFFFFF,&u8Val); + memWrite8((u32)p->addr&0x0FFFFFFF,(u8)(u8Val&(p->data&0x000000FF))); + }else if ((p->data&0x00F00000) == 0x00300000){ // 7aaaaaaa 0030vvvv + memRead16((u32)p->addr&0x0FFFFFFF,&u16Val); + memWrite16((u32)p->addr&0x0FFFFFFF,(u16)(u16Val&(p->data&0x0000FFFF))); + }else if ((p->data&0x00F00000) == 0x00400000){ // 7aaaaaaa 004000vv + memRead8((u32)p->addr&0x0FFFFFFF,&u8Val); + memWrite8((u32)p->addr&0x0FFFFFFF,(u8)(u8Val^(p->data&0x000000FF))); + }else if ((p->data&0x00F00000) == 0x00500000){ // 7aaaaaaa 0050vvvv + memRead16((u32)p->addr&0x0FFFFFFF,&u16Val); + memWrite16((u32)p->addr&0x0FFFFFFF,(u16)(u16Val^(p->data&0x0000FFFF))); + } + } + else if (p->addr < 0xE0000000) { + if ((((u32)p->data & 0xFFFF0000)==0x00000000) || + (((u32)p->data & 0xFFFF0000)==0x00100000) || + (((u32)p->data & 0xFFFF0000)==0x00200000) || + (((u32)p->data & 0xFFFF0000)==0x00300000)) { + memRead16((u32)p->addr&0x0FFFFFFF,&u16Val); + if (u16Val != (0x0000FFFF&(u32)p->data)) + SkipCount = 1; + PrevCheatType= 0; + } + } + else if (p->addr < 0xF0000000) { + if ((((u32)p->data&0xF0000000)==0x00000000) || + (((u32)p->data&0xF0000000)==0x10000000) || + (((u32)p->data&0xF0000000)==0x20000000) || + (((u32)p->data&0xF0000000)==0x30000000)) { + memRead16((u32)p->data&0x0FFFFFFF,&u16Val); + if (u16Val != (0x0000FFFF&(u32)p->addr)) + SkipCount = ((u32)p->addr&0xFFF0000)/0x10000; + PrevCheatType= 0; + } + } + } + } + break; + case CPU_IOP: { + switch (p->type) { + case BYTE_T: + psxMemWrite8(p->addr, (u8)p->data); + break; + case SHORT_T: + psxMemWrite16(p->addr, (u16)p->data); + break; + case WORD_T: + psxMemWrite32(p->addr, (u32)p->data); + break; + } + } + } +} + + +//this is for apply patches directly to memory +void applypatch(int place) { + int i; + + if (place == 0) { + Console::WriteLn(" patchnumber: %d", params patchnumber); + } + + for ( i = 0; i < patchnumber; i++ ) { + _applypatch(place, &patch[i]); + } +} + +void patchFunc_comment( char * text1, char * text2 ) +{ + Console::WriteLn( "comment: %s", params text2 ); +} + + +void patchFunc_gametitle( char * text1, char * text2 ) +{ + Console::WriteLn( "gametitle: %s", params text2 ); + sprintf(strgametitle,"%s",text2); + Console::SetTitle(strgametitle); +} + +void patchFunc_patch( char * cmd, char * param ) +{ + char * pText; + + if ( patchnumber >= MAX_PATCH ) + { + Console::Error( "Patch ERROR: Maximum number of patches reached: %s=%s", params cmd, param ); + return; + } + + pText = strtok( param, "," ); + pText = param; + + patch[ patchnumber ].placetopatch = strtol( pText, (char **)NULL, 0 ); + + pText = strtok( NULL, "," ); + inifile_trim( pText ); + patch[ patchnumber ].cpu = (patch_cpu_type)PatchTableExecute( pText, NULL, cpuCore ); + if ( patch[ patchnumber ].cpu == 0 ) + { + Console::Error( "Unrecognized patch '%s'", params pText ); + return; + } + + pText = strtok( NULL, "," ); + inifile_trim( pText ); + sscanf( pText, "%X", &patch[ patchnumber ].addr ); + + pText = strtok( NULL, "," ); + inifile_trim( pText ); + patch[ patchnumber ].type = (patch_data_type)PatchTableExecute( pText, NULL, dataType ); + if ( patch[ patchnumber ].type == 0 ) + { + Console::Error( "Unrecognized patch '%s'", params pText ); + return; + } + + pText = strtok( NULL, "," ); + inifile_trim( pText ); + sscanf( pText, "%I64X", &patch[ patchnumber ].data ); + + patch[ patchnumber ].enabled = 1; + + patchnumber++; +} + +//this routine is for executing the commands of the ini file +void inifile_command( char * cmd ) +{ + int code; + char command[ 256 ]; + char parameter[ 256 ]; + + // extract param part (after '=') + char * pEqual = strchr( cmd, '=' ); + + if ( ! pEqual ) + { + // fastmemory doesn't have = + pEqual = cmd+strlen(cmd); + } + + memzero_obj( command ); + memzero_obj( parameter ); + + strncpy( command, cmd, pEqual - cmd ); + strncpy( parameter, pEqual + 1, sizeof( parameter ) ); + + inifile_trim( command ); + inifile_trim( parameter ); + + code = PatchTableExecute( command, parameter, commands ); +} + +void inifile_trim( char * buffer ) +{ + char * pInit = buffer; + char * pEnd = NULL; + + while ( ( *pInit == ' ' ) || ( *pInit == '\t' ) ) //skip space + { + pInit++; + } + if ( ( pInit[ 0 ] == '/' ) && ( pInit[ 1 ] == '/' ) ) //remove comment + { + buffer[ 0 ] = '\0'; + return; + } + pEnd = pInit + strlen( pInit ) - 1; + if ( pEnd <= pInit ) + { + buffer[ 0 ] = '\0'; + return; + } + while ( ( *pEnd == '\r' ) || ( *pEnd == '\n' ) || + ( *pEnd == ' ' ) || ( *pEnd == '\t' ) ) + { + pEnd--; + } + if ( pEnd <= pInit ) + { + buffer[ 0 ] = '\0'; + return; + } + memmove( buffer, pInit, pEnd - pInit + 1 ); + buffer[ pEnd - pInit + 1 ] = '\0'; +} + +void inisection_process( FILE * f1 ) +{ + char buffer[ 1024 ]; + while( fgets( buffer, sizeof( buffer ), f1 ) ) + { + inifile_trim( buffer ); + if ( buffer[ 0 ] ) + { + inifile_command( buffer ); + } + } +} + +//this routine is for reading the ini file + +void inifile_read( const char * name ) +{ + FILE * f1; + char buffer[ 1024 ]; + + patchnumber = 0; +#ifdef _WIN32 + sprintf( buffer, PATCHES_DIR "\\%s.pnach", name ); +#else + sprintf( buffer, PATCHES_DIR "/%s.pnach", name ); +#endif + + f1 = fopen( buffer, "rt" ); + +#ifndef _WIN32 + if( !f1 ) { + // try all upper case because linux is case sensitive + char* pstart = buffer+8; + char* pend = buffer+strlen(buffer); + while(pstart != pend ) { + // stop at the first . since we only want to update the hex + if( *pstart == '.' ) + break; + *pstart++ = toupper(*pstart); + } + + f1 = fopen(buffer, "rt"); + } +#endif + + if( !f1 ) + { + Console::WriteLn( _( "No patch found.Resuming execution without a patch (this is NOT an error)." )); + return; + } + + inisection_process( f1 ); + + fclose( f1 ); +} + +void resetpatch( void ) +{ + patchnumber = 0; +} + +int AddPatch(int Mode, int Place, int Address, int Size, u64 data) +{ + + if ( patchnumber >= MAX_PATCH ) + { + Console::Error( "Patch ERROR: Maximum number of patches reached."); + return -1; + } + + patch[patchnumber].placetopatch = Mode; + + patch[patchnumber].cpu = (patch_cpu_type)Place; + patch[patchnumber].addr=Address; + patch[patchnumber].type=(patch_data_type)Size; + patch[patchnumber].data = data; + return patchnumber++; +} + +void patchFunc_ffxhack( char * cmd, char * param ) +{ + g_FFXHack = 1; +} + +void patchFunc_xkickdelay( char * cmd, char * param ) +{ + g_VUGameFixes |= VUFIX_XGKICKDELAY2; +} + +void patchFunc_fastmemory( char * cmd, char * param ) +{ + // only valid for recompilers + SetFastMemory(1); +} + + +void patchFunc_vunanmode( char * cmd, char * param ) +{ + // only valid for recompilers + SetVUNanMode(param != NULL ? atoi(param) : 1); +} + +void patchFunc_path3hack( char * cmd, char * param ) +{ + path3hack = 1; +} + +void patchFunc_roundmode( char * cmd, char * param ) +{ + int index; + char * pText; + + u32 eetype = (Config.sseMXCSR & 0x6000); + u32 vutype = (Config.sseVUMXCSR & 0x6000); + + index = 0; + pText = strtok( param, ", " ); + while(pText != NULL) { + u32 type = 0xffff; + + if( stricmp(pText, "near") == 0 ) { + type = 0x0000; + } + else if( stricmp(pText, "down") == 0 ) { + type = 0x2000; + } + else if( stricmp(pText, "up") == 0 ) { + type = 0x4000; + } + else if( stricmp(pText, "chop") == 0 ) { + type = 0x6000; + } + + if( type == 0xffff ) { + printf("bad argument (%s) to round mode! skipping...\n", pText); + break; + } + + if( index == 0 ) + eetype=type; + else + vutype=type; + + if( index == 1 ) + break; + + index++; + pText = strtok(NULL, ", "); + } + + SetRoundMode(eetype,vutype); +} + +void patchFunc_zerogs(char* cmd, char* param) +{ + sscanf(param, "%x", &g_ZeroGSOptions); +} + +void SetRoundMode(u32 ee, u32 vu) +{ + // don't set a state for interpreter only + //Msgbox::Alert("SetRoundMode: Config.sseMXCSR = %x; Config.sseVUMXCSR = %x \n", Config.sseMXCSR, Config.sseVUMXCSR); + + SetCPUState( (Config.sseMXCSR & 0x9fff) | ee, (Config.sseVUMXCSR & 0x9fff) | vu); +} diff --git a/pcsx2/Patch.h b/pcsx2/Patch.h new file mode 100644 index 0000000000..12dacdf8f8 --- /dev/null +++ b/pcsx2/Patch.h @@ -0,0 +1,134 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +#ifndef __PATCH_H__ +#define __PATCH_H__ + +#ifdef _WIN32 +#include +#endif + +#include "PS2Etypes.h" + +// +// Defines +// +#define MAX_PATCH 1024 + +#define IFIS(x,str) if(!strnicmp(x,str,sizeof(str)-1)) + +#define GETNEXT_PARAM() \ + while ( *param && ( *param != ',' ) ) param++; \ + if ( *param ) param++; \ + while ( *param && ( *param == ' ' ) ) param++; \ + if ( *param == 0 ) { Console::Error( _( "Not enough params for inicommand" ) ); return; } + +// +// Enums +// + +enum patch_cpu_type { + NO_CPU, + CPU_EE, + CPU_IOP +}; + +enum patch_data_type { + NO_TYPE, + BYTE_T, + SHORT_T, + WORD_T, + DOUBLE_T, + EXTENDED_T +}; + +// +// Typedefs +// +typedef void (*PATCHTABLEFUNC)( char * text1, char * text2 ); + +struct PatchTextTable +{ + const char *text; + int code; + PATCHTABLEFUNC func; +}; + +struct IniPatch +{ + int enabled; + int group; + patch_data_type type; + patch_cpu_type cpu; + int placetopatch; + u32 addr; + u64 data; +}; + +// +// Function prototypes +// +void patchFunc_comment( char * text1, char * text2 ); +void patchFunc_gametitle( char * text1, char * text2 ); +void patchFunc_patch( char * text1, char * text2 ); +void patchFunc_fastmemory( char * text1, char * text2 ); +void patchFunc_path3hack( char * text1, char * text2 ); +void patchFunc_roundmode( char * text1, char * text2 ); +void patchFunc_zerogs( char * text1, char * text2 ); +void patchFunc_vunanmode( char * text1, char * text2 ); +void patchFunc_ffxhack( char * text1, char * text2 ); +void patchFunc_xkickdelay( char * text1, char * text2 ); + +void inifile_trim( char * buffer ); + +// +// Variables +// +extern PatchTextTable commands[]; +extern PatchTextTable dataType[]; +extern PatchTextTable cpuCore[]; +extern IniPatch patch[ MAX_PATCH ]; +extern int patchnumber; + +#ifdef __LINUX__ +// Nasty, currently neccessary hack +extern u32 LinuxsseMXCSR; +extern u32 LinuxsseVUMXCSR; +#endif + +void applypatch( int place ); +void inifile_read( const char * name ); +void inifile_command( char * cmd ); +void resetpatch( void ); + +int AddPatch(int Mode, int Place, int Address, int Size, u64 data); + +extern void SetFastMemory(int); // iR5900LoadStore.c +extern void SetVUNanMode(int mode); + +extern int path3hack; +extern int g_FFXHack; +//extern int g_VUGameFixes; +extern int g_ZeroGSOptions; +extern u32 g_sseMXCSR; +extern u32 g_sseVUMXCSR; + +void SetRoundMode(u32 ee, u32 vu); +int LoadPatch(const std::string& patchfile); + +#endif /* __PATCH_H__ */ + diff --git a/pcsx2/PathUtils.cpp b/pcsx2/PathUtils.cpp new file mode 100644 index 0000000000..0a20cc21db --- /dev/null +++ b/pcsx2/PathUtils.cpp @@ -0,0 +1,145 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "Common.h" + +#ifdef __LINUX__ +#ifndef _S_IFDIR +#define _S_IFDIR S_IFDIR +#endif + +#ifndef _S_IFREG +#define _S_IFREG S_IFREG +#endif +#endif + +namespace Path +{ + +#ifdef WIN32 +static const char Separator = '\\'; +#else +static const char Separator = '/'; +#endif + +bool Exists( const string& path ) +{ + struct stat sbuf; + return stat( path.c_str(), &sbuf ) == 0; +} + +// This function returns false if the path does not exist, or if the path exists and +// is a file. +bool isDirectory( const string& path ) +{ + struct stat sbuf; + if( stat( path.c_str(), &sbuf ) == -1 ) return false; + return !!(sbuf.st_mode & _S_IFDIR); +} + +// This function returns false if the path does not exist, or if the path exists and +// is a directory. +bool isFile( const string& path ) +{ + struct stat sbuf; + if( stat( path.c_str(), &sbuf ) == -1 ) return false; + return !!(sbuf.st_mode & _S_IFREG); +} + +// Returns the length of the file. +// returns -1 if the file is not found. +int getFileSize( const string& path ) +{ + struct stat sbuf; + if( stat( path.c_str(), &sbuf ) == -1 ) return -1; + return sbuf.st_size; +} + +bool isRooted( const string& path ) +{ + // if the first character is a backslash or period, or the second character + // a colon, it's a safe bet we're rooted. + + if( path[0] == 0 ) return FALSE; +#ifdef WIN32 + return (path[0] == '/') || (path[0] == '\\') || (path[1] == ':'); +#else + return (path[0] == Separator); +#endif +} + +// Concatenates two pathnames together, inserting delimiters (backslash on win32) +// as needed! Assumes the 'dest' is allocated to at least g_MaxPath length. +void Combine( string& dest, const string& srcPath, const string& srcFile ) +{ + int pathlen, guesslen; + + if( srcFile.empty() ) + { + // No source filename? Return the path unmodified. + dest = srcPath; + return; + } + + if( isRooted( srcFile ) || srcPath.empty() ) + { + // No source path? Or source filename is rooted? + // Return the filename unmodified. + dest = srcFile; + return; + } + + // strip off the srcPath's trailing backslashes (if any) + // Note: The win32 build works better if I check for both forward and backslashes. + // This might be a problem on Linux builds or maybe it doesn't matter? + + pathlen = srcPath.length(); + while( pathlen > 0 && ((srcPath[pathlen-1] == '\\') || (srcPath[pathlen-1] == '/')) ) + --pathlen; + + // Concatenate strings: + guesslen = pathlen + srcFile.length() + 2; + + if( guesslen >= g_MaxPath ) + throw Exception::PathTooLong(); + + // Concatenate! + + dest.assign( srcPath.begin(), srcPath.begin()+pathlen ); + dest += Separator; + dest += srcFile; +} + +// Replaces the extension of the file with the one given. +void ReplaceExtension( string& dest, const string& src, const string& ext ) +{ + int pos = src.find_last_of( '.' ); + if( pos == 0 ) + dest = src; + else + dest.assign( src.begin(), src.begin()+pos ); + + if( !ext.empty() ) + { + dest += '.'; + dest += ext; + } +} + +} \ No newline at end of file diff --git a/pcsx2/Paths.h b/pcsx2/Paths.h new file mode 100644 index 0000000000..1756733e24 --- /dev/null +++ b/pcsx2/Paths.h @@ -0,0 +1,33 @@ +#ifndef _PCSX2_PATHS_H_ +#define _PCSX2_PATHS_H_ + +#define CONFIG_DIR "inis" + +#define DEFAULT_BIOS_DIR "bios" +#define DEFAULT_PLUGINS_DIR "plugins" + +#define MEMCARDS_DIR "memcards" +#define PATCHES_DIR "patches" + +#define SSTATES_DIR "sstates" +#define LANGS_DIR "Langs" +#define LOGS_DIR "logs" + +#define DEFAULT_MEMCARD1 "Mcd001.ps2" +#define DEFAULT_MEMCARD2 "Mcd002.ps2" + +#define g_MaxPath 255 // 255 is safer with antiquated Win32 ASCII APIs. + +namespace Path +{ + void Combine( string& dest, const string& srcPath, const string& srcFile ); + bool isRooted( const string& path ); + bool isDirectory( const string& path ); + bool isFile( const string& path ); + bool Exists( const string& path ); + int getFileSize( const string& path ); + + void ReplaceExtension( string& dest, const string& src, const string& ext ); +} + +#endif diff --git a/pcsx2/Plugins.cpp b/pcsx2/Plugins.cpp new file mode 100644 index 0000000000..508201eadc --- /dev/null +++ b/pcsx2/Plugins.cpp @@ -0,0 +1,884 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "PsxCommon.h" +#include "GS.h" + +_GSinit GSinit; +_GSopen GSopen; +_GSclose GSclose; +_GSshutdown GSshutdown; +_GSvsync GSvsync; +_GSgifTransfer1 GSgifTransfer1; +_GSgifTransfer2 GSgifTransfer2; +_GSgifTransfer3 GSgifTransfer3; +_GSgetLastTag GSgetLastTag; +_GSgifSoftReset GSgifSoftReset; +_GSreadFIFO GSreadFIFO; +_GSreadFIFO2 GSreadFIFO2; + +_GSkeyEvent GSkeyEvent; +_GSchangeSaveState GSchangeSaveState; +_GSmakeSnapshot GSmakeSnapshot; +_GSmakeSnapshot2 GSmakeSnapshot2; +_GSirqCallback GSirqCallback; +_GSprintf GSprintf; +_GSsetBaseMem GSsetBaseMem; +_GSsetGameCRC GSsetGameCRC; +_GSsetFrameSkip GSsetFrameSkip; +_GSsetupRecording GSsetupRecording; +_GSreset GSreset; +_GSwriteCSR GSwriteCSR; +_GSgetDriverInfo GSgetDriverInfo; +#ifdef _WIN32 +_GSsetWindowInfo GSsetWindowInfo; +#endif +_GSfreeze GSfreeze; +_GSconfigure GSconfigure; +_GStest GStest; +_GSabout GSabout; + +// PAD1 +_PADinit PAD1init; +_PADopen PAD1open; +_PADclose PAD1close; +_PADshutdown PAD1shutdown; +_PADkeyEvent PAD1keyEvent; +_PADstartPoll PAD1startPoll; +_PADpoll PAD1poll; +_PADquery PAD1query; +_PADupdate PAD1update; + +_PADgsDriverInfo PAD1gsDriverInfo; +_PADconfigure PAD1configure; +_PADtest PAD1test; +_PADabout PAD1about; + +// PAD2 +_PADinit PAD2init; +_PADopen PAD2open; +_PADclose PAD2close; +_PADshutdown PAD2shutdown; +_PADkeyEvent PAD2keyEvent; +_PADstartPoll PAD2startPoll; +_PADpoll PAD2poll; +_PADquery PAD2query; +_PADupdate PAD2update; + +_PADgsDriverInfo PAD2gsDriverInfo; +_PADconfigure PAD2configure; +_PADtest PAD2test; +_PADabout PAD2about; + +// SIO[2] +_SIOinit SIOinit[2][9]; +_SIOopen SIOopen[2][9]; +_SIOclose SIOclose[2][9]; +_SIOshutdown SIOshutdown[2][9]; +_SIOstartPoll SIOstartPoll[2][9]; +_SIOpoll SIOpoll[2][9]; +_SIOquery SIOquery[2][9]; + +_SIOconfigure SIOconfigure[2][9]; +_SIOtest SIOtest[2][9]; +_SIOabout SIOabout[2][9]; + +// SPU2 +_SPU2init SPU2init; +_SPU2open SPU2open; +_SPU2close SPU2close; +_SPU2shutdown SPU2shutdown; +_SPU2write SPU2write; +_SPU2read SPU2read; +_SPU2readDMA4Mem SPU2readDMA4Mem; +_SPU2writeDMA4Mem SPU2writeDMA4Mem; +_SPU2interruptDMA4 SPU2interruptDMA4; +_SPU2readDMA7Mem SPU2readDMA7Mem; +_SPU2writeDMA7Mem SPU2writeDMA7Mem; +_SPU2setDMABaseAddr SPU2setDMABaseAddr; +_SPU2interruptDMA7 SPU2interruptDMA7; +_SPU2ReadMemAddr SPU2ReadMemAddr; +_SPU2setupRecording SPU2setupRecording; +_SPU2WriteMemAddr SPU2WriteMemAddr; +_SPU2irqCallback SPU2irqCallback; + +_SPU2setClockPtr SPU2setClockPtr; +_SPU2setTimeStretcher SPU2setTimeStretcher; + +_SPU2async SPU2async; +_SPU2freeze SPU2freeze; +_SPU2configure SPU2configure; +_SPU2test SPU2test; +_SPU2about SPU2about; + +// CDVD +_CDVDinit CDVDinit; +_CDVDopen CDVDopen; +_CDVDclose CDVDclose; +_CDVDshutdown CDVDshutdown; +_CDVDreadTrack CDVDreadTrack; +_CDVDgetBuffer CDVDgetBuffer; +_CDVDreadSubQ CDVDreadSubQ; +_CDVDgetTN CDVDgetTN; +_CDVDgetTD CDVDgetTD; +_CDVDgetTOC CDVDgetTOC; +_CDVDgetDiskType CDVDgetDiskType; +_CDVDgetTrayStatus CDVDgetTrayStatus; +_CDVDctrlTrayOpen CDVDctrlTrayOpen; +_CDVDctrlTrayClose CDVDctrlTrayClose; + +_CDVDconfigure CDVDconfigure; +_CDVDtest CDVDtest; +_CDVDabout CDVDabout; +_CDVDnewDiskCB CDVDnewDiskCB; + +// DEV9 +_DEV9init DEV9init; +_DEV9open DEV9open; +_DEV9close DEV9close; +_DEV9shutdown DEV9shutdown; +_DEV9read8 DEV9read8; +_DEV9read16 DEV9read16; +_DEV9read32 DEV9read32; +_DEV9write8 DEV9write8; +_DEV9write16 DEV9write16; +_DEV9write32 DEV9write32; +_DEV9readDMA8Mem DEV9readDMA8Mem; +_DEV9writeDMA8Mem DEV9writeDMA8Mem; +_DEV9irqCallback DEV9irqCallback; +_DEV9irqHandler DEV9irqHandler; + +_DEV9configure DEV9configure; +_DEV9freeze DEV9freeze; +_DEV9test DEV9test; +_DEV9about DEV9about; + +// USB +_USBinit USBinit; +_USBopen USBopen; +_USBclose USBclose; +_USBshutdown USBshutdown; +_USBread8 USBread8; +_USBread16 USBread16; +_USBread32 USBread32; +_USBwrite8 USBwrite8; +_USBwrite16 USBwrite16; +_USBwrite32 USBwrite32; +_USBasync USBasync; + +_USBirqCallback USBirqCallback; +_USBirqHandler USBirqHandler; +_USBsetRAM USBsetRAM; + +_USBconfigure USBconfigure; +_USBfreeze USBfreeze; +_USBtest USBtest; +_USBabout USBabout; + +// FW +_FWinit FWinit; +_FWopen FWopen; +_FWclose FWclose; +_FWshutdown FWshutdown; +_FWread32 FWread32; +_FWwrite32 FWwrite32; +_FWirqCallback FWirqCallback; + +_FWconfigure FWconfigure; +_FWfreeze FWfreeze; +_FWtest FWtest; +_FWabout FWabout; + + +DEV9handler dev9Handler; +USBhandler usbHandler; + +#define Sfy(x) #x +#define Strfy(x) Sfy(x) +#define MapSymbolVarType(var,type,name) var = (type)SysLoadSym(drv,Strfy(name)) +#define MapSymbolVar(var,name) MapSymbolVarType(var,_##name,name) +#define MapSymbolVar_Fallback(var,name,fallback) if((MapSymbolVar(var,name))==NULL) var = fallback +#define MapSymbolVar_Error(var,name) if((MapSymbolVar(var,name))==NULL) \ +{ \ + const char* errString = SysLibError(); \ + Msgbox::Alert("%s: Error loading %hs: %s", params &filename, #name, errString); \ + return -1; \ +} + +#define MapSymbol(name) MapSymbolVar(name,name) +#define MapSymbol_Fallback(name,fallback) MapSymbolVar_Fallback(name,name,fallback) +#define MapSymbol_Error(name) MapSymbolVar_Error(name,name) + +// for pad1/2 +#define MapSymbolPAD(var,sym,name) MapSymbolVar(var##name,sym##name) +#define MapSymbolPAD_Fallback(var,sym,name) if((MapSymbolVarType(var##name,_##sym##name,sym##name))==NULL) var##name = var##_##name +#define MapSymbolPAD_Error(var,sym,name) MapSymbolVar_Error(var##name,sym##name) + +#define TestPS2Esyms(type) if(_TestPS2Esyms(drv,PS2E_LT_##type,PS2E_##type##_VERSION,filename) < 0) return -1; + +int _TestPS2Esyms(void* drv, int type, int expected_version, const string& filename) +{ + _PS2EgetLibType PS2EgetLibType; + _PS2EgetLibVersion2 PS2EgetLibVersion2; + _PS2EgetLibName PS2EgetLibName; + + MapSymbol_Error(PS2EgetLibType); + MapSymbol_Error(PS2EgetLibVersion2); + MapSymbol_Error(PS2EgetLibName); + + int actual_version = ((PS2EgetLibVersion2(type) >> 16)&0xff); + + if( actual_version != expected_version) { + Msgbox::Alert("Can't load '%hs', wrong PS2E version (%x != %x)", params &filename, actual_version, expected_version); + return -1; + } + + return 0; +} + +//static const char *err; +//static int errval; + +void *GSplugin; + +void CALLBACK GS_printf(int timeout, char *fmt, ...) { + va_list list; + char msg[512]; + + va_start(list, fmt); + vsprintf(msg, fmt, list); + va_end(list); + + SysPrintf(msg); +} + +s32 CALLBACK GS_freeze(int mode, freezeData *data) { data->size = 0; return 0; } +void CALLBACK GS_keyEvent(keyEvent *ev) {} +void CALLBACK GS_makeSnapshot(const char *path) {} +void CALLBACK GS_irqCallback(void (*callback)()) {} +void CALLBACK GS_configure() {} +void CALLBACK GS_about() {} +s32 CALLBACK GS_test() { return 0; } + +int LoadGSplugin(const string& filename) +{ + void *drv; + + GSplugin = SysLoadLibrary(filename.c_str()); + if (GSplugin == NULL) { Msgbox::Alert ("Could Not Load GS Plugin '%hs': %s", params &filename, SysLibError()); return -1; } + drv = GSplugin; + TestPS2Esyms(GS); + MapSymbol_Error(GSinit); + MapSymbol_Error(GSshutdown); + MapSymbol_Error(GSopen); + MapSymbol_Error(GSclose); + MapSymbol_Error(GSgifTransfer1); + MapSymbol_Error(GSgifTransfer2); + MapSymbol_Error(GSgifTransfer3); + MapSymbol_Error(GSreadFIFO); + MapSymbol(GSgetLastTag); + MapSymbol(GSreadFIFO2); // optional + MapSymbol_Error(GSvsync); + + MapSymbol_Fallback(GSkeyEvent,GS_keyEvent); + MapSymbol(GSchangeSaveState); + MapSymbol(GSgifSoftReset); + MapSymbol_Fallback(GSmakeSnapshot,GS_makeSnapshot); + MapSymbol_Fallback(GSirqCallback,GS_irqCallback); + MapSymbol_Fallback(GSprintf,GS_printf); + MapSymbol_Error(GSsetBaseMem); + MapSymbol(GSsetGameCRC); + MapSymbol_Error(GSreset); + MapSymbol_Error(GSwriteCSR); + MapSymbol(GSmakeSnapshot2); + MapSymbol(GSgetDriverInfo); + + MapSymbol(GSsetFrameSkip); + MapSymbol(GSsetupRecording); + +#ifdef _WIN32 + MapSymbol(GSsetWindowInfo); +#endif + MapSymbol_Fallback(GSfreeze,GS_freeze); + MapSymbol_Fallback(GSconfigure,GS_configure); + MapSymbol_Fallback(GSabout,GS_about); + MapSymbol_Fallback(GStest,GS_test); + + return 0; +} + +void *PAD1plugin; + +void CALLBACK PAD1_configure() {} +void CALLBACK PAD1_about() {} +s32 CALLBACK PAD1_test() { return 0; } + +int LoadPAD1plugin(const string& filename) { + void *drv; + + PAD1plugin = SysLoadLibrary(filename.c_str()); + if (PAD1plugin == NULL) { Msgbox::Alert("Could Not Load PAD1 Plugin '%hs': %s", params &filename, SysLibError()); return -1; } + drv = PAD1plugin; + TestPS2Esyms(PAD); + MapSymbolPAD_Error(PAD1,PAD,init); + MapSymbolPAD_Error(PAD1,PAD,shutdown); + MapSymbolPAD_Error(PAD1,PAD,open); + MapSymbolPAD_Error(PAD1,PAD,close); + MapSymbolPAD_Error(PAD1,PAD,keyEvent); + MapSymbolPAD_Error(PAD1,PAD,startPoll); + MapSymbolPAD_Error(PAD1,PAD,poll); + MapSymbolPAD_Error(PAD1,PAD,query); + MapSymbolPAD(PAD1,PAD,update); + + MapSymbolPAD(PAD1,PAD,gsDriverInfo); + MapSymbolPAD_Fallback(PAD1,PAD,configure); + MapSymbolPAD_Fallback(PAD1,PAD,about); + MapSymbolPAD_Fallback(PAD1,PAD,test); + + return 0; +} + +void *PAD2plugin; + +void CALLBACK PAD2_configure() {} +void CALLBACK PAD2_about() {} +s32 CALLBACK PAD2_test() { return 0; } + +int LoadPAD2plugin(const string& filename) { + void *drv; + + PAD2plugin = SysLoadLibrary(filename.c_str()); + if (PAD2plugin == NULL) { Msgbox::Alert("Could Not Load PAD2 Plugin '%hs': %s", params &filename, SysLibError()); return -1; } + drv = PAD2plugin; + TestPS2Esyms(PAD); + MapSymbolPAD_Error(PAD2,PAD,init); + MapSymbolPAD_Error(PAD2,PAD,shutdown); + MapSymbolPAD_Error(PAD2,PAD,open); + MapSymbolPAD_Error(PAD2,PAD,close); + MapSymbolPAD_Error(PAD2,PAD,keyEvent); + MapSymbolPAD_Error(PAD2,PAD,startPoll); + MapSymbolPAD_Error(PAD2,PAD,poll); + MapSymbolPAD_Error(PAD2,PAD,query); + MapSymbolPAD(PAD2,PAD,update); + + MapSymbolPAD(PAD2,PAD,gsDriverInfo); + MapSymbolPAD_Fallback(PAD2,PAD,configure); + MapSymbolPAD_Fallback(PAD2,PAD,about); + MapSymbolPAD_Fallback(PAD2,PAD,test); + + return 0; +} + +void *SPU2plugin; + +s32 CALLBACK SPU2_freeze(int mode, freezeData *data) { data->size = 0; return 0; } +void CALLBACK SPU2_configure() {} +void CALLBACK SPU2_about() {} +s32 CALLBACK SPU2_test() { return 0; } + +int LoadSPU2plugin(const string& filename) { + void *drv; + + SPU2plugin = SysLoadLibrary(filename.c_str()); + if (SPU2plugin == NULL) { Msgbox::Alert("Could Not Load SPU2 Plugin '%hs': %s", params &filename, SysLibError()); return -1; } + drv = SPU2plugin; + TestPS2Esyms(SPU2); + MapSymbol_Error(SPU2init); + MapSymbol_Error(SPU2shutdown); + MapSymbol_Error(SPU2open); + MapSymbol_Error(SPU2close); + MapSymbol_Error(SPU2write); + MapSymbol_Error(SPU2read); + MapSymbol_Error(SPU2readDMA4Mem); + MapSymbol_Error(SPU2writeDMA4Mem); + MapSymbol_Error(SPU2interruptDMA4); + MapSymbol_Error(SPU2readDMA7Mem); + MapSymbol_Error(SPU2writeDMA7Mem); + MapSymbol_Error(SPU2interruptDMA7); + MapSymbol(SPU2setDMABaseAddr); + MapSymbol_Error(SPU2ReadMemAddr); + MapSymbol_Error(SPU2WriteMemAddr); + MapSymbol_Error(SPU2irqCallback); + + MapSymbol(SPU2setClockPtr); + + MapSymbol(SPU2setupRecording); + + MapSymbol_Fallback(SPU2freeze,SPU2_freeze); + MapSymbol_Fallback(SPU2configure,SPU2_configure); + MapSymbol_Fallback(SPU2about,SPU2_about); + MapSymbol_Fallback(SPU2test,SPU2_test); + MapSymbol(SPU2async); + + return 0; +} + +void *CDVDplugin; + +void CALLBACK CDVD_configure() {} +void CALLBACK CDVD_about() {} +s32 CALLBACK CDVD_test() { return 0; } + +int LoadCDVDplugin(const string& filename) { + void *drv; + + CDVDplugin = SysLoadLibrary(filename.c_str()); + if (CDVDplugin == NULL) { Msgbox::Alert("Could Not Load CDVD Plugin '%hs': %s", params &filename, SysLibError()); return -1; } + drv = CDVDplugin; + TestPS2Esyms(CDVD); + MapSymbol_Error(CDVDinit); + MapSymbol_Error(CDVDshutdown); + MapSymbol_Error(CDVDopen); + MapSymbol_Error(CDVDclose); + MapSymbol_Error(CDVDreadTrack); + MapSymbol_Error(CDVDgetBuffer); + MapSymbol_Error(CDVDreadSubQ); + MapSymbol_Error(CDVDgetTN); + MapSymbol_Error(CDVDgetTD); + MapSymbol_Error(CDVDgetTOC); + MapSymbol_Error(CDVDgetDiskType); + MapSymbol_Error(CDVDgetTrayStatus); + MapSymbol_Error(CDVDctrlTrayOpen); + MapSymbol_Error(CDVDctrlTrayClose); + + MapSymbol_Fallback(CDVDconfigure,CDVD_configure); + MapSymbol_Fallback(CDVDabout,CDVD_about); + MapSymbol_Fallback(CDVDtest,CDVD_test); + MapSymbol(CDVDnewDiskCB); + + return 0; +} + +void *DEV9plugin; + +s32 CALLBACK DEV9_freeze(int mode, freezeData *data) { data->size = 0; return 0; } +void CALLBACK DEV9_configure() {} +void CALLBACK DEV9_about() {} +s32 CALLBACK DEV9_test() { return 0; } + +int LoadDEV9plugin(const string& filename) { + void *drv; + + DEV9plugin = SysLoadLibrary(filename.c_str()); + if (DEV9plugin == NULL) { Msgbox::Alert("Could Not Load DEV9 Plugin '%hs': %s", params &filename, SysLibError()); return -1; } + drv = DEV9plugin; + TestPS2Esyms(DEV9); + MapSymbol_Error(DEV9init); + MapSymbol_Error(DEV9shutdown); + MapSymbol_Error(DEV9open); + MapSymbol_Error(DEV9close); + MapSymbol_Error(DEV9read8); + MapSymbol_Error(DEV9read16); + MapSymbol_Error(DEV9read32); + MapSymbol_Error(DEV9write8); + MapSymbol_Error(DEV9write16); + MapSymbol_Error(DEV9write32); + MapSymbol_Error(DEV9readDMA8Mem); + MapSymbol_Error(DEV9writeDMA8Mem); + MapSymbol_Error(DEV9irqCallback); + MapSymbol_Error(DEV9irqHandler); + + MapSymbol_Fallback(DEV9freeze,DEV9_freeze); + MapSymbol_Fallback(DEV9configure,DEV9_configure); + MapSymbol_Fallback(DEV9about,DEV9_about); + MapSymbol_Fallback(DEV9test,DEV9_test); + + return 0; +} + +void *USBplugin; + +s32 CALLBACK USB_freeze(int mode, freezeData *data) { data->size = 0; return 0; } +void CALLBACK USB_configure() {} +void CALLBACK USB_about() {} +s32 CALLBACK USB_test() { return 0; } + +int LoadUSBplugin(const string& filename) { + void *drv; + + USBplugin = SysLoadLibrary(filename.c_str()); + if (USBplugin == NULL) { Msgbox::Alert("Could Not Load USB Plugin '%hs': %s", params &filename, SysLibError()); return -1; } + drv = USBplugin; + TestPS2Esyms(USB); + MapSymbol_Error(USBinit); + MapSymbol_Error(USBshutdown); + MapSymbol_Error(USBopen); + MapSymbol_Error(USBclose); + MapSymbol_Error(USBread8); + MapSymbol_Error(USBread16); + MapSymbol_Error(USBread32); + MapSymbol_Error(USBwrite8); + MapSymbol_Error(USBwrite16); + MapSymbol_Error(USBwrite32); + MapSymbol_Error(USBirqCallback); + MapSymbol_Error(USBirqHandler); + MapSymbol_Error(USBsetRAM); + + MapSymbol(USBasync); + + MapSymbol_Fallback(USBfreeze,USB_freeze); + MapSymbol_Fallback(USBconfigure,USB_configure); + MapSymbol_Fallback(USBabout,USB_about); + MapSymbol_Fallback(USBtest,USB_test); + + return 0; +} +void *FWplugin; + +s32 CALLBACK FW_freeze(int mode, freezeData *data) { data->size = 0; return 0; } +void CALLBACK FW_configure() {} +void CALLBACK FW_about() {} +s32 CALLBACK FW_test() { return 0; } + +int LoadFWplugin(const string& filename) { + void *drv; + + FWplugin = SysLoadLibrary(filename.c_str()); + if (FWplugin == NULL) { Msgbox::Alert("Could Not Load FW Plugin '%hs': %s", params &filename, SysLibError()); return -1; } + drv = FWplugin; + TestPS2Esyms(FW); + MapSymbol_Error(FWinit); + MapSymbol_Error(FWshutdown); + MapSymbol_Error(FWopen); + MapSymbol_Error(FWclose); + MapSymbol_Error(FWread32); + MapSymbol_Error(FWwrite32); + MapSymbol_Error(FWirqCallback); + + MapSymbol_Fallback(FWfreeze,FW_freeze); + MapSymbol_Fallback(FWconfigure,FW_configure); + MapSymbol_Fallback(FWabout,FW_about); + MapSymbol_Fallback(FWtest,FW_test); + + return 0; +} + +struct PluginOpenStatusFlags +{ + u8 GS : 1 + , CDVD : 1 + , DEV9 : 1 + , USB : 1 + , SPU2 : 1 + , PAD1 : 1 + , PAD2 : 1 + , FW : 1; + +}; + +static PluginOpenStatusFlags OpenStatus = {0}; + +static bool loadp=false; + +int InitPlugins() { + int ret; + + ret = GSinit(); + if (ret != 0) { Msgbox::Alert("GSinit error: %d", params ret); return -1; } + ret = PAD1init(1); + if (ret != 0) { Msgbox::Alert("PAD1init error: %d", params ret); return -1; } + ret = PAD2init(2); + if (ret != 0) { Msgbox::Alert("PAD2init error: %d", params ret); return -1; } + ret = SPU2init(); + if (ret != 0) { Msgbox::Alert("SPU2init error: %d", params ret); return -1; } + ret = CDVDinit(); + if (ret != 0) { Msgbox::Alert("CDVDinit error: %d", params ret); return -1; } + ret = DEV9init(); + if (ret != 0) { Msgbox::Alert("DEV9init error: %d", params ret); return -1; } + ret = USBinit(); + if (ret != 0) { Msgbox::Alert("USBinit error: %d", params ret); return -1; } + ret = FWinit(); + if (ret != 0) { Msgbox::Alert("FWinit error: %d", params ret); return -1; } + return 0; +} + +void ShutdownPlugins() +{ + ClosePlugins(); + + // GS is a special case: It needs closed first usually. + // (the GS isn't always closed during emulation pauses) + if( OpenStatus.GS ) + { + gsClose(); + OpenStatus.GS = false; + } + + if( GSshutdown != NULL ) + GSshutdown(); + + if( PAD1shutdown != NULL ) + PAD1shutdown(); + if( PAD2shutdown != NULL ) + PAD2shutdown(); + + if( SPU2shutdown != NULL ) + SPU2shutdown(); + + if( CDVDshutdown != NULL ) + CDVDshutdown(); + + if( DEV9shutdown != NULL ) + DEV9shutdown(); + + if( USBshutdown != NULL ) + USBshutdown(); + + if( FWshutdown != NULL ) + FWshutdown(); +} + +int LoadPlugins() { + + if( loadp ) return 0; + string Plugin; + + Path::Combine( Plugin, Config.PluginsDir, Config.GS ); + if (LoadGSplugin(Plugin) == -1) return -1; + + Path::Combine( Plugin, Config.PluginsDir, Config.PAD1 ); + if (LoadPAD1plugin(Plugin) == -1) return -1; + + Path::Combine( Plugin, Config.PluginsDir, Config.PAD2); + if (LoadPAD2plugin(Plugin) == -1) return -1; + + Path::Combine( Plugin, Config.PluginsDir, Config.SPU2); + if (LoadSPU2plugin(Plugin) == -1) return -1; + + Path::Combine( Plugin, Config.PluginsDir, Config.CDVD); + if (LoadCDVDplugin(Plugin) == -1) return -1; + + Path::Combine( Plugin, Config.PluginsDir, Config.DEV9); + if (LoadDEV9plugin(Plugin) == -1) return -1; + + Path::Combine( Plugin, Config.PluginsDir, Config.USB); + if (LoadUSBplugin(Plugin) == -1) return -1; + + Path::Combine( Plugin, Config.PluginsDir, Config.FW); + if (LoadFWplugin(Plugin) == -1) return -1; + + if (InitPlugins() == -1) return -1; + + loadp=true; + + return 0; +} + +uptr pDsp; +extern void spu2DMA4Irq(); +extern void spu2DMA7Irq(); +extern void spu2Irq(); + +int OpenPlugins(const char* pTitleFilename) { + GSdriverInfo info; + int ret; + + if ( !loadp ) + throw Exception::InvalidOperation( "OpenPlugins cannot be called while the plugin state is uninitialized." ); + +#ifndef _WIN32 + // change dir so that CDVD can find its config file + char file[255], pNewTitle[255]; + getcwd(file, ARRAYSIZE(file)); + chdir(Config.PluginsDir); + + if( pTitleFilename != NULL && pTitleFilename[0] != '/' ) { + // because we are changing the dir, we have to set a new title if it is a relative dir + sprintf(pNewTitle, "%s/%s", file, pTitleFilename); + pTitleFilename = pNewTitle; + } +#endif + + if( !OpenStatus.CDVD ) + { + //first we need the data + if (CDVDnewDiskCB) CDVDnewDiskCB(cdvdNewDiskCB); + + ret = CDVDopen(pTitleFilename); + + if (ret != 0) { Msgbox::Alert("Error Opening CDVD Plugin"); goto OpenError; } + OpenStatus.CDVD = true; + cdvdNewDiskCB(); + } + + if( !OpenStatus.GS ) { + ret = gsOpen(); + if (ret != 0) { Msgbox::Alert("Error Opening GS Plugin"); goto OpenError; } + OpenStatus.GS = true; + + //then the user input + if (GSgetDriverInfo) { + GSgetDriverInfo(&info); + if (PAD1gsDriverInfo) PAD1gsDriverInfo(&info); + if (PAD2gsDriverInfo) PAD2gsDriverInfo(&info); + } + } + + if( !OpenStatus.PAD1 ) + { + ret = PAD1open((void *)&pDsp); + if (ret != 0) { Msgbox::Alert("Error Opening PAD1 Plugin"); goto OpenError; } + OpenStatus.PAD1 = true; + } + + if( !OpenStatus.PAD2 ) + { + ret = PAD2open((void *)&pDsp); + if (ret != 0) { Msgbox::Alert("Error Opening PAD2 Plugin"); goto OpenError; } + OpenStatus.PAD2 = true; + } + + //the sound + + if( !OpenStatus.SPU2 ) + { + SPU2irqCallback(spu2Irq,spu2DMA4Irq,spu2DMA7Irq); + if( SPU2setDMABaseAddr != NULL ) + SPU2setDMABaseAddr((uptr)psxM); + + if(SPU2setClockPtr != NULL) + SPU2setClockPtr(&psxRegs.cycle); + + ret = SPU2open((void*)&pDsp); + if (ret != 0) { Msgbox::Alert("Error Opening SPU2 Plugin"); goto OpenError; } + OpenStatus.SPU2 = true; + } + + //and last the dev9 + if( !OpenStatus.DEV9 ) + { + DEV9irqCallback(dev9Irq); + dev9Handler = DEV9irqHandler(); + ret = DEV9open(&psxRegs.pc); //((void *)&pDsp); + if (ret != 0) { Msgbox::Alert("Error Opening DEV9 Plugin"); goto OpenError; } + OpenStatus.DEV9 = true; + } + + if( !OpenStatus.USB ) + { + USBirqCallback(usbIrq); + usbHandler = USBirqHandler(); + USBsetRAM(psxM); + ret = USBopen((void *)&pDsp); + if (ret != 0) { Msgbox::Alert("Error Opening USB Plugin"); goto OpenError; } + OpenStatus.USB = true; + } + + if( !OpenStatus.FW ) + { + FWirqCallback(fwIrq); + ret = FWopen((void *)&pDsp); + if (ret != 0) { Msgbox::Alert("Error Opening FW Plugin"); goto OpenError; } + OpenStatus.FW = true; + } + +#ifdef __LINUX__ + chdir(file); +#endif + return 0; + +OpenError: + ClosePlugins(); +#ifdef __LINUX__ + chdir(file); +#endif + + return -1; +} + + +#define CLOSE_PLUGIN( name ) \ + if( OpenStatus.name ) { \ + name##close(); \ + OpenStatus.name = false; \ + } + + +void ClosePlugins() +{ + // GS plugin is special and is not always closed during emulation pauses. + // (that's because the GS is the most complicated plugin and to close it would + // require we save the GS state -- plus, Gsdx doesn't really support being + // closed) + + if( OpenStatus.GS ) + mtgsWaitGS(); + + CLOSE_PLUGIN( PAD1 ); + CLOSE_PLUGIN( PAD2 ); + + CLOSE_PLUGIN( CDVD ); + CLOSE_PLUGIN( DEV9 ); + CLOSE_PLUGIN( USB ); + CLOSE_PLUGIN( FW ); + CLOSE_PLUGIN( SPU2 ); +} + +void ResetPlugins() +{ + mtgsWaitGS(); + + ShutdownPlugins(); + InitPlugins(); +} + +void ReleasePlugins() +{ + if (!loadp) return; + + if (GSplugin == NULL || PAD1plugin == NULL || PAD2plugin == NULL || + SPU2plugin == NULL || CDVDplugin == NULL || DEV9plugin == NULL || + USBplugin == NULL || FWplugin == NULL) return; + + ShutdownPlugins(); + + SysCloseLibrary(GSplugin); GSplugin = NULL; + SysCloseLibrary(PAD1plugin); PAD1plugin = NULL; + SysCloseLibrary(PAD2plugin); PAD2plugin = NULL; + SysCloseLibrary(SPU2plugin); SPU2plugin = NULL; + SysCloseLibrary(CDVDplugin); CDVDplugin = NULL; + SysCloseLibrary(DEV9plugin); DEV9plugin = NULL; + SysCloseLibrary(USBplugin); USBplugin = NULL; + SysCloseLibrary(FWplugin); FWplugin = NULL; + loadp = false; +} + +void PluginsResetGS() +{ + CLOSE_PLUGIN( PAD1 ); + CLOSE_PLUGIN( PAD2 ); + + if( OpenStatus.GS ) + { + gsClose(); + OpenStatus.GS = false; + } + + if( OpenStatus.PAD1 ) + { + PAD1close(); + } + + GSshutdown(); + + int ret = GSinit(); + if (ret != 0) { Msgbox::Alert("GSinit error: %d", params ret); } +} diff --git a/pcsx2/Plugins.h b/pcsx2/Plugins.h new file mode 100644 index 0000000000..0509c15bdc --- /dev/null +++ b/pcsx2/Plugins.h @@ -0,0 +1,45 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __PLUGINS_H__ +#define __PLUGINS_H__ + +#define PLUGINtypedefs +#define PLUGINfuncs +#include "PS2Edefs.h" + +// Loads plugins as specified in the Config global. +int LoadPlugins(); + +// Unloads all plugin DLLs. To change plugins, call ReleasePlugins followed by +// changes to Config.Plugins filenames, and then call LoadPlugins. +void ReleasePlugins(); + +int OpenPlugins(const char* pTitleFilename); +void ClosePlugins(); + +int InitPlugins(); + +// Completely shuts down all plugins and re-initializes them. (clean slate) +// Plugins are not unloaded, so changes to Config.Plugins values will not +// take effect. Use a manual set oc alls to ReleasePlugins and LoadPlugins for that. +void ResetPlugins(); + +void PluginsResetGS(); + +#endif /* __PLUGINS_H__ */ diff --git a/pcsx2/PrecompiledHeader.cpp b/pcsx2/PrecompiledHeader.cpp new file mode 100644 index 0000000000..f07790a065 --- /dev/null +++ b/pcsx2/PrecompiledHeader.cpp @@ -0,0 +1 @@ +#include "PrecompiledHeader.h" \ No newline at end of file diff --git a/pcsx2/PrecompiledHeader.h b/pcsx2/PrecompiledHeader.h new file mode 100644 index 0000000000..98edbb7016 --- /dev/null +++ b/pcsx2/PrecompiledHeader.h @@ -0,0 +1,118 @@ +#ifndef _PCSX2_PRECOMPILED_HEADER_ +#define _PCSX2_PRECOMPILED_HEADER_ + +#if defined (__linux__) // some distributions are lower case +# define __LINUX__ +#endif + +#ifndef _WIN32 +# include +#else + +// For now Windows headers are needed by all modules, so include it here so +// that it compiles nice and fast... + +// Force availability of to WinNT APIs (change to 0x600 to enable XP-specific APIs) +# define WINVER 0x0501 +# define _WIN32_WINNT 0x0501 + +# include + +// disable Windows versions of min/max -- we'll use the typesafe STL versions instead. +#undef min +#undef max + +#endif + +// Include the STL junk that's actually handy. + +#include +#include +#include +#include +#include // string.h under c++ +#include // stdio.h under c++ +#include +#include + +// ... and include some ANSI/POSIX C libs that are useful too, just for good measure. +// (these compile lightning fast with or without PCH, but they never change so +// might as well add them here) + +#include +#include +#include +#include +#include + +// TODO : Add items here that are local to Pcsx2 but stay relatively unchanged for +// long periods of time. + +#ifdef _WIN32 +// disable warning C4244: '=' : conversion from 'big' to 'small', possible loss of data +#pragma warning(disable:4244) +#endif + +using std::string; // we use it enough, so bring it into the global namespace. + +#include "zlib.h" +#include "PS2Etypes.h" +#include "StringUtils.h" + +//////////////////////////////////////////////////////////////////// +// Compiler/OS specific macros and defines -- Begin Section + +#if defined(_MSC_VER) + +# define strnicmp _strnicmp +# define stricmp _stricmp + +#else // must be GCC... + +# include +# include + +// Definitions added Feb 16, 2006 by efp +# ifndef __declspec +# define __declspec(x) +# endif + +// functions that linux lacks... +// fixme: this should probably be in a __LINUX__ conditional rather than +// a GCC conditional (since GCC on a windows platform would have these functions) +# define Sleep(seconds) usleep(1000*(seconds)) + +static __forceinline u32 timeGetTime() +{ + struct timeb t; + ftime(&t); + return (u32)(t.time*1000+t.millitm); +} + +# define BOOL int + +# undef TRUE +# undef FALSE +# define TRUE 1 +# define FALSE 0 + +# ifndef strnicmp +# define strnicmp strncasecmp +# endif + +# ifndef stricmp +# define stricmp strcasecmp +# endif + +#endif // end GCC/Linux stuff + +// compile-time assert +#ifndef C_ASSERT +# define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +#endif + +#ifndef __LINUX__ +# define __unused +#endif + +#endif diff --git a/pcsx2/PsxCommon.h b/pcsx2/PsxCommon.h new file mode 100644 index 0000000000..ea669ab79e --- /dev/null +++ b/pcsx2/PsxCommon.h @@ -0,0 +1,53 @@ +/* Pcsx2 - Pc Ps2 Emulator * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __PSXCOMMON_H__ +#define __PSXCOMMON_H__ + +#ifdef _WIN32 +#include +#endif + +#include "PS2Etypes.h" + +#include + +#include "System.h" +#include + +extern long LoadCdBios; +extern int cdOpenCase; + +#define PSXCLK (36864000ULL) /* 36.864 Mhz */ + +#include "Plugins.h" +#include "R3000A.h" +#include "IopMem.h" +#include "IopHw.h" +#include "IopBios.h" +#include "IopDma.h" +#include "IopCounters.h" +#include "CdRom.h" +#include "Sio.h" +#include "DebugTools/Debug.h" +#include "IopSio2.h" +#include "CDVD.h" +#include "Memory.h" +#include "Hw.h" +#include "Sif.h" + +#endif /* __PSXCOMMON_H__ */ diff --git a/pcsx2/R3000A.cpp b/pcsx2/R3000A.cpp new file mode 100644 index 0000000000..8e191a4e62 --- /dev/null +++ b/pcsx2/R3000A.cpp @@ -0,0 +1,283 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" +#include "Misc.h" + +using namespace R3000A; + +R3000Acpu *psxCpu; + +// used for constant propagation +u32 g_psxConstRegs[32]; +u32 g_psxHasConstReg, g_psxFlushedConstReg; + +// Controls when branch tests are performed. +u32 g_psxNextBranchCycle = 0; + +// This value is used when the IOP execution is broken to return control to the EE. +// (which happens when the IOP throws EE-bound interrupts). It holds the value of +// psxCycleEE (which is set to zero to facilitate the code break), so that the unrun +// cycles can be accounted for later. +s32 psxBreak = 0; + +// tracks the IOP's current sync status with the EE. When it dips below zero, +// control is returned to the EE. +s32 psxCycleEE = -1; + +// Used to signal to the EE when important actions that need IOP-attention have +// happened (hsyncs, vsyncs, IOP exceptions, etc). IOP runs code whenever this +// is true, even if it's already running ahead a bit. +bool iopBranchAction = false; + +bool iopEventTestIsActive = false; + +PCSX2_ALIGNED16(psxRegisters psxRegs); + +void psxReset() +{ + memzero_obj(psxRegs); + + psxRegs.pc = 0xbfc00000; // Start in bootstrap + psxRegs.CP0.n.Status = 0x10900000; // COP0 enabled | BEV = 1 | TS = 1 + psxRegs.CP0.n.PRid = 0x0000001f; // PRevID = Revision ID, same as the IOP R3000A + + psxBreak = 0; + psxCycleEE = -1; + g_psxNextBranchCycle = psxRegs.cycle + 4; + + psxHwReset(); + psxBiosInit(); + //psxExecuteBios(); +} + +void psxShutdown() { + psxBiosShutdown(); + psxSIOShutdown(); + //psxCpu->Shutdown(); +} + +void psxException(u32 code, u32 bd) { +// PSXCPU_LOG("psxException %x: %x, %x\n", code, psxHu32(0x1070), psxHu32(0x1074)); + //SysPrintf("!! psxException %x: %x, %x\n", code, psxHu32(0x1070), psxHu32(0x1074)); + // Set the Cause + psxRegs.CP0.n.Cause &= ~0x7f; + psxRegs.CP0.n.Cause |= code; + + // Set the EPC & PC + if (bd) + { + PSXCPU_LOG("bd set\n"); + psxRegs.CP0.n.Cause|= 0x80000000; + psxRegs.CP0.n.EPC = (psxRegs.pc - 4); + } + else + psxRegs.CP0.n.EPC = (psxRegs.pc); + + if (psxRegs.CP0.n.Status & 0x400000) + psxRegs.pc = 0xbfc00180; + else + psxRegs.pc = 0x80000080; + + // Set the Status + psxRegs.CP0.n.Status = (psxRegs.CP0.n.Status &~0x3f) | + ((psxRegs.CP0.n.Status & 0xf) << 2); + + /*if ((((PSXMu32(psxRegs.CP0.n.EPC) >> 24) & 0xfe) == 0x4a)) { + // "hokuto no ken" / "Crash Bandicot 2" ... fix + PSXMu32(psxRegs.CP0.n.EPC)&= ~0x02000000; + }*/ + + if (Config.PsxOut && !CHECK_EEREC) { + u32 call = psxRegs.GPR.n.t1 & 0xff; + switch (psxRegs.pc & 0x1fffff) { + case 0xa0: + + if (call != 0x28 && call != 0xe) + PSXBIOS_LOG("Bios call a0: %s (%x) %x,%x,%x,%x\n", biosA0n[call], call, psxRegs.GPR.n.a0, psxRegs.GPR.n.a1, psxRegs.GPR.n.a2, psxRegs.GPR.n.a3); + + if (biosA0[call]) + biosA0[call](); + break; + + case 0xb0: + if (call != 0x17 && call != 0xb) + PSXBIOS_LOG("Bios call b0: %s (%x) %x,%x,%x,%x\n", biosB0n[call], call, psxRegs.GPR.n.a0, psxRegs.GPR.n.a1, psxRegs.GPR.n.a2, psxRegs.GPR.n.a3); + + if (biosB0[call]) + biosB0[call](); + break; + + case 0xc0: + PSXBIOS_LOG("Bios call c0: %s (%x) %x,%x,%x,%x\n", biosC0n[call], call, psxRegs.GPR.n.a0, psxRegs.GPR.n.a1, psxRegs.GPR.n.a2, psxRegs.GPR.n.a3); + + if (biosC0[call]) + biosC0[call](); + break; + } + } + + /*if (psxRegs.CP0.n.Cause == 0x400 && (!(psxHu32(0x1450) & 0x8))) { + hwIntcIrq(1); + }*/ +} + +__forceinline void psxSetNextBranch( u32 startCycle, s32 delta ) +{ + // typecast the conditional to signed so that things don't blow up + // if startCycle is greater than our next branch cycle. + + if( (int)(g_psxNextBranchCycle - startCycle) > delta ) + g_psxNextBranchCycle = startCycle + delta; +} + +__forceinline void psxSetNextBranchDelta( s32 delta ) +{ + psxSetNextBranch( psxRegs.cycle, delta ); +} + +__forceinline int psxTestCycle( u32 startCycle, s32 delta ) +{ + // typecast the conditional to signed so that things don't explode + // if the startCycle is ahead of our current cpu cycle. + + return (int)(psxRegs.cycle - startCycle) >= delta; +} + +__forceinline void PSX_INT( IopEventId n, s32 ecycle ) +{ + // Generally speaking games shouldn't throw ints that haven't been cleared yet. + // It's usually indicative os something amiss in our emulation, so uncomment this + // code to help trap those sort of things. + + // Exception: IRQ16 - SIO - it drops ints like crazy when handling PAD stuff. + //if( /*n!=16 &&*/ psxRegs.interrupt & (1< Twice-thrown int on IRQ %d\n", n ); + + psxRegs.interrupt |= 1 << n; + + psxRegs.sCycle[n] = psxRegs.cycle; + psxRegs.eCycle[n] = ecycle; + + psxSetNextBranchDelta( ecycle ); + + if( psxCycleEE < 0 ) + { + // The EE called this int, so inform it to branch as needed: + // fixme - this doesn't take into account EE/IOP sync (the IOP may be running + // ahead or behind the EE as per the EEsCycles value) + s32 iopDelta = (g_psxNextBranchCycle-psxRegs.cycle)*8; + cpuSetNextBranchDelta( iopDelta ); + } +} + +static __forceinline void IopTestEvent( IopEventId n, void (*callback)() ) +{ + if( !(psxRegs.interrupt & (1 << n)) ) return; + + if( psxTestCycle( psxRegs.sCycle[n], psxRegs.eCycle[n] ) ) + { + psxRegs.interrupt &= ~(1 << n); + callback(); + } + else + psxSetNextBranch( psxRegs.sCycle[n], psxRegs.eCycle[n] ); +} + +static __forceinline void _psxTestInterrupts() +{ + IopTestEvent(IopEvt_SIF0, sif0Interrupt); // SIF0 + IopTestEvent(IopEvt_SIF1, sif1Interrupt); // SIF1 + IopTestEvent(IopEvt_SIO, sioInterrupt); + IopTestEvent(IopEvt_CdvdRead, cdvdReadInterrupt); + + // Profile-guided Optimization (sorta) + // The following ints are rarely called. Encasing them in a conditional + // as follows helps speed up most games. + + if( psxRegs.interrupt & ( (1ul<<5) | (3ul<<11) | (3ul<<20) | (3ul<<17) ) ) + { + IopTestEvent(IopEvt_Cdvd, cdvdActionInterrupt); + IopTestEvent(IopEvt_Dma11, psxDMA11Interrupt); // SIO2 + IopTestEvent(IopEvt_Dma12, psxDMA12Interrupt); // SIO2 + IopTestEvent(IopEvt_Cdrom, cdrInterrupt); + IopTestEvent(IopEvt_CdromRead, cdrReadInterrupt); + IopTestEvent(IopEvt_DEV9, dev9Interrupt); + IopTestEvent(IopEvt_USB, usbInterrupt); + } +} + +void psxBranchTest() +{ + if( psxTestCycle( psxNextsCounter, psxNextCounter ) ) + { + psxRcntUpdate(); + iopBranchAction = true; + } + + // start the next branch at the next counter event by default + // the interrupt code below will assign nearer branches if needed. + g_psxNextBranchCycle = psxNextsCounter+psxNextCounter; + + if (psxRegs.interrupt) + { + iopEventTestIsActive = true; + _psxTestInterrupts(); + iopEventTestIsActive = false; + } + + if( psxHu32(0x1078) == 0 ) return; + if( (psxHu32(0x1070) & psxHu32(0x1074)) == 0 ) return; + + if ((psxRegs.CP0.n.Status & 0xFE01) >= 0x401) + { +// PSXCPU_LOG("Interrupt: %x %x\n", HWMu32(0x1070), HWMu32(0x1074)); + psxException(0, 0); + iopBranchAction = true; + } +} + +void iopTestIntc() +{ + if( psxHu32(0x1078) == 0 ) return; + if( (psxHu32(0x1070) & psxHu32(0x1074)) == 0 ) return; + + if( !eeEventTestIsActive ) + { + // An iop exception has occured while the EE is running code. + // Inform the EE to branch so the IOP can handle it promptly: + + cpuSetNextBranchDelta( 16 ); + iopBranchAction = true; + //Console::Error( "** IOP Needs an EE EventText, kthx ** %d", params psxCycleEE ); + + // Note: No need to set the iop's branch delta here, since the EE + // will run an IOP branch test regardless. + } + else if( !iopEventTestIsActive ) + psxSetNextBranchDelta( 2 ); +} + +void psxExecuteBios() { +/* while (psxRegs.pc != 0x80030000) + psxCpu->ExecuteBlock(); + PSX_LOG("*BIOS END*\n"); +*/ +} diff --git a/pcsx2/R3000A.h b/pcsx2/R3000A.h new file mode 100644 index 0000000000..119e1d91a9 --- /dev/null +++ b/pcsx2/R3000A.h @@ -0,0 +1,217 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __R3000A_H__ +#define __R3000A_H__ + +#include + +union GPRRegs { + struct { + u32 r0, at, v0, v1, a0, a1, a2, a3, + t0, t1, t2, t3, t4, t5, t6, t7, + s0, s1, s2, s3, s4, s5, s6, s7, + t8, t9, k0, k1, gp, sp, s8, ra, hi, lo; // hi needs to be at index 32! don't change + } n; + u32 r[34]; /* Lo, Hi in r[33] and r[32] */ +}; + +union CP0Regs { + struct { + u32 Index, Random, EntryLo0, EntryLo1, + Context, PageMask, Wired, Reserved0, + BadVAddr, Count, EntryHi, Compare, + Status, Cause, EPC, PRid, + Config, LLAddr, WatchLO, WatchHI, + XContext, Reserved1, Reserved2, Reserved3, + Reserved4, Reserved5, ECC, CacheErr, + TagLo, TagHi, ErrorEPC, Reserved6; + } n; + u32 r[32]; +}; + +struct SVector2D { + short x, y; +}; + +struct SVector2Dz { + short z, pad; +}; + +struct SVector3D { + short x, y, z, pad; +}; + +struct LVector3D { + short x, y, z, pad; +}; + +struct CBGR { + unsigned char r, g, b, c; +}; + +struct SMatrix3D { + short m11, m12, m13, m21, m22, m23, m31, m32, m33, pad; +}; + +union CP2Data { + struct { + SVector3D v0, v1, v2; + CBGR rgb; + s32 otz; + s32 ir0, ir1, ir2, ir3; + SVector2D sxy0, sxy1, sxy2, sxyp; + SVector2Dz sz0, sz1, sz2, sz3; + CBGR rgb0, rgb1, rgb2; + s32 reserved; + s32 mac0, mac1, mac2, mac3; + u32 irgb, orgb; + s32 lzcs, lzcr; + } n; + u32 r[32]; +}; + +union CP2Ctrl { + struct { + SMatrix3D rMatrix; + s32 trX, trY, trZ; + SMatrix3D lMatrix; + s32 rbk, gbk, bbk; + SMatrix3D cMatrix; + s32 rfc, gfc, bfc; + s32 ofx, ofy; + s32 h; + s32 dqa, dqb; + s32 zsf3, zsf4; + s32 flag; + } n; + u32 r[32]; +}; + +struct psxRegisters { + GPRRegs GPR; /* General Purpose Registers */ + CP0Regs CP0; /* Coprocessor0 Registers */ + CP2Data CP2D; /* Cop2 data registers */ + CP2Ctrl CP2C; /* Cop2 control registers */ + u32 pc; /* Program counter */ + u32 code; /* The instruction */ + u32 cycle; + u32 interrupt; + u32 sCycle[64]; // start cycle for signaled ints + s32 eCycle[64]; // cycle delta for signaled ints (sCycle + eCycle == branch cycle) + u32 _msflag[32]; + u32 _smflag[32]; +}; + +extern PCSX2_ALIGNED16_DECL(psxRegisters psxRegs); + +extern u32 g_psxNextBranchCycle; +extern s32 psxBreak; // used when the IOP execution is broken and control returned to the EE +extern s32 psxCycleEE; // tracks IOP's current sych status with the EE + +#ifndef _PC_ + +#define _i32(x) (s32)x +#define _u32(x) x + +#define _i16(x) (short)x +#define _u16(x) (unsigned short)x + +#define _i8(x) (char)x +#define _u8(x) (unsigned char)x + +/**** R3000A Instruction Macros ****/ +#define _PC_ psxRegs.pc // The next PC to be executed + +#define _Funct_ ((psxRegs.code ) & 0x3F) // The funct part of the instruction register +#define _Rd_ ((psxRegs.code >> 11) & 0x1F) // The rd part of the instruction register +#define _Rt_ ((psxRegs.code >> 16) & 0x1F) // The rt part of the instruction register +#define _Rs_ ((psxRegs.code >> 21) & 0x1F) // The rs part of the instruction register +#define _Sa_ ((psxRegs.code >> 6) & 0x1F) // The sa part of the instruction register +#define _Im_ ((unsigned short)psxRegs.code) // The immediate part of the instruction register +#define _Target_ (psxRegs.code & 0x03ffffff) // The target part of the instruction register + +#define _Imm_ ((short)psxRegs.code) // sign-extended immediate +#define _ImmU_ (psxRegs.code&0xffff) // zero-extended immediate + +#define _rRs_ psxRegs.GPR.r[_Rs_] // Rs register +#define _rRt_ psxRegs.GPR.r[_Rt_] // Rt register +#define _rRd_ psxRegs.GPR.r[_Rd_] // Rd register +#define _rSa_ psxRegs.GPR.r[_Sa_] // Sa register +#define _rFs_ psxRegs.CP0.r[_Rd_] // Fs register + +#define _c2dRs_ psxRegs.CP2D.r[_Rs_] // Rs cop2 data register +#define _c2dRt_ psxRegs.CP2D.r[_Rt_] // Rt cop2 data register +#define _c2dRd_ psxRegs.CP2D.r[_Rd_] // Rd cop2 data register +#define _c2dSa_ psxRegs.CP2D.r[_Sa_] // Sa cop2 data register + +#define _rHi_ psxRegs.GPR.n.hi // The HI register +#define _rLo_ psxRegs.GPR.n.lo // The LO register + +#define _JumpTarget_ ((_Target_ << 2) + (_PC_ & 0xf0000000)) // Calculates the target during a jump instruction +#define _BranchTarget_ (((s32)(s16)_Imm_ * 4) + _PC_) // Calculates the target during a branch instruction +//#define _JumpTarget_ ((_Target_ * 4) + (_PC_ & 0xf0000000)) // Calculates the target during a jump instruction +//#define _BranchTarget_ ((short)_Im_ * 4 + _PC_) // Calculates the target during a branch instruction + +#define _SetLink(x) psxRegs.GPR.r[x] = _PC_ + 4; // Sets the return address in the link register + +extern s32 EEsCycle; +extern u32 EEoCycle; + +#endif + +extern s32 psxNextCounter; +extern u32 psxNextsCounter; +extern bool iopBranchAction; +extern bool iopEventTestIsActive; + +// Branching status used when throwing exceptions. +extern bool iopIsDelaySlot; + +//////////////////////////////////////////////////////////////////// +// R3000A Public Interface / API + +struct R3000Acpu { + void (*Allocate)(); + void (*Reset)(); + void (*Execute)(); + s32 (*ExecuteBlock)( s32 eeCycles ); // executes the given number of EE cycles. + void (*Clear)(u32 Addr, u32 Size); + void (*Shutdown)(); +}; + +extern R3000Acpu *psxCpu; +extern R3000Acpu psxInt; +extern R3000Acpu psxRec; + +void psxReset(); +void psxShutdown(); +void psxException(u32 code, u32 step); +void psxBranchTest(); +void psxExecuteBios(); +void psxMemReset(); + +// Subsets +extern void (*psxBSC[64])(); +extern void (*psxSPC[64])(); +extern void (*psxREG[32])(); +extern void (*psxCP0[32])(); +extern void (*psxCP2[64])(); +extern void (*psxCP2BSC[32])(); + +#endif /* __R3000A_H__ */ diff --git a/pcsx2/R3000AInterpreter.cpp b/pcsx2/R3000AInterpreter.cpp new file mode 100644 index 0000000000..d22585c5ad --- /dev/null +++ b/pcsx2/R3000AInterpreter.cpp @@ -0,0 +1,445 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" +#include "Common.h" + +using namespace R3000A; + +// Used to flag delay slot instructions when throwig exceptions. +bool iopIsDelaySlot = false; + +static bool branch2 = 0; +static u32 branchPC; + +static void doBranch(s32 tar); // forward declared prototype + +struct irxlib { + char name[16]; + char names[64][64]; + int maxn; +}; + +#define IRXLIBS 14 +irxlib irxlibs[32] = { +/*00*/ { { "sysmem" } , + { "start", "init_memory", "retonly", "return_addr_of_memsize", + "AllocSysMemory", "FreeSysMemory", "QueryMemSize", "QueryMaxFreeMemSize", + "QueryTotalFreeMemSize", "QueryBlockTopAddress", "QueryBlockSize", "retonly", + "retonly", "retonly", "Kprintf", "set_Kprintf" } , + 16 }, +/*01*/ { { "loadcore" } , + { "start", "retonly", "retonly_", "return_LibraryEntryTable", + "FlushIcache", "FlushDcache", "RegisterLibraryEntries", "ReleaseLibraryEntries", + "findFixImports", "restoreImports", "RegisterNonAutoLinkEntries", "QueryLibraryEntryTable", + "QueryBootMode", "RegisterBootMode", "setFlag", "resetFlag", + "linkModule", "unlinkModule", "retonly_", "retonly_", + "registerFunc", "jumpA0001B34", "read_header", "load_module", + "findImageInfo" }, + 25 }, +/*02*/ { { "excepman" } , + { "start", "reinit", "deinit", "getcommon", + "RegisterExceptionHandler", "RegisterPriorityExceptionHandler", + "RegisterDefaultExceptionHandler", "ReleaseExceptionHandler", + "ReleaseDefaultExceptionHandler" } , + 9 }, +/*03_4*/{ { "intrman" } , + { "start", "return_0", "deinit", "call3", + "RegisterIntrHandler", "ReleaseIntrHandler", "EnableIntr", "DisableIntr", + "CpuDisableIntr", "CpuEnableIntr", "syscall04", "syscall08", + "resetICTRL", "setICTRL", "syscall0C", "call15", + "call16", "CpuSuspendIntr", "CpuResumeIntr", "CpuSuspendIntr", + "CpuResumeIntr", "syscall10", "syscall14", "QueryIntrContext", + "QueryIntrStack", "iCatchMultiIntr", "retonly", "call27", + "set_h1", "reset_h1", "set_h2", "reset_h2" } , + 0x20 }, +/*05*/ { { "ssbusc" } , + { "start", "retonly", "return_0", "retonly", + "setTable1", "getTable1", "setTable2", "getTable2", + "setCOM_DELAY_1st", "getCOM_DELAY_1st", "setCOM_DELAY_2nd", "getCOM_DELAY_2nd", + "setCOM_DELAY_3rd", "getCOM_DELAY_3rd", "setCOM_DELAY_4th", "getCOM_DELAY_4th", + "setCOM_DELAY", "getCOM_DELAY" } , + 18 }, +/*06*/ { { "dmacman" } , + { "start", "retonly", "deinit", "retonly", + "SetD_MADR", "GetD_MADR", "SetD_BCR", "GetD_BCR", + "SetD_CHCR", "GetD_CHCR", "SetD_TADR", "GetD_TADR", + "Set_4_9_A", "Get_4_9_A", "SetDPCR", "GetDPCR", + "SetDPCR2", "GetDPCR2", "SetDPCR3", "GetDPCR3", + "SetDICR", "GetDICR", "SetDICR2", "GetDICR2", + "SetBF80157C", "GetBF80157C", "SetBF801578", "GetBF801578", + "SetDMA", "SetDMA_chainedSPU_SIF0", "SetDMA_SIF0", "SetDMA_SIF1", + "StartTransfer", "SetVal", "EnableDMAch", "DisableDMAch" } , + 36 }, +/*07_8*/{ { "timrman" } , + { "start", "retonly", "retonly", "call3", + "AllocHardTimer", "ReferHardTimer", "FreeHardTimer", "SetTimerMode", + "GetTimerStatus", "SetTimerCounter", "GetTimerCounter", "SetTimerCompare", + "GetTimerCompare", "SetHoldMode", "GetHoldMode", "GetHoldReg", + "GetHardTimerIntrCode" } , + 17 }, +/*09*/ { { "sysclib" } , + { "start", "reinit", "retonly", "retonly", + "setjmp", "longjmp", "toupper", "tolower", + "look_ctype_table", "get_ctype_table", "memchr", "memcmp", + "memcpy", "memmove", "memset", "bcmp", + "bcopy", "bzero", "prnt", "sprintf", + "strcat", "strchr", "strcmp", "strcpy", + "strcspn", "index", "rindex", "strlen", + "strncat", "strncmp", "strncpy", "strpbrk", + "strrchr", "strspn", "strstr", "strtok", + "strtol", "atob", "strtoul", "wmemcopy", + "wmemset", "vsprintf" } , + 0x2b }, +/*0A*/ { { "heaplib" } , + { "start", "retonly", "retonly", "retonly", + "CreateHeap", "DestroyHeap", "HeapMalloc", "HeapFree", + "HeapSize", "retonly", "retonly", "call11", + "call12", "call13", "call14", "call15", + "retonly", "retonly" } , + 18 }, +/*13*/ { { "stdio" } , + { "start", "unknown", "unknown", "unknown", + "printf" } , + 5 }, +/*14*/ { { "sifman" } , + { "start", "retonly", "deinit", "retonly", + "sceSif2Init", "sceSifInit", "sceSifSetDChain", "sceSifSetDma", + "sceSifDmaStat", "sceSifSend", "sceSifSendSync", "sceSifIsSending", + "sceSifSetSIF0DMA", "sceSifSendSync0", "sceSifIsSending0", "sceSifSetSIF1DMA", + "sceSifSendSync1", "sceSifIsSending1", "sceSifSetSIF2DMA", "sceSifSendSync2", + "sceSifIsSending2", "getEEIOPflags", "setEEIOPflags", "getIOPEEflags", + "setIOPEEflags", "getEErcvaddr", "getIOPrcvaddr", "setIOPrcvaddr", + "call28", "sceSifCheckInit", "setSif0CB", "resetSif0CB", + "retonly", "retonly", "retonly", "retonly" } , + 36 }, +/*16*/ { { "sifcmd" } , + { "start", "retonly", "deinit", "retonly", + "sceSifInitCmd", "sceSifExitCmd", "sceSifGetSreg", "sceSifSetSreg", + "sceSifSetCmdBuffer", "sceSifSetSysCmdBuffer", + "sceSifAddCmdHandler", "sceSifRemoveCmdHandler", + "sceSifSendCmd", "isceSifSendCmd", "sceSifInitRpc", "sceSifBindRpc", + "sceSifCallRpc", "sceSifRegisterRpc", + "sceSifCheckStatRpc", "sceSifSetRpcQueue", + "sceSifGetNextRequest", "sceSifExecRequest", + "sceSifRpcLoop", "sceSifGetOtherData", + "sceSifRemoveRpc", "sceSifRemoveRpcQueue", + "setSif1CB", "resetSif1CB", + "retonly", "retonly", "retonly", "retonly" } , + 32 }, +/*19*/ { { "cdvdman" } , + { "start", "retonly", "retonly", "retonly", + "sceCdInit", "sceCdStandby", "sceCdRead", "sceCdSeek", + "sceCdGetError", "sceCdGetToc", "sceCdSearchFile", "sceCdSync", + "sceCdGetDiskType", "sceCdDiskReady", "sceCdTrayReq", "sceCdStop", + "sceCdPosToInt", "sceCdIntToPos", "retonly", "call19", + "sceDvdRead", "sceCdCheckCmd", "_sceCdRI", "sceCdWriteILinkID", + "sceCdReadClock", "sceCdWriteRTC", "sceCdReadNVM", "sceCdWriteNVM", + "sceCdStatus", "sceCdApplySCmd", "setHDmode", "sceCdOpenConfig", + "sceCdCloseConfig", "sceCdReadConfig", "sceCdWriteConfig", "sceCdReadKey", + "sceCdDecSet", "sceCdCallback", "sceCdPause", "sceCdBreak", + "call40", "sceCdReadConsoleID", "sceCdWriteConsoleID", "sceCdGetMecaconVersion", + "sceCdGetReadPos", "AudioDigitalOut", "sceCdNop", "_sceGetFsvRbuf", + "_sceCdstm0Cb", "_sceCdstm1Cb", "_sceCdSC", "_sceCdRC", + "sceCdForbidDVDP", "sceCdReadSubQ", "sceCdApplyNCmd", "AutoAdjustCtrl", + "sceCdStInit", "sceCdStRead", "sceCdStSeek", "sceCdStStart", + "sceCdStStat", "sceCdStStop" } , + 62 }, +/*??*/ { { "sio2man" } , + { "start", "retonly", "deinit", "retonly", + "set8268_ctrl", "get8268_ctrl", "get826C_recv1", "call7_send1", + "call8_send1", "call9_send2", "call10_send2", "get8270_recv2", + "call12_set_params", "call13_get_params", "get8274_recv3", "set8278", + "get8278", "set827C", "get827C", "set8260_datain", + "get8264_dataout", "set8280_intr", "get8280_intr", "signalExchange1", + "signalExchange2", "packetExchange" } , + 26 } +}; + +#define Ra0 ((char*)PSXM(psxRegs.GPR.n.a0)) +#define Ra1 ((char*)PSXM(psxRegs.GPR.n.a1)) +#define Ra2 ((char*)PSXM(psxRegs.GPR.n.a2)) +#define Ra3 ((char*)PSXM(psxRegs.GPR.n.a3)) + +const char* intrname[]={ +"INT_VBLANK", "INT_GM", "INT_CDROM", "INT_DMA", //00 +"INT_RTC0", "INT_RTC1", "INT_RTC2", "INT_SIO0", //04 +"INT_SIO1", "INT_SPU", "INT_PIO", "INT_EVBLANK", //08 +"INT_DVD", "INT_PCMCIA", "INT_RTC3", "INT_RTC4", //0C +"INT_RTC5", "INT_SIO2", "INT_HTR0", "INT_HTR1", //10 +"INT_HTR2", "INT_HTR3", "INT_USB", "INT_EXTR", //14 +"INT_FWRE", "INT_FDMA", "INT_1A", "INT_1B", //18 +"INT_1C", "INT_1D", "INT_1E", "INT_1F", //1C +"INT_dmaMDECi", "INT_dmaMDECo", "INT_dmaGPU", "INT_dmaCD", //20 +"INT_dmaSPU", "INT_dmaPIO", "INT_dmaOTC", "INT_dmaBERR", //24 +"INT_dmaSPU2", "INT_dma8", "INT_dmaSIF0", "INT_dmaSIF1", //28 +"INT_dmaSIO2i", "INT_dmaSIO2o", "INT_2E", "INT_2F", //2C +"INT_30", "INT_31", "INT_32", "INT_33", //30 +"INT_34", "INT_35", "INT_36", "INT_37", //34 +"INT_38", "INT_39", "INT_3A", "INT_3B", //38 +"INT_3C", "INT_3D", "INT_3E", "INT_3F", //3C +"INT_MAX" //40 +}; + +void zeroEx() +{ +#ifdef PCSX2_DEVBUILD + u32 pc; + u32 code; + char *lib; + char *fname = NULL; + int i; + + if (!Config.PsxOut) return; + + pc = psxRegs.pc; + while (PSXMu32(pc) != 0x41e00000) pc-=4; + + lib = (char*)PSXM(pc+12); + code = PSXMu32(psxRegs.pc - 4) & 0xffff; + + for (i=0; i= (u32)irxlibs[i].maxn) break; + + fname = irxlibs[i].names[code]; + //if( strcmp(fname, "setIOPrcvaddr") == 0 ) { +// SysPrintf("yo\n"); +// varLog |= 0x100000; +// Log = 1; +// } + break; + } + } + + {char libz[9]; memcpy(libz, lib, 8); libz[8]=0; + PSXBIOS_LOG("%s: %s (%x)" + " (%x, %x, %x, %x)" //comment this line to disable param showing + , libz, fname == NULL ? "unknown" : fname, code, + psxRegs.GPR.n.a0, psxRegs.GPR.n.a1, psxRegs.GPR.n.a2, psxRegs.GPR.n.a3); + } + +// Log=0; +// if (!strcmp(lib, "intrman") && code == 0x11) Log=1; +// if (!strcmp(lib, "sifman") && code == 0x5) Log=1; +// if (!strcmp(lib, "sifcmd") && code == 0x4) Log=1; +// if (!strcmp(lib, "thbase") && code == 0x6) Log=1; +/* + if (!strcmp(lib, "sifcmd") && code == 0xe) { + branchPC = psxRegs.GPR.n.ra; + psxRegs.GPR.n.v0 = 0; + return; + } +*/ + if (!strncmp(lib, "ioman", 5) && code == 7) { + if (psxRegs.GPR.n.a0 == 1) { + pc = psxRegs.pc; + bios_write(); + psxRegs.pc = pc; + } + } + + if (!strncmp(lib, "sysmem", 6) && code == 0xe) { + bios_printf(); + psxRegs.pc = psxRegs.GPR.n.ra; + } + + if (!strncmp(lib, "loadcore", 8) && code == 6) { + DevCon::WriteLn("loadcore RegisterLibraryEntries (%x): %8.8s", params psxRegs.pc, PSXM(psxRegs.GPR.n.a0+12)); + } + + if (!strncmp(lib, "intrman", 7) && code == 4) { + DevCon::WriteLn("intrman RegisterIntrHandler (%x): intr %s, handler %x", params psxRegs.pc, intrname[psxRegs.GPR.n.a0], psxRegs.GPR.n.a2); + } + + if (!strncmp(lib, "sifcmd", 6) && code == 17) { + DevCon::WriteLn("sifcmd sceSifRegisterRpc (%x): rpc_id %x", params psxRegs.pc, psxRegs.GPR.n.a1); + } + + if (!strncmp(lib, "sysclib", 8)) + { + switch (code) + { + case 0x16: // strcmp + PSXBIOS_LOG(" \"%s\": \"%s\"", Ra0, Ra1); + break; + + case 0x1e: // strncpy + PSXBIOS_LOG(" \"%s\"", Ra1); + break; + } + } + +/* psxRegs.pc = branchPC; + pc = psxRegs.GPR.n.ra; + while (psxRegs.pc != pc) psxCpu->ExecuteBlock(); + + PSXBIOS_LOG("%s: %s (%x) END\n", lib, fname == NULL ? "unknown" : fname, code);*/ +#endif + +} +/*/==========================================CALL LOG +char* getName(char *file, u32 addr){ + FILE *f; u32 a; + static char name[100]; + + f=fopen(file, "r"); + if (!f) + name[0]=0; + else{ + while (!feof(f)){ + fscanf(f, "%08X %s\n", &a, name); + if (a==addr)break; + } + fclose(f); + } + return name; +} + +void spyFunctions(){ + register irxImageInfo *iii; + if (psxRegs.pc >= 0x200000) return; + for (iii=(irxImageInfo*)PSXM(0x800); iii && iii->text_size; + iii=iii->next ? (irxImageInfo*)PSXM(iii->next) : NULL) + if (iii->vaddr<=psxRegs.pc && psxRegs.pcvaddr+iii->text_size+iii->data_size+iii->bss_size){ + if (strcmp("secrman_for_cex", PSXM(iii->name))==0){ + char *name=getName("secrman.fun", psxRegs.pc-iii->vaddr); + if (strncmp("__push_params", name, 13)==0){ + PAD_LOG(PSXM(psxRegs.GPR.n.a0), psxRegs.GPR.n.a1, psxRegs.GPR.n.a2, psxRegs.GPR.n.a3); + }else{ + PAD_LOG("secrman: %s (ra=%06X cycle=%d)\n", name, psxRegs.GPR.n.ra-iii->vaddr, psxRegs.cycle);}}else + if (strcmp("mcman", PSXM(iii->name))==0){ + PAD_LOG("mcman: %s (ra=%06X cycle=%d)\n", getName("mcman.fun", psxRegs.pc-iii->vaddr), psxRegs.GPR.n.ra-iii->vaddr, psxRegs.cycle);}else + if (strcmp("padman", PSXM(iii->name))==0){ + PAD_LOG("padman: %s (ra=%06X cycle=%d)\n", getName("padman.fun", psxRegs.pc-iii->vaddr), psxRegs.GPR.n.ra-iii->vaddr, psxRegs.cycle);}else + if (strcmp("sio2man", PSXM(iii->name))==0){ + PAD_LOG("sio2man: %s (ra=%06X cycle=%d)\n", getName("sio2man.fun", psxRegs.pc-iii->vaddr), psxRegs.GPR.n.ra-iii->vaddr, psxRegs.cycle);} + break; + } +} +*/ + +/********************************************************* +* Register branch logic * +* Format: OP rs, offset * +*********************************************************/ +#define RepZBranchi32(op) if(_i32(_rRs_) op 0) doBranch(_BranchTarget_); +#define RepZBranchLinki32(op) if(_i32(_rRs_) op 0) { _SetLink(31); doBranch(_BranchTarget_); } + +void psxBGEZ() { RepZBranchi32(>=) } // Branch if Rs >= 0 +void psxBGEZAL() { RepZBranchLinki32(>=) } // Branch if Rs >= 0 and link +void psxBGTZ() { RepZBranchi32(>) } // Branch if Rs > 0 +void psxBLEZ() { RepZBranchi32(<=) } // Branch if Rs <= 0 +void psxBLTZ() { RepZBranchi32(<) } // Branch if Rs < 0 +void psxBLTZAL() { RepZBranchLinki32(<) } // Branch if Rs < 0 and link + +/********************************************************* +* Register branch logic * +* Format: OP rs, rt, offset * +*********************************************************/ +#define RepBranchi32(op) if(_i32(_rRs_) op _i32(_rRt_)) doBranch(_BranchTarget_); + +void psxBEQ() { RepBranchi32(==) } // Branch if Rs == Rt +void psxBNE() { RepBranchi32(!=) } // Branch if Rs != Rt + +/********************************************************* +* Jump to target * +* Format: OP target * +*********************************************************/ +void psxJ() { doBranch(_JumpTarget_); } +void psxJAL() { _SetLink(31); doBranch(_JumpTarget_); /*spyFunctions();*/ } + +/********************************************************* +* Register jump * +* Format: OP rs, rd * +*********************************************************/ +void psxJR() { doBranch(_u32(_rRs_)); } +void psxJALR() { if (_Rd_) { _SetLink(_Rd_); } doBranch(_u32(_rRs_)); } + +/////////////////////////////////////////// +// These macros are used to assemble the repassembler functions + +static __forceinline void execI() { + psxRegs.code = PSXMu32(psxRegs.pc); + + PSXCPU_LOG("%s\n", disR3000AF(psxRegs.code, psxRegs.pc)); + + psxRegs.pc+= 4; + psxRegs.cycle++; + psxCycleEE-=8; + + psxBSC[psxRegs.code >> 26](); +} + + +static void doBranch(s32 tar) { + branch2 = iopIsDelaySlot = true; + branchPC = tar; + execI(); + iopIsDelaySlot = false; + psxRegs.pc = branchPC; + + psxBranchTest(); +} + +static void intAlloc() { +} + +static void intReset() { +} + +static void intExecute() { + for (;;) execI(); +} + +#ifdef _DEBUG +extern u32 psxdump; +extern void iDumpPsxRegisters(u32,u32); +#endif + +static s32 intExecuteBlock( s32 eeCycles ) +{ + psxBreak = 0; + psxCycleEE = eeCycles; + + while (psxCycleEE > 0){ + branch2 = 0; + while (!branch2) { + execI(); + } + } + return psxBreak + psxCycleEE; +} + +static void intClear(u32 Addr, u32 Size) { +} + +static void intShutdown() { +} + +R3000Acpu psxInt = { + intAlloc, + intReset, + intExecute, + intExecuteBlock, + intClear, + intShutdown +}; diff --git a/pcsx2/R3000AOpcodeTables.cpp b/pcsx2/R3000AOpcodeTables.cpp new file mode 100644 index 0000000000..6e00ff3ccb --- /dev/null +++ b/pcsx2/R3000AOpcodeTables.cpp @@ -0,0 +1,383 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" +#include "Common.h" + +extern void zeroEx(); + +// Note: Branch instructions of the Interpreter are defined externally because +// the recompiler shouldn't be using them (it isn't entirely safe, due to the +// delay slot and event handling differences between recs and ints) +void psxBGEZ(); +void psxBGEZAL(); +void psxBGTZ(); +void psxBLEZ(); +void psxBLTZ(); +void psxBLTZAL(); + +void psxBEQ(); +void psxBNE(); +void psxJ(); +void psxJAL(); + +void psxJR(); +void psxJALR(); + +/********************************************************* +* Arithmetic with immediate operand * +* Format: OP rt, rs, immediate * +*********************************************************/ +void psxADDI() { if (!_Rt_) return; _rRt_ = _u32(_rRs_) + _Imm_ ; } // Rt = Rs + Im (Exception on Integer Overflow) +void psxADDIU() { // Rt = Rs + Im + if (!_Rt_) + { + zeroEx(); + return; + } + _rRt_ = _u32(_rRs_) + _Imm_ ; +} +void psxANDI() { if (!_Rt_) return; _rRt_ = _u32(_rRs_) & _ImmU_; } // Rt = Rs And Im +void psxORI() { if (!_Rt_) return; _rRt_ = _u32(_rRs_) | _ImmU_; } // Rt = Rs Or Im +void psxXORI() { if (!_Rt_) return; _rRt_ = _u32(_rRs_) ^ _ImmU_; } // Rt = Rs Xor Im +void psxSLTI() { if (!_Rt_) return; _rRt_ = _i32(_rRs_) < _Imm_ ; } // Rt = Rs < Im (Signed) +void psxSLTIU() { if (!_Rt_) return; _rRt_ = _u32(_rRs_) < _ImmU_; } // Rt = Rs < Im (Unsigned) + +/********************************************************* +* Register arithmetic * +* Format: OP rd, rs, rt * +*********************************************************/ +void psxADD() { if (!_Rd_) return; _rRd_ = _u32(_rRs_) + _u32(_rRt_); } // Rd = Rs + Rt (Exception on Integer Overflow) +void psxADDU() { if (!_Rd_) return; _rRd_ = _u32(_rRs_) + _u32(_rRt_); } // Rd = Rs + Rt +void psxSUB() { if (!_Rd_) return; _rRd_ = _u32(_rRs_) - _u32(_rRt_); } // Rd = Rs - Rt (Exception on Integer Overflow) +void psxSUBU() { if (!_Rd_) return; _rRd_ = _u32(_rRs_) - _u32(_rRt_); } // Rd = Rs - Rt +void psxAND() { if (!_Rd_) return; _rRd_ = _u32(_rRs_) & _u32(_rRt_); } // Rd = Rs And Rt +void psxOR() { if (!_Rd_) return; _rRd_ = _u32(_rRs_) | _u32(_rRt_); } // Rd = Rs Or Rt +void psxXOR() { if (!_Rd_) return; _rRd_ = _u32(_rRs_) ^ _u32(_rRt_); } // Rd = Rs Xor Rt +void psxNOR() { if (!_Rd_) return; _rRd_ =~(_u32(_rRs_) | _u32(_rRt_)); }// Rd = Rs Nor Rt +void psxSLT() { if (!_Rd_) return; _rRd_ = _i32(_rRs_) < _i32(_rRt_); } // Rd = Rs < Rt (Signed) +void psxSLTU() { if (!_Rd_) return; _rRd_ = _u32(_rRs_) < _u32(_rRt_); } // Rd = Rs < Rt (Unsigned) + +/********************************************************* +* Register mult/div & Register trap logic * +* Format: OP rs, rt * +*********************************************************/ +void psxDIV() { + if (_rRt_ != 0) { + _rLo_ = _i32(_rRs_) / _i32(_rRt_); + _rHi_ = _i32(_rRs_) % _i32(_rRt_); + } +} + +void psxDIVU() { + if (_rRt_ != 0) { + _rLo_ = _rRs_ / _rRt_; + _rHi_ = _rRs_ % _rRt_; + } +} + +void psxMULT() { + u64 res = (s64)((s64)_i32(_rRs_) * (s64)_i32(_rRt_)); + + psxRegs.GPR.n.lo = (u32)(res & 0xffffffff); + psxRegs.GPR.n.hi = (u32)((res >> 32) & 0xffffffff); +} + +void psxMULTU() { + u64 res = (u64)((u64)_u32(_rRs_) * (u64)_u32(_rRt_)); + + psxRegs.GPR.n.lo = (u32)(res & 0xffffffff); + psxRegs.GPR.n.hi = (u32)((res >> 32) & 0xffffffff); +} + +/********************************************************* +* Shift arithmetic with constant shift * +* Format: OP rd, rt, sa * +*********************************************************/ +void psxSLL() { if (!_Rd_) return; _rRd_ = _u32(_rRt_) << _Sa_; } // Rd = Rt << sa +void psxSRA() { if (!_Rd_) return; _rRd_ = _i32(_rRt_) >> _Sa_; } // Rd = Rt >> sa (arithmetic) +void psxSRL() { if (!_Rd_) return; _rRd_ = _u32(_rRt_) >> _Sa_; } // Rd = Rt >> sa (logical) + +/********************************************************* +* Shift arithmetic with variant register shift * +* Format: OP rd, rt, rs * +*********************************************************/ +void psxSLLV() { if (!_Rd_) return; _rRd_ = _u32(_rRt_) << _u32(_rRs_); } // Rd = Rt << rs +void psxSRAV() { if (!_Rd_) return; _rRd_ = _i32(_rRt_) >> _u32(_rRs_); } // Rd = Rt >> rs (arithmetic) +void psxSRLV() { if (!_Rd_) return; _rRd_ = _u32(_rRt_) >> _u32(_rRs_); } // Rd = Rt >> rs (logical) + +/********************************************************* +* Load higher 16 bits of the first word in GPR with imm * +* Format: OP rt, immediate * +*********************************************************/ +void psxLUI() { if (!_Rt_) return; _rRt_ = psxRegs.code << 16; } // Upper halfword of Rt = Im + +/********************************************************* +* Move from HI/LO to GPR * +* Format: OP rd * +*********************************************************/ +void psxMFHI() { if (!_Rd_) return; _rRd_ = _rHi_; } // Rd = Hi +void psxMFLO() { if (!_Rd_) return; _rRd_ = _rLo_; } // Rd = Lo + +/********************************************************* +* Move to GPR to HI/LO & Register jump * +* Format: OP rs * +*********************************************************/ +void psxMTHI() { _rHi_ = _rRs_; } // Hi = Rs +void psxMTLO() { _rLo_ = _rRs_; } // Lo = Rs + +/********************************************************* +* Special purpose instructions * +* Format: OP * +*********************************************************/ +void psxBREAK() { + // Break exception - psx rom doens't handles this + psxRegs.pc -= 4; + psxException(0x24, iopIsDelaySlot); +} + +void psxSYSCALL() { + psxRegs.pc -= 4; + psxException(0x20, iopIsDelaySlot); + +} + +void psxRFE() { +// SysPrintf("RFE\n"); + psxRegs.CP0.n.Status = (psxRegs.CP0.n.Status & 0xfffffff0) | + ((psxRegs.CP0.n.Status & 0x3c) >> 2); +// Log=0; +} + +/********************************************************* +* Load and store for GPR * +* Format: OP rt, offset(base) * +*********************************************************/ + +#define _oB_ (_u32(_rRs_) + _Imm_) + +void psxLB() { + if (_Rt_) { + _rRt_ = (s8 )psxMemRead8(_oB_); + } else { + psxMemRead8(_oB_); + } +} + +void psxLBU() { + if (_Rt_) { + _rRt_ = psxMemRead8(_oB_); + } else { + psxMemRead8(_oB_); + } +} + +void psxLH() { + if (_Rt_) { + _rRt_ = (s16)psxMemRead16(_oB_); + } else { + psxMemRead16(_oB_); + } +} + +void psxLHU() { + if (_Rt_) { + _rRt_ = psxMemRead16(_oB_); + } else { + psxMemRead16(_oB_); + } +} + +void psxLW() { + if (_Rt_) { + _rRt_ = psxMemRead32(_oB_); + } else { + psxMemRead32(_oB_); + } +} + +void psxLWL() { + u32 shift = (_oB_ & 3) << 3; + u32 mem = psxMemRead32(_oB_ & 0xfffffffc); + + if (!_Rt_) return; + _rRt_ = ( _u32(_rRt_) & (0x00ffffff >> shift) ) | + ( mem << (24 - shift) ); + + /* + Mem = 1234. Reg = abcd + + 0 4bcd (mem << 24) | (reg & 0x00ffffff) + 1 34cd (mem << 16) | (reg & 0x0000ffff) + 2 234d (mem << 8) | (reg & 0x000000ff) + 3 1234 (mem ) | (reg & 0x00000000) + + */ +} + +void psxLWR() { + u32 shift = (_oB_ & 3) << 3; + u32 mem = psxMemRead32(_oB_ & 0xfffffffc); + + if (!_Rt_) return; + _rRt_ = ( _u32(_rRt_) & (0xffffff00 << (24 - shift)) ) | + ( mem >> shift ); + + /* + Mem = 1234. Reg = abcd + + 0 1234 (mem ) | (reg & 0x00000000) + 1 a123 (mem >> 8) | (reg & 0xff000000) + 2 ab12 (mem >> 16) | (reg & 0xffff0000) + 3 abc1 (mem >> 24) | (reg & 0xffffff00) + + */ +} + +void psxSB() { psxMemWrite8 (_oB_, _u8 (_rRt_)); } +void psxSH() { psxMemWrite16(_oB_, _u16(_rRt_)); } +void psxSW() { psxMemWrite32(_oB_, _u32(_rRt_)); } + +void psxSWL() { + u32 shift = (_oB_ & 3) << 3; + u32 mem = psxMemRead32(_oB_ & 0xfffffffc); + + psxMemWrite32((_oB_ & 0xfffffffc), ( ( _u32(_rRt_) >> (24 - shift) ) ) | + ( mem & (0xffffff00 << shift) )); + /* + Mem = 1234. Reg = abcd + + 0 123a (reg >> 24) | (mem & 0xffffff00) + 1 12ab (reg >> 16) | (mem & 0xffff0000) + 2 1abc (reg >> 8) | (mem & 0xff000000) + 3 abcd (reg ) | (mem & 0x00000000) + + */ +} + +void psxSWR() { + u32 shift = (_oB_ & 3) << 3; + u32 mem = psxMemRead32(_oB_ & 0xfffffffc); + + psxMemWrite32((_oB_ & 0xfffffffc), ( ( _u32(_rRt_) << shift ) | + (mem & (0x00ffffff >> (24 - shift)) ) ) ); + /* + Mem = 1234. Reg = abcd + + 0 abcd (reg ) | (mem & 0x00000000) + 1 bcd4 (reg << 8) | (mem & 0x000000ff) + 2 cd34 (reg << 16) | (mem & 0x0000ffff) + 3 d234 (reg << 24) | (mem & 0x00ffffff) + + */ +} + +/********************************************************* +* Moves between GPR and COPx * +* Format: OP rt, fs * +*********************************************************/ +void psxMFC0() { if (!_Rt_) return; _rRt_ = (int)_rFs_; } +void psxCFC0() { if (!_Rt_) return; _rRt_ = (int)_rFs_; } + +void psxMTC0() { _rFs_ = _u32(_rRt_); } +void psxCTC0() { _rFs_ = _u32(_rRt_); } + +/********************************************************* +* Unknow instruction (would generate an exception) * +* Format: ? * +*********************************************************/ +void psxNULL() { +SysPrintf("psx: Unimplemented op %x\n", psxRegs.code); +} + +void psxSPECIAL() { + psxSPC[_Funct_](); +} + +void psxREGIMM() { + psxREG[_Rt_](); +} + +void psxCOP0() { + psxCP0[_Rs_](); +} + +void psxCOP2() { + psxCP2[_Funct_](); +} + +void psxBASIC() { + psxCP2BSC[_Rs_](); +} + +void (*psxBSC[64])() = { + psxSPECIAL, psxREGIMM, psxJ , psxJAL , psxBEQ , psxBNE , psxBLEZ, psxBGTZ, + psxADDI , psxADDIU , psxSLTI, psxSLTIU, psxANDI, psxORI , psxXORI, psxLUI , + psxCOP0 , psxNULL , psxCOP2, psxNULL , psxNULL, psxNULL, psxNULL, psxNULL, + psxNULL , psxNULL , psxNULL, psxNULL , psxNULL, psxNULL, psxNULL, psxNULL, + psxLB , psxLH , psxLWL , psxLW , psxLBU , psxLHU , psxLWR , psxNULL, + psxSB , psxSH , psxSWL , psxSW , psxNULL, psxNULL, psxSWR , psxNULL, + psxNULL , psxNULL , psxNULL, psxNULL , psxNULL, psxNULL, psxNULL, psxNULL, + psxNULL , psxNULL , psxNULL, psxNULL , psxNULL, psxNULL, psxNULL, psxNULL +}; + + +void (*psxSPC[64])() = { + psxSLL , psxNULL , psxSRL , psxSRA , psxSLLV , psxNULL , psxSRLV, psxSRAV, + psxJR , psxJALR , psxNULL, psxNULL, psxSYSCALL, psxBREAK, psxNULL, psxNULL, + psxMFHI, psxMTHI , psxMFLO, psxMTLO, psxNULL , psxNULL , psxNULL, psxNULL, + psxMULT, psxMULTU, psxDIV , psxDIVU, psxNULL , psxNULL , psxNULL, psxNULL, + psxADD , psxADDU , psxSUB , psxSUBU, psxAND , psxOR , psxXOR , psxNOR , + psxNULL, psxNULL , psxSLT , psxSLTU, psxNULL , psxNULL , psxNULL, psxNULL, + psxNULL, psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, psxNULL, + psxNULL, psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, psxNULL +}; + +void (*psxREG[32])() = { + psxBLTZ , psxBGEZ , psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, + psxNULL , psxNULL , psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, + psxBLTZAL, psxBGEZAL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, + psxNULL , psxNULL , psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL +}; + +void (*psxCP0[32])() = { + psxMFC0, psxNULL, psxCFC0, psxNULL, psxMTC0, psxNULL, psxCTC0, psxNULL, + psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, + psxRFE , psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, + psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL +}; + +void (*psxCP2[64])() = { + psxBASIC, psxNULL , psxNULL , psxNULL, psxNULL, psxNULL , psxNULL, psxNULL, // 00 + psxNULL , psxNULL , psxNULL , psxNULL, psxNULL , psxNULL , psxNULL , psxNULL, // 08 + psxNULL , psxNULL, psxNULL, psxNULL, psxNULL , psxNULL , psxNULL , psxNULL, // 10 + psxNULL , psxNULL , psxNULL , psxNULL, psxNULL , psxNULL , psxNULL , psxNULL, // 18 + psxNULL , psxNULL , psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, // 20 + psxNULL , psxNULL , psxNULL , psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, // 28 + psxNULL , psxNULL , psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, // 30 + psxNULL , psxNULL , psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL // 38 +}; + +void (*psxCP2BSC[32])() = { + psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, + psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, + psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, + psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL +}; diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp new file mode 100644 index 0000000000..661d0598d0 --- /dev/null +++ b/pcsx2/R5900.cpp @@ -0,0 +1,693 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "Counters.h" + +#include "Memory.h" +#include "Hw.h" +#include "DebugTools/Debug.h" +#include "R3000A.h" +#include "VUmicro.h" +#include "COP0.h" + +#include "GS.h" +#include "IPU/IPU.h" +#include "Vif.h" +#include "VifDma.h" +#include "SPR.h" +#include "Sif.h" + +#include "Paths.h" + +using namespace R5900; // for R5900 disasm tools + +s32 EEsCycle; // used to sync the IOP to the EE +u32 EEoCycle; + +static int inter; + +PCSX2_ALIGNED16(cpuRegisters cpuRegs); +PCSX2_ALIGNED16(fpuRegisters fpuRegs); +PCSX2_ALIGNED16(tlbs tlb[48]); +R5900cpu *Cpu = NULL; + +u32 bExecBIOS = 0; // set if the BIOS has already been executed + +static bool cpuIsInitialized = false; +static uint eeWaitCycles = 1024; + +bool eeEventTestIsActive = false; + +// A run-once procedure for initializing the emulation state. +// Can be done anytime after allocating memory, and before calling Cpu->Execute(). +// Multiple calls to this function are automatically ignored. +/*void cpuInit() +{ + DevCon::WriteLn( "cpuInit > %s", params cpuIsInitialized ? "Initializing..." : "Skipping (already initialized)" ); + + if( cpuIsInitialized ) return; + + cpuIsInitialized = true; + + // non memInit() currently since we don't support soft resets. memory is initialized in full + // instead during cpuReset() [using memReset()] + //memInit(); +}*/ + +void cpuReset() +{ + mtgsWaitGS(); // GS better be done processing before we reset the EE, just in case. + //cpuInit(); // more just-in-caseness! + + //if( !cpuIsInitialized ) + { + cpuIsInitialized = true; + } + + memReset(); + psxMemReset(); + vuMicroMemReset(); + + memzero_obj(cpuRegs); + memzero_obj(fpuRegs); + memzero_obj(tlb); + + cpuRegs.pc = 0xbfc00000; ///set pc reg to stack + cpuRegs.CP0.n.Config = 0x440; + cpuRegs.CP0.n.Status.val = 0x70400004; //0x10900000 <-- wrong; // COP0 enabled | BEV = 1 | TS = 1 + cpuRegs.CP0.n.PRid = 0x00002e20; // PRevID = Revision ID, same as R5900 + fpuRegs.fprc[0] = 0x00002e00; // fpu Revision.. + fpuRegs.fprc[31] = 0x01000001; // fpu Status/Control + + g_nextBranchCycle = cpuRegs.cycle + 4; + EEsCycle = 0; + EEoCycle = cpuRegs.cycle; + eeWaitCycles = CHECK_WAITCYCLE_HACK ? 3072 : 768; + + // Cyclerate hacks effectively speed up the rate of event tests, so we can safely boost + // the WaitCycles value here for x2 and x3 modes: + if( CHECK_EE_CYCLERATE > 1 ) + eeWaitCycles += 1024; + + hwReset(); + vif0Reset(); + vif1Reset(); + rcntInit(); + psxReset(); +} + +void cpuShutdown() +{ + //if( !cpuIsInitialized ) return; + //cpuIsInitialized = false; + + mtgsWaitGS(); + + hwShutdown(); +// biosShutdown(); + psxShutdown(); + disR5900FreeSyms(); +} + +void cpuException(u32 code, u32 bd) +{ + cpuRegs.branch = 0; // Tells the interpreter that an exception occurred during a branch. + + u32 offset; + cpuRegs.CP0.n.Cause = code & 0xffff; + + if(cpuRegs.CP0.n.Status.b.ERL == 0){ //Error Level 0-1 + if(((code & 0x7C) >= 0x8) && ((code & 0x7C) <= 0xC)) offset = 0x0; //TLB Refill + else if ((code & 0x7C) == 0x0) offset = 0x200; //Interrupt + else offset = 0x180; // Everything else + + + if (cpuRegs.CP0.n.Status.b.EXL == 0) { + cpuRegs.CP0.n.Status.b.EXL = 1; + if (bd) { + Console::Notice("branch delay!!"); + cpuRegs.CP0.n.EPC = cpuRegs.pc - 4; + cpuRegs.CP0.n.Cause |= 0x80000000; + } else { + cpuRegs.CP0.n.EPC = cpuRegs.pc; + cpuRegs.CP0.n.Cause &= ~0x80000000; + } + } else { + offset = 0x180; //Overrride the cause + //Console::Notice("cpuException: Status.EXL = 1 cause %x", params code); + } + if (cpuRegs.CP0.n.Status.b.BEV == 0) { + cpuRegs.pc = 0x80000000 + offset; + } else { + cpuRegs.pc = 0xBFC00200 + offset; + } + } else { //Error Level 2 + Console::Error("*PCSX2* FIX ME: Level 2 cpuException"); + if((code & 0x38000) <= 0x8000 ) { //Reset / NMI + cpuRegs.pc = 0xBFC00000; + Console::Notice("Reset request"); + UpdateCP0Status(); + return; + } else if((code & 0x38000) == 0x10000) offset = 0x80; //Performance Counter + else if((code & 0x38000) == 0x18000) offset = 0x100; //Debug + else Console::Error("Unknown Level 2 Exception!! Cause %x", params code); + + if (cpuRegs.CP0.n.Status.b.EXL == 0) { + cpuRegs.CP0.n.Status.b.EXL = 1; + if (bd) { + Console::Notice("branch delay!!"); + cpuRegs.CP0.n.EPC = cpuRegs.pc - 4; + cpuRegs.CP0.n.Cause |= 0x80000000; + } else { + cpuRegs.CP0.n.EPC = cpuRegs.pc; + cpuRegs.CP0.n.Cause &= ~0x80000000; + } + } else { + offset = 0x180; //Overrride the cause + Console::Notice("cpuException: Status.EXL = 1 cause %x", params code); + } + + if (cpuRegs.CP0.n.Status.b.DEV == 0) { + cpuRegs.pc = 0x80000000 + offset; + } else { + cpuRegs.pc = 0xBFC00200 + offset; + } + } + UpdateCP0Status(); +} + +void cpuTlbMiss(u32 addr, u32 bd, u32 excode) { + Console::Error("cpuTlbMiss pc:%x, cycl:%x, addr: %x, status=%x, code=%x", params cpuRegs.pc, cpuRegs.cycle, addr, cpuRegs.CP0.n.Status.val, excode); + if (bd) { + Console::Notice("branch delay!!"); + } + + assert(0); // temporary + + cpuRegs.CP0.n.BadVAddr = addr; + cpuRegs.CP0.n.Context &= 0xFF80000F; + cpuRegs.CP0.n.Context |= (addr >> 9) & 0x007FFFF0; + cpuRegs.CP0.n.EntryHi = (addr & 0xFFFFE000) | (cpuRegs.CP0.n.EntryHi & 0x1FFF); + + cpuRegs.CP0.n.Cause = excode; + if (!(cpuRegs.CP0.n.Status.val & 0x2)) { // EXL bit + cpuRegs.CP0.n.EPC = cpuRegs.pc - 4; + } + + if ((cpuRegs.CP0.n.Status.val & 0x1) == 0) { + cpuRegs.pc = 0x80000000; + } else { + cpuRegs.pc = 0x80000180; + } + + cpuRegs.CP0.n.Status.b.EXL = 1; + UpdateCP0Status(); +// Log=1; varLog|= 0x40000000; +} + +void cpuTlbMissR(u32 addr, u32 bd) { + cpuTlbMiss(addr, bd, EXC_CODE_TLBL); +} + +void cpuTlbMissW(u32 addr, u32 bd) { + cpuTlbMiss(addr, bd, EXC_CODE_TLBS); +} + +void JumpCheckSym(u32 addr, u32 pc) { +#if 0 +// if (addr == 0x80051770) { SysPrintf("Log!: %s\n", PSM(cpuRegs.GPR.n.a0.UL[0])); Log=1; varLog|= 0x40000000; } + if (addr == 0x8002f150) { SysPrintf("printk: %s\n", PSM(cpuRegs.GPR.n.a0.UL[0])); } + if (addr == 0x8002aba0) return; + if (addr == 0x8002f450) return; + if (addr == 0x800dd520) return; +// if (addr == 0x80049300) SysPrintf("register_blkdev: %x\n", cpuRegs.GPR.n.a0.UL[0]); + if (addr == 0x8013cb70) { SysPrintf("change_root: %x\n", cpuRegs.GPR.n.a0.UL[0]); } +// if (addr == 0x8013d1e8) { SysPrintf("Log!\n"); Log++; if (Log==2) exit(0); varLog|= 0x40000000; } +// if (addr == 0x00234e88) { SysPrintf("StoreImage\n"); Log=1; /*psMu32(0x234e88) = 0x03e00008; psMu32(0x234e8c) = 0;*/ } +#endif +/* if ((pc >= 0x00131D50 && + pc < 0x00132454) || + (pc >= 0x00786a90 && + pc < 0x00786ac8))*/ + /*if (varLog & 0x40000000) { + char *str; + char *strf; + + str = disR5900GetSym(addr); + if (str != NULL) { + strf = disR5900GetUpperSym(pc); + if (strf) { + SysPrintf("Func %8.8x: %s (called by %8.8x: %s)\n", addr, str, pc, strf); + } else { + SysPrintf("Func %8.8x: %s (called by %x)\n", addr, str, pc); + } + if (!strcmp(str, "printf")) { SysPrintf("%s\n", (char*)PSM(cpuRegs.GPR.n.a0.UL[0])); } + if (!strcmp(str, "printk")) { SysPrintf("%s\n", (char*)PSM(cpuRegs.GPR.n.a0.UL[0])); } + } + }*/ +} + +void JumpCheckSymRet(u32 addr) { + /*if (varLog & 0x40000000) { + char *str; + str = disR5900GetUpperSym(addr); + if (str != NULL) { + SysPrintf("Return : %s, v0=%8.8x\n", str, cpuRegs.GPR.n.v0.UL[0]); + } + }*/ +} + +__forceinline void _cpuTestMissingINTC() { + if (cpuRegs.CP0.n.Status.val & 0x400 && + psHu32(INTC_STAT) & psHu32(INTC_MASK)) { + if ((cpuRegs.interrupt & (1 << 30)) == 0) { + SysPrintf("*PCSX2*: Error, missing INTC Interrupt\n"); + } + } +} + +__forceinline void _cpuTestMissingDMAC() { + if (cpuRegs.CP0.n.Status.val & 0x800 && + (psHu16(0xe012) & psHu16(0xe010) || + psHu16(0xe010) & 0x8000)) { + if ((cpuRegs.interrupt & (1 << 31)) == 0) { + SysPrintf("*PCSX2*: Error, missing DMAC Interrupt\n"); + } + } +} + +void cpuTestMissingHwInts() { + if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) { + _cpuTestMissingINTC(); + _cpuTestMissingDMAC(); +// _cpuTestTIMR(); + } +} + +// sets a branch test to occur some time from an arbitrary starting point. +__forceinline int cpuSetNextBranch( u32 startCycle, s32 delta ) +{ + // typecast the conditional to signed so that things don't blow up + // if startCycle is greater than our next branch cycle. + + if( (int)(g_nextBranchCycle - startCycle) > delta ) + { + g_nextBranchCycle = startCycle + delta; + return 1; + } + return 0; +} + +// sets a branch to occur some time from the current cycle +__forceinline int cpuSetNextBranchDelta( s32 delta ) +{ + return cpuSetNextBranch( cpuRegs.cycle, delta ); +} + +// tests the cpu cycle agaisnt the given start and delta values. +// Returns true if the delta time has passed. +__forceinline int cpuTestCycle( u32 startCycle, s32 delta ) +{ + // typecast the conditional to signed so that things don't explode + // if the startCycle is ahead of our current cpu cycle. + + return (int)(cpuRegs.cycle - startCycle) >= delta; +} + +// tells the EE to run the branch test the next time it gets a chance. +__forceinline void cpuSetBranch() +{ + g_nextBranchCycle = cpuRegs.cycle; +} + +void cpuClearInt( uint i ) +{ + jASSUME( i < 32 ); + cpuRegs.interrupt &= ~(1 << i); +} + +static __forceinline void TESTINT( u8 n, void (*callback)() ) +{ + if( !(cpuRegs.interrupt & (1 << n)) ) return; + + if( cpuTestCycle( cpuRegs.sCycle[n], cpuRegs.eCycle[n] ) ) + { + cpuClearInt( n ); + callback(); + } + else + cpuSetNextBranch( cpuRegs.sCycle[n], cpuRegs.eCycle[n] ); +} + +static __forceinline void _cpuTestInterrupts() +{ + /* These are 'pcsx2 interrupts', they handle asynchronous stuff + that depends on the cycle timings */ + + TESTINT(1, vif1Interrupt); + TESTINT(2, gsInterrupt); + TESTINT(5, EEsif0Interrupt); + TESTINT(6, EEsif1Interrupt); + + // Profile-guided Optimization (sorta) + // The following ints are rarely called. Encasing them in a conditional + // as follows helps speed up most games. + + if( cpuRegs.interrupt & ( 1 | (3 << 3) | (3<<8) | (3<<10)) ) + { + TESTINT(0, vif0Interrupt); +#ifndef IPU_INLINE_IRQS + TESTINT(3, ipu0Interrupt); + TESTINT(4, ipu1Interrupt); +#endif + TESTINT(8, SPRFROMinterrupt); + TESTINT(9, SPRTOinterrupt); + + TESTINT(10, vifMFIFOInterrupt); + TESTINT(11, gifMFIFOInterrupt); + } +} + +static __forceinline void _cpuTestTIMR() +{ + cpuRegs.CP0.n.Count += cpuRegs.cycle-s_iLastCOP0Cycle; + s_iLastCOP0Cycle = cpuRegs.cycle; + + // fixme: this looks like a hack to make up for the fact that the TIMR + // doesn't yet have a proper mechanism for setting itself up on a nextBranchCycle. + // A proper fix would schedule the TIMR to trigger at a specific cycle anytime + // the Count or Compare registers are modified. + + if ( (cpuRegs.CP0.n.Status.val & 0x8000) && + cpuRegs.CP0.n.Count >= cpuRegs.CP0.n.Compare && cpuRegs.CP0.n.Count < cpuRegs.CP0.n.Compare+1000 ) + { + Console::Status("timr intr: %x, %x", params cpuRegs.CP0.n.Count, cpuRegs.CP0.n.Compare); + cpuException(0x808000, cpuRegs.branch); + } +} + +static __forceinline void _cpuTestPERF() +{ + // fixme - The interpreter and recompiler both re-calculate these values + // whenever they are read, so updating them at regular intervals *should be* + // merely a common courtesy. But when I set them up to be called less + // frequently some games would crash. I'd like to figure out why someday. [Air] + + if((cpuRegs.PERF.n.pccr & 0x800003E0) == 0x80000020) { + cpuRegs.PERF.n.pcr0 += cpuRegs.cycle-s_iLastPERFCycle[0]; + s_iLastPERFCycle[0] = cpuRegs.cycle; + } + if((cpuRegs.PERF.n.pccr & 0x800F8000) == 0x80008000) { + cpuRegs.PERF.n.pcr1 += cpuRegs.cycle-s_iLastPERFCycle[1]; + s_iLastPERFCycle[1] = cpuRegs.cycle; + } +} + +// Checks the COP0.Status for exception enablings. +// Exception handling for certain modes is *not* currently supported, this function filters +// them out. Exceptions while the exception handler is active (EIE), or exceptions of any +// level other than 0 are ignored here. + +static bool cpuIntsEnabled() +{ + return cpuRegs.CP0.n.Status.b.EIE && cpuRegs.CP0.n.Status.b.IE && + !cpuRegs.CP0.n.Status.b.EXL && (cpuRegs.CP0.n.Status.b.ERL == 0); +} + +// if cpuRegs.cycle is greater than this cycle, should check cpuBranchTest for updates +u32 g_nextBranchCycle = 0; + +// Shared portion of the branch test, called from both the Interpreter +// and the recompiler. (moved here to help alleviate redundant code) +__forceinline bool _cpuBranchTest_Shared() +{ + eeEventTestIsActive = true; + g_nextBranchCycle = cpuRegs.cycle + eeWaitCycles; + + EEsCycle += cpuRegs.cycle - EEoCycle; + EEoCycle = cpuRegs.cycle; + + if( EEsCycle > 0 ) + iopBranchAction = true; + + // ---- Counters ------------- + bool vsyncEvent = false; + rcntUpdate_hScanline(); + + if( cpuTestCycle( nextsCounter, nextCounter ) ) + { + vsyncEvent = rcntUpdate(); + _cpuTestPERF(); + } + + _cpuTestTIMR(); + + // ---- Interrupts ------------- + // Handles all interrupts except 30 and 31, which are handled later. + + if( cpuRegs.interrupt & ~(3<<30) ) + _cpuTestInterrupts(); + + // ---- IOP ------------- + // * It's important to run a psxBranchTest before calling ExecuteBlock. This + // is because the IOP does not always perform branch tests before returning + // (during the prev branch) and also so it can act on the state the EE has + // given it before executing any code. + // + // * The IOP cannot always be run. If we run IOP code every time through the + // cpuBranchTest, the IOP generally starts to run way ahead of the EE. + + psxBranchTest(); + + if( iopBranchAction ) + { + //if( EEsCycle < -450 ) + // Console::WriteLn( " IOP ahead by: %d cycles", params -EEsCycle ); + + // Experimental and Probably Unnecessry Logic --> + // Check if the EE already has an exception pending, and if so we shouldn't + // waste too much time updating the IOP. Theory being that the EE and IOP should + // run closely in sync during raised exception events. But in practice it didn't + // seem to make much of a difference. + + // Note: The IOP is very good about chaining blocks together so it tends to + // run lots of cycles, even with only 32 (4 IOP) cycles specified here. That's + // probably why it doesn't improve sync much. + + /*bool eeExceptPending = cpuIntsEnabled() && + //( cpuRegs.CP0.n.Status.b.EIE && cpuRegs.CP0.n.Status.b.IE && (cpuRegs.CP0.n.Status.b.ERL == 0) ) && + //( (cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001 ) && + ( (cpuRegs.interrupt & (3<<30)) != 0 ); + + if( eeExceptPending ) + { + // ExecuteBlock returns a negative value, so subtract it from the cycle count + // specified to get the total cycles processed! :D + int cycleCount = std::min( EEsCycle, (s32)(eeWaitCycles>>4) ); + int cyclesRun = cycleCount - psxCpu->ExecuteBlock( cycleCount ); + EEsCycle -= cyclesRun; + //Console::Notice( "IOP Exception-Pending Execution -- EEsCycle: %d", params EEsCycle ); + } + else*/ + { + EEsCycle = psxCpu->ExecuteBlock( EEsCycle ); + } + + iopBranchAction = false; + } + + // ---- VU0 ------------- + + if (VU0.VI[REG_VPU_STAT].UL & 0x1) + { + // We're in a BranchTest. All dynarec registers are flushed + // so there is no need to freeze registers here. + CpuVU0.ExecuteBlock(); + + // This might be needed to keep the EE and VU0 in sync. + // A better fix will require hefty changes to the VU recs. -_- + if(VU0.VI[REG_VPU_STAT].UL & 0x1) + cpuSetNextBranchDelta( 768 ); + } + + // Note: We don't update the VU1 here because it runs it's micro-programs in + // one shot always. That is, when a program is executed the VU1 doesn't even + // bother to return until the program is completely finished. + + // ---- Schedule Next Event Test -------------- + + if( EEsCycle > 192 ) + { + // EE's running way ahead of the IOP still, so we should branch quickly to give the + // IOP extra timeslices in short order. + + cpuSetNextBranchDelta( 48 ); + //Console::Notice( "EE ahead of the IOP -- Rapid Branch! %d", params EEsCycle ); + } + + // The IOP could be running ahead/behind of us, so adjust the iop's next branch by its + // relative position to the EE (via EEsCycle) + cpuSetNextBranchDelta( ((g_psxNextBranchCycle-psxRegs.cycle)*8) - EEsCycle ); + + // Apply the hsync counter's nextCycle + cpuSetNextBranch( counters[4].sCycle, counters[4].CycleT ); + + // Apply vsync and other counter nextCycles + cpuSetNextBranch( nextsCounter, nextCounter ); + + eeEventTestIsActive = false; + + // ---- INTC / DMAC Exceptions ----------------- + // Raise the INTC and DMAC interrupts here, which usually throw exceptions. + // This should be done last since the IOP and the VU0 can raise several EE + // exceptions. + + //if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) + if( cpuIntsEnabled() ) + { + TESTINT(30, intcInterrupt); + TESTINT(31, dmacInterrupt); + } + + return vsyncEvent; +} + +void cpuTestINTCInts() +{ + if( cpuRegs.interrupt & (1 << 30) ) return; + //if( (cpuRegs.CP0.n.Status.val & 0x10407) != 0x10401 ) return; + if( !cpuIntsEnabled() ) return; + if( (psHu32(INTC_STAT) & psHu32(INTC_MASK)) == 0 ) return; + + cpuRegs.interrupt|= 1 << 30; + cpuRegs.sCycle[30] = cpuRegs.cycle; + cpuRegs.eCycle[30] = 0; + + // only set the next branch delta if the exception won't be handled for + // the current branch... + if( !eeEventTestIsActive ) + cpuSetNextBranchDelta( 4 ); + else if(psxCycleEE > 0) + { + psxBreak += psxCycleEE; // record the number of cycles the IOP didn't run. + psxCycleEE = 0; + } +} + +__forceinline void cpuTestDMACInts() +{ + if ( cpuRegs.interrupt & (1 << 31) ) return; + if ((cpuRegs.CP0.n.Status.val & 0x10807) != 0x10801) return; + + if ( ( (psHu16(0xe012) & psHu16(0xe010)) == 0) && + ( (psHu16(0xe010) & 0x8000) == 0) ) return; + + cpuRegs.interrupt|= 1 << 31; + cpuRegs.sCycle[31] = cpuRegs.cycle; + cpuRegs.eCycle[31] = 0; + + // only set the next branch delta if the exception won't be handled for + // the current branch... + if( !eeEventTestIsActive ) + cpuSetNextBranchDelta( 4 ); + else if(psxCycleEE > 0) + { + psxBreak += psxCycleEE; // record the number of cycles the IOP didn't run. + psxCycleEE = 0; + } +} + +__forceinline void cpuTestTIMRInts() { + if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) { + _cpuTestPERF(); + _cpuTestTIMR(); + } +} + +void cpuTestHwInts() { + cpuTestINTCInts(); + cpuTestDMACInts(); + cpuTestTIMRInts(); +} + +// This function performs a "hackish" execution of the BIOS stub, which initializes EE +// memory and hardware. It forcefully breaks execution when the stub is finished, prior +// to the PS2 logos being displayed. This allows us to "shortcut" right into a game +// without having to wait through the logos or endure game/bios localization checks. +void cpuExecuteBios() +{ + // Set the video mode to user's default request: + // (right now we always default to NTSC) + gsSetVideoRegionType( Config.PsxType & 1 ); + + Console::Notice( "* PCSX2 *: ExecuteBios" ); + + bExecBIOS = TRUE; + while (cpuRegs.pc != 0x00200008 && + cpuRegs.pc != 0x00100008) { + g_nextBranchCycle = cpuRegs.cycle; + Cpu->ExecuteBlock(); + } + + bExecBIOS = FALSE; +// { +// FILE* f = fopen("eebios.bin", "wb"); +// fwrite(PSM(0x80000000), 0x100000, 1, f); +// fclose(f); +// exit(0); + +// f = fopen("iopbios.bin", "wb"); +// fwrite(PS2MEM_PSX, 0x80000, 1, f); +// fclose(f); +// } + +// REC_CLEARM(0x00200008); +// REC_CLEARM(0x00100008); +// REC_CLEARM(cpuRegs.pc); + + // Reset the EErecs here, because the bios generates "slow" blocks that have hacky + // bBiosEnd checks in them and stuff. This deletes them so that the recs replace them + // with new faster versions: + Cpu->Reset(); + + Console::Notice("* PCSX2 *: ExecuteBios Complete"); + //GSprintf(5, "PCSX2 " PCSX2_VERSION "\nExecuteBios Complete\n"); +} + +__forceinline void CPU_INT( u32 n, s32 ecycle) +{ + cpuRegs.interrupt|= 1 << n; + cpuRegs.sCycle[n] = cpuRegs.cycle; + cpuRegs.eCycle[n] = ecycle; + + // Interrupt is happening soon: make sure both EE and IOP are aware. + + if( ecycle <= 28 && psxCycleEE > 0 ) + { + // If running in the IOP, force it to break immediately into the EE. + // the EE's branch test is due to run. + + psxBreak += psxCycleEE; // record the number of cycles the IOP didn't run. + psxCycleEE = 0; + } + + cpuSetNextBranchDelta( cpuRegs.eCycle[n] ); +} diff --git a/pcsx2/R5900.h b/pcsx2/R5900.h new file mode 100644 index 0000000000..4462742071 --- /dev/null +++ b/pcsx2/R5900.h @@ -0,0 +1,276 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __R5900_H__ +#define __R5900_H__ + +extern bool g_EEFreezeRegs; + +// EE Bios function name tables. +namespace R5900 { +extern const char* const bios[256]; +} + +extern s32 EEsCycle; +extern u32 EEoCycle; +extern u32 bExecBIOS; + +union GPR_reg { // Declare union type GPR register + u64 UD[2]; //128 bits + s64 SD[2]; + u32 UL[4]; + s32 SL[4]; + u16 US[8]; + s16 SS[8]; + u8 UC[16]; + s8 SC[16]; +}; + +union GPRregs { + struct { + GPR_reg r0, at, v0, v1, a0, a1, a2, a3, + t0, t1, t2, t3, t4, t5, t6, t7, + s0, s1, s2, s3, s4, s5, s6, s7, + t8, t9, k0, k1, gp, sp, s8, ra; + } n; + GPR_reg r[32]; +}; + +union PERFregs { + struct { + u32 pccr, pcr0, pcr1, pad; + } n; + u32 r[4]; +}; + +union CP0regs { + struct { + u32 Index, Random, EntryLo0, EntryLo1, + Context, PageMask, Wired, Reserved0, + BadVAddr, Count, EntryHi, Compare; + union { + struct { + u32 IE:1; // Bit 0: Interrupt Enable flag. + u32 EXL:1; // Bit 1: Exception Level, set on any exception not covered by ERL. + u32 ERL:1; // Bit 2: Error level, set on Resetm NMI, perf/debug exceptions. + u32 KSU:2; // Bits 3-4: Kernel [clear] / Supervisor [set] mode + u32 unused0:3; + u32 IM:8; // Bits 10-15: Interrupt mask (bits 12,13,14 are unused) + u32 EIE:1; // Bit 16: IE bit enabler. When cleared, ints are disabled regardless of IE status. + u32 _EDI:1; // Bit 17: Interrupt Enable (set enables ints in all modes, clear enables ints in kernel mode only) + u32 CH:1; // Bit 18: Status of most recent cache instruction (set for hit, clear for miss) + u32 unused1:3; + u32 BEV:1; // Bit 22: if set, use bootstrap for TLB/general exceptions + u32 DEV:1; // Bit 23: if set, use bootstrap for perf/debug exceptions + u32 unused2:2; + u32 FR:1; // (?) + u32 unused3:1; + u32 CU:4; // Bits 28-31: Co-processor Usable flag + } b; + u32 val; + } Status; + u32 Cause, EPC, PRid, + Config, LLAddr, WatchLO, WatchHI, + XContext, Reserved1, Reserved2, Debug, + DEPC, PerfCnt, ErrCtl, CacheErr, + TagLo, TagHi, ErrorEPC, DESAVE; + } n; + u32 r[32]; +}; + +struct cpuRegisters { + GPRregs GPR; // GPR regs + // NOTE: don't change order since recompiler uses it + GPR_reg HI; + GPR_reg LO; // hi & log 128bit wide + CP0regs CP0; // is COP0 32bit? + u32 sa; // shift amount (32bit), needs to be 16 byte aligned + u32 constzero; // always 0, for MFSA + u32 pc; // Program counter, when changing offset in struct, check iR5900-X.S to make sure offset is correct + u32 code; // The instruction + PERFregs PERF; + u32 eCycle[32]; + u32 sCycle[32]; // for internal counters + u32 cycle; // calculate cpucycles.. + u32 interrupt; + int branch; + int opmode; // operating mode + u32 tempcycles; +}; + +// used for optimization +union GPR_reg64 { + u64 UD[1]; //64 bits + s64 SD[1]; + u32 UL[2]; + s32 SL[3]; + u16 US[4]; + s16 SS[4]; + u8 UC[8]; + s8 SC[8]; +}; + +union FPRreg { + float f; + u32 UL; +}; + +struct fpuRegisters { + FPRreg fpr[32]; // 32bit floating point registers + u32 fprc[32]; // 32bit floating point control registers + FPRreg ACC; // 32 bit accumulator +}; + +struct tlbs +{ + u32 PageMask,EntryHi; + u32 EntryLo0,EntryLo1; + u32 Mask, nMask; + u32 G; + u32 ASID; + u32 VPN2; + u32 PFN0; + u32 PFN1; + u32 S; +}; + +#ifndef _PC_ + +#define _i64(x) (s64)x +#define _u64(x) (u64)x + +#define _i32(x) (s32)x +#define _u32(x) (u32)x + +#define _i16(x) (s16)x +#define _u16(x) (u16)x + +#define _i8(x) (s8)x +#define _u8(x) (u8)x + +//////////////////////////////////////////////////////////////////// +// R5900 Instruction Macros + +#define _PC_ cpuRegs.pc // The next PC to be executed + +#define _Funct_ ((cpuRegs.code ) & 0x3F) // The funct part of the instruction register +#define _Rd_ ((cpuRegs.code >> 11) & 0x1F) // The rd part of the instruction register +#define _Rt_ ((cpuRegs.code >> 16) & 0x1F) // The rt part of the instruction register +#define _Rs_ ((cpuRegs.code >> 21) & 0x1F) // The rs part of the instruction register +#define _Sa_ ((cpuRegs.code >> 6) & 0x1F) // The sa part of the instruction register +#define _Im_ ((u16)cpuRegs.code) // The immediate part of the instruction register +#define _Target_ (cpuRegs.code & 0x03ffffff) // The target part of the instruction register + +#define _Imm_ ((s16)cpuRegs.code) // sign-extended immediate +#define _ImmU_ (cpuRegs.code&0xffff) // zero-extended immediate +#define _ImmSB_ (cpuRegs.code&0x8000) // gets the sign-bit of the immediate value + +#define _Opcode_ (cpuRegs.code >> 26 ) + +#define _JumpTarget_ ((_Target_ << 2) + (_PC_ & 0xf0000000)) // Calculates the target during a jump instruction +#define _BranchTarget_ (((s32)(s16)_Im_ * 4) + _PC_) // Calculates the target during a branch instruction + +#define _SetLink(x) cpuRegs.GPR.r[x].UD[0] = _PC_ + 4; // Sets the return address in the link register + +#endif + +void JumpCheckSym(u32 addr, u32 pc); +void JumpCheckSymRet(u32 addr); + +extern PCSX2_ALIGNED16_DECL(cpuRegisters cpuRegs); +extern PCSX2_ALIGNED16_DECL(fpuRegisters fpuRegs); +extern PCSX2_ALIGNED16_DECL(tlbs tlb[48]); + +extern u32 g_nextBranchCycle; +extern bool eeEventTestIsActive; +extern u32 s_iLastCOP0Cycle; +extern u32 s_iLastPERFCycle[2]; + +bool intEventTest(); +void intSetBranch(); + +// This is a special form of the interpreter's doBranch that is run from various +// parts of the Recs (namely COP0's branch codes and stuff). +void __fastcall intDoBranch(u32 target); + +//////////////////////////////////////////////////////////////////// +// R5900 Public Interface / API + +struct R5900cpu +{ + void (*Allocate)(); // throws exceptions on failure. + void (*Reset)(); + void (*Step)(); + void (*Execute)(); /* executes up to a break */ + void (*ExecuteBlock)(); + void (*Clear)(u32 Addr, u32 Size); + void (*Shutdown)(); // deallocates memory reserved by Allocate +}; + +extern R5900cpu *Cpu; +extern R5900cpu intCpu; +extern R5900cpu recCpu; + +extern void cpuInit(); +extern void cpuReset(); // can throw Exception::FileNotFound. +extern void cpuShutdown(); +extern void cpuExecuteBios(); +extern void cpuException(u32 code, u32 bd); +extern void cpuTlbMissR(u32 addr, u32 bd); +extern void cpuTlbMissW(u32 addr, u32 bd); +extern void cpuTestHwInts(); + +extern int cpuSetNextBranch( u32 startCycle, s32 delta ); +extern int cpuSetNextBranchDelta( s32 delta ); +extern int cpuTestCycle( u32 startCycle, s32 delta ); +extern void cpuSetBranch(); + +extern bool _cpuBranchTest_Shared(); // for internal use by the Dynarecs and Ints inside R5900: + +extern void cpuTestINTCInts(); +extern void cpuTestDMACInts(); +extern void cpuTestTIMRInts(); + +//////////////////////////////////////////////////////////////////// +// Exception Codes + +#define EXC_CODE(x) ((x)<<2) + +#define EXC_CODE_Int EXC_CODE(0) +#define EXC_CODE_Mod EXC_CODE(1) /* TLB Modification exception */ +#define EXC_CODE_TLBL EXC_CODE(2) /* TLB Miss exception (load or instruction fetch) */ +#define EXC_CODE_TLBS EXC_CODE(3) /* TLB Miss exception (store) */ +#define EXC_CODE_AdEL EXC_CODE(4) +#define EXC_CODE_AdES EXC_CODE(5) +#define EXC_CODE_IBE EXC_CODE(6) +#define EXC_CODE_DBE EXC_CODE(7) +#define EXC_CODE_Sys EXC_CODE(8) +#define EXC_CODE_Bp EXC_CODE(9) +#define EXC_CODE_Ri EXC_CODE(10) +#define EXC_CODE_CpU EXC_CODE(11) +#define EXC_CODE_Ov EXC_CODE(12) +#define EXC_CODE_Tr EXC_CODE(13) +#define EXC_CODE_FPE EXC_CODE(15) +#define EXC_CODE_WATCH EXC_CODE(23) +#define EXC_CODE__MASK 0x0000007c +#define EXC_CODE__SHIFT 2 + +#define EXC_TLB_STORE 1 +#define EXC_TLB_LOAD 0 + +#endif /* __R5900_H__ */ diff --git a/pcsx2/R5900OpcodeImpl.cpp b/pcsx2/R5900OpcodeImpl.cpp new file mode 100644 index 0000000000..046f334350 --- /dev/null +++ b/pcsx2/R5900OpcodeImpl.cpp @@ -0,0 +1,816 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900.h" +#include "R5900OpcodeTables.h" + +#include + +namespace R5900 +{ + const OPCODE& GetCurrentInstruction() + { + const OPCODE* opcode = &R5900::OpcodeTables::tbl_Standard[_Opcode_]; + + while( opcode->getsubclass != NULL ) + opcode = &opcode->getsubclass(); + + return *opcode; + } + + const char * const bios[256]= + { + //0x00 + "RFU000_FullReset", "ResetEE", "SetGsCrt", "RFU003", + "Exit", "RFU005", "LoadExecPS2", "ExecPS2", + "RFU008", "RFU009", "AddSbusIntcHandler", "RemoveSbusIntcHandler", + "Interrupt2Iop", "SetVTLBRefillHandler", "SetVCommonHandler", "SetVInterruptHandler", + //0x10 + "AddIntcHandler", "RemoveIntcHandler", "AddDmacHandler", "RemoveDmacHandler", + "_EnableIntc", "_DisableIntc", "_EnableDmac", "_DisableDmac", + "_SetAlarm", "_ReleaseAlarm", "_iEnableIntc", "_iDisableIntc", + "_iEnableDmac", "_iDisableDmac", "_iSetAlarm", "_iReleaseAlarm", + //0x20 + "CreateThread", "DeleteThread", "StartThread", "ExitThread", + "ExitDeleteThread", "TerminateThread", "iTerminateThread", "DisableDispatchThread", + "EnableDispatchThread", "ChangeThreadPriority", "iChangeThreadPriority", "RotateThreadReadyQueue", + "iRotateThreadReadyQueue", "ReleaseWaitThread", "iReleaseWaitThread", "GetThreadId", + //0x30 + "ReferThreadStatus","iReferThreadStatus", "SleepThread", "WakeupThread", + "_iWakeupThread", "CancelWakeupThread", "iCancelWakeupThread", "SuspendThread", + "iSuspendThread", "ResumeThread", "iResumeThread", "JoinThread", + "RFU060", "RFU061", "EndOfHeap", "RFU063", + //0x40 + "CreateSema", "DeleteSema", "SignalSema", "iSignalSema", + "WaitSema", "PollSema", "iPollSema", "ReferSemaStatus", + "iReferSemaStatus", "RFU073", "SetOsdConfigParam", "GetOsdConfigParam", + "GetGsHParam", "GetGsVParam", "SetGsHParam", "SetGsVParam", + //0x50 + "RFU080_CreateEventFlag", "RFU081_DeleteEventFlag", + "RFU082_SetEventFlag", "RFU083_iSetEventFlag", + "RFU084_ClearEventFlag", "RFU085_iClearEventFlag", + "RFU086_WaitEventFlag", "RFU087_PollEventFlag", + "RFU088_iPollEventFlag", "RFU089_ReferEventFlagStatus", + "RFU090_iReferEventFlagStatus", "RFU091_GetEntryAddress", + "EnableIntcHandler_iEnableIntcHandler", + "DisableIntcHandler_iDisableIntcHandler", + "EnableDmacHandler_iEnableDmacHandler", + "DisableDmacHandler_iDisableDmacHandler", + //0x60 + "KSeg0", "EnableCache", "DisableCache", "GetCop0", + "FlushCache", "RFU101", "CpuConfig", "iGetCop0", + "iFlushCache", "RFU105", "iCpuConfig", "sceSifStopDma", + "SetCPUTimerHandler", "SetCPUTimer", "SetOsdConfigParam2", "SetOsdConfigParam2", + //0x70 + "GsGetIMR_iGsGetIMR", "GsGetIMR_iGsPutIMR", "SetPgifHandler", "SetVSyncFlag", + "RFU116", "print", "sceSifDmaStat_isceSifDmaStat", "sceSifSetDma_isceSifSetDma", + "sceSifSetDChain_isceSifSetDChain", "sceSifSetReg", "sceSifGetReg", "ExecOSD", + "Deci2Call", "PSMode", "MachineType", "GetMemorySize", + }; + +namespace Interpreter { +namespace OpcodeImpl { + +void COP2() +{ + //std::string disOut; + //disR5900Fasm(disOut, cpuRegs.code, cpuRegs.pc); + + //VU0_LOG("%s\n", disOut.c_str()); + Int_COP2PrintTable[_Rs_](); +} + +void Unknown() { + CPU_LOG("%8.8lx: Unknown opcode called\n", cpuRegs.pc); +} + +void MMI_Unknown() { Console::Notice("Unknown MMI opcode called"); } +void COP0_Unknown() { Console::Notice("Unknown COP0 opcode called"); } +void COP1_Unknown() { Console::Notice("Unknown FPU/COP1 opcode called"); } + +/********************************************************* +* Arithmetic with immediate operand * +* Format: OP rt, rs, immediate * +*********************************************************/ +void ADDI() { if (!_Rt_) return; cpuRegs.GPR.r[_Rt_].UD[0] = cpuRegs.GPR.r[_Rs_].SL[0] + _Imm_; }// Rt = Rs + Im signed!!!! +void ADDIU() { if (!_Rt_) return; cpuRegs.GPR.r[_Rt_].UD[0] = cpuRegs.GPR.r[_Rs_].SL[0] + _Imm_; }// Rt = Rs + Im signed !!! +void DADDI() { if (!_Rt_) return; cpuRegs.GPR.r[_Rt_].UD[0] = cpuRegs.GPR.r[_Rs_].SD[0] + _Imm_; }// Rt = Rs + Im +void DADDIU() { if (!_Rt_) return; cpuRegs.GPR.r[_Rt_].UD[0] = cpuRegs.GPR.r[_Rs_].SD[0] + _Imm_; }// Rt = Rs + Im +void ANDI() { if (!_Rt_) return; cpuRegs.GPR.r[_Rt_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] & (u64)_ImmU_; } // Rt = Rs And Im (zero-extended) +void ORI() { if (!_Rt_) return; cpuRegs.GPR.r[_Rt_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] | (u64)_ImmU_; } // Rt = Rs Or Im (zero-extended) +void XORI() { if (!_Rt_) return; cpuRegs.GPR.r[_Rt_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] ^ (u64)_ImmU_; } // Rt = Rs Xor Im (zero-extended) +void SLTI() { if (!_Rt_) return; cpuRegs.GPR.r[_Rt_].UD[0] = cpuRegs.GPR.r[_Rs_].SD[0] < (s64)(_Imm_); } // Rt = Rs < Im (signed) +void SLTIU() { if (!_Rt_) return; cpuRegs.GPR.r[_Rt_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] < (u64)(_Imm_); } // Rt = Rs < Im (unsigned) + +/********************************************************* +* Register arithmetic * +* Format: OP rd, rs, rt * +*********************************************************/ +void ADD() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].SL[0] + cpuRegs.GPR.r[_Rt_].SL[0];} // Rd = Rs + Rt (Exception on Integer Overflow) +void ADDU() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].SL[0] + cpuRegs.GPR.r[_Rt_].SL[0];} // Rd = Rs + Rt +void DADD() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].SD[0] + cpuRegs.GPR.r[_Rt_].SD[0]; } +void DADDU() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].SD[0] + cpuRegs.GPR.r[_Rt_].SD[0]; } +void SUB() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].SL[0] - cpuRegs.GPR.r[_Rt_].SL[0];} // Rd = Rs - Rt (Exception on Integer Overflow) +void SUBU() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].SL[0] - cpuRegs.GPR.r[_Rt_].SL[0]; } // Rd = Rs - Rt +void DSUB() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].SD[0] - cpuRegs.GPR.r[_Rt_].SD[0];} +void DSUBU() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].SD[0] - cpuRegs.GPR.r[_Rt_].SD[0]; } +void AND() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] & cpuRegs.GPR.r[_Rt_].UD[0]; } // Rd = Rs And Rt +void OR() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] | cpuRegs.GPR.r[_Rt_].UD[0]; } // Rd = Rs Or Rt +void XOR() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] ^ cpuRegs.GPR.r[_Rt_].UD[0]; } // Rd = Rs Xor Rt +void NOR() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] =~(cpuRegs.GPR.r[_Rs_].UD[0] | cpuRegs.GPR.r[_Rt_].UD[0]); }// Rd = Rs Nor Rt +void SLT() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].SD[0] < cpuRegs.GPR.r[_Rt_].SD[0]; } // Rd = Rs < Rt (signed) +void SLTU() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0] < cpuRegs.GPR.r[_Rt_].UD[0]; } // Rd = Rs < Rt (unsigned) + +/********************************************************* +* Register mult/div & Register trap logic * +* Format: OP rs, rt * +*********************************************************/ +void DIV() { + if (cpuRegs.GPR.r[_Rt_].SL[0] != 0) { + cpuRegs.LO.SD[0] = cpuRegs.GPR.r[_Rs_].SL[0] / cpuRegs.GPR.r[_Rt_].SL[0]; + cpuRegs.HI.SD[0] = cpuRegs.GPR.r[_Rs_].SL[0] % cpuRegs.GPR.r[_Rt_].SL[0]; + } +} + +void DIVU() { + if (cpuRegs.GPR.r[_Rt_].UL[0] != 0) { + cpuRegs.LO.SD[0] = cpuRegs.GPR.r[_Rs_].UL[0] / cpuRegs.GPR.r[_Rt_].UL[0]; + cpuRegs.HI.SD[0] = cpuRegs.GPR.r[_Rs_].UL[0] % cpuRegs.GPR.r[_Rt_].UL[0]; + } +} + +void MULT() { //different in ps2... + s64 res = (s64)cpuRegs.GPR.r[_Rs_].SL[0] * (s64)cpuRegs.GPR.r[_Rt_].SL[0]; + + cpuRegs.LO.UD[0] = (s32)(res & 0xffffffff); + cpuRegs.HI.UD[0] = (s32)(res >> 32); + + if (!_Rd_) return; + cpuRegs.GPR.r[_Rd_].UD[0]= cpuRegs.LO.UD[0]; //that is the difference +} + +void MULTU() { //different in ps2.. + u64 res = (u64)cpuRegs.GPR.r[_Rs_].UL[0] * (u64)cpuRegs.GPR.r[_Rt_].UL[0]; + + cpuRegs.LO.UD[0] = (s32)(res & 0xffffffff); + cpuRegs.HI.UD[0] = (s32)(res >> 32); + + if (!_Rd_) return; + cpuRegs.GPR.r[_Rd_].UD[0]= cpuRegs.LO.UD[0]; //that is the difference +} + +/********************************************************* +* Load higher 16 bits of the first word in GPR with imm * +* Format: OP rt, immediate * +*********************************************************/ +void LUI() { + if (!_Rt_) return; + cpuRegs.GPR.r[_Rt_].UD[0] = (s32)(cpuRegs.code << 16); +} + +/********************************************************* +* Move from HI/LO to GPR * +* Format: OP rd * +*********************************************************/ +void MFHI() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.HI.UD[0]; } // Rd = Hi +void MFLO() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.LO.UD[0]; } // Rd = Lo + +/********************************************************* +* Move to GPR to HI/LO & Register jump * +* Format: OP rs * +*********************************************************/ +void MTHI() { cpuRegs.HI.UD[0] = cpuRegs.GPR.r[_Rs_].UD[0]; } // Hi = Rs +void MTLO() { cpuRegs.LO.UD[0] = cpuRegs.GPR.r[_Rs_].UD[0]; } // Lo = Rs + + +/********************************************************* +* Shift arithmetic with constant shift * +* Format: OP rd, rt, sa * +*********************************************************/ +void SLL() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].SD[0] = (s32)(cpuRegs.GPR.r[_Rt_].UL[0] << _Sa_); } // Rd = Rt << sa +void DSLL() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = (u64)(cpuRegs.GPR.r[_Rt_].UD[0] << _Sa_); } +void DSLL32(){ if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = (u64)(cpuRegs.GPR.r[_Rt_].UD[0] << (_Sa_+32));} +void SRA() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].SD[0] = (s32)(cpuRegs.GPR.r[_Rt_].SL[0] >> _Sa_); } // Rd = Rt >> sa (arithmetic) +void DSRA() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].SD[0] = (u64)(cpuRegs.GPR.r[_Rt_].SD[0] >> _Sa_); } +void DSRA32(){ if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].SD[0] = (u64)(cpuRegs.GPR.r[_Rt_].SD[0] >> (_Sa_+32));} +void SRL() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].SD[0] = (s32)(cpuRegs.GPR.r[_Rt_].UL[0] >> _Sa_); } // Rd = Rt >> sa (logical) +void DSRL() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = (u64)(cpuRegs.GPR.r[_Rt_].UD[0] >> _Sa_); } +void DSRL32(){ if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = (u64)(cpuRegs.GPR.r[_Rt_].UD[0] >> (_Sa_+32));} + +/********************************************************* +* Shift arithmetic with variant register shift * +* Format: OP rd, rt, rs * +*********************************************************/ +void SLLV() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].SD[0] = (s32)(cpuRegs.GPR.r[_Rt_].UL[0] << (cpuRegs.GPR.r[_Rs_].UL[0] &0x1f));} // Rd = Rt << rs +void SRAV() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].SD[0] = (s32)(cpuRegs.GPR.r[_Rt_].SL[0] >> (cpuRegs.GPR.r[_Rs_].UL[0] &0x1f));} // Rd = Rt >> rs (arithmetic) +void SRLV() { if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].SD[0] = (s32)(cpuRegs.GPR.r[_Rt_].UL[0] >> (cpuRegs.GPR.r[_Rs_].UL[0] &0x1f));} // Rd = Rt >> rs (logical) +void DSLLV(){ if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = (u64)(cpuRegs.GPR.r[_Rt_].UD[0] << (cpuRegs.GPR.r[_Rs_].UL[0] &0x3f));} +void DSRAV(){ if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].SD[0] = (s64)(cpuRegs.GPR.r[_Rt_].SD[0] >> (cpuRegs.GPR.r[_Rs_].UL[0] &0x3f));} +void DSRLV(){ if (!_Rd_) return; cpuRegs.GPR.r[_Rd_].UD[0] = (u64)(cpuRegs.GPR.r[_Rt_].UD[0] >> (cpuRegs.GPR.r[_Rs_].UL[0] &0x3f));} + +/********************************************************* +* Load and store for GPR * +* Format: OP rt, offset(base) * +*********************************************************/ + +void LB() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u8 temp; + const u32 rt=_Rt_; + + memRead8(addr, &temp); + if(rt!=0) + { + cpuRegs.GPR.r[rt].UD[0]=(s8)temp; + } +} + +void LBU() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u8 temp; + const u32 rt=_Rt_; + memRead8(addr, &temp); + if(rt!=0) + { + cpuRegs.GPR.r[rt].UD[0]=temp; + } +} + +void LH() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u16 temp; + const u32 rt=_Rt_; + memRead16(addr, &temp); + if(rt!=0) + { + cpuRegs.GPR.r[rt].UD[0]=(s16)temp; + } +} + +void LHU() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u16 temp; + const u32 rt=_Rt_; + memRead16(addr, &temp); + if(rt!=0) + { + cpuRegs.GPR.r[rt].UD[0]=temp; + } +} + + +void LW() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + + u32 temp; + const u32 rt=_Rt_; + memRead32(addr, &temp); + if(rt!=0) + { + cpuRegs.GPR.r[rt].UD[0]=(s32)temp; + } +} + +void LWU() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + + u32 temp; + const u32 rt=_Rt_; + memRead32(addr, &temp); + if(rt!=0) + { + cpuRegs.GPR.r[rt].UD[0]=temp; + } +} + +u32 LWL_MASK[4] = { 0xffffff, 0xffff, 0xff, 0 }; +u32 LWL_SHIFT[4] = { 24, 16, 8, 0 }; + +void LWL() { + s32 addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u32 shift = addr & 3; + u32 mem; + + if (!_Rt_) return; + memRead32(addr & ~3, &mem); + cpuRegs.GPR.r[_Rt_].UD[0] = (cpuRegs.GPR.r[_Rt_].UL[0] & LWL_MASK[shift]) | + (mem << LWL_SHIFT[shift]); + + /* + Mem = 1234. Reg = abcd + + 0 4bcd (mem << 24) | (reg & 0x00ffffff) + 1 34cd (mem << 16) | (reg & 0x0000ffff) + 2 234d (mem << 8) | (reg & 0x000000ff) + 3 1234 (mem ) | (reg & 0x00000000) + */ +} + +u32 LWR_MASK[4] = { 0, 0xff000000, 0xffff0000, 0xffffff00 }; +u32 LWR_SHIFT[4] = { 0, 8, 16, 24 }; + +void LWR() { + s32 addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u32 shift = addr & 3; + u32 mem; + + if (!_Rt_) return; + memRead32(addr & ~3, &mem); + cpuRegs.GPR.r[_Rt_].UD[0] = (cpuRegs.GPR.r[_Rt_].UL[0] & LWR_MASK[shift]) | + (mem >> LWR_SHIFT[shift]); + + /* + Mem = 1234. Reg = abcd + + 0 1234 (mem ) | (reg & 0x00000000) + 1 a123 (mem >> 8) | (reg & 0xff000000) + 2 ab12 (mem >> 16) | (reg & 0xffff0000) + 3 abc1 (mem >> 24) | (reg & 0xffffff00) + */ +} + +void LD() { + s32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + if (_Rt_) { + memRead64(addr, &cpuRegs.GPR.r[_Rt_].UD[0]); + } else { + u64 dummy; + memRead64(addr, &dummy); + } +} + +u64 LDL_MASK[8] = { 0x00ffffffffffffffLL, 0x0000ffffffffffffLL, 0x000000ffffffffffLL, 0x00000000ffffffffLL, + 0x0000000000ffffffLL, 0x000000000000ffffLL, 0x00000000000000ffLL, 0x0000000000000000LL }; +u32 LDL_SHIFT[8] = { 56, 48, 40, 32, 24, 16, 8, 0 }; + +void LDL() { + u32 addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u32 shift = addr & 7; + u64 mem; + + if (!_Rt_) return; + memRead64(addr & ~7, &mem); + cpuRegs.GPR.r[_Rt_].UD[0] = (cpuRegs.GPR.r[_Rt_].UD[0] & LDL_MASK[shift]) | + (mem << LDL_SHIFT[shift]); +} + +u64 LDR_MASK[8] = { 0x0000000000000000LL, 0xff00000000000000LL, 0xffff000000000000LL, 0xffffff0000000000LL, + 0xffffffff00000000LL, 0xffffffffff000000LL, 0xffffffffffff0000LL, 0xffffffffffffff00LL }; +u32 LDR_SHIFT[8] = { 0, 8, 16, 24, 32, 40, 48, 56 }; + +void LDR() { + u32 addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u32 shift = addr & 7; + u64 mem; + + if (!_Rt_) return; + memRead64(addr & ~7, &mem); + cpuRegs.GPR.r[_Rt_].UD[0] = (cpuRegs.GPR.r[_Rt_].UD[0] & LDR_MASK[shift]) | + (mem >> LDR_SHIFT[shift]); +} + +void LQ() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + addr&=~0xf; + + if (_Rt_) { + memRead128(addr, &cpuRegs.GPR.r[_Rt_].UD[0]); + } else { + u64 val[2]; + memRead128(addr, val); + } +} + +void SB() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + memWrite8(addr, cpuRegs.GPR.r[_Rt_].UC[0]); +} + +void SH() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + memWrite16(addr, cpuRegs.GPR.r[_Rt_].US[0]); +} + +void SW(){ + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + memWrite32(addr, cpuRegs.GPR.r[_Rt_].UL[0]); +} + +u32 SWL_MASK[4] = { 0xffffff00, 0xffff0000, 0xff000000, 0x00000000 }; +u32 SWL_SHIFT[4] = { 24, 16, 8, 0 }; + +void SWL() { + u32 addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u32 shift = addr & 3; + u32 mem; + + memRead32(addr & ~3, &mem); + + memWrite32(addr & ~3, (cpuRegs.GPR.r[_Rt_].UL[0] >> SWL_SHIFT[shift]) | + ( mem & SWL_MASK[shift]) ); + /* + Mem = 1234. Reg = abcd + + 0 123a (reg >> 24) | (mem & 0xffffff00) + 1 12ab (reg >> 16) | (mem & 0xffff0000) + 2 1abc (reg >> 8) | (mem & 0xff000000) + 3 abcd (reg ) | (mem & 0x00000000) + */ +} + +u32 SWR_MASK[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff }; +u32 SWR_SHIFT[4] = { 0, 8, 16, 24 }; + +void SWR() { + u32 addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u32 shift = addr & 3; + u32 mem; + + memRead32(addr & ~3, &mem); + + memWrite32(addr & ~3, (cpuRegs.GPR.r[_Rt_].UL[0] << SWR_SHIFT[shift]) | + ( mem & SWR_MASK[shift]) ); + + /* + Mem = 1234. Reg = abcd + + 0 abcd (reg ) | (mem & 0x00000000) + 1 bcd4 (reg << 8) | (mem & 0x000000ff) + 2 cd34 (reg << 16) | (mem & 0x0000ffff) + 3 d234 (reg << 24) | (mem & 0x00ffffff) + */ +} + +void SD() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + memWrite64(addr,&cpuRegs.GPR.r[_Rt_].UD[0]); +} + +u64 SDL_MASK[8] = { 0xffffffffffffff00LL, 0xffffffffffff0000LL, 0xffffffffff000000LL, 0xffffffff00000000LL, + 0xffffff0000000000LL, 0xffff000000000000LL, 0xff00000000000000LL, 0x0000000000000000LL }; +u32 SDL_SHIFT[8] = { 56, 48, 40, 32, 24, 16, 8, 0 }; + +void SDL() { + u32 addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u32 shift = addr & 7; + u64 mem; + + memRead64(addr & ~7, &mem); + mem =(cpuRegs.GPR.r[_Rt_].UD[0] >> SDL_SHIFT[shift]) | + ( mem & SDL_MASK[shift]); + memWrite64(addr & ~7, &mem); +} + +u64 SDR_MASK[8] = { 0x0000000000000000LL, 0x00000000000000ffLL, 0x000000000000ffffLL, 0x0000000000ffffffLL, + 0x00000000ffffffffLL, 0x000000ffffffffffLL, 0x0000ffffffffffffLL, 0x00ffffffffffffffLL }; +u32 SDR_SHIFT[8] = { 0, 8, 16, 24, 32, 40, 48, 56 }; + +void SDR() { + u32 addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + u32 shift = addr & 7; + u64 mem; + + memRead64(addr & ~7, &mem); + mem=(cpuRegs.GPR.r[_Rt_].UD[0] << SDR_SHIFT[shift]) | + ( mem & SDR_MASK[shift]); + memWrite64(addr & ~7, &mem ); +} + +void SQ() { + u32 addr; + + addr = cpuRegs.GPR.r[_Rs_].UL[0] + _Imm_; + addr&=~0xf; + memWrite128(addr, &cpuRegs.GPR.r[_Rt_].UD[0]); +} + +/********************************************************* +* Conditional Move * +* Format: OP rd, rs, rt * +*********************************************************/ + +void MOVZ() { + if (!_Rd_) return; + if (cpuRegs.GPR.r[_Rt_].UD[0] == 0) { + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0]; + } +} +void MOVN() { + if (!_Rd_) return; + if (cpuRegs.GPR.r[_Rt_].UD[0] != 0) { + cpuRegs.GPR.r[_Rd_].UD[0] = cpuRegs.GPR.r[_Rs_].UD[0]; + } +} + +/********************************************************* +* Special purpose instructions * +* Format: OP * +*********************************************************/ + +#include "Sifcmd.h" +/* +int __Deci2Call(int call, u32 *addr); +*/ +u32 *deci2addr = NULL; +u32 deci2handler; +char deci2buffer[256]; + +/* + * int Deci2Call(int, u_int *); + */ + +int __Deci2Call(int call, u32 *addr) +{ + if (call > 0x10) + return -1; + + switch (call) + { + case 1: // open + if( addr != NULL ) + { + deci2addr = (u32*)PSM(addr[1]); + BIOS_LOG("deci2open: %x,%x,%x,%x\n", + addr[3], addr[2], addr[1], addr[0]); + deci2handler = addr[2]; + } + else + { + deci2handler = NULL; + DevCon::Notice( "Deci2Call.Open > NULL address ignored." ); + } + return 1; + + case 2: // close + return 1; + + case 3: // reqsend + { + char reqaddr[128]; + if( addr != NULL ) + sprintf( reqaddr, "%x %x %x %x", addr[3], addr[2], addr[1], addr[0] ); + + BIOS_LOG("deci2reqsend: %s: deci2addr: %x,%x,%x,buf=%x %x,%x,len=%x,%x\n", + (( addr == NULL ) ? "NULL" : reqaddr), + deci2addr[7], deci2addr[6], deci2addr[5], deci2addr[4], + deci2addr[3], deci2addr[2], deci2addr[1], deci2addr[0]); + +// cpuRegs.pc = deci2handler; +// SysPrintf("deci2msg: %s", (char*)PSM(deci2addr[4]+0xc)); + if (deci2addr == NULL) return 1; + if (deci2addr[1]>0xc){ + u8* pdeciaddr = (u8*)dmaGetAddr(deci2addr[4]+0xc); + if( pdeciaddr == NULL ) + pdeciaddr = (u8*)PSM(deci2addr[4]+0xc); + else + pdeciaddr += (deci2addr[4]+0xc)%16; + memcpy(deci2buffer, pdeciaddr, deci2addr[1]-0xc); + deci2buffer[deci2addr[1]-0xc>=255?255:deci2addr[1]-0xc]='\0'; + Console::Write( Color_Cyan, deci2buffer ); + } + deci2addr[3] = 0; + return 1; + } + + case 4: // poll + if( addr != NULL ) + BIOS_LOG("deci2poll: %x,%x,%x,%x\n", addr[3], addr[2], addr[1], addr[0]); + return 1; + + case 5: // exrecv + return 1; + + case 6: // exsend + return 1; + + case 0x10://kputs + if( addr != NULL ) + Console::Write( Color_Cyan, "%s", params PSM(*addr)); + return 1; + } + + return 0; +} + + +void SYSCALL() +{ + u8 call; + + if (cpuRegs.GPR.n.v1.SL[0] < 0) + call = (u8)(-cpuRegs.GPR.n.v1.SL[0]); + else + call = cpuRegs.GPR.n.v1.UC[0]; + + BIOS_LOG("Bios call: %s (%x)\n", bios[call], call); + + if (call == 0x7c) + { + if(cpuRegs.GPR.n.a0.UL[0] == 0x10) + Console::Write( Color_Cyan, (char*)PSM(PSMu32(cpuRegs.GPR.n.a1.UL[0])) ); + else + __Deci2Call( cpuRegs.GPR.n.a0.UL[0], (u32*)PSM(cpuRegs.GPR.n.a1.UL[0]) ); + } + + if (call == 0x77) + { + t_sif_dma_transfer *dmat; + //struct t_sif_cmd_header *hdr; + //struct t_sif_rpc_bind *bind; + //struct t_rpc_server_data *server; + int n_transfer; + u32 addr; + //int sid; + + n_transfer = cpuRegs.GPR.n.a1.UL[0] - 1; + if (n_transfer >= 0) + { + addr = cpuRegs.GPR.n.a0.UL[0] + n_transfer * sizeof(t_sif_dma_transfer); + dmat = (t_sif_dma_transfer*)PSM(addr); + + BIOS_LOG("bios_%s: n_transfer=%d, size=%x, attr=%x, dest=%x, src=%x\n", + bios[cpuRegs.GPR.n.v1.UC[0]], n_transfer, + dmat->size, dmat->attr, + dmat->dest, dmat->src); + } + } + + cpuRegs.pc -= 4; + cpuException(0x20, cpuRegs.branch); +} + +void BREAK(void) { + cpuRegs.pc -= 4; + cpuException(0x24, cpuRegs.branch); +} + +void MFSA( void ) { + if (!_Rd_) return; + cpuRegs.GPR.r[_Rd_].SD[0] = (s64)cpuRegs.sa; +} + +void MTSA( void ) { + cpuRegs.sa = (s32)cpuRegs.GPR.r[_Rs_].SD[0]; +} + +void SYNC( void ) +{ +} + +void PREF( void ) +{ +} + + + +/********************************************************* +* Register trap * +* Format: OP rs, rt * +*********************************************************/ + +void TGE() { + if (cpuRegs.GPR.r[_Rs_].SD[0]>= cpuRegs.GPR.r[_Rt_].SD[0]) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: TGE\n" ); +} + +void TGEU() { + if (cpuRegs.GPR.r[_Rs_].UD[0]>= cpuRegs.GPR.r[_Rt_].UD[0]) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: TGEU\n" ); +} + +void TLT() { + if (cpuRegs.GPR.r[_Rs_].SD[0] < cpuRegs.GPR.r[_Rt_].SD[0]) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: TLT\n" ); +} + +void TLTU() { + if (cpuRegs.GPR.r[_Rs_].UD[0] < cpuRegs.GPR.r[_Rt_].UD[0]) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: TLTU\n" ); +} + +void TEQ() { + if (cpuRegs.GPR.r[_Rs_].SD[0] == cpuRegs.GPR.r[_Rt_].SD[0]) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: TEQ\n" ); +} + +void TNE() { + if (cpuRegs.GPR.r[_Rs_].SD[0] != cpuRegs.GPR.r[_Rt_].SD[0]) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: TNE\n" ); +} + +/********************************************************* +* Trap with immediate operand * +* Format: OP rs, rt * +*********************************************************/ + +void TGEI() { + + if (cpuRegs.GPR.r[_Rs_].SD[0] >= _Imm_) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: Immediate\n" ); +} + +void TGEIU() { + if (cpuRegs.GPR.r[_Rs_].UD[0] >= _ImmU_) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: Immediate\n" ); +} + +void TLTI() { + if(cpuRegs.GPR.r[_Rs_].SD[0] < _Imm_) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: Immediate\n" ); +} + +void TLTIU() { + if (cpuRegs.GPR.r[_Rs_].UD[0] < _ImmU_) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: Immediate\n" ); +} + +void TEQI() { + if (cpuRegs.GPR.r[_Rs_].SD[0] == _Imm_) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: Immediate\n" ); +} + +void TNEI() { + if (cpuRegs.GPR.r[_Rs_].SD[0] != _Imm_) { + cpuException(EXC_CODE_Tr, cpuRegs.branch); + } + //SysPrintf( "TrapInstruction: Immediate\n" ); +} + +/********************************************************* +* Sa intructions * +* Format: OP rs, rt * +*********************************************************/ + +void MTSAB() { + cpuRegs.sa = ((cpuRegs.GPR.r[_Rs_].UL[0] & 0xF) ^ (_Imm_ & 0xF)) << 3; +} + +void MTSAH() { + cpuRegs.sa = ((cpuRegs.GPR.r[_Rs_].UL[0] & 0x7) ^ (_Imm_ & 0x7)) << 4; +} + +} } } // end namespace R5900::Interpreter::OpcodeImpl diff --git a/pcsx2/R5900OpcodeTables.cpp b/pcsx2/R5900OpcodeTables.cpp new file mode 100644 index 0000000000..32913f8ef7 --- /dev/null +++ b/pcsx2/R5900OpcodeTables.cpp @@ -0,0 +1,696 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +//all tables for R5900 are define here.. + +#include "PrecompiledHeader.h" +#include "R5900OpcodeTables.h" +#include "R5900.h" + +#include "x86/iR5900AritImm.h" +#include "x86/iR5900Arit.h" +#include "x86/iR5900MultDiv.h" +#include "x86/iR5900Shift.h" +#include "x86/iR5900Branch.h" +#include "x86/iR5900Jump.h" +#include "x86/iR5900LoadStore.h" +#include "x86/iR5900Move.h" +#include "x86/iMMI.h" +#include "x86/iCOP0.h" +#include "x86/iFPU.h" + +namespace R5900 +{ + namespace Opcodes + { + // Generates an entry for the given opcode name. + // Assumes the default function naming schemes for interpreter and recompiler functions. + # define MakeOpcode( name, cycles ) \ + static const OPCODE name = { \ + #name, \ + cycles, \ + NULL, \ + ::R5900::Interpreter::OpcodeImpl::name, \ + ::R5900::Dynarec::OpcodeImpl::rec##name, \ + ::R5900::OpcodeDisasm::name \ + } + +# define MakeOpcodeM( name, cycles ) \ + static const OPCODE name = { \ + #name, \ + cycles, \ + NULL, \ + ::R5900::Interpreter::OpcodeImpl::MMI::name, \ + ::R5900::Dynarec::OpcodeImpl::MMI::rec##name, \ + ::R5900::OpcodeDisasm::name \ + } + +# define MakeOpcode0( name, cycles ) \ + static const OPCODE name = { \ + #name, \ + cycles, \ + NULL, \ + ::R5900::Interpreter::OpcodeImpl::COP0::name, \ + ::R5900::Dynarec::OpcodeImpl::COP0::rec##name, \ + ::R5900::OpcodeDisasm::name \ + } + + # define MakeOpcode1( name, cycles ) \ + static const OPCODE name = { \ + #name, \ + cycles, \ + NULL, \ + ::R5900::Interpreter::OpcodeImpl::COP1::name, \ + ::R5900::Dynarec::OpcodeImpl::COP1::rec##name, \ + ::R5900::OpcodeDisasm::name \ + } + + # define MakeOpcodeClass( name ) \ + static const OPCODE name = { \ + #name, \ + 0, \ + R5900::Opcodes::Class_##name, \ + NULL, \ + NULL, \ + NULL \ + } + + // We're working on new hopefully better cycle ratios, but they're still a WIP. + // And yes this whole thing is an ugly hack. I'll clean it up once we have + // a better idea how exactly the cycle ratios will work best. + + namespace Cycles + { + static const int Default = 9; + static const int Branch = 11; + static const int CopDefault = 7; + + static const int Mult = 2*8; + static const int Div = 14*8; + static const int MMI_Mult = 3*8; + static const int MMI_Div = 22*8; + static const int MMI_Default = 14; + + static const int FPU_Mult = 12; + + static const int Store = 28; + static const int Load = 22; + + static const int StoreFast = 14; + static const int LoadFast = 12; + } + + using namespace Cycles; + + MakeOpcode( Unknown, Default ); + MakeOpcode( MMI_Unknown, Default ); + MakeOpcode( COP0_Unknown, Default ); + MakeOpcode( COP1_Unknown, Default ); + + // Class Subset Opcodes + // (not really opcodes, but rather entire subsets of other opcode classes) + + MakeOpcodeClass( SPECIAL ); + MakeOpcodeClass( REGIMM ); + //MakeOpcodeClass( COP2 ); + MakeOpcodeClass( MMI ); + MakeOpcodeClass( MMI0 ); + MakeOpcodeClass( MMI2 ); + MakeOpcodeClass( MMI1 ); + MakeOpcodeClass( MMI3 ); + + MakeOpcodeClass( COP0 ); + MakeOpcodeClass( COP1 ); + + // Misc Junk + + MakeOpcode( COP2, Default ); + + MakeOpcode( CACHE, Default ); + MakeOpcode( PREF, Default ); + MakeOpcode( SYSCALL, Default ); + MakeOpcode( BREAK, Default ); + MakeOpcode( SYNC, Default ); + + // Branch/Jump Opcodes + + MakeOpcode( J , Default ); + MakeOpcode( JAL, Default ); + MakeOpcode( JR, Default ); + MakeOpcode( JALR, Default ); + + MakeOpcode( BEQ, Branch ); + MakeOpcode( BNE, Branch ); + MakeOpcode( BLEZ, Branch ); + MakeOpcode( BGTZ, Branch ); + MakeOpcode( BEQL, Branch ); + MakeOpcode( BNEL, Branch ); + MakeOpcode( BLEZL, Branch ); + MakeOpcode( BGTZL, Branch ); + MakeOpcode( BLTZ, Branch ); + MakeOpcode( BGEZ, Branch ); + MakeOpcode( BLTZL, Branch ); + MakeOpcode( BGEZL, Branch ); + MakeOpcode( BLTZAL, Branch ); + MakeOpcode( BGEZAL, Branch ); + MakeOpcode( BLTZALL, Branch ); + MakeOpcode( BGEZALL, Branch ); + + MakeOpcode( TGEI, Branch ); + MakeOpcode( TGEIU, Branch ); + MakeOpcode( TLTI, Branch ); + MakeOpcode( TLTIU, Branch ); + MakeOpcode( TEQI, Branch ); + MakeOpcode( TNEI, Branch ); + MakeOpcode( TGE, Branch ); + MakeOpcode( TGEU, Branch ); + MakeOpcode( TLT, Branch ); + MakeOpcode( TLTU, Branch ); + MakeOpcode( TEQ, Branch ); + MakeOpcode( TNE, Branch ); + + // Arithmetic + + MakeOpcode( MULT, Mult ); + MakeOpcode( MULTU, Mult ); + MakeOpcode( MULT1, Mult ); + MakeOpcode( MULTU1, Mult ); + MakeOpcode( MADD, Mult ); + MakeOpcode( MADDU, Mult ); + MakeOpcode( MADD1, Mult ); + MakeOpcode( MADDU1, Mult ); + MakeOpcode( DIV, Div ); + MakeOpcode( DIVU, Div ); + MakeOpcode( DIV1, Div ); + MakeOpcode( DIVU1, Div ); + + MakeOpcode( ADDI, Default ); + MakeOpcode( ADDIU, Default ); + MakeOpcode( DADDI, Default ); + MakeOpcode( DADDIU, Default ); + MakeOpcode( DADD, Default ); + MakeOpcode( DADDU, Default ); + MakeOpcode( DSUB, Default ); + MakeOpcode( DSUBU, Default ); + MakeOpcode( ADD, Default ); + MakeOpcode( ADDU, Default ); + MakeOpcode( SUB, Default ); + MakeOpcode( SUBU, Default ); + + MakeOpcode( ANDI, Default ); + MakeOpcode( ORI, Default ); + MakeOpcode( XORI, Default ); + MakeOpcode( AND, Default ); + MakeOpcode( OR, Default ); + MakeOpcode( XOR, Default ); + MakeOpcode( NOR, Default ); + MakeOpcode( SLTI, Default ); + MakeOpcode( SLTIU, Default ); + MakeOpcode( SLT, Default ); + MakeOpcode( SLTU, Default ); + MakeOpcode( LUI, Default ); + MakeOpcode( SLL, Default ); + MakeOpcode( SRL, Default ); + MakeOpcode( SRA, Default ); + MakeOpcode( SLLV, Default ); + MakeOpcode( SRLV, Default ); + MakeOpcode( SRAV, Default ); + MakeOpcode( MOVZ, Default ); + MakeOpcode( MOVN, Default ); + MakeOpcode( DSLLV, Default ); + MakeOpcode( DSRLV, Default ); + MakeOpcode( DSRAV, Default ); + MakeOpcode( DSLL, Default ); + MakeOpcode( DSRL, Default ); + MakeOpcode( DSRA, Default ); + MakeOpcode( DSLL32, Default ); + MakeOpcode( DSRL32, Default ); + MakeOpcode( DSRA32, Default ); + + MakeOpcode( MFHI, Default ); + MakeOpcode( MTHI, Default ); + MakeOpcode( MFLO, Default ); + MakeOpcode( MTLO, Default ); + MakeOpcode( MFSA, Default ); + MakeOpcode( MTSA, Default ); + MakeOpcode( MTSAB, Default ); + MakeOpcode( MTSAH, Default ); + MakeOpcode( MFHI1, Default ); + MakeOpcode( MTHI1, Default ); + MakeOpcode( MFLO1, Default ); + MakeOpcode( MTLO1, Default ); + + // Loads! + + MakeOpcode( LDL, Load ); + MakeOpcode( LDR, Load ); + MakeOpcode( LQ, Load ); + MakeOpcode( LB, Load ); + MakeOpcode( LH, Load ); + MakeOpcode( LWL, Load ); + MakeOpcode( LW, LoadFast ); + MakeOpcode( LBU, Load ); + MakeOpcode( LHU, Load ); + MakeOpcode( LWR, Load ); + MakeOpcode( LWU, Load ); + MakeOpcode( LWC1, Load ); + MakeOpcode( LQC2, Load ); + MakeOpcode( LD, LoadFast ); + + // Stores! + + MakeOpcode( SQ, Store ); + MakeOpcode( SB, Store );//slow + MakeOpcode( SH, Store );//slow + MakeOpcode( SWL, Store ); + MakeOpcode( SW, StoreFast ); + MakeOpcode( SDL, Store ); + MakeOpcode( SDR, Store ); + MakeOpcode( SWR, Store ); + MakeOpcode( SWC1, Store ); + MakeOpcode( SQC2, Store ); + MakeOpcode( SD, StoreFast ); + + + // Multimedia Instructions! + + MakeOpcodeM( PLZCW, MMI_Default ); + MakeOpcodeM( PMFHL, MMI_Default ); + MakeOpcodeM( PMTHL, MMI_Default ); + MakeOpcodeM( PSLLH, MMI_Default ); + MakeOpcodeM( PSRLH, MMI_Default ); + MakeOpcodeM( PSRAH, MMI_Default ); + MakeOpcodeM( PSLLW, MMI_Default ); + MakeOpcodeM( PSRLW, MMI_Default ); + MakeOpcodeM( PSRAW, MMI_Default ); + + MakeOpcodeM( PADDW, MMI_Default ); + MakeOpcodeM( PADDH, MMI_Default ); + MakeOpcodeM( PADDB, MMI_Default ); + MakeOpcodeM( PADDSW, MMI_Default ); + MakeOpcodeM( PADDSH, MMI_Default ); + MakeOpcodeM( PADDSB, MMI_Default ); + MakeOpcodeM( PADDUW, MMI_Default ); + MakeOpcodeM( PADDUH, MMI_Default ); + MakeOpcodeM( PADDUB, MMI_Default ); + MakeOpcodeM( PSUBW, MMI_Default ); + MakeOpcodeM( PSUBH, MMI_Default ); + MakeOpcodeM( PSUBB, MMI_Default ); + MakeOpcodeM( PSUBSW, MMI_Default ); + MakeOpcodeM( PSUBSH, MMI_Default ); + MakeOpcodeM( PSUBSB, MMI_Default ); + MakeOpcodeM( PSUBUW, MMI_Default ); + MakeOpcodeM( PSUBUH, MMI_Default ); + MakeOpcodeM( PSUBUB, MMI_Default ); + + MakeOpcodeM( PCGTW, MMI_Default ); + MakeOpcodeM( PMAXW, MMI_Default ); + MakeOpcodeM( PMAXH, MMI_Default ); + MakeOpcodeM( PCGTH, MMI_Default ); + MakeOpcodeM( PCGTB, MMI_Default ); + MakeOpcodeM( PEXTLW, MMI_Default ); + MakeOpcodeM( PEXTLH, MMI_Default ); + MakeOpcodeM( PEXTLB, MMI_Default ); + MakeOpcodeM( PEXT5, MMI_Default ); + MakeOpcodeM( PPACW, MMI_Default ); + MakeOpcodeM( PPACH, MMI_Default ); + MakeOpcodeM( PPACB, MMI_Default ); + MakeOpcodeM( PPAC5, MMI_Default ); + + MakeOpcodeM( PABSW, MMI_Default ); + MakeOpcodeM( PABSH, MMI_Default ); + MakeOpcodeM( PCEQW, MMI_Default ); + MakeOpcodeM( PMINW, MMI_Default ); + MakeOpcodeM( PMINH, MMI_Default ); + MakeOpcodeM( PADSBH, MMI_Default ); + MakeOpcodeM( PCEQH, MMI_Default ); + MakeOpcodeM( PCEQB, MMI_Default ); + MakeOpcodeM( PEXTUW, MMI_Default ); + MakeOpcodeM( PEXTUH, MMI_Default ); + MakeOpcodeM( PEXTUB, MMI_Default ); + MakeOpcodeM( PSLLVW, MMI_Default ); + MakeOpcodeM( PSRLVW, MMI_Default ); + + MakeOpcodeM( QFSRV, MMI_Default ); + + MakeOpcodeM( PMADDH, MMI_Mult ); + MakeOpcodeM( PHMADH, MMI_Mult ); + MakeOpcodeM( PMSUBH, MMI_Mult ); + MakeOpcodeM( PHMSBH, MMI_Mult ); + MakeOpcodeM( PMULTH, MMI_Mult ); + MakeOpcodeM( PMADDW, MMI_Mult ); + MakeOpcodeM( PMSUBW, MMI_Mult ); + MakeOpcodeM( PMFHI, MMI_Mult ); + MakeOpcodeM( PMFLO, MMI_Mult ); + MakeOpcodeM( PMULTW, MMI_Mult ); + MakeOpcodeM( PMADDUW, MMI_Mult ); + MakeOpcodeM( PMULTUW, MMI_Mult ); + MakeOpcodeM( PDIVUW, MMI_Div ); + MakeOpcodeM( PDIVW, MMI_Div ); + MakeOpcodeM( PDIVBW, MMI_Div ); + + MakeOpcodeM( PINTH, MMI_Default ); + MakeOpcodeM( PCPYLD, MMI_Default ); + MakeOpcodeM( PAND, MMI_Default ); + MakeOpcodeM( PXOR, MMI_Default ); + MakeOpcodeM( PEXEH, MMI_Default ); + MakeOpcodeM( PREVH, MMI_Default ); + MakeOpcodeM( PEXEW, MMI_Default ); + MakeOpcodeM( PROT3W, MMI_Default ); + + MakeOpcodeM( PSRAVW, MMI_Default ); + MakeOpcodeM( PMTHI, MMI_Default ); + MakeOpcodeM( PMTLO, MMI_Default ); + MakeOpcodeM( PINTEH, MMI_Default ); + MakeOpcodeM( PCPYUD, MMI_Default ); + MakeOpcodeM( POR, MMI_Default ); + MakeOpcodeM( PNOR, MMI_Default ); + MakeOpcodeM( PEXCH, MMI_Default ); + MakeOpcodeM( PCPYH, MMI_Default ); + MakeOpcodeM( PEXCW, MMI_Default ); + + ////////////////////////////////////////////////////////// + // COP0 Instructions + + MakeOpcodeClass( COP0_C0 ); + MakeOpcodeClass( COP0_BC0 ); + + MakeOpcode0( MFC0, CopDefault ); + MakeOpcode0( MTC0, CopDefault ); + + MakeOpcode0( BC0F, Branch ); + MakeOpcode0( BC0T, Branch ); + MakeOpcode0( BC0FL, Branch ); + MakeOpcode0( BC0TL, Branch ); + + MakeOpcode0( TLBR, CopDefault ); + MakeOpcode0( TLBWI, CopDefault ); + MakeOpcode0( TLBWR, CopDefault ); + MakeOpcode0( TLBP, CopDefault ); + MakeOpcode0( ERET, CopDefault ); + MakeOpcode0( EI, CopDefault ); + MakeOpcode0( DI, CopDefault ); + + ////////////////////////////////////////////////////////// + // COP1 Instructions! + + MakeOpcodeClass( COP1_BC1 ); + MakeOpcodeClass( COP1_S ); + MakeOpcodeClass( COP1_W ); // contains CVT_S instruction *only* + + MakeOpcode1( MFC1, CopDefault ); + MakeOpcode1( CFC1, CopDefault ); + MakeOpcode1( MTC1, CopDefault ); + MakeOpcode1( CTC1, CopDefault ); + + MakeOpcode1( BC1F, Branch ); + MakeOpcode1( BC1T, Branch ); + MakeOpcode1( BC1FL, Branch ); + MakeOpcode1( BC1TL, Branch ); + + MakeOpcode1( ADD_S, CopDefault ); + MakeOpcode1( ADDA_S, CopDefault ); + MakeOpcode1( SUB_S, CopDefault ); + MakeOpcode1( SUBA_S, CopDefault ); + + MakeOpcode1( ABS_S, CopDefault ); + MakeOpcode1( MOV_S, CopDefault ); + MakeOpcode1( NEG_S, CopDefault ); + MakeOpcode1( MAX_S, CopDefault ); + MakeOpcode1( MIN_S, CopDefault ); + + MakeOpcode1( MUL_S, FPU_Mult ); + MakeOpcode1( DIV_S, 3*8 ); + MakeOpcode1( SQRT_S, 3*8 ); + MakeOpcode1( RSQRT_S, 4*8 ); + MakeOpcode1( MULA_S, FPU_Mult ); + MakeOpcode1( MADD_S, FPU_Mult ); + MakeOpcode1( MSUB_S, FPU_Mult ); + MakeOpcode1( MADDA_S, FPU_Mult ); + MakeOpcode1( MSUBA_S, FPU_Mult ); + + MakeOpcode1( C_F, CopDefault ); + MakeOpcode1( C_EQ, CopDefault ); + MakeOpcode1( C_LT, CopDefault ); + MakeOpcode1( C_LE, CopDefault ); + + MakeOpcode1( CVT_S, CopDefault ); + MakeOpcode1( CVT_W, CopDefault ); + } + + namespace OpcodeTables + { + using namespace Opcodes; + + const OPCODE tbl_Standard[64] = + { + SPECIAL, REGIMM, J, JAL, BEQ, BNE, BLEZ, BGTZ, + ADDI, ADDIU, SLTI, SLTIU, ANDI, ORI, XORI, LUI, + COP0, COP1, COP2, Unknown, BEQL, BNEL, BLEZL, BGTZL, + DADDI, DADDIU, LDL, LDR, MMI, Unknown, LQ, SQ, + LB, LH, LWL, LW, LBU, LHU, LWR, LWU, + SB, SH, SWL, SW, SDL, SDR, SWR, CACHE, + Unknown, LWC1, Unknown, PREF, Unknown, Unknown, LQC2, LD, + Unknown, SWC1, Unknown, Unknown, Unknown, Unknown, SQC2, SD + }; + + static const OPCODE tbl_Special[64] = + { + SLL, Unknown, SRL, SRA, SLLV, Unknown, SRLV, SRAV, + JR, JALR, MOVZ, MOVN, SYSCALL, BREAK, Unknown, SYNC, + MFHI, MTHI, MFLO, MTLO, DSLLV, Unknown, DSRLV, DSRAV, + MULT, MULTU, DIV, DIVU, Unknown, Unknown, Unknown, Unknown, + ADD, ADDU, SUB, SUBU, AND, OR, XOR, NOR, + MFSA, MTSA, SLT, SLTU, DADD, DADDU, DSUB, DSUBU, + TGE, TGEU, TLT, TLTU, TEQ, Unknown, TNE, Unknown, + DSLL, Unknown, DSRL, DSRA, DSLL32, Unknown, DSRL32, DSRA32 + }; + + static const OPCODE tbl_RegImm[32] = { + BLTZ, BGEZ, BLTZL, BGEZL, Unknown, Unknown, Unknown, Unknown, + TGEI, TGEIU, TLTI, TLTIU, TEQI, Unknown, TNEI, Unknown, + BLTZAL, BGEZAL, BLTZALL, BGEZALL, Unknown, Unknown, Unknown, Unknown, + MTSAB, MTSAH , Unknown, Unknown, Unknown, Unknown, Unknown, Unknown, + }; + + static const OPCODE tbl_MMI[64] = + { + MADD, MADDU, MMI_Unknown, MMI_Unknown, PLZCW, MMI_Unknown, MMI_Unknown, MMI_Unknown, + MMI0, MMI2, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, + MFHI1, MTHI1, MFLO1, MTLO1, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, + MULT1, MULTU1, DIV1, DIVU1, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, + MADD1, MADDU1, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, + MMI1, MMI3, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, + PMFHL, PMTHL, MMI_Unknown, MMI_Unknown, PSLLH, MMI_Unknown, PSRLH, PSRAH, + MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, PSLLW, MMI_Unknown, PSRLW, PSRAW, + }; + + static const OPCODE tbl_MMI0[32] = + { + PADDW, PSUBW, PCGTW, PMAXW, + PADDH, PSUBH, PCGTH, PMAXH, + PADDB, PSUBB, PCGTB, MMI_Unknown, + MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, + PADDSW, PSUBSW, PEXTLW, PPACW, + PADDSH, PSUBSH, PEXTLH, PPACH, + PADDSB, PSUBSB, PEXTLB, PPACB, + MMI_Unknown, MMI_Unknown, PEXT5, PPAC5, + }; + + static const OPCODE tbl_MMI1[32] = + { + MMI_Unknown, PABSW, PCEQW, PMINW, + PADSBH, PABSH, PCEQH, PMINH, + MMI_Unknown, MMI_Unknown, PCEQB, MMI_Unknown, + MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, + PADDUW, PSUBUW, PEXTUW, MMI_Unknown, + PADDUH, PSUBUH, PEXTUH, MMI_Unknown, + PADDUB, PSUBUB, PEXTUB, QFSRV, + MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, + }; + + + static const OPCODE tbl_MMI2[32] = + { + PMADDW, MMI_Unknown, PSLLVW, PSRLVW, + PMSUBW, MMI_Unknown, MMI_Unknown, MMI_Unknown, + PMFHI, PMFLO, PINTH, MMI_Unknown, + PMULTW, PDIVW, PCPYLD, MMI_Unknown, + PMADDH, PHMADH, PAND, PXOR, + PMSUBH, PHMSBH, MMI_Unknown, MMI_Unknown, + MMI_Unknown, MMI_Unknown, PEXEH, PREVH, + PMULTH, PDIVBW, PEXEW, PROT3W, + }; + + static const OPCODE tbl_MMI3[32] = + { + PMADDUW, MMI_Unknown, MMI_Unknown, PSRAVW, + MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, + PMTHI, PMTLO, PINTEH, MMI_Unknown, + PMULTUW, PDIVUW, PCPYUD, MMI_Unknown, + MMI_Unknown, MMI_Unknown, POR, PNOR, + MMI_Unknown, MMI_Unknown, MMI_Unknown, MMI_Unknown, + MMI_Unknown, MMI_Unknown, PEXCH, PCPYH, + MMI_Unknown, MMI_Unknown, PEXCW, MMI_Unknown, + }; + + static const OPCODE tbl_COP0[32] = + { + MFC0, COP0_Unknown, COP0_Unknown, COP0_Unknown, MTC0, COP0_Unknown, COP0_Unknown, COP0_Unknown, + COP0_BC0, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + COP0_C0, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + }; + + static const OPCODE tbl_COP0_BC0[32] = + { + BC0F, BC0T, BC0FL, BC0TL, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + }; + + static const OPCODE tbl_COP0_C0[64] = + { + COP0_Unknown, TLBR, TLBWI, COP0_Unknown, COP0_Unknown, COP0_Unknown, TLBWR, COP0_Unknown, + TLBP, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + ERET, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, + EI, DI, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown, COP0_Unknown + }; + + static const OPCODE tbl_COP1[32] = + { + MFC1, COP1_Unknown, CFC1, COP1_Unknown, MTC1, COP1_Unknown, CTC1, COP1_Unknown, + COP1_BC1, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, + COP1_S, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_W, COP1_Unknown, COP1_Unknown, COP1_Unknown, + COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, + }; + + static const OPCODE tbl_COP1_BC1[32] = + { + BC1F, BC1T, BC1FL, BC1TL, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, + COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, + COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, + COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, COP1_Unknown, + }; + + static const OPCODE tbl_COP1_S[64] = + { + ADD_S, SUB_S, MUL_S, DIV_S, SQRT_S, ABS_S, MOV_S, NEG_S, + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,RSQRT_S, COP1_Unknown, + ADDA_S, SUBA_S, MULA_S, COP1_Unknown,MADD_S, MSUB_S, MADDA_S, MSUBA_S, + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,CVT_W, COP1_Unknown,COP1_Unknown,COP1_Unknown, + MAX_S, MIN_S, COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + C_F, COP1_Unknown,C_EQ, COP1_Unknown,C_LT, COP1_Unknown,C_LE, COP1_Unknown, + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + }; + + static const OPCODE tbl_COP1_W[64] = + { + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + CVT_S, COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown,COP1_Unknown, + }; + + } // end namespace R5900::OpcodeTables + + namespace Opcodes + { + using namespace OpcodeTables; + + const OPCODE& Class_SPECIAL() { return tbl_Special[_Funct_]; } + const OPCODE& Class_REGIMM() { return tbl_RegImm[_Rt_]; } + + const OPCODE& Class_MMI() { return tbl_MMI[_Funct_]; } + const OPCODE& Class_MMI0() { return tbl_MMI0[_Sa_]; } + const OPCODE& Class_MMI1() { return tbl_MMI1[_Sa_]; } + const OPCODE& Class_MMI2() { return tbl_MMI2[_Sa_]; } + const OPCODE& Class_MMI3() { return tbl_MMI3[_Sa_]; } + + const OPCODE& Class_COP0() { return tbl_COP0[_Rs_]; } + const OPCODE& Class_COP0_BC0() { return tbl_COP0_BC0[(cpuRegs.code >> 16) & 0x03]; } + const OPCODE& Class_COP0_C0() { return tbl_COP0_C0[_Funct_]; } + + const OPCODE& Class_COP1() { return tbl_COP1[_Rs_]; } + const OPCODE& Class_COP1_BC1() { return tbl_COP1_BC1[_Rt_]; } + const OPCODE& Class_COP1_S() { return tbl_COP1_S[_Funct_]; } + const OPCODE& Class_COP1_W() { return tbl_COP1_W[_Funct_]; } + + // These are for future use when the COP2 tables are completed. + //const OPCODE& Class_COP2() { return tbl_COP2[_Rs_]; } + //const OPCODE& Class_COP2_BC2() { return tbl_COP2_BC2[_Rt_]; } + //const OPCODE& Class_COP2_SPECIAL() { return tbl_COP2_SPECIAL[_Funct_]; } + //const OPCODE& Class_COP2_SPECIAL2() { return tbl_COP2_SPECIAL2[(cpuRegs.code & 0x3) | ((cpuRegs.code >> 4) & 0x7c)]; } + } +} // end namespace R5900 + +void (*Int_COP2PrintTable[32])() = { + COP2_Unknown, QMFC2, CFC2, COP2_Unknown, COP2_Unknown, QMTC2, CTC2, COP2_Unknown, + COP2_BC2, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, + COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, + COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, COP2_SPECIAL, +}; + +void (*Int_COP2BC2PrintTable[32])() = { + BC2F, BC2T, BC2FL, BC2TL, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, + COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, + COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, + COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, COP2_Unknown, +}; + +void (*Int_COP2SPECIAL1PrintTable[64])() = +{ + VADDx, VADDy, VADDz, VADDw, VSUBx, VSUBy, VSUBz, VSUBw, + VMADDx, VMADDy, VMADDz, VMADDw, VMSUBx, VMSUBy, VMSUBz, VMSUBw, + VMAXx, VMAXy, VMAXz, VMAXw, VMINIx, VMINIy, VMINIz, VMINIw, + VMULx, VMULy, VMULz, VMULw, VMULq, VMAXi, VMULi, VMINIi, + VADDq, VMADDq, VADDi, VMADDi, VSUBq, VMSUBq, VSUBi, VMSUBi, + VADD, VMADD, VMUL, VMAX, VSUB, VMSUB, VOPMSUB, VMINI, + VIADD, VISUB, VIADDI, COP2_Unknown,VIAND, VIOR, COP2_Unknown, COP2_Unknown, + VCALLMS, VCALLMSR, COP2_Unknown,COP2_Unknown,COP2_SPECIAL2,COP2_SPECIAL2,COP2_SPECIAL2,COP2_SPECIAL2, +}; + +void (*Int_COP2SPECIAL2PrintTable[128])() = +{ + VADDAx ,VADDAy ,VADDAz ,VADDAw ,VSUBAx ,VSUBAy ,VSUBAz ,VSUBAw, + VMADDAx ,VMADDAy ,VMADDAz ,VMADDAw ,VMSUBAx ,VMSUBAy ,VMSUBAz ,VMSUBAw, + VITOF0 ,VITOF4 ,VITOF12 ,VITOF15 ,VFTOI0 ,VFTOI4 ,VFTOI12 ,VFTOI15, + VMULAx ,VMULAy ,VMULAz ,VMULAw ,VMULAq ,VABS ,VMULAi ,VCLIPw, + VADDAq ,VMADDAq ,VADDAi ,VMADDAi ,VSUBAq ,VMSUBAq ,VSUBAi ,VMSUBAi, + VADDA ,VMADDA ,VMULA ,COP2_Unknown,VSUBA ,VMSUBA ,VOPMULA ,VNOP, + VMOVE ,VMR32 ,COP2_Unknown,COP2_Unknown,VLQI ,VSQI ,VLQD ,VSQD, + VDIV ,VSQRT ,VRSQRT ,VWAITQ ,VMTIR ,VMFIR ,VILWR ,VISWR, + VRNEXT ,VRGET ,VRINIT ,VRXOR ,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown, + COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown, + COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown, + COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown, + COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown, + COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown, + COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown, + COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown,COP2_Unknown, +}; \ No newline at end of file diff --git a/pcsx2/R5900OpcodeTables.h b/pcsx2/R5900OpcodeTables.h new file mode 100644 index 0000000000..80a9f9d45c --- /dev/null +++ b/pcsx2/R5900OpcodeTables.h @@ -0,0 +1,879 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +#ifndef _R5900_OPCODETABLES_H +#define _R5900_OPCODETABLES_H + +#include + +#include "PS2Etypes.h" + +// TODO : Move these into the OpcodeTables namespace +extern void (*Int_COP2PrintTable[32])(); +extern void (*Int_COP2BC2PrintTable[32])(); +extern void (*Int_COP2SPECIAL1PrintTable[64])(); +extern void (*Int_COP2SPECIAL2PrintTable[128])(); + +void COP2_BC2(); +void COP2_SPECIAL(); +void COP2_SPECIAL2(); +void COP2_Unknown(); + + +namespace R5900 +{ + namespace Dynarec { + namespace OpcodeImpl + { + void recNULL(); + void recUnknown(); + void recMMI_Unknown(); + void recCOP0_Unknown(); + void recCOP1_Unknown(); + + void recCOP2(); + + void recCACHE(); + void recPREF(); + void recSYSCALL(); + void recBREAK(); + void recSYNC(); + + void recMFSA(); + void recMTSA(); + void recMTSAB(); + void recMTSAH(); + + void recTGE(); + void recTGEU(); + void recTLT(); + void recTLTU(); + void recTEQ(); + void recTNE(); + void recTGEI(); + void recTGEIU(); + void recTLTI(); + void recTLTIU(); + void recTEQI(); + void recTNEI(); + + } } + + /////////////////////////////////////////////////////////////////////////// + // Encapsulates information about every opcode on the Emotion Engine and + // it's many co-processors. + struct OPCODE + { + // Textual name of the instruction. + const char Name[16]; + + // Number of cycles this instruction normally uses. + u8 cycles; + + const OPCODE& (*getsubclass)(); + + // Process the instruction using the interpreter. + // The action is performed immediately on the EE's cpu state. + void (*interpret)(); + + // Generate recompiled code for this instruction, injected into + // the current EErec block state. + void (*recompile)(); + + // Generates a string representation of the instruction and it's parameters, + // and pastes it into the given output parameter. + void (*disasm)( std::string& output ); + }; + + // Returns the current real instruction, as per the current cpuRegs settings. + const OPCODE& GetCurrentInstruction(); + + namespace OpcodeTables + { + using ::R5900::OPCODE; + + extern const OPCODE tbl_Standard[64]; + + /*extern const OPCODE Standard[64]; + extern const OPCODE Special[64]; + extern const OPCODE RegImm[32]; + extern const OPCODE MMI[64]; + extern const OPCODE MMI0[32]; + extern const OPCODE MMI1[32]; + extern const OPCODE MMI2[32]; + extern const OPCODE MMI3[32]; + + extern const OPCODE COP0[32]; + extern const OPCODE COP0_BC0[32]; + extern const OPCODE COP0_C0[64]; + + extern const OPCODE COP1[32]; + extern const OPCODE COP1_BC1[32]; + extern const OPCODE COP1_S[64]; + extern const OPCODE COP1_W[64];*/ + } + + namespace Opcodes + { + using ::R5900::OPCODE; + + const OPCODE& Class_SPECIAL(); + const OPCODE& Class_REGIMM(); + const OPCODE& Class_MMI(); + const OPCODE& Class_MMI0(); + const OPCODE& Class_MMI1(); + const OPCODE& Class_MMI2(); + const OPCODE& Class_MMI3(); + + const OPCODE& Class_COP0(); + const OPCODE& Class_COP0_BC0(); + const OPCODE& Class_COP0_C0(); + + const OPCODE& Class_COP1(); + const OPCODE& Class_COP1_BC1(); + const OPCODE& Class_COP1_S(); + const OPCODE& Class_COP1_W(); + } + + namespace OpcodeDisasm + { +//**************************************************************** + void Unknown( std::string& output ); + void COP0_Unknown( std::string& output ); + void COP1_Unknown( std::string& output ); + void MMI_Unknown( std::string& output ); + + void COP2( std::string& output ); + +// **********************Standard Opcodes************************** + void J( std::string& output ); + void JAL( std::string& output ); + void BEQ( std::string& output ); + void BNE( std::string& output ); + void BLEZ( std::string& output ); + void BGTZ( std::string& output ); + void ADDI( std::string& output ); + void ADDIU( std::string& output ); + void SLTI( std::string& output ); + void SLTIU( std::string& output ); + void ANDI( std::string& output ); + void ORI( std::string& output ); + void XORI( std::string& output ); + void LUI( std::string& output ); + void BEQL( std::string& output ); + void BNEL( std::string& output ); + void BLEZL( std::string& output ); + void BGTZL( std::string& output ); + void DADDI( std::string& output ); + void DADDIU( std::string& output ); + void LDL( std::string& output ); + void LDR( std::string& output ); + void LB( std::string& output ); + void LH( std::string& output ); + void LWL( std::string& output ); + void LW( std::string& output ); + void LBU( std::string& output ); + void LHU( std::string& output ); + void LWR( std::string& output ); + void LWU( std::string& output ); + void SB( std::string& output ); + void SH( std::string& output ); + void SWL( std::string& output ); + void SW( std::string& output ); + void SDL( std::string& output ); + void SDR( std::string& output ); + void SWR( std::string& output ); + void CACHE( std::string& output ); + void LWC1( std::string& output ); + void PREF( std::string& output ); + void LQC2( std::string& output ); + void LD( std::string& output ); + void SQC2( std::string& output ); + void SD( std::string& output ); + void LQ( std::string& output ); + void SQ( std::string& output ); + void SWC1( std::string& output ); +//*****************end of standard opcodes********************** +//********************SPECIAL OPCODES*************************** + void SLL( std::string& output ); + void SRL( std::string& output ); + void SRA( std::string& output ); + void SLLV( std::string& output ); + void SRLV( std::string& output ); + void SRAV( std::string& output ); + void JR( std::string& output ); + void JALR( std::string& output ); + void SYSCALL( std::string& output ); + void BREAK( std::string& output ); + void SYNC( std::string& output ); + void MFHI( std::string& output ); + void MTHI( std::string& output ); + void MFLO( std::string& output ); + void MTLO( std::string& output ); + void DSLLV( std::string& output ); + void DSRLV( std::string& output ); + void DSRAV( std::string& output ); + void MULT( std::string& output ); + void MULTU( std::string& output ); + void DIV( std::string& output ); + void DIVU( std::string& output ); + void ADD( std::string& output ); + void ADDU( std::string& output ); + void SUB( std::string& output ); + void SUBU( std::string& output ); + void AND( std::string& output ); + void OR( std::string& output ); + void XOR( std::string& output ); + void NOR( std::string& output ); + void SLT( std::string& output ); + void SLTU( std::string& output ); + void DADD( std::string& output ); + void DADDU( std::string& output ); + void DSUB( std::string& output ); + void DSUBU( std::string& output ); + void TGE( std::string& output ); + void TGEU( std::string& output ); + void TLT( std::string& output ); + void TLTU( std::string& output ); + void TEQ( std::string& output ); + void TNE( std::string& output ); + void DSLL( std::string& output ); + void DSRL( std::string& output ); + void DSRA( std::string& output ); + void DSLL32( std::string& output ); + void DSRL32( std::string& output ); + void DSRA32( std::string& output ); + void MOVZ( std::string& output ); + void MOVN( std::string& output ); + void MFSA( std::string& output ); + void MTSA( std::string& output ); +//*******************END OF SPECIAL OPCODES************************ +//***********************REGIMM OPCODES**************************** + void BLTZ( std::string& output ); + void BGEZ( std::string& output ); + void BLTZL( std::string& output ); + void BGEZL( std::string& output ); + void TGEI( std::string& output ); + void TGEIU( std::string& output ); + void TLTI( std::string& output ); + void TLTIU( std::string& output ); + void TEQI( std::string& output ); + void TNEI( std::string& output ); + void BLTZAL( std::string& output ); + void BGEZAL( std::string& output ); + void BLTZALL( std::string& output ); + void BGEZALL( std::string& output ); + void MTSAB( std::string& output ); + void MTSAH( std::string& output ); +//*******************END OF REGIMM OPCODES*********************** +//***********************MMI OPCODES***************************** + void MADD( std::string& output ); + void MADDU( std::string& output ); + void PLZCW( std::string& output ); + void MADD1( std::string& output ); + void MADDU1( std::string& output ); + void MFHI1( std::string& output ); + void MTHI1( std::string& output ); + void MFLO1( std::string& output ); + void MTLO1( std::string& output ); + void MULT1( std::string& output ); + void MULTU1( std::string& output ); + void DIV1( std::string& output ); + void DIVU1( std::string& output ); + void PMFHL( std::string& output ); + void PMTHL( std::string& output ); + void PSLLH( std::string& output ); + void PSRLH( std::string& output ); + void PSRAH( std::string& output ); + void PSLLW( std::string& output ); + void PSRLW( std::string& output ); + void PSRAW( std::string& output ); +//********************END OF MMI OPCODES*********************** +//***********************MMI0 OPCODES************************** + void PADDW( std::string& output ); + void PSUBW( std::string& output ); + void PCGTW( std::string& output ); + void PMAXW( std::string& output ); + void PADDH( std::string& output ); + void PSUBH( std::string& output ); + void PCGTH( std::string& output ); + void PMAXH( std::string& output ); + void PADDB( std::string& output ); + void PSUBB( std::string& output ); + void PCGTB( std::string& output ); + void PADDSW( std::string& output ); + void PSUBSW( std::string& output ); + void PEXTLW( std::string& output ); + void PPACW( std::string& output ); + void PADDSH( std::string& output ); + void PSUBSH( std::string& output ); + void PEXTLH( std::string& output ); + void PPACH( std::string& output ); + void PADDSB( std::string& output ); + void PSUBSB( std::string& output ); + void PEXTLB( std::string& output ); + void PPACB( std::string& output ); + void PEXT5( std::string& output ); + void PPAC5( std::string& output ); +//******************END OF MMI0 OPCODES*********************** +//*********************MMI1 OPCODES*************************** + void PABSW( std::string& output ); + void PCEQW( std::string& output ); + void PMINW( std::string& output ); + void PADSBH( std::string& output ); + void PABSH( std::string& output ); + void PCEQH( std::string& output ); + void PMINH( std::string& output ); + void PCEQB( std::string& output ); + void PADDUW( std::string& output ); + void PSUBUW( std::string& output ); + void PEXTUW( std::string& output ); + void PADDUH( std::string& output ); + void PSUBUH( std::string& output ); + void PEXTUH( std::string& output ); + void PADDUB( std::string& output ); + void PSUBUB( std::string& output ); + void PEXTUB( std::string& output ); + void QFSRV( std::string& output ); +//*****************END OF MMI1 OPCODES*********************** +//*********************MMI2 OPCODES************************** + void PMADDW( std::string& output ); + void PSLLVW( std::string& output ); + void PSRLVW( std::string& output ); + void PMSUBW( std::string& output ); + void PMFHI( std::string& output ); + void PMFLO( std::string& output ); + void PINTH( std::string& output ); + void PMULTW( std::string& output ); + void PDIVW( std::string& output ); + void PCPYLD( std::string& output ); + void PMADDH( std::string& output ); + void PHMADH( std::string& output ); + void PAND( std::string& output ); + void PXOR( std::string& output ); + void PMSUBH( std::string& output ); + void PHMSBH( std::string& output ); + void PEXEH( std::string& output ); + void PREVH( std::string& output ); + void PMULTH( std::string& output ); + void PDIVBW( std::string& output ); + void PEXEW( std::string& output ); + void PROT3W( std::string& output ); +//********************END OF MMI2 OPCODES******************** +//***********************MMI3 OPCODES************************ + void PMADDUW( std::string& output ); + void PSRAVW( std::string& output ); + void PMTHI( std::string& output ); + void PMTLO( std::string& output ); + void PINTEH( std::string& output ); + void PMULTUW( std::string& output ); + void PDIVUW( std::string& output ); + void PCPYUD( std::string& output ); + void POR( std::string& output ); + void PNOR( std::string& output ); + void PEXCH( std::string& output ); + void PCPYH( std::string& output ); + void PEXCW( std::string& output ); +//*********************END OF MMI3 OPCODES******************* +//************************COP0 OPCODES*********************** + void MFC0( std::string& output ); + void MTC0( std::string& output ); + void BC0F( std::string& output ); + void BC0T( std::string& output ); + void BC0FL( std::string& output ); + void BC0TL( std::string& output ); + void TLBR( std::string& output ); + void TLBWI( std::string& output ); + void TLBWR( std::string& output ); + void TLBP( std::string& output ); + void ERET( std::string& output ); + void DI( std::string& output ); + void EI( std::string& output ); +//***********************END OF COP0************************* +//**************COP1 - Floating Point Unit (FPU)************* + void MFC1( std::string& output ); + void CFC1( std::string& output ); + void MTC1( std::string& output ); + void CTC1( std::string& output ); + void BC1F( std::string& output ); + void BC1T( std::string& output ); + void BC1FL( std::string& output ); + void BC1TL( std::string& output ); + void ADD_S( std::string& output ); + void SUB_S( std::string& output ); + void MUL_S( std::string& output ); + void DIV_S( std::string& output ); + void SQRT_S( std::string& output ); + void ABS_S( std::string& output ); + void MOV_S( std::string& output ); + void NEG_S( std::string& output ); + void RSQRT_S( std::string& output ); + void ADDA_S( std::string& output ); + void SUBA_S( std::string& output ); + void MULA_S( std::string& output ); + void MADD_S( std::string& output ); + void MSUB_S( std::string& output ); + void MADDA_S( std::string& output ); + void MSUBA_S( std::string& output ); + void CVT_W( std::string& output ); + void MAX_S( std::string& output ); + void MIN_S( std::string& output ); + void C_F( std::string& output ); + void C_EQ( std::string& output ); + void C_LT( std::string& output ); + void C_LE( std::string& output ); + void CVT_S( std::string& output ); +//**********************END OF COP1*********************** + } + + namespace Interpreter { + namespace OpcodeImpl + { + using namespace ::R5900; + + void COP2(); + + void Unknown(); + void MMI_Unknown(); + void COP0_Unknown(); + void COP1_Unknown(); + +// **********************Standard Opcodes************************** + void J(); + void JAL(); + void BEQ(); + void BNE(); + void BLEZ(); + void BGTZ(); + void ADDI(); + void ADDIU(); + void SLTI(); + void SLTIU(); + void ANDI(); + void ORI(); + void XORI(); + void LUI(); + void BEQL(); + void BNEL(); + void BLEZL(); + void BGTZL(); + void DADDI(); + void DADDIU(); + void LDL(); + void LDR(); + void LB(); + void LH(); + void LWL(); + void LW(); + void LBU(); + void LHU(); + void LWR(); + void LWU(); + void SB(); + void SH(); + void SWL(); + void SW(); + void SDL(); + void SDR(); + void SWR(); + void CACHE(); + void LWC1(); + void PREF(); + void LQC2(); + void LD(); + void SQC2(); + void SD(); + void LQ(); + void SQ(); + void SWC1(); +//*****************end of standard opcodes********************** +//********************SPECIAL OPCODES*************************** + void SLL(); + void SRL(); + void SRA(); + void SLLV(); + void SRLV(); + void SRAV(); + void JR(); + void JALR(); + void SYSCALL(); + void BREAK(); + void SYNC(); + void MFHI(); + void MTHI(); + void MFLO(); + void MTLO(); + void DSLLV(); + void DSRLV(); + void DSRAV(); + void MULT(); + void MULTU(); + void DIV(); + void DIVU(); + void ADD(); + void ADDU(); + void SUB(); + void SUBU(); + void AND(); + void OR(); + void XOR(); + void NOR(); + void SLT(); + void SLTU(); + void DADD(); + void DADDU(); + void DSUB(); + void DSUBU(); + void TGE(); + void TGEU(); + void TLT(); + void TLTU(); + void TEQ(); + void TNE(); + void DSLL(); + void DSRL(); + void DSRA(); + void DSLL32(); + void DSRL32(); + void DSRA32(); + void MOVZ(); + void MOVN(); + void MFSA(); + void MTSA(); +//*******************END OF SPECIAL OPCODES************************ +//***********************REGIMM OPCODES**************************** + void BLTZ(); + void BGEZ(); + void BLTZL(); + void BGEZL(); + void TGEI(); + void TGEIU(); + void TLTI(); + void TLTIU(); + void TEQI(); + void TNEI(); + void BLTZAL(); + void BGEZAL(); + void BLTZALL(); + void BGEZALL(); + void MTSAB(); + void MTSAH(); +//*******************END OF REGIMM OPCODES*********************** +//***********************MMI OPCODES***************************** + void MADD(); + void MADDU(); + void MADD1(); + void MADDU1(); + void MFHI1(); + void MTHI1(); + void MFLO1(); + void MTLO1(); + void MULT1(); + void MULTU1(); + void DIV1(); + void DIVU1(); + + namespace MMI { + void PLZCW(); + void PMFHL(); + void PMTHL(); + void PSLLH(); + void PSRLH(); + void PSRAH(); + void PSLLW(); + void PSRLW(); + void PSRAW(); +//********************END OF MMI OPCODES*********************** +//***********************MMI0 OPCODES************************** + void PADDW(); + void PSUBW(); + void PCGTW(); + void PMAXW(); + void PADDH(); + void PSUBH(); + void PCGTH(); + void PMAXH(); + void PADDB(); + void PSUBB(); + void PCGTB(); + void PADDSW(); + void PSUBSW(); + void PEXTLW(); + void PPACW(); + void PADDSH(); + void PSUBSH(); + void PEXTLH(); + void PPACH(); + void PADDSB(); + void PSUBSB(); + void PEXTLB(); + void PPACB(); + void PEXT5(); + void PPAC5(); +//******************END OF MMI0 OPCODES*********************** +//*********************MMI1 OPCODES*************************** + void PABSW(); + void PCEQW(); + void PMINW(); + void PADSBH(); + void PABSH(); + void PCEQH(); + void PMINH(); + void PCEQB(); + void PADDUW(); + void PSUBUW(); + void PEXTUW(); + void PADDUH(); + void PSUBUH(); + void PEXTUH(); + void PADDUB(); + void PSUBUB(); + void PEXTUB(); + void QFSRV(); +//*****************END OF MMI1 OPCODES*********************** +//*********************MMI2 OPCODES************************** + void PMADDW(); + void PSLLVW(); + void PSRLVW(); + void PMSUBW(); + void PMFHI(); + void PMFLO(); + void PINTH(); + void PMULTW(); + void PDIVW(); + void PCPYLD(); + void PMADDH(); + void PHMADH(); + void PAND(); + void PXOR(); + void PMSUBH(); + void PHMSBH(); + void PEXEH(); + void PREVH(); + void PMULTH(); + void PDIVBW(); + void PEXEW(); + void PROT3W(); +//********************END OF MMI2 OPCODES******************** +//***********************MMI3 OPCODES************************ + void PMADDUW(); + void PSRAVW(); + void PMTHI(); + void PMTLO(); + void PINTEH(); + void PMULTUW(); + void PDIVUW(); + void PCPYUD(); + void POR(); + void PNOR(); + void PEXCH(); + void PCPYH(); + void PEXCW(); + } +//**********************END OF MMI3 OPCODES******************** +//*************************COP0 OPCODES************************ + namespace COP0 { + void MFC0(); + void MTC0(); + void BC0F(); + void BC0T(); + void BC0FL(); + void BC0TL(); + void TLBR(); + void TLBWI(); + void TLBWR(); + void TLBP(); + void ERET(); + void DI(); + void EI(); + } +//********************END OF COP0 OPCODES************************ +//************COP1 OPCODES - Floating Point Unit***************** + namespace COP1 { + void MFC1(); + void CFC1(); + void MTC1(); + void CTC1(); + void BC1F(); + void BC1T(); + void BC1FL(); + void BC1TL(); + void ADD_S(); + void SUB_S(); + void MUL_S(); + void DIV_S(); + void SQRT_S(); + void ABS_S(); + void MOV_S(); + void NEG_S(); + void RSQRT_S(); + void ADDA_S(); + void SUBA_S(); + void MULA_S(); + void MADD_S(); + void MSUB_S(); + void MADDA_S(); + void MSUBA_S(); + void CVT_W(); + void MAX_S(); + void MIN_S(); + void C_F(); + void C_EQ(); + void C_LT(); + void C_LE(); + void CVT_S(); + } + } } +} // End namespace R5900 + +//**************************************************************************** +//** COP2 - (VU0) ** +//**************************************************************************** +void QMFC2(); +void CFC2(); +void QMTC2(); +void CTC2(); +void BC2F(); +void BC2T(); +void BC2FL(); +void BC2TL(); +//*****************SPECIAL 1 VUO TABLE******************************* +void VADDx(); +void VADDy(); +void VADDz(); +void VADDw(); +void VSUBx(); +void VSUBy(); +void VSUBz(); +void VSUBw(); +void VMADDx(); +void VMADDy(); +void VMADDz(); +void VMADDw(); +void VMSUBx(); +void VMSUBy(); +void VMSUBz(); +void VMSUBw(); +void VMAXx(); +void VMAXy(); +void VMAXz(); +void VMAXw(); +void VMINIx(); +void VMINIy(); +void VMINIz(); +void VMINIw(); +void VMULx(); +void VMULy(); +void VMULz(); +void VMULw(); +void VMULq(); +void VMAXi(); +void VMULi(); +void VMINIi(); +void VADDq(); +void VMADDq(); +void VADDi(); +void VMADDi(); +void VSUBq(); +void VMSUBq(); +void VSUBi(); +void VMSUBi(); +void VADD(); +void VMADD(); +void VMUL(); +void VMAX(); +void VSUB(); +void VMSUB(); +void VOPMSUB(); +void VMINI(); +void VIADD(); +void VISUB(); +void VIADDI(); +void VIAND(); +void VIOR(); +void VCALLMS(); +void VCALLMSR(); +//***********************************END OF SPECIAL1 VU0 TABLE***************************** +//******************************SPECIAL2 VUO TABLE***************************************** +void VADDAx(); +void VADDAy(); +void VADDAz(); +void VADDAw(); +void VSUBAx(); +void VSUBAy(); +void VSUBAz(); +void VSUBAw(); +void VMADDAx(); +void VMADDAy(); +void VMADDAz(); +void VMADDAw(); +void VMSUBAx(); +void VMSUBAy(); +void VMSUBAz(); +void VMSUBAw(); +void VITOF0(); +void VITOF4(); +void VITOF12(); +void VITOF15(); +void VFTOI0(); +void VFTOI4(); +void VFTOI12(); +void VFTOI15(); +void VMULAx(); +void VMULAy(); +void VMULAz(); +void VMULAw(); +void VMULAq(); +void VABS(); +void VMULAi(); +void VCLIPw(); +void VADDAq(); +void VMADDAq(); +void VADDAi(); +void VMADDAi(); +void VSUBAq(); +void VMSUBAq(); +void VSUBAi(); +void VMSUBAi(); +void VADDA(); +void VMADDA(); +void VMULA(); +void VSUBA(); +void VMSUBA(); +void VOPMULA(); +void VNOP(); +void VMOVE(); +void VMR32(); +void VLQI(); +void VSQI(); +void VLQD(); +void VSQD(); +void VDIV(); +void VSQRT(); +void VRSQRT(); +void VWAITQ(); +void VMTIR(); +void VMFIR(); +void VILWR(); +void VISWR(); +void VRNEXT(); +void VRGET(); +void VRINIT(); +void VRXOR(); +//*******************END OF SPECIAL2 ********************* + +#endif diff --git a/pcsx2/RDebug/Makefile.am b/pcsx2/RDebug/Makefile.am new file mode 100644 index 0000000000..57ab1d35e4 --- /dev/null +++ b/pcsx2/RDebug/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = -I@srcdir@/../ -I@srcdir@/../common/ +noinst_LIBRARIES = libRDebug.a + +libRDebug_a_SOURCES = \ +deci2.cpp deci2_dcmp.cpp deci2_drfp.h deci2_iloadp.h deci2_ttyp.cpp \ +deci2_dbgp.cpp deci2_dcmp.h deci2.h deci2_netmp.cpp deci2_ttyp.h \ +deci2_dbgp.h deci2_iloadp.cpp deci2_netmp.h \ No newline at end of file diff --git a/pcsx2/RDebug/deci2.cpp b/pcsx2/RDebug/deci2.cpp new file mode 100644 index 0000000000..43f061f272 --- /dev/null +++ b/pcsx2/RDebug/deci2.cpp @@ -0,0 +1,28 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "deci2.h" + +void exchangeSD(DECI2_HEADER *h){ + u8 tmp =h->source; + h->source =h->destination; + h->destination =tmp; +} diff --git a/pcsx2/RDebug/deci2.h b/pcsx2/RDebug/deci2.h new file mode 100644 index 0000000000..2c1562d38b --- /dev/null +++ b/pcsx2/RDebug/deci2.h @@ -0,0 +1,71 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __DECI2_H__ +#define __DECI2_H__ + +#include "Common.h" +#include "deci2_dcmp.h" +#include "deci2_iloadp.h" +#include "deci2_dbgp.h" +#include "deci2_netmp.h" +#include "deci2_ttyp.h" + +#define PROTO_DCMP 0x0001 +#define PROTO_ITTYP 0x0110 +#define PROTO_IDBGP 0x0130 +#define PROTO_ILOADP 0x0150 +#define PROTO_ETTYP 0x0220 +#define PROTO_EDBGP 0x0230 +#define PROTO_NETMP 0x0400 + + +#pragma pack(1) +struct DECI2_HEADER { + u16 length, //+00 + _pad, //+02 + protocol; //+04 + char source, //+06 + destination;//+07 +}; //=08 + +struct DECI2_DBGP_BRK{ + u32 address, //+00 + count; //+04 +}; //=08 +#pragma pack() + +#define STOP 0 +#define RUN 1 + +extern DECI2_DBGP_BRK ebrk[32], ibrk[32]; +extern int ebrk_count, ibrk_count; +extern volatile long runStatus; +extern int runCode, runCount; + +#ifdef _WIN32 +extern HANDLE runEvent; //i don't like this; +#endif + +extern int connected; + //when add linux code this might change + +int writeData(const u8 *result); +void exchangeSD(DECI2_HEADER *h); + +#endif//__DECI2_H__ diff --git a/pcsx2/RDebug/deci2.txt b/pcsx2/RDebug/deci2.txt new file mode 100644 index 0000000000..8cf7221c99 --- /dev/null +++ b/pcsx2/RDebug/deci2.txt @@ -0,0 +1,27 @@ +pcsx2 log->debugger tty mapping +ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÑÍÍÍÄÄÄÂÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ +Bios ³EE³ 0 ³IOP³ Bios ³ +CPU & MMI & COP0 & FPU ³EE³ 1 ³IOP³ IOP cpu ³ +VU0 & VUMicro ³EE³ 2 ³IOP³ HW ³ +VIF ³EE³ 3 ³IOP³ GTE ³ +GIF ³EE³ 4 ³IOP³ GPU ³ +DMA ³EE³ 5 ³IOP³ DMA ³ +HW & Unknown Memory ³EE³ 6 ³IOP³ Unknown Memory ³ +ELF & Scratch pad ³EE³ 7 ³IOP³ PAD ³ +IPU ³EE³ 8 ³IOP³ CDR ³ +SIF & RPC services ³EE³ 9 ³IOP³ ³ + ³ ³ ³ ³ ³ +SysMessage ³EE³Kernel³IOP³ SysMessage ³ +ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÁÄÄÄÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ +PRODG: 230,130, 120,121,122,110-119,11F,210-219,21F,410 +CW: 230,130,150,120,121, 210-219,21F,110-119,11F + +0400 PROTO_NETMP +0001 PROTO_DCMP + PROTO_MTWKS +012? PROTO_DRFP (%s) +0230 PROTO_ESDBG +0130 PROTO_ISDBG +011? PROTO_I%dTTY +021? PROTO_E%dTTY + diff --git a/pcsx2/RDebug/deci2_dbgp.cpp b/pcsx2/RDebug/deci2_dbgp.cpp new file mode 100644 index 0000000000..0267aabe5c --- /dev/null +++ b/pcsx2/RDebug/deci2_dbgp.cpp @@ -0,0 +1,434 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "PsxCommon.h" +#include "VUmicro.h" +#include "deci2.h" + +#include "Threading.h" +using namespace Threading; + +using namespace R5900; + +struct DECI2_DBGP_HEADER{ + DECI2_HEADER h; //+00 + u16 id; //+08 + u8 type, //+0A + code, //+0B + result, //+0C + count; //+0D + u16 _pad; //+0E +}; //=10 + +struct DECI2_DBGP_CONF{ + u32 major_ver, //+00 + minor_ver, //+04 + target_id, //+08 + _pad, //+0C + mem_align, //+10 + _pad2, //+14 + reg_size, //+18 + nreg, //+1C + nbrkpt, //+20 + ncont, //+24 + nstep, //+28 + nnext, //+2C + mem_limit_align, //+30 + mem_limit_size, //+34 + run_stop_state, //+38 + hdbg_area_addr, //+3C + hdbg_area_size; //+40 +}; //=44 + +DECI2_DBGP_CONF +cpu={3, 0, PROTO_EDBGP, 0, 0x41F, 1, 7, 32, 32, 1, 0xFF, 0xFF, 0x1F, 0x400, 2, 0x80020c70, 0x100}, +vu0={3, 0, PROTO_EDBGP, 0, 0x41F, 1, 7, 32, 32, 1, 0xFF, 0xFF, 0x1F, 0x400, 2, 0x80020c70, 0x100}, +vu1={3, 0, PROTO_EDBGP, 0, 0x41F, 1, 7, 32, 32, 1, 0xFF, 0xFF, 0x1F, 0x400, 2, 0x80020c70, 0x100}, +iop={3, 0, PROTO_IDBGP, 0, 0x00F, 1, 5, 62, 32, 1, 0x00, 0x00, 0x07, 0x200, 1, 0x0001E670, 0x100}; +//iop={3, 0, PROTO_IDBGP, 0, 0x00F, 1, 5, 62, 0, 1, 0x00, 0x00, 0x07, 0x200, 0, 0x00006940, 0x100}; + +#pragma pack(2) +struct DECI2_DBGP_EREG{ + u8 kind, //+00 + number; //+01 + u16 _pad; //+02 + u64 value[2]; //+04 +}; //=14 + +struct DECI2_DBGP_IREG{ + u8 kind, //+00 + number; //+01 + u16 _pad; //+02 + u32 value; //+04 +}; //=08 + +struct DECI2_DBGP_MEM{ + u8 space, //+00 + align; //+01 + u16 _pad; //+02 + u32 address; //+04 + u32 length; //+08 +}; //=0C + +struct DECI2_DBGP_RUN{ + u32 entry, //+00 + gp, //+04 + _pad, //+08 + _pad1, //+0C + argc; //+10 + //u32 argv; //+14 +}; //=18 +#pragma pack() + +void D2_DBGP(const u8 *inbuffer, u8 *outbuffer, char *message, char *eepc, char *ioppc, char *eecy, char *iopcy){ + const DECI2_DBGP_HEADER *in=(DECI2_DBGP_HEADER*)inbuffer; + DECI2_DBGP_HEADER* out=(DECI2_DBGP_HEADER*)outbuffer; + + DECI2_DBGP_EREG *eregs=(DECI2_DBGP_EREG*)&out[1]; + DECI2_DBGP_IREG *iregs=(DECI2_DBGP_IREG*)&out[1]; + DECI2_DBGP_MEM *mem =(DECI2_DBGP_MEM*) &out[1]; + const DECI2_DBGP_RUN*run =(DECI2_DBGP_RUN*) &in[1]; + + static char line[1024]; + int i, s; + + memcpy(outbuffer, inbuffer, 128*1024);//BUFFERSIZE + //out->h.length=sizeof(DECI2_DBGP_HEADER); + out->type++; + out->result=0; //ok + exchangeSD((DECI2_HEADER*)out); + switch(in->type){ + case 0x00://ok + sprintf(line, "%s/GETCONF", in->id==0?"CPU":in->id==1?"VU0":"VU1"); + + if (in->h.destination=='I'){ + memcpy(&out[1], &iop, sizeof(DECI2_DBGP_CONF)); + }else + switch(in->id){ + case 0:memcpy(&out[1], &cpu, sizeof(DECI2_DBGP_CONF));break; + case 1:memcpy(&out[1], &vu0, sizeof(DECI2_DBGP_CONF));break; + case 2:memcpy(&out[1], &vu1, sizeof(DECI2_DBGP_CONF));break; + } + break; + case 0x02://ok + sprintf(line, "%s/2", in->id==0?"CPU":in->id==1?"VU0":"VU1"); + break; + case 0x04://ok + sprintf(line, "%s/GETREG count=%d kind[0]=%d number[0]=%d", + in->id==0?"CPU":in->id==1?"VU0":"VU1", in->count, eregs[0].kind, eregs[0].number); + if (in->h.destination=='I'){ + for (i=0; icount; i++) + switch (iregs[i].kind){ + case 1:switch (iregs[i].number){ + case 0:iregs[i].value=psxRegs.GPR.n.lo;break; + case 1:iregs[i].value=psxRegs.GPR.n.hi;break; + } + break; + case 2:iregs[i].value=psxRegs.GPR.r[iregs[i].number]; break; + case 3: + if (iregs[i].number==14) psxRegs.CP0.n.EPC=psxRegs.pc; + iregs[i].value=psxRegs.CP0.r[iregs[i].number]; + break; + case 6:iregs[i].value=psxRegs.CP2D.r[iregs[i].number]; break; + case 7:iregs[i].value=psxRegs.CP2C.r[iregs[i].number]; break; + default: + iregs[0].value++;//dummy; might be assert(0) + } + }else + for (i=0; icount; i++) + switch (eregs[i].kind){ + case 0:memcpy(eregs[i].value, &cpuRegs.GPR.r[eregs[i].number], 16);break; + case 1: + switch(eregs[i].number){ + case 0:memcpy(eregs[i].value, &cpuRegs.HI.UD[0], 8);break; + case 1:memcpy(eregs[i].value, &cpuRegs.LO.UD[0], 8);break; + case 2:memcpy(eregs[i].value, &cpuRegs.HI.UD[1], 8);break; + case 3:memcpy(eregs[i].value, &cpuRegs.LO.UD[1], 8);break; + case 4:memcpy(eregs[i].value, &cpuRegs.sa, 4); break; + } + case 2: + if (eregs[i].number==14) cpuRegs.CP0.n.EPC=cpuRegs.pc; + memcpy(eregs[i].value, &cpuRegs.CP0.r[eregs[i].number], 4); + break; + case 3:break;//performance counter 32x3 + case 4:break;//hw debug reg 32x8 + case 5:memcpy(eregs[i].value, &fpuRegs.fpr[eregs[i].number], 4);break; + case 6:memcpy(eregs[i].value, &fpuRegs.fprc[eregs[i].number], 4);break; + case 7:memcpy(eregs[i].value, &VU0.VF[eregs[i].number], 16);break; + case 8:memcpy(eregs[i].value, &VU0.VI[eregs[i].number], 4);break; + case 9:memcpy(eregs[i].value, &VU1.VF[eregs[i].number], 16);break; + case 10:memcpy(eregs[i].value, &VU1.VI[eregs[i].number], 4);break; + default: + eregs[0].value[0]++;//dummy; might be assert(0) + } + break; + case 0x06://ok + sprintf(line, "%s/PUTREG count=%d kind[0]=%d number[0]=%d value=%016I64X_%016I64X", + in->id==0?"CPU":in->id==1?"VU0":"VU1", in->count, eregs[0].kind, eregs[0].number, eregs[0].value[1], eregs[0].value[0]); + if (in->h.destination=='I'){ + for (i=0; icount; i++) + switch (iregs[i].kind){ + case 1:switch (iregs[i].number){ + case 0:psxRegs.GPR.n.lo=iregs[i].value;break; + case 1:psxRegs.GPR.n.hi=iregs[i].value;break; + } + break; + case 2:psxRegs.GPR.r[iregs[i].number]=iregs[i].value; break; + case 3: + psxRegs.CP0.r[iregs[i].number]=iregs[i].value; + if (iregs[i].number==14) psxRegs.pc=psxRegs.CP0.n.EPC; + break; + case 6:psxRegs.CP2D.r[iregs[i].number]=iregs[i].value; break; + case 7:psxRegs.CP2C.r[iregs[i].number]=iregs[i].value; break; + default: + ;//dummy; might be assert(0) + } + }else + for (i=0; icount; i++) + switch (eregs[i].kind){ + case 0:memcpy(&cpuRegs.GPR.r[eregs[i].number], eregs[i].value, 16);break; + case 1: + switch(eregs[i].number){ + case 0:memcpy(&cpuRegs.HI.UD[0], eregs[i].value, 8);break; + case 1:memcpy(&cpuRegs.LO.UD[0], eregs[i].value, 8);break; + case 2:memcpy(&cpuRegs.HI.UD[1], eregs[i].value, 8);break; + case 3:memcpy(&cpuRegs.LO.UD[1], eregs[i].value, 8);break; + case 4:memcpy(&cpuRegs.sa, eregs[i].value, 4); break; + } + break; + case 2: + memcpy(&cpuRegs.CP0.r[eregs[i].number], eregs[i].value, 4); + if (eregs[i].number==14) cpuRegs.pc=cpuRegs.CP0.n.EPC; + break; + case 3:break;//performance counter 32x3 + case 4:break;//hw debug reg 32x8 + case 5:memcpy(&fpuRegs.fpr[eregs[i].number], eregs[i].value, 4);break; + case 6:memcpy(&fpuRegs.fprc[eregs[i].number], eregs[i].value, 4);break; + case 7:memcpy(&VU0.VF[eregs[i].number], eregs[i].value, 16);break; + case 8:memcpy(&VU0.VI[eregs[i].number], eregs[i].value, 4);break; + case 9:memcpy(&VU1.VF[eregs[i].number], eregs[i].value, 16);break; + case 10:memcpy(&VU1.VI[eregs[i].number], eregs[i].value, 4);break; + default: + ;//dummy; might be assert(0) + } + break; + case 0x08://ok + { + sprintf(line, "%s/RDMEM %08X/%X", + in->id==0?"CPU":in->id==1?"VU0":"VU1", mem->address, mem->length); + u8* data =(u8*)out+ //kids: don't try this at home! :D + ((sizeof(DECI2_DBGP_HEADER)+sizeof(DECI2_DBGP_MEM)+(1 << mem->align) - 1) & (0xFFFFFFFF << mem->align)); + + if ((mem->address & ((1 << mem->align)-1)) || + (mem->length & ((1 << mem->align)-1))){ + out->result=1; + strcat(line, " ALIGN ERROR"); + break; + } + if (in->h.destination=='I') + if (PSXM(mem->address & 0x1FFFFFFF)) + memcpy(data, PSXM(mem->address & 0x1FFFFFFF), mem->length); + else{ + out->result=0x11; + strcat(line, " ADDRESS ERROR"); + break; + } + else + { + switch(mem->space){ + case 0: + if ((mem->address & 0xF0000000) == 0x70000000) + memcpy(data, PSM(mem->address), mem->length); + else + if ((((mem->address & 0x1FFFFFFF)>128*1024*1024) || ((mem->address & 0x1FFFFFFF)<32*1024*1024)) && PSM(mem->address & 0x1FFFFFFF)) + memcpy(data, PSM(mem->address & 0x1FFFFFFF), mem->length); + else{ + out->result=0x11; + strcat(line, " ADDRESS ERROR"); + } + break; + case 1: + if (in->id==1) + memcpy(data, &VU0.Mem[mem->address & 0xFFF], mem->length); + else + memcpy(data, &VU1.Mem[mem->address & 0x3FFF], mem->length); + break; + case 2: + if (in->id==1) + memcpy(data, &VU0.Micro[mem->address & 0xFFF], mem->length); + else + memcpy(data, &VU1.Micro[mem->address & 0x3FFF], mem->length); + break; + } + } + out->h.length=mem->length+data-(u8*)out; + break; + } + + case 0x0a://ok + { + sprintf(line, "%s/WRMEM %08X/%X", + in->id==0?"CPU":in->id==1?"VU0":"VU1", mem->address, mem->length); + const u8* data=(u8*)in+ //kids: don't try this at home! :D + ((sizeof(DECI2_DBGP_HEADER)+sizeof(DECI2_DBGP_MEM)+(1 << mem->align) - 1) & (0xFFFFFFFF << mem->align)); + if (mem->length==4 && *(int*)data==0x0000000D) + strcat(line, " BREAKPOINT"); + if ((mem->address & ((1 << mem->align)-1)) || + (mem->length & ((1 << mem->align)-1))){ + out->result=1; + strcat(line, " ALIGN ERROR"); + break; + } + if (in->h.destination=='I') + if (PSXM(mem->address & 0x1FFFFFFF)) + memcpy(PSXM(mem->address & 0x1FFFFFFF), data, mem->length); + else{ + out->result=0x11; + strcat(line, " ADDRESS ERROR"); + break; + } + else + switch(mem->space){ + case 0: + if ((mem->address & 0xF0000000) == 0x70000000) + memcpy(PSM(mem->address), data, mem->length); + else + if (PSM(mem->address & 0x1FFFFFFF)) + memcpy(PSM(mem->address & 0x1FFFFFFF), data, mem->length); + else{ + out->result=0x11; + strcat(line, " ADDRESS ERROR"); + } + break; + case 1: + if (in->id==1) + memcpy(&VU0.Mem[mem->address & 0xFFF], data, mem->length); + else + memcpy(&VU1.Mem[mem->address & 0x3FFF], data, mem->length); + break; + case 2: + if (in->id==1) + memcpy(&VU0.Micro[mem->address & 0xFFF], data, mem->length); + else + memcpy(&VU1.Micro[mem->address & 0x3FFF], data, mem->length); + break; + } + out->h.length=sizeof(DECI2_DBGP_HEADER)+sizeof(DECI2_DBGP_MEM); + break; + } + case 0x10://ok + { + sprintf(line, "%s/GETBRKPT count=%d", + in->id==0?"CPU":in->id==1?"VU0":"VU1", in->count); + if (in->h.destination=='I') memcpy(&out[1], ibrk, out->count=ibrk_count); + else memcpy(&out[1], ebrk, out->count=ebrk_count); + out->h.length=sizeof(DECI2_DBGP_HEADER)+out->count*sizeof(DECI2_DBGP_BRK); + break; + } + case 0x12://ok [does not break on iop brkpts] + sprintf(line, "%s/PUTBRKPT count=%d", + in->id==0?"CPU":in->id==1?"VU0":"VU1", in->count); + out->h.length=sizeof(DECI2_DBGP_HEADER); + if (in->count>32){ + out->result=1; + strcat(line, "TOO MANY"); + break; + } + if (in->h.destination=='I') memcpy(ibrk, &out[1], ibrk_count=in->count); + else memcpy(ebrk, &out[1], ebrk_count=in->count); + out->count=0; + break; + case 0x14://ok, [w/o iop] + sprintf(line, "%s/BREAK count=%d", + in->id==0?"CPU":in->id==1?"VU0":"VU1", in->count); + if (in->h.destination=='I') + ; + else{ + out->result = ( pcsx2_InterlockedExchange(&runStatus, STOP)==STOP ? + 0x20 : 0x21 ); + out->code=0xFF; + Sleep(50); + } + break; + case 0x16://ok, [w/o iop] + sprintf(line, "%s/CONTINUE code=%s count=%d", + in->id==0?"CPU":in->id==1?"VU0":"VU1", + in->code==0?"CONT":in->code==1?"STEP":"NEXT", in->count); + if (in->h.destination=='I') + ; + else{ + pcsx2_InterlockedExchange(&runStatus, STOP); + Sleep(100);//first get the run thread to Wait state + runCount=in->count; + runCode=in->code; +#ifdef _WIN32 + SetEvent(runEvent);//kick it +#endif + } + break; + case 0x18://ok [without argc/argv stuff] + { + sprintf(line, "%s/RUN code=%d count=%d entry=0x%08X gp=0x%08X argc=%d", + in->id==0?"CPU":in->id==1?"VU0":"VU1", in->code, in->count, + run->entry, run->gp, run->argc); + cpuRegs.CP0.n.EPC=cpuRegs.pc=run->entry; + cpuRegs.GPR.n.gp.UL[0]=run->gp; +// threads_array[0].argc = run->argc; + u32* argv = (u32*)&run[1]; + for (i=0, s=0; i<(int)run->argc; i++, argv++) s+=argv[i]; + memcpy(PSM(0), argv, s); +// threads_array[0].argstring = 0; + pcsx2_InterlockedExchange((volatile long*)&runStatus, (u32)STOP); + Sleep(1000);//first get the run thread to Wait state + runCount=0; + runCode=0xFF; +#ifdef _WIN32 + SetEvent(runEvent);//awake it +#endif + out->h.length=sizeof(DECI2_DBGP_HEADER); + break; + } + default: + sprintf(line, "type=0x%02X code=%d count=%d [unknown]", in->type, in->code, in->count); + } + sprintf(message, "[DBGP %c->%c/%04X] %s", in->h.source, in->h.destination, in->h.length, line); + sprintf(eepc, "%08X", cpuRegs.pc); + sprintf(ioppc, "%08X", psxRegs.pc); + sprintf(eecy, "%d", cpuRegs.cycle); + sprintf(iopcy, "%d", psxRegs.cycle); + writeData(outbuffer); +} + +void sendBREAK(u8 source, u16 id, u8 code, u8 result, u8 count){ + static DECI2_DBGP_HEADER tmp; + tmp.h.length =sizeof(DECI2_DBGP_HEADER); + tmp.h._pad =0; + tmp.h.protocol =( source=='E' ? PROTO_EDBGP : PROTO_IDBGP ); + tmp.h.source =source; + tmp.h.destination='H'; + tmp.id =id; + tmp.type =0x15; + tmp.code =code; + tmp.result =result; + tmp.count =count; + tmp._pad =0; + writeData((u8*)&tmp); +} diff --git a/pcsx2/RDebug/deci2_dbgp.h b/pcsx2/RDebug/deci2_dbgp.h new file mode 100644 index 0000000000..4e0b508ece --- /dev/null +++ b/pcsx2/RDebug/deci2_dbgp.h @@ -0,0 +1,28 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __DECI2DBGP_H__ +#define __DECI2DBGP_H__ + +#include "Common.h" +#include "deci2.h" + +void D2_DBGP(const u8 *inbuffer, u8 *outbuffer, char *message, char *eepc, char *ioppc, char *eecy, char *iopcy); +void sendBREAK(u8 source, u16 id, u8 code, u8 result, u8 count); + +#endif//__DECI2DBGP_H__ diff --git a/pcsx2/RDebug/deci2_dcmp.cpp b/pcsx2/RDebug/deci2_dcmp.cpp new file mode 100644 index 0000000000..6318137fe6 --- /dev/null +++ b/pcsx2/RDebug/deci2_dcmp.cpp @@ -0,0 +1,85 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "deci2.h" + +struct DECI2_DCMP_HEADER{ + DECI2_HEADER h; //+00 + u8 type, //+08 + code; //+09 + u16 _pad; //+0A +}; //=0C + +struct DECI2_DCMP_CONNECT{ + u8 result, //+00 + _pad[3];//+01 + u64 EEboot, //+04 + IOPboot;//+0C +}; //=14 + +struct DECI2_DCMP_ECHO{ + u16 identifier, //+00 + sequence; //+02 + u8 data[32]; //+04 +}; //=24 + +void D2_DCMP(const u8 *inbuffer, u8 *outbuffer, char *message){ + DECI2_DCMP_HEADER *in=(DECI2_DCMP_HEADER*)inbuffer, + *out=(DECI2_DCMP_HEADER*)outbuffer; + u8 *data=(u8*)in+sizeof(DECI2_DCMP_HEADER); + + //DECI2_DCMP_CONNECT *connect=(DECI2_DCMP_CONNECT*)data; + //DECI2_DCMP_ECHO *echo =(DECI2_DCMP_ECHO*)data; + + memcpy(outbuffer, inbuffer, 128*1024);//BUFFERSIZE + out->h.length=sizeof(DECI2_DCMP_HEADER); +/* switch(in->type){ + case 0: + sprintf(message, " [DCMP] type=CONNECT code=%s EEboot=0x%I64X IOP=0x%I64X", + in->code==0?"CONNECT":"DISCONNECT", connect->EEboot, connect->IOPboot); + data=(u8*)out+sizeof(DECI2_DCMP_HEADER); + connect=(DECI2_DCMP_CONNECT*)data; + connect->result=0; + break; + case 1: + sprintf(message, " [DCMP] type=ECHO id=%X seq=%X", echo->identifier, echo->sequence); + exchangeSD(&out->h); + break; +// not implemented, not needed? + default: + sprintf(message, " [DCMP] type=%d[unknown]", in->type); + }*/ + out->code++; +} + +void sendDCMP(u16 protocol, u8 source, u8 destination, u8 type, u8 code, char *data, int size){ + static u8 tmp[100]; + ((DECI2_DCMP_HEADER*)tmp)->h.length =sizeof(DECI2_DCMP_HEADER)+size; + ((DECI2_DCMP_HEADER*)tmp)->h._pad =0; + ((DECI2_DCMP_HEADER*)tmp)->h.protocol =protocol; + ((DECI2_DCMP_HEADER*)tmp)->h.source =source; + ((DECI2_DCMP_HEADER*)tmp)->h.destination=destination; + ((DECI2_DCMP_HEADER*)tmp)->type =type; + ((DECI2_DCMP_HEADER*)tmp)->code =code; + ((DECI2_DCMP_HEADER*)tmp)->_pad =0; + memcpy(&tmp[sizeof(DECI2_DCMP_HEADER)], data, size); + writeData(tmp); +} \ No newline at end of file diff --git a/pcsx2/RDebug/deci2_dcmp.h b/pcsx2/RDebug/deci2_dcmp.h new file mode 100644 index 0000000000..5fe87de7fb --- /dev/null +++ b/pcsx2/RDebug/deci2_dcmp.h @@ -0,0 +1,28 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __DECI2DCMP_H__ +#define __DECI2DCMP_H__ + +#include "Common.h" +#include "deci2.h" + +void D2_DCMP(const u8 *inbuffer, u8 *outbuffer, char *message); +void sendDCMP(u16 protocol, u8 source, u8 destination, u8 type, u8 code, char *data, int size); + +#endif//__DECI2DCMP_H__ diff --git a/pcsx2/RDebug/deci2_drfp.cpp b/pcsx2/RDebug/deci2_drfp.cpp new file mode 100644 index 0000000000..a26af1dad5 --- /dev/null +++ b/pcsx2/RDebug/deci2_drfp.cpp @@ -0,0 +1,48 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "Common.h" +#include "deci2.h" + +typedef struct tag_DECI2_DCMP_HEADER{ + DECI2_HEADER h; //+00 + u8 type, //+08 + code; //+09 + u16 _pad; //+0A +} DECI2_DCMP_HEADER; //=0C + +extern char d2_message[100]; + +void D2_DCMP(char *inbuffer, char *outbuffer, char *message){ + DECI2_DCMP_HEADER *in=(DECI2_DCMP_HEADER*)inbuffer, + *out=(DECI2_DCMP_HEADER*)outbuffer; + u8 *data=(u8*)in+sizeof(DECI2_DCMP_HEADER); + + memcpy(outbuffer, inbuffer, 128*1024);//BUFFERSIZE + out->h.length=sizeof(DECI2_DCMP_HEADER); + switch(in->type){ + case 4://[OK] + sprintf(message, " [DCMP] code=MESSAGE %s", data);//null terminated by the memset with 0 call + strcpy(d2_message, data); + break; + default: + sprintf(message, " [DCMP] code=%d[unknown] result=%d", netmp->code, netmp->result); + } + result->code++; + result->result=0; //ok +} diff --git a/pcsx2/RDebug/deci2_drfp.h b/pcsx2/RDebug/deci2_drfp.h new file mode 100644 index 0000000000..78c78eb254 --- /dev/null +++ b/pcsx2/RDebug/deci2_drfp.h @@ -0,0 +1,27 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __DECI2DRFP_H__ +#define __DECI2DRFP_H__ + +#include "Common.h" +#include "deci2.h" + +void D2_(char *inbuffer, char *outbuffer, char *message); + +#endif//__DECI2DRFP_H__ \ No newline at end of file diff --git a/pcsx2/RDebug/deci2_iloadp.cpp b/pcsx2/RDebug/deci2_iloadp.cpp new file mode 100644 index 0000000000..bc18f7a8e4 --- /dev/null +++ b/pcsx2/RDebug/deci2_iloadp.cpp @@ -0,0 +1,108 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "PsxCommon.h" +#include "IopBios2.h" +#include "deci2.h" + +struct DECI2_ILOADP_HEADER{ + DECI2_HEADER h; //+00 + u8 code, //+08 cmd + action, //+09 + result, //+0A + stamp; //+0B + u32 moduleId; //+0C +}; //=10 + +struct DECI2_ILOADP_INFO{ + u16 version, //+00 + flags; //+02 + u32 module_address, //+04 + text_size, //+08 + data_size, //+0C + bss_size, //+10 + _pad[3]; //+14 +}; + +void writeInfo(DECI2_ILOADP_INFO *info, + u16 version, u16 flags, u32 module_address, + u32 text_size, u32 data_size, u32 bss_size){ + info->version =version; + info->flags =flags; + info->module_address=module_address; + info->text_size =text_size; + info->data_size =data_size; + info->bss_size =bss_size; + info->_pad[0]=info->_pad[1]=info->_pad[2]=0; +} + +void D2_ILOADP(const u8 *inbuffer, u8 *outbuffer, char *message){ + DECI2_ILOADP_HEADER *in=(DECI2_ILOADP_HEADER*)inbuffer, + *out=(DECI2_ILOADP_HEADER*)outbuffer; + u8 *data=(u8*)in+sizeof(DECI2_ILOADP_HEADER); + irxImageInfo *iii; + static char line[1024]; + + memcpy(outbuffer, inbuffer, 128*1024);//BUFFERSIZE + out->h.length=sizeof(DECI2_ILOADP_HEADER); + out->code++; + out->result=0; //ok + exchangeSD((DECI2_HEADER*)out); + switch(in->code){ + case 0: + sprintf(line, "code=START action=%d stamp=%d moduleId=0x%X", in->action, in->stamp, in->moduleId); + break; + case 2: + sprintf(line, "code=REMOVE action=%d stamp=%d moduleId=0x%X", in->action, in->stamp, in->moduleId); + break; + case 4: + sprintf(line, "code=LIST action=%d stamp=%d moduleId=0x%X", in->action, in->stamp, in->moduleId); + data=(u8*)out+sizeof(DECI2_ILOADP_HEADER); + for (iii=(irxImageInfo*)PSXM(0x800); iii; iii=iii->next?(irxImageInfo*)PSXM(iii->next):0, data+=4) + *(u32*)data=iii->index; + + out->h.length=data-(u8*)out; + break; + case 6: + sprintf(line, "code=INFO action=%d stamp=%d moduleId=0x%X", in->action, in->stamp, in->moduleId); + data=(u8*)out+sizeof(DECI2_ILOADP_HEADER); + for(iii=(irxImageInfo*)PSXM(0x800); iii; iii=iii->next?(irxImageInfo*)PSXM(iii->next):0) + if (iii->index==in->moduleId){ + writeInfo((DECI2_ILOADP_INFO*)data, + iii->version, iii->flags, iii->vaddr, iii->text_size, iii->data_size, iii->bss_size); + data+=sizeof(DECI2_ILOADP_INFO); + strcpy((char*)data, (char*)PSXM(iii->name)); + data+=strlen((char*)PSXM(iii->name))+4; + data=(u8*)((int)data & 0xFFFFFFFC); + break; + + } + out->h.length=data-(u8*)out; + break; + case 8: + sprintf(line, "code=WATCH action=%d stamp=%d moduleId=0x%X", in->action, in->stamp, in->moduleId); + break; + default: + sprintf(line, "code=%d[unknown]", in->code); + } + sprintf(message, "[ILOADP %c->%c/%04X] %s", in->h.source, in->h.destination, in->h.length, line); + writeData(outbuffer); +} diff --git a/pcsx2/RDebug/deci2_iloadp.h b/pcsx2/RDebug/deci2_iloadp.h new file mode 100644 index 0000000000..ee556235e0 --- /dev/null +++ b/pcsx2/RDebug/deci2_iloadp.h @@ -0,0 +1,27 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __DECI2ILOADP_H__ +#define __DECI2ILOADP_H__ + +#include "Common.h" +#include "deci2.h" + +void D2_ILOADP(const u8 *inbuffer, u8 *outbuffer, char *message); + +#endif//__DECI2ILOADP_H__ diff --git a/pcsx2/RDebug/deci2_netmp.cpp b/pcsx2/RDebug/deci2_netmp.cpp new file mode 100644 index 0000000000..ca11d8131b --- /dev/null +++ b/pcsx2/RDebug/deci2_netmp.cpp @@ -0,0 +1,136 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "deci2.h" + +struct DECI2_NETMP_HEADER{ + DECI2_HEADER h; //+00 + u8 code, //+08 + result; //+09 +}; //=0A + +struct DECI2_NETMP_CONNECT{ + u8 priority, //+00 + _pad; //+01 + u16 protocol; //+02 +}; //=04 + +char d2_message[100]; +int d2_count=1; +DECI2_NETMP_CONNECT d2_connect[50]={0xFF, 0, 0x400}; + +void D2_NETMP(const u8 *inbuffer, u8 *outbuffer, char *message){ + DECI2_NETMP_HEADER *in=(DECI2_NETMP_HEADER*)inbuffer, + *out=(DECI2_NETMP_HEADER*)outbuffer; + u8 *data=(u8*)in+sizeof(DECI2_NETMP_HEADER); + DECI2_NETMP_CONNECT *connect=(DECI2_NETMP_CONNECT*)data; //connect + int i, n; + static char p[100], line[1024]; + u64 EEboot, IOPboot; //reset + u16 node; + + memcpy(outbuffer, inbuffer, 128*1024);//BUFFERSIZE + out->h.length=sizeof(DECI2_NETMP_HEADER); + out->code++; + out->result=0; //ok + switch(in->code){ + case 0: + n=(in->h.length-sizeof(DECI2_NETMP_HEADER)) / sizeof(DECI2_NETMP_CONNECT); + sprintf(line, "code=CONNECT"); + for (i=0; ih.length=data-(u8*)out; + writeData(outbuffer); + + node=(u16)'I'; + sendDCMP(PROTO_DCMP, 'H', 'H', 2, 0, (char*)&node, sizeof(node)); + + node=(u16)'E'; + sendDCMP(PROTO_DCMP, 'H', 'H', 2, 0, (char*)&node, sizeof(node)); + + node=PROTO_ILOADP; + sendDCMP(PROTO_DCMP, 'I', 'H', 2, 1, (char*)&node, sizeof(node)); + + for (i=0; i<10; i++){ + node=PROTO_ETTYP+i; + sendDCMP(PROTO_DCMP, 'E', 'H', 2, 1, (char*)&node, sizeof(node)); + + node=PROTO_ITTYP+i; + sendDCMP(PROTO_DCMP, 'E', 'H', 2, 1, (char*)&node, sizeof(node)); + } + node=PROTO_ETTYP+0xF; + sendDCMP(PROTO_DCMP, 'E', 'H', 2, 1, (char*)&node, sizeof(node)); + + node=PROTO_ITTYP+0xF; + sendDCMP(PROTO_DCMP, 'E', 'H', 2, 1, (char*)&node, sizeof(node)); + break; + case 4://[OK] + sprintf(line, "code=MESSAGE %s", data);//null terminated by the memset with 0 call + strcpy(d2_message, (char*)data); + writeData(outbuffer); + break; + case 6://[ok] + sprintf(line, "code=STATUS"); + data=(u8*)out+sizeof(DECI2_NETMP_HEADER)+2; + /* + memcpy(data, d2_connect, 1*sizeof(DECI2_NETMP_CONNECT)); + data+=1*sizeof(DECI2_NETMP_CONNECT); + *(u32*)data=1;//quite fast;) + data+=4; + memcpy(data, d2_message, strlen(d2_message)); + data+=strlen(d2_message); + *(u32*)data=0;//null end the string on a word boundary + data+=3;data=(u8*)((int)data & 0xFFFFFFFC);*/ + + out->h.length=data-(u8*)out; + writeData(outbuffer); + break; + case 8: + sprintf(line, "code=KILL protocol=0x%04X", *(u16*)data); + writeData(outbuffer); + break; + case 10: + sprintf(line, "code=VERSION %s", data); + data=(u8*)out+sizeof(DECI2_NETMP_HEADER); + strcpy((char*)data, "0.2.0");data+=strlen("0.2.0");//emu version;) + out->h.length=data-(u8*)out; + writeData(outbuffer); + break; + default: + sprintf(line, "code=%d[unknown] result=%d", in->code, in->result); + writeData(outbuffer); + } + sprintf(message, "[NETMP %c->%c/%04X] %s", in->h.source, in->h.destination, in->h.length, line); +} diff --git a/pcsx2/RDebug/deci2_netmp.h b/pcsx2/RDebug/deci2_netmp.h new file mode 100644 index 0000000000..fae6767118 --- /dev/null +++ b/pcsx2/RDebug/deci2_netmp.h @@ -0,0 +1,27 @@ + /* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __DECI2NETMP_H__ +#define __DECI2NETMP_H__ + +#include "Common.h" +#include "deci2.h" + +void D2_NETMP(const u8 *inbuffer, u8 *outbuffer, char *message); + +#endif//__DECI2NETMP_H__ diff --git a/pcsx2/RDebug/deci2_ttyp.cpp b/pcsx2/RDebug/deci2_ttyp.cpp new file mode 100644 index 0000000000..c397a5aa7e --- /dev/null +++ b/pcsx2/RDebug/deci2_ttyp.cpp @@ -0,0 +1,43 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "deci2.h" + +struct DECI2_TTYP_HEADER{ + DECI2_HEADER h; //+00 + u32 flushreq; //+08 + //u8 data[0]; //+0C // Not used, so commented out (cottonvibes) +}; //=0C + +void sendTTYP(u16 protocol, u8 source, char *data){ + static char tmp[2048]; + ((DECI2_TTYP_HEADER*)tmp)->h.length =sizeof(DECI2_TTYP_HEADER)+strlen(data); + ((DECI2_TTYP_HEADER*)tmp)->h._pad =0; + ((DECI2_TTYP_HEADER*)tmp)->h.protocol =protocol +(source=='E' ? PROTO_ETTYP : PROTO_ITTYP); + ((DECI2_TTYP_HEADER*)tmp)->h.source =source; + ((DECI2_TTYP_HEADER*)tmp)->h.destination='H'; + ((DECI2_TTYP_HEADER*)tmp)->flushreq =0; + if (((DECI2_TTYP_HEADER*)tmp)->h.length>2048) + Msgbox::Alert("TTYP: Buffer overflow"); + else + memcpy(&tmp[sizeof(DECI2_TTYP_HEADER)], data, strlen(data)); + //writeData(tmp); +} diff --git a/pcsx2/RDebug/deci2_ttyp.h b/pcsx2/RDebug/deci2_ttyp.h new file mode 100644 index 0000000000..4edf5739b2 --- /dev/null +++ b/pcsx2/RDebug/deci2_ttyp.h @@ -0,0 +1,28 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __DECI2TTYP_H__ +#define __DECI2TTYP_H__ + +#include "Common.h" +#include "deci2.h" + +//void D2_(char *inbuffer, char *outbuffer, char *message); +void sendTTYP(u16 protocol, u8 source, char *data); + +#endif//__DECI2TTYP_H__ diff --git a/pcsx2/RDebug/iloadp.txt b/pcsx2/RDebug/iloadp.txt new file mode 100644 index 0000000000..c8b6c68834 --- /dev/null +++ b/pcsx2/RDebug/iloadp.txt @@ -0,0 +1,32 @@ +ILOADP_MODULE_INFO{ + u16 flags ÄÄÄÄ¿ <ÄÄÄ¿ + u16 version <ÄÄÄÙ ÄÄÄÄÙ + u32 addr + u32 sz_text + u32 sz_data + u32 sz_bss + u32 reserved1 + u32 reserved2 + u32 reserved3 + u8* name +}; + +ILOADP_HDR{ + u8 cmd + u8 action + u8 result + u8 stamp + u32 module_id +}; + +ILOADP_START(cmd==00) +------------------------------------------ +ILOADP_HDR_START{ + ILOADP_HDR hdr //+00 + u8* modulename //+10 //asciiz +}; //=10+strlen(modulename)+1 +action 0D(IOP_LOAD_IRX) +modulename host:E:\TEMP2\GCC\share\SIO2D.IRX + +action 02(IOP_RUN) +modulename E:\TEMP2\GCC\share\SIO2D.IRX param diff --git a/pcsx2/SPR.cpp b/pcsx2/SPR.cpp new file mode 100644 index 0000000000..02f1949f15 --- /dev/null +++ b/pcsx2/SPR.cpp @@ -0,0 +1,388 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "SPR.h" +#include "iR5900.h" +#include "VUmicro.h" + +#define spr0 ((DMACh*)&PS2MEM_HW[0xD000]) +#define spr1 ((DMACh*)&PS2MEM_HW[0xD400]) + +void sprInit() { +} + +//__forceinline static void SPR0transfer(u32 *data, int size) { +///* while (size > 0) { +// SPR_LOG("SPR1transfer: %x\n", *data); +// data++; size--; +// }*/ +// size <<= 2; +// if ((psHu32(DMAC_CTRL) & 0xC) == 0xC || // GIF MFIFO +// (psHu32(DMAC_CTRL) & 0xC) == 0x8) { // VIF1 MFIFO +// hwMFIFOWrite(spr0->madr, (u8*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff], size); +// } else { +// u32 * p = (u32*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff]; +// //WriteCodeSSE2(p,data,size >> 4); +// memcpy_fast((u8*)data, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], size); +// } +// spr0->sadr+= size; +//} + +static void TestClearVUs(u32 madr, u32 size) +{ + if( madr >= 0x11000000 ) { + if( madr < 0x11004000 ) { + DbgCon::Notice("scratch pad clearing vu0\n"); + CpuVU0.Clear(madr&0xfff, size); + } + else if( madr >= 0x11008000 && madr < 0x1100c000 ) { + DbgCon::Notice("scratch pad clearing vu1\n"); + CpuVU1.Clear(madr&0x3fff, size); + } + } +} + +int _SPR0chain() { + u32 *pMem; + + if (spr0->qwc == 0) return 0; + pMem = (u32*)dmaGetAddr(spr0->madr); + if (pMem == NULL) return -1; + + //SPR0transfer(pMem, qwc << 2); + + if ((psHu32(DMAC_CTRL) & 0xC) >= 0x8) { // 0x8 VIF1 MFIFO, 0xC GIF MFIFO + if((spr0->madr & ~psHu32(DMAC_RBSR)) != psHu32(DMAC_RBOR)) SysPrintf("SPR MFIFO Write outside MFIFO area\n"); + hwMFIFOWrite(spr0->madr, (u8*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4); + spr0->madr += spr0->qwc << 4; + spr0->madr = psHu32(DMAC_RBOR) + (spr0->madr & psHu32(DMAC_RBSR)); + } else { + memcpy_fast((u8*)pMem, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4); + Cpu->Clear(spr0->madr, spr0->qwc<<2); + // clear VU mem also! + TestClearVUs(spr0->madr, spr0->qwc << 2); // Wtf is going on here? AFAIK, only VIF should affect VU micromem (cottonvibes) + + spr0->madr += spr0->qwc << 4; + } + spr0->sadr += spr0->qwc << 4; + + + return (spr0->qwc) * BIAS; // bus is 1/2 the ee speed +} + +#define SPR0chain() \ + cycles += _SPR0chain(); \ + spr0->qwc = 0; + + +void _SPR0interleave() { + int qwc = spr0->qwc; + int sqwc = psHu32(DMAC_SQWC) & 0xff; + int tqwc = (psHu32(DMAC_SQWC) >> 16) & 0xff; + int cycles = 0; + u32 *pMem; + if(tqwc == 0) tqwc = qwc; + //SysPrintf("dmaSPR0 interleave\n"); + SPR_LOG("SPR0 interleave size=%d, tqwc=%d, sqwc=%d, addr=%lx sadr=%lx\n", + spr0->qwc, tqwc, sqwc, spr0->madr, spr0->sadr); + + while (qwc > 0) { + spr0->qwc = std::min(tqwc, qwc); qwc-= spr0->qwc; + pMem = (u32*)dmaGetAddr(spr0->madr); + if ((psHu32(DMAC_CTRL) & 0xC) == 0xC || // GIF MFIFO + (psHu32(DMAC_CTRL) & 0xC) == 0x8) { // VIF1 MFIFO + hwMFIFOWrite(spr0->madr, (u8*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc<<4); + } else { + Cpu->Clear(spr0->madr, spr0->qwc<<2); + // clear VU mem also! + TestClearVUs(spr0->madr, spr0->qwc<<2); + memcpy_fast((u8*)pMem, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc<<4); + } + cycles += tqwc * BIAS; + spr0->sadr+= spr0->qwc * 16; + spr0->madr+= (sqwc+spr0->qwc)*16; //qwc-= sqwc; + } + + spr0->qwc = 0; + CPU_INT(8, cycles); +} + +static __forceinline void _dmaSPR0() { + + + if ((psHu32(DMAC_CTRL) & 0x30) == 0x20) { // STS == fromSPR + SysPrintf("SPR0 stall %d\n", (psHu32(DMAC_CTRL)>>6)&3); + } + + + + // Transfer Dn_QWC from SPR to Dn_MADR + + + + if ((spr0->chcr & 0xc) == 0x0) { // Normal Mode + int cycles = 0; + SPR0chain(); + CPU_INT(8, cycles); + + return; + } else if ((spr0->chcr & 0xc) == 0x4) { + int cycles = 0; + u32 *ptag; + int id; + int done = 0; + + if(spr0->qwc > 0){ + SPR0chain(); + CPU_INT(8, cycles); + + return; + } + // Destination Chain Mode + + while (done == 0) { // Loop while Dn_CHCR.STR is 1 + ptag = (u32*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff]; + spr0->sadr+= 16; + + // Transfer dma tag if tte is set +// if (spr0->chcr & 0x40) SPR0transfer(ptag, 4); + + spr0->chcr = ( spr0->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + + id = (ptag[0] >> 28) & 0x7; //ID for DmaChain copied from bit 28 of the tag + spr0->qwc = (u16)ptag[0]; //QWC set to lower 16bits of the tag + spr0->madr = ptag[1]; //MADR = ADDR field + + SPR_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx\n", + ptag[1], ptag[0], spr0->qwc, id, spr0->madr); + + if ((psHu32(DMAC_CTRL) & 0x30) == 0x20) { // STS == fromSPR + SysPrintf("SPR stall control\n"); + } + + switch (id) { + case 0: // CNTS - Transfer QWC following the tag (Stall Control) + if ((psHu32(DMAC_CTRL) & 0x30) == 0x20 ) psHu32(DMAC_STADR) = spr0->madr + (spr0->qwc * 16); //Copy MADR to DMAC_STADR stall addr register + break; + + case 1: // CNT - Transfer QWC following the tag. + break; + + case 7: // End - Transfer QWC following the tag + done = 1; //End Transfer + break; + } + SPR0chain(); + if (spr0->chcr & 0x80 && ptag[0] >> 31) { //Check TIE bit of CHCR and IRQ bit of tag + //SysPrintf("SPR0 TIE\n"); + done = 1; + spr0->qwc = 0; + break; + } + + +/* if (spr0->chcr & 0x80 && ptag[0] >> 31) { + SPR_LOG("dmaIrq Set\n"); + + spr0->chcr&= ~0x100; + hwDmacIrq(8); + return; + }*/ + } + CPU_INT(8, cycles); + } else { // Interleave Mode + _SPR0interleave(); + } + + + +} + +void SPRFROMinterrupt() +{ + spr0->chcr&= ~0x100; + hwDmacIrq(8); +} + +extern void mfifoGIFtransfer(int); +#define gif ((DMACh*)&PS2MEM_HW[0xA000]) +void dmaSPR0() { // fromSPR + int qwc = spr0->qwc; + + SPR_LOG("dmaSPR0 chcr = %lx, madr = %lx, qwc = %lx, sadr = %lx\n", + spr0->chcr, spr0->madr, spr0->qwc, spr0->sadr); + + _dmaSPR0(); + if ((psHu32(DMAC_CTRL) & 0xC) == 0xC) { // GIF MFIFO + if((spr0->madr & ~psHu32(DMAC_RBSR)) != psHu32(DMAC_RBOR)) SysPrintf("GIF MFIFO Write outside MFIFO area\n"); + spr0->madr = psHu32(DMAC_RBOR) + (spr0->madr & psHu32(DMAC_RBSR)); + //SysPrintf("mfifoGIFtransfer %x madr %x, tadr %x\n", gif->chcr, gif->madr, gif->tadr); + mfifoGIFtransfer(qwc); + } else + if ((psHu32(DMAC_CTRL) & 0xC) == 0x8) { // VIF1 MFIFO + if((spr0->madr & ~psHu32(DMAC_RBSR)) != psHu32(DMAC_RBOR)) SysPrintf("VIF MFIFO Write outside MFIFO area\n"); + spr0->madr = psHu32(DMAC_RBOR) + (spr0->madr & psHu32(DMAC_RBSR)); + //SysPrintf("mfifoVIF1transfer %x madr %x, tadr %x\n", vif1ch->chcr, vif1ch->madr, vif1ch->tadr); + //vifqwc+= qwc; + mfifoVIF1transfer(qwc); + } + +} + +__forceinline static void SPR1transfer(u32 *data, int size) { +/* { + int i; + for (i=0; isadr+i*4) & 0x3fff, data[i] ); + } + }*/ + //Cpu->Clear(spr1->sadr, size); // why? + memcpy_fast(&PS2MEM_SCRATCH[spr1->sadr & 0x3fff], (u8*)data, size << 2); + + spr1->sadr+= size << 2; +} + +int _SPR1chain() { + u32 *pMem; + + if (spr1->qwc == 0) return 0; + + pMem = (u32*)dmaGetAddr(spr1->madr); + if (pMem == NULL) return -1; + + SPR1transfer(pMem, spr1->qwc << 2); + spr1->madr+= spr1->qwc << 4; + + return (spr1->qwc) * BIAS; +} + +#define SPR1chain() \ + cycles += _SPR1chain(); \ + spr1->qwc = 0; + + +void _SPR1interleave() { + int qwc = spr1->qwc; + int sqwc = psHu32(DMAC_SQWC) & 0xff; + int tqwc = (psHu32(DMAC_SQWC) >> 16) & 0xff; + int cycles = 0; + u32 *pMem; + if(tqwc == 0) tqwc = qwc; + SPR_LOG("SPR1 interleave size=%d, tqwc=%d, sqwc=%d, addr=%lx sadr=%lx\n", + spr1->qwc, tqwc, sqwc, spr1->madr, spr1->sadr); + + while (qwc > 0) { + spr1->qwc = std::min(tqwc, qwc); qwc-= spr1->qwc; + pMem = (u32*)dmaGetAddr(spr1->madr); + memcpy_fast(&PS2MEM_SCRATCH[spr1->sadr & 0x3fff], (u8*)pMem, spr1->qwc <<4); + spr1->sadr += spr1->qwc * 16; + cycles += spr1->qwc * BIAS; + spr1->madr+= (sqwc + spr1->qwc) * 16; //qwc-= sqwc; + } + + spr1->qwc = 0; + CPU_INT(9, cycles); + +} + +void dmaSPR1() { // toSPR + + +#ifdef SPR_LOG + SPR_LOG("dmaSPR1 chcr = 0x%x, madr = 0x%x, qwc = 0x%x\n" + " tadr = 0x%x, sadr = 0x%x\n", + spr1->chcr, spr1->madr, spr1->qwc, + spr1->tadr, spr1->sadr); +#endif + + + + + if ((spr1->chcr & 0xc) == 0) { // Normal Mode + int cycles = 0; + //if(spr1->qwc == 0 && (spr1->chcr & 0xc) == 1) spr1->qwc = 0xffff; + // Transfer Dn_QWC from Dn_MADR to SPR1 + SPR1chain(); + CPU_INT(9, cycles); + return; + } else if ((spr1->chcr & 0xc) == 0x4){ + int cycles = 0; + u32 *ptag; + int id, done=0; + + + if(spr1->qwc > 0){ + //if(spr1->qwc == 0 && (spr1->chcr & 0xc) == 1) spr1->qwc = 0xffff; + // Transfer Dn_QWC from Dn_MADR to SPR1 + SPR1chain(); + CPU_INT(9, cycles); + return; + } + // Chain Mode + + while (done == 0) { // Loop while Dn_CHCR.STR is 1 + ptag = (u32*)dmaGetAddr(spr1->tadr); //Set memory pointer to TADR + if (ptag == NULL) { //Is ptag empty? + SysPrintf("SPR1 Tag BUSERR\n"); + spr1->chcr = ( spr1->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + psHu32(DMAC_STAT)|= 1<<15; //If yes, set BEIS (BUSERR) in DMAC_STAT register + done = 1; + break; + } + spr1->chcr = ( spr1->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + + id = (ptag[0] >> 28) & 0x7; //ID for DmaChain copied from bit 28 of the tag + spr1->qwc = (u16)ptag[0]; //QWC set to lower 16bits of the tag + spr1->madr = ptag[1]; //MADR = ADDR field + + // Transfer dma tag if tte is set + if (spr1->chcr & 0x40) { + SPR_LOG("SPR TTE: %x_%x\n", ptag[3], ptag[2]); + SPR1transfer(ptag, 4); //Transfer Tag + } + + SPR_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx\n", + ptag[1], ptag[0], spr1->qwc, id, spr1->madr); + + done = hwDmacSrcChain(spr1, id); + SPR1chain(); //Transfers the data set by the switch + + if (spr1->chcr & 0x80 && ptag[0] >> 31) { //Check TIE bit of CHCR and IRQ bit of tag + SPR_LOG("dmaIrq Set\n"); + + //SysPrintf("SPR1 TIE\n"); + spr1->qwc = 0; + break; + } + } + CPU_INT(9, cycles); + } else { // Interleave Mode + _SPR1interleave(); + } + +} + +void SPRTOinterrupt() +{ + spr1->chcr &= ~0x100; + hwDmacIrq(9); +} + diff --git a/pcsx2/SPR.h b/pcsx2/SPR.h new file mode 100644 index 0000000000..e263231fb4 --- /dev/null +++ b/pcsx2/SPR.h @@ -0,0 +1,29 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __SPR_H__ +#define __SPR_H__ + +#include "Common.h" + +void sprInit(); +void dmaSPR0(); +void dmaSPR1(); +void SPRFROMinterrupt(); +void SPRTOinterrupt(); +#endif /* __SPR_H__ */ diff --git a/pcsx2/SafeArray.h b/pcsx2/SafeArray.h new file mode 100644 index 0000000000..821ff44863 --- /dev/null +++ b/pcsx2/SafeArray.h @@ -0,0 +1,254 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __SAFEARRAY_H__ +#define __SAFEARRAY_H__ + +#include "MemcpyFast.h" + +extern void* __fastcall pcsx2_aligned_malloc(size_t size, size_t align); +extern void* __fastcall pcsx2_aligned_realloc(void* handle, size_t size, size_t align); +extern void pcsx2_aligned_free(void* pmem); + +// aligned_malloc: Implement/declare linux equivalents here! +#if !defined(_MSC_VER) && !defined(HAVE_ALIGNED_MALLOC) +# define _aligned_malloc pcsx2_aligned_malloc +# define _aligned_free pcsx2_aligned_free +# define _aligned_realloc pcsx2_aligned_realloc +#endif + +////////////////////////////////////////////////////////////// +// Safe deallocation macros -- always check pointer validity (non-null) +// and set pointer to null on deallocation. + +#define safe_delete( ptr ) \ + if( ptr != NULL ) { \ + delete ptr; \ + ptr = NULL; \ + } + +#define safe_delete_array( ptr ) \ + if( ptr != NULL ) { \ + delete[] ptr; \ + ptr = NULL; \ + } + +#define safe_free( ptr ) \ + if( ptr != NULL ) { \ + free( ptr ); \ + ptr = NULL; \ + } + +#define safe_aligned_free( ptr ) \ + if( ptr != NULL ) { \ + _aligned_free( ptr ); \ + ptr = NULL; \ + } + +#define SafeSysMunmap( ptr, size ) \ + if( ptr != NULL ) { \ + SysMunmap( (uptr)ptr, size ); \ + ptr = NULL; \ + } + + +////////////////////////////////////////////////////////////////// +// Handy little class for allocating a resizable memory block, complete with +// exception-based error handling and automatic cleanup. + +template< typename T > +class MemoryAlloc : public NoncopyableObject +{ +public: + static const int DefaultChunkSize = 0x1000 * sizeof(T); + +public: + const std::string Name; // user-assigned block name + int ChunkSize; + +protected: + T* m_ptr; + int m_size; // size of the allocation of memory + + const static std::string m_str_Unnamed; +protected: + // Internal contructor for use by derrived classes. This allws a derrived class to + // use its own memory allocation (with an aligned memory, for example). + // Throws: + // Exception::OutOfMemory if the allocated_mem pointr is NULL. + explicit MemoryAlloc( const std::string& name, T* allocated_mem, int initSize ) : + Name( name ) + , ChunkSize( DefaultChunkSize ) + , m_ptr( allocated_mem ) + , m_size( initSize ) + { + if( m_ptr == NULL ) + throw Exception::OutOfMemory(); + } + + virtual T* _virtual_realloc( int newsize ) + { + return (T*)realloc( m_ptr, newsize * sizeof(T) ); + } + +public: + virtual ~MemoryAlloc() + { + safe_free( m_ptr ); + } + + explicit MemoryAlloc( const std::string& name="Unnamed" ) : + Name( name ) + , ChunkSize( DefaultChunkSize ) + , m_ptr( NULL ) + , m_size( 0 ) + { + } + + explicit MemoryAlloc( int initialSize, const std::string& name="Unnamed" ) : + Name( name ) + , ChunkSize( DefaultChunkSize ) + , m_ptr( (T*)malloc( initialSize * sizeof(T) ) ) + , m_size( initialSize ) + { + if( m_ptr == NULL ) + throw Exception::OutOfMemory(); + } + + // Returns the size of the memory allocation, as according to the array type. + int GetLength() const { return m_size; } + // Returns the size of the memory allocation in bytes. + int GetSizeInBytes() const { return m_size * sizeof(T); } + + // Ensures that the allocation is large enough to fit data of the + // amount requested. The memory allocation is not resized smaller. + void MakeRoomFor( int blockSize ) + { + std::string temp; + + if( blockSize > m_size ) + { + const uint newalloc = blockSize + ChunkSize; + m_ptr = _virtual_realloc( newalloc ); + if( m_ptr == NULL ) + { + throw Exception::OutOfMemory( + "Out-of-memory on block re-allocation. " + "Old size: " + to_string( m_size ) + " bytes, " + "New size: " + to_string( newalloc ) + " bytes" + ); + } + m_size = newalloc; + } + } + + // Gets a pointer to the requested allocation index. + // DevBuilds : Throws std::out_of_range() if the index is invalid. + T *GetPtr( uint idx=0 ) { return _getPtr( idx ); } + const T *GetPtr( uint idx=0 ) const { return _getPtr( idx ); } + + // Gets an element of this memory allocation much as if it were an array. + // DevBuilds : Throws std::out_of_range() if the index is invalid. + T& operator[]( int idx ) { return *_getPtr( (uint)idx ); } + const T& operator[]( int idx ) const { return *_getPtr( (uint)idx ); } + + virtual MemoryAlloc* Clone() const + { + MemoryAlloc* retval = new MemoryAlloc( m_size ); + memcpy_fast( retval->GetPtr(), m_ptr, sizeof(T) * m_size ); + return retval; + } + +protected: + // A safe array index fetcher. Throws an exception if the array index + // is outside the bounds of the array. + // Performance Considerations: This function adds quite a bit of overhead + // to array indexing and thus should be done infrequently if used in + // time-critical situations. Indead of using it from inside loops, cache + // the pointer into a local variable and use stad (unsafe) C indexes. + T* _getPtr( uint i ) const + { +#ifdef PCSX2_DEVBUILD + if( i >= (uint)m_size ) + { + throw Exception::IndexBoundsFault( + "Index out of bounds on MemoryAlloc: " + Name + + " (index=" + to_string(i) + + ", size=" + to_string(m_size) + ")" + ); + } +#endif + return &m_ptr[i]; + } + +}; + +////////////////////////////////////////////////////////////////// +// Handy little class for allocating a resizable memory block, complete with +// exception-based error handling and automatic cleanup. +// This one supports aligned data allocations too! + +template< typename T, uint Alignment > +class SafeAlignedArray : public MemoryAlloc +{ +protected: + T* _virtual_realloc( int newsize ) + { + return (T*)_aligned_realloc( this->m_ptr, newsize * sizeof(T), Alignment ); + } + + // Appends "(align: xx)" to the name of the allocation in devel builds. + // Maybe useful,maybe not... no harm in atatching it. :D + string _getName( const string& src ) + { +#ifdef PCSX2_DEVBUILD + return src + "(align:" + to_string(Alignment) + ")"; +#endif + return src; + } + +public: + virtual ~SafeAlignedArray() + { + safe_aligned_free( this->m_ptr ); + // mptr is set to null, so the parent class's destructor won't re-free it. + } + + explicit SafeAlignedArray( const std::string& name="Unnamed" ) : + MemoryAlloc::MemoryAlloc( name ) + { + } + + explicit SafeAlignedArray( int initialSize, const std::string& name="Unnamed" ) : + MemoryAlloc::MemoryAlloc( + _getName(name), + (T*)_aligned_malloc( initialSize * sizeof(T), Alignment ), + initialSize + ) + { + } + + virtual SafeAlignedArray* Clone() const + { + SafeAlignedArray* retval = new SafeAlignedArray( this->m_size ); + memcpy_fast( retval->GetPtr(), this->m_ptr, sizeof(T) * this->m_size ); + return retval; + } +}; + +#endif diff --git a/pcsx2/SamplProf.h b/pcsx2/SamplProf.h new file mode 100644 index 0000000000..d34988d7c7 --- /dev/null +++ b/pcsx2/SamplProf.h @@ -0,0 +1,32 @@ +#ifndef _SAMPLPROF_H_ +#define _SAMPLPROF_H_ + +#include "Common.h" + +// The profiler does not have a Linux version yet. +// So for now we turn it into duds for non-Win32 platforms. + +#if !defined( _DEBUG ) && defined( WIN32 ) + +void ProfilerInit(); +void ProfilerTerm(); +void ProfilerSetEnabled(bool Enabled); +void ProfilerRegisterSource(const char* Name, const void* buff, u32 sz); +void ProfilerRegisterSource(const char* Name, const void* function); +void ProfilerTerminateSource( const char* Name ); + +#else + +// Disables the profiler in Debug & Linux builds. +// Profiling info in debug builds isn't much use anyway and the console +// popups are annoying when you're trying to trace debug logs and stuff. + +#define ProfilerInit() (void)0 +#define ProfilerTerm() (void)0 +#define ProfilerSetEnabled 0&& +#define ProfilerRegisterSource 0&& +#define ProfilerTerminateSource 0&& + +#endif + +#endif diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp new file mode 100644 index 0000000000..52317fc3e9 --- /dev/null +++ b/pcsx2/SaveState.cpp @@ -0,0 +1,370 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "PsxCommon.h" +#include "SaveState.h" + +#include "CDVDisodrv.h" +#include "VUmicro.h" +#include "VU.h" +#include "iCore.h" +#include "iVUzerorec.h" + +#include "GS.h" +#include "COP0.h" +#include "Cache.h" + + +using namespace R5900; + +extern int g_psxWriteOk; +extern void recResetEE(); +extern void recResetIOP(); + +// STATES + +static void PreLoadPrep() +{ + SysResetExecutionState(); +} + +static void PostLoadPrep() +{ + memzero_obj(pCache); +// WriteCP0Status(cpuRegs.CP0.n.Status.val); + for(int i=0; i<48; i++) MapTLB(i); +} + +void SaveState::GetFilename( string& dest, int slot ) +{ + string elfcrcText; + ssprintf( elfcrcText, "%8.8X.%3.3d", ElfCRC, slot ); + Path::Combine( dest, SSTATES_DIR, elfcrcText ); +} + +string SaveState::GetFilename( int slot ) +{ + string elfcrcText, dest; + GetFilename( dest, slot ); + return dest; +} + + +SaveState::SaveState( const char* msg, const string& destination ) : m_version( g_SaveVersion ) +{ + Console::WriteLn( "%s %hs", params msg, &destination ); +} + +s32 CALLBACK gsSafeFreeze( int mode, freezeData *data ) +{ + if( mtgsThread != NULL ) + { + if( mode == 2 ) + return GSfreeze( 2, data ); + + // have to call in thread, otherwise weird stuff will start happening + mtgsThread->SendPointerPacket( GS_RINGTYPE_FREEZE, mode, data ); + mtgsWaitGS(); + return 0; + } + else + { + // Single threaded... + return GSfreeze( mode, data ); + } +} + +void SaveState::FreezeAll() +{ + if( IsLoading() ) + PreLoadPrep(); + + FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory + FreezeMem(PS2MEM_ROM, Ps2MemSize::Rom); // 4 mb rom memory + FreezeMem(PS2MEM_ROM1, Ps2MemSize::Rom1); // 256kb rom1 memory + FreezeMem(PS2MEM_SCRATCH, Ps2MemSize::Scratch); // scratch pad + FreezeMem(PS2MEM_HW, Ps2MemSize::Hardware); // hardware memory + + Freeze(cpuRegs); // cpu regs + COP0 + Freeze(psxRegs); // iop regs + Freeze(fpuRegs); // fpu regs + Freeze(tlb); // tlbs + + Freeze(EEsCycle); + Freeze(EEoCycle); + Freeze(psxRegs.cycle); // used to be IOPoCycle. This retains compatibility. + Freeze(g_nextBranchCycle); + Freeze(g_psxNextBranchCycle); + + Freeze(s_iLastCOP0Cycle); + if( m_version >= 0x7a30000e ) + Freeze(s_iLastPERFCycle); + + Freeze(g_psxWriteOk); + + //hope didn't forgot any cpu.... + + rcntFreeze(); + gsFreeze(); + vuMicroFreeze(); + vif0Freeze(); + vif1Freeze(); + sifFreeze(); + ipuFreeze(); + + // iop now + FreezeMem(psxM, Ps2MemSize::IopRam); // 2 MB main memory + FreezeMem(psxH, Ps2MemSize::IopHardware); // hardware memory + //FreezeMem(psxS, 0x00010000); // sif memory + + sioFreeze(); + cdrFreeze(); + cdvdFreeze(); + psxRcntFreeze(); + sio2Freeze(); + + FreezePlugin( "GS", gsSafeFreeze ); + FreezePlugin( "SPU2", SPU2freeze ); + FreezePlugin( "DEV9", DEV9freeze ); + FreezePlugin( "USB", USBfreeze ); + + if( IsLoading() ) + PostLoadPrep(); +} + +// this function is yet incomplete. Version numbers hare still < 0x12 so it won't be run. +// (which is good because it won't work :P) +void SaveState::_testCdvdCrc() +{ + /*if( GetVersion() < 0x0012 ) return; + + u32 thiscrc = ElfCRC; + Freeze( thiscrc ); + if( thiscrc != ElfCRC ) + throw Exception::StateCrcMismatch( thiscrc, ElfCRC );*/ +} + +///////////////////////////////////////////////////////////////////////////// +// gzipped to/from disk state saves implementation + +gzBaseStateInfo::gzBaseStateInfo( const char* msg, const string& filename ) : + SaveState( msg, filename ) +, m_filename( filename ) +, m_file( NULL ) +{ +} + +gzBaseStateInfo::~gzBaseStateInfo() +{ + if( m_file != NULL ) + { + gzclose( m_file ); + m_file = NULL; + } +} + + +gzSavingState::gzSavingState( const string& filename ) : + gzBaseStateInfo( _("Saving state to: "), filename ) +{ + m_file = gzopen(filename.c_str(), "wb"); + if( m_file == NULL ) + throw Exception::FileNotFound(); + + Freeze( m_version ); +} + + +gzLoadingState::gzLoadingState( const string& filename ) : + gzBaseStateInfo( _("Loading state from: "), filename ) +{ + m_file = gzopen(filename.c_str(), "rb"); + if( m_file == NULL ) + throw Exception::FileNotFound(); + + gzread( m_file, &m_version, 4 ); + + if( m_version != g_SaveVersion ) + { + if( ( m_version >> 16 ) == 0x7a30 ) + { + Console::Error( + "Savestate load aborted:\n" + "\tVTLB edition cannot safely load savestates created by the VM edition." ); + throw Exception::UnsupportedStateVersion( m_version ); + } + } + + _testCdvdCrc(); +} + +gzLoadingState::~gzLoadingState() { } + + +void gzSavingState::FreezeMem( void* data, int size ) +{ + gzwrite( m_file, data, size ); +} + +void gzLoadingState::FreezeMem( void* data, int size ) +{ + gzread( m_file, data, size ); + if( gzeof( m_file ) ) + throw Exception::BadSavedState( m_filename ); +} + +void gzSavingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) ) +{ + Console::WriteLn( "\tSaving %s", params name ); + freezeData fP = { 0, NULL }; + + if (freezer(FREEZE_SIZE, &fP) == -1) + throw Exception::FreezePluginFailure( name, "saving" ); + + gzwrite(m_file, &fP.size, sizeof(fP.size)); + if( fP.size == 0 ) return; + + fP.data = (s8*)malloc(fP.size); + if (fP.data == NULL) + throw Exception::OutOfMemory(); + + if(freezer(FREEZE_SAVE, &fP) == -1) + throw Exception::FreezePluginFailure( name, "saving" ); + + if (fP.size) + { + gzwrite(m_file, fP.data, fP.size); + free(fP.data); + } +} + +void gzLoadingState::FreezePlugin( const char* name, s32 (CALLBACK *freezer)(int mode, freezeData *data) ) +{ + freezeData fP = { 0, NULL }; + Console::WriteLn( "\tLoading %s", params name ); + + gzread(m_file, &fP.size, sizeof(fP.size)); + if( fP.size == 0 ) return; + + fP.data = (s8*)malloc(fP.size); + if (fP.data == NULL) + throw Exception::OutOfMemory(); + gzread(m_file, fP.data, fP.size); + + if( gzeof( m_file ) ) + throw Exception::BadSavedState( m_filename ); + + if(freezer(FREEZE_LOAD, &fP) == -1) + throw Exception::FreezePluginFailure( name, "loading" ); + + if (fP.size) free(fP.data); +} + +////////////////////////////////////////////////////////////////////////////////// +// uncompressed to/from memory state saves implementation + +memBaseStateInfo::memBaseStateInfo( MemoryAlloc& memblock, const char* msg ) : + SaveState( msg, "Memory" ) +, m_memory( memblock ) +, m_idx( 0 ) +{ + // Always clear the MTGS thread state. + mtgsWaitGS(); +} + +memSavingState::memSavingState( MemoryAlloc& save_to ) : memBaseStateInfo( save_to, _("Saving state to: ") ) +{ + save_to.ChunkSize = ReallocThreshold; + save_to.MakeRoomFor( MemoryBaseAllocSize ); +} + +// Saving of state data to a memory buffer +void memSavingState::FreezeMem( void* data, int size ) +{ + const int end = m_idx+size; + m_memory.MakeRoomFor( end ); + + u8* dest = (u8*)m_memory.GetPtr(); + const u8* src = (u8*)data; + + for( ; m_idx& load_from ) : + memBaseStateInfo( load_from, _("Loading state from: ") ) +{ +} + +memLoadingState::~memLoadingState() { } + +// Loading of state data from a memory buffer... +void memLoadingState::FreezeMem( void* data, int size ) +{ + const int end = m_idx+size; + const u8* src = (u8*)m_memory.GetPtr(); + u8* dest = (u8*)data; + + for( ; m_idx m_memory.GetSizeInBytes() ) + { + assert(0); + throw Exception::BadSavedState( "memory" ); + } + + fP.data = ((s8*)m_memory.GetPtr()) + m_idx; + if(freezer(FREEZE_LOAD, &fP) == -1) + throw Exception::FreezePluginFailure( name, "loading" ); + + m_idx += fP.size; +} diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h new file mode 100644 index 0000000000..a85ee83955 --- /dev/null +++ b/pcsx2/SaveState.h @@ -0,0 +1,205 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef _SAVESTATE_H_ +#define _SAVESTATE_H_ + +#include + +// This shouldn't break Win compiles, but it does. +#ifdef __LINUX__ +#include "PS2Edefs.h" +#endif + +// Savestate Versioning! +// If you make changes to the savestate version, please increment the value below. + +static const u32 g_SaveVersion = 0x8b400002; + +// this function is meant to be sued in the place of GSfreeze, and provides a safe layer +// between the GS saving function and the MTGS's needs. :) +extern s32 CALLBACK gsSafeFreeze( int mode, freezeData *data ); + +// This class provides the base API for both loading and saving savestates. +// Normally you'll want to use one of the four "functional" derrived classes rather +// than this class directly: gzLoadingState, gzSavingState (gzipped disk-saved +// states), and memLoadingState, memSavingState (uncompressed memory states). +class SaveState +{ +protected: + u32 m_version; // version of the savestate being loaded. + +public: + SaveState( const char* msg, const string& destination ); + virtual ~SaveState() { } + + static void GetFilename( string& dest, int slot ); + static string GetFilename( int slot ); + + // Gets the version of savestate that this object is acting on. + // The version refers to the low 16 bits only (high 16 bits classifies Pcsx2 build types) + u32 GetVersion() const + { + // HACK! Matches the vTLB build versions with VM + return (m_version & 0xffff) + 0x10; + } + + // Loads or saves the entire emulation state. + // Note: The Cpu state must be reset, and plugins *open*, prior to Defrosting + // (loading) a state! + void FreezeAll(); + + // Loads or saves an arbitrary data type. Usable on atomic types, structs, and arrays. + // For dynamically allocated pointers use FreezeMem instead. + template + void Freeze( T& data ) + { + FreezeMem( &data, sizeof( T ) ); + } + + // FreezeLegacy can be used to load structures short of their full size, which is + // useful for loading structures that have had new stuff added since a previous version. + template + void FreezeLegacy( T& data, int sizeOfNewStuff ) + { + FreezeMem( &data, sizeof( T ) - sizeOfNewStuff ); + } + + // Loads or saves a plugin. Plugin name is for console logging purposes. + virtual void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )=0; + + // Loads or saves a memory block. + virtual void FreezeMem( void* data, int size )=0; + + // Returns true if this object is a StateSaving type object. + virtual bool IsSaving() const=0; + + // Returns true if this object is a StateLoading type object. + bool IsLoading() const { return !IsSaving(); } + + // note: gsFreeze() needs to be public because of the GSState recorder. + +public: + virtual void gsFreeze(); + +protected: + + // Used internally by constructors to check the cdvd's crc against the CRC of the savestate. + // This allows for proper exception handling of changed CDs on-the-fly. + void _testCdvdCrc(); + + + // Load/Save functions for the various components of our glorious emulator! + + void rcntFreeze(); + void vuMicroFreeze(); + void vif0Freeze(); + void vif1Freeze(); + void sifFreeze(); + void ipuFreeze(); + + void sioFreeze(); + void cdrFreeze(); + void cdvdFreeze(); + void psxRcntFreeze(); + void sio2Freeze(); + + // called by gsFreeze automatically. + void mtgsFreeze(); + +}; + +///////////////////////////////////////////////////////////////////////////////// +// Class Declarations for Savestates using zlib + +class gzBaseStateInfo : public SaveState +{ +protected: + const string m_filename; + gzFile m_file; // used for reading/writing disk saves + +public: + gzBaseStateInfo( const char* msg, const string& filename ); + + virtual ~gzBaseStateInfo(); +}; + +class gzSavingState : public gzBaseStateInfo +{ +public: + virtual ~gzSavingState() {} + gzSavingState( const string& filename ) ; + void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) ); + void FreezeMem( void* data, int size ); + bool IsSaving() const { return true; } +}; + +class gzLoadingState : public gzBaseStateInfo +{ +public: + virtual ~gzLoadingState(); + gzLoadingState( const string& filename ); + + void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) ); + void FreezeMem( void* data, int size ); + bool IsSaving() const { return false; } + bool Finished() const { return !!gzeof( m_file ); } +}; + +////////////////////////////////////////////////////////////////////////////////// + +class memBaseStateInfo : public SaveState +{ +protected: + MemoryAlloc& m_memory; + int m_idx; // current read/write index of the allocation + +public: + virtual ~memBaseStateInfo() { } + memBaseStateInfo( MemoryAlloc& memblock, const char* msg ); +}; + +class memSavingState : public memBaseStateInfo +{ +protected: + static const int ReallocThreshold = 0x200000; // 256k reallocation block size. + static const int MemoryBaseAllocSize = 0x02a00000; // 42 meg base alloc + +public: + virtual ~memSavingState() { } + memSavingState( MemoryAlloc& save_to ); + + void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) ); + // Saving of state data to a memory buffer + void FreezeMem( void* data, int size ); + bool IsSaving() const { return true; } +}; + +class memLoadingState : public memBaseStateInfo +{ +public: + virtual ~memLoadingState(); + memLoadingState(MemoryAlloc& load_from ); + + void FreezePlugin( const char* name, s32(CALLBACK *freezer)(int mode, freezeData *data) ); + // Loading of state data from a memory buffer... + void FreezeMem( void* data, int size ); + bool IsSaving() const { return false; } +}; + +#endif \ No newline at end of file diff --git a/pcsx2/Sif.cpp b/pcsx2/Sif.cpp new file mode 100644 index 0000000000..4a5cee18e7 --- /dev/null +++ b/pcsx2/Sif.cpp @@ -0,0 +1,608 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Sif.h" +#include "IopHw.h" +#include "Sifcmd.h" + +using namespace std; + +#define sif0dma ((DMACh*)&PS2MEM_HW[0xc000]) +#define sif1dma ((DMACh*)&PS2MEM_HW[0xc400]) +#define sif2dma ((DMACh*)&PS2MEM_HW[0xc800]) + +int eeSifTransfer; + +DMACh *sif0ch; +DMACh *sif1ch; +DMACh *sif2ch; + + +#define FIFO_SIF0_W 128 +#define FIFO_SIF1_W 128 + +struct _sif0{ + u32 fifoData[FIFO_SIF0_W]; + int fifoReadPos; + int fifoWritePos; + int fifoSize; + int chain; + int end; + int tagMode; + int counter; + struct sifData sifData; +}; + +struct _sif1 { + u32 fifoData[FIFO_SIF1_W]; + int fifoReadPos; + int fifoWritePos; + int fifoSize; + int chain; + int end; + int tagMode; + int counter; +}; + +static _sif0 sif0; +static _sif1 sif1; + +int eesifbusy[2] = { 0, 0 }; +extern int iopsifbusy[2]; + +void sifInit() +{ + memzero_obj(sif0); + memzero_obj(sif1); + memzero_obj(eesifbusy); + memzero_obj(iopsifbusy); +} + +static __forceinline void SIF0write(u32 *from, int words) +{ + /*if(FIFO_SIF0_W < (words+sif0.fifoWritePos)) {*/ + + const int wP0 = min((FIFO_SIF0_W-sif0.fifoWritePos),words); + const int wP1 = words - wP0; + + memcpy(&sif0.fifoData[sif0.fifoWritePos], from, wP0 << 2); + memcpy(&sif0.fifoData[0], &from[wP0], wP1 << 2); + + sif0.fifoWritePos = (sif0.fifoWritePos + words) & (FIFO_SIF0_W-1); + /*} + else + { + memcpy_fast(&sif0.fifoData[sif0.fifoWritePos], from, words << 2); + sif0.fifoWritePos += words; + }*/ + + sif0.fifoSize += words; + SIF_LOG(" SIF0 + %d = %d (pos=%d)\n", words, sif0.fifoSize, sif0.fifoWritePos); +} + +static __forceinline void SIF0read(u32 *to, int words) +{ + /*if(FIFO_SIF0_W < (words+sif0.fifoReadPos)) + {*/ + const int wP0 = min((FIFO_SIF0_W-sif0.fifoReadPos),words); + const int wP1 = words - wP0; + + memcpy(to, &sif0.fifoData[sif0.fifoReadPos], wP0 << 2); + memcpy(&to[wP0], &sif0.fifoData[0], wP1 << 2); + + sif0.fifoReadPos = (sif0.fifoReadPos + words) & (FIFO_SIF0_W-1); + /*} + else + { + memcpy_fast(to, &sif0.fifoData[sif0.fifoReadPos], words << 2); + sif0.fifoReadPos += words; + }*/ + + sif0.fifoSize -= words; + SIF_LOG(" SIF0 - %d = %d (pos=%d)\n", words, sif0.fifoSize, sif0.fifoReadPos); +} + +__forceinline void SIF1write(u32 *from, int words) +{ + /*if(FIFO_SIF1_W < (words+sif1.fifoWritePos)) + {*/ + const int wP0 = min((FIFO_SIF1_W-sif1.fifoWritePos),words); + const int wP1 = words - wP0; + + memcpy(&sif1.fifoData[sif1.fifoWritePos], from, wP0 << 2); + memcpy(&sif1.fifoData[0], &from[wP0], wP1 << 2); + + sif1.fifoWritePos = (sif1.fifoWritePos + words) & (FIFO_SIF1_W-1); + /*} + else + { + memcpy_fast(&sif1.fifoData[sif1.fifoWritePos], from, words << 2); + sif1.fifoWritePos += words; + }*/ + + sif1.fifoSize += words; + SIF_LOG(" SIF1 + %d = %d (pos=%d)\n", words, sif1.fifoSize, sif1.fifoWritePos); +} + +static __forceinline void SIF1read(u32 *to, int words) +{ + /*if(FIFO_SIF1_W < (words+sif1.fifoReadPos)) + {*/ + const int wP0 = min((FIFO_SIF1_W-sif1.fifoReadPos),words); + const int wP1 = words - wP0; + + memcpy(to, &sif1.fifoData[sif1.fifoReadPos], wP0 << 2); + memcpy(&to[wP0], &sif1.fifoData[0], wP1 << 2); + + sif1.fifoReadPos = (sif1.fifoReadPos + words) & (FIFO_SIF1_W-1); + /*} + else + { + memcpy_fast(to, &sif1.fifoData[sif1.fifoReadPos], words << 2); + sif1.fifoReadPos += words; + }*/ + + sif1.fifoSize -= words; + SIF_LOG(" SIF1 - %d = %d (pos=%d)\n", words, sif1.fifoSize, sif1.fifoReadPos); +} + +__forceinline void SIF0Dma() +{ + u32 *ptag; + int notDone = 1; + int cycles = 0, psxCycles = 0; + + SIF_LOG("SIF0 DMA start...\n"); + + do + { + + /*if ((psHu32(DMAC_CTRL) & 0xC0)) { + SysPrintf("DMA Stall Control %x\n",(psHu32(DMAC_CTRL) & 0xC0)); + }*/ + if(iopsifbusy[0] == 1) // If EE SIF0 is enabled + { + //int size = sif0.counter; //HW_DMA9_BCR >> 16; + + if(sif0.counter == 0) // If there's no more to transfer + { + // Note.. add normal mode here + if (sif0.sifData.data & 0xC0000000) // If NORMAL mode or end of CHAIN, or interrupt then stop DMA + { + SIF_LOG(" IOP SIF Stopped\n"); + + // Stop & signal interrupts on IOP + //HW_DMA9_CHCR &= ~0x01000000; //reset TR flag + //psxDmaInterrupt2(2); + iopsifbusy[0] = 0; + PSX_INT(IopEvt_SIF0, psxCycles); + // iop is 1/8th the clock rate of the EE and psxcycles is in words (not quadwords) + // So when we're all done, the equation looks like thus: + //PSX_INT(IopEvt_SIF0, ( ( psxCycles*BIAS ) / 4 ) / 8); + + //hwIntcIrq(INTC_SBUS); + sif0.sifData.data = 0; + notDone = 0; + } + else // Chain mode + { + // Process DMA tag at HW_DMA9_TADR + sif0.sifData = *(struct sifData *)PSXM(HW_DMA9_TADR); + + sif0.sifData.words = (sif0.sifData.words + 3) & 0xfffffffc; // Round up to nearest 4. + + SIF0write((u32*)PSXM(HW_DMA9_TADR+8), 4); + + //psxCycles += 2; + + HW_DMA9_MADR = sif0.sifData.data & 0xFFFFFF; + HW_DMA9_TADR += 16; ///HW_DMA9_MADR + 16 + sif0.sifData.words << 2; + //HW_DMA9_BCR = (sif0.sifData.words << 16) | 1; + sif0.counter = sif0.sifData.words & 0xFFFFFF; + notDone = 1; + + SIF_LOG(" SIF0 Tag: madr=%lx, tadr=%lx, counter=%lx (%08X_%08X)\n", HW_DMA9_MADR, HW_DMA9_TADR, sif0.counter, sif0.sifData.words, sif0.sifData.data); + if(sif0.sifData.data & 0x40000000) + SIF_LOG(" END\n"); + else + SIF_LOG(" CNT %08X, %08X\n", sif0.sifData.data, sif0.sifData.words); + } + } + else // There's some data ready to transfer into the fifo.. + { + int wTransfer = min(sif0.counter, FIFO_SIF0_W-sif0.fifoSize); // HW_DMA9_BCR >> 16; + + SIF_LOG("+++++++++++ %lX of %lX\n", wTransfer, sif0.counter /*(HW_DMA9_BCR >> 16)*/ ); + + SIF0write((u32*)PSXM(HW_DMA9_MADR), wTransfer); + HW_DMA9_MADR += wTransfer << 2; + //HW_DMA9_BCR = (HW_DMA9_BCR & 0xFFFF) | (((HW_DMA9_BCR >> 16) - wTransfer)<<16); + psxCycles += (wTransfer / 4) * BIAS; // fixme : should be / 16 + //psxCycles += wTransfer; + sif0.counter -= wTransfer; + + //notDone = 1; + } + } + + if(eesifbusy[0] == 1) // If EE SIF enabled and there's something to transfer + { + int size = sif0dma->qwc; + if ((psHu32(DMAC_CTRL) & 0x30) == 0x10) { // STS == fromSIF0 + SIF_LOG("SIF0 stall control\n"); + } + if(size > 0) // If we're reading something continue to do so + { + /*if(sif0.fifoSize > 0) + {*/ + int readSize = min(size, (sif0.fifoSize>>2)); + + //SIF_LOG(" EE SIF doing transfer %04Xqw to %08X\n", readSize, sif0dma->madr); + SIF_LOG("----------- %lX of %lX\n", readSize << 2, size << 2 ); + + _dmaGetAddr(sif0dma, ptag, sif0dma->madr, 5); + + SIF0read((u32*)ptag, readSize<<2); +// { +// int i; +// for(i = 0; i < readSize; ++i) { +// SIF_LOG("EE SIF0 read madr: %x %x %x %x\n", ((u32*)ptag)[4*i+0], ((u32*)ptag)[4*i+1], ((u32*)ptag)[4*i+2], ((u32*)ptag)[4*i+3]); +// } +// } + + Cpu->Clear(sif0dma->madr, readSize*4); + + cycles += readSize * BIAS; // fixme : BIAS is factored in below + //cycles += readSize; + sif0dma->qwc -= readSize; + sif0dma->madr += readSize << 4; + + //notDone = 1; + //} + } + + if(sif0dma->qwc == 0) + { + if((sif0dma->chcr & 0x80000080) == 0x80000080) // Stop on tag IRQ + { + // Tag interrupt + SIF_LOG(" EE SIF interrupt\n"); + + //sif0dma->chcr &= ~0x100; + eesifbusy[0] = 0; + CPU_INT(5, cycles*BIAS); + //hwDmacIrq(5); + notDone = 0; + } + else if(sif0.end) // Stop on tag END + { + // End tag. + SIF_LOG(" EE SIF end\n"); + + //sif0dma->chcr &= ~0x100; + //hwDmacIrq(5); + eesifbusy[0] = 0; + CPU_INT(5, cycles*BIAS); + notDone = 0; + } + else if(sif0.fifoSize >= 4) // Read a tag + { + static PCSX2_ALIGNED16(u32 tag[4]); + SIF0read((u32*)&tag[0], 4); // Tag + SIF_LOG(" EE SIF read tag: %x %x %x %x\n", tag[0], tag[1], tag[2], tag[3]); + + sif0dma->qwc = (u16)tag[0]; + sif0dma->madr = tag[1]; + sif0dma->chcr = (sif0dma->chcr & 0xffff) | (tag[0] & 0xffff0000); + + /*if ((sif0dma->chcr & 0x80) && (tag[0] >> 31)) { + SysPrintf("SIF0 TIE\n"); + }*/ + SIF_LOG(" EE SIF dest chain tag madr:%08X qwc:%04X id:%X irq:%d(%08X_%08X)\n", sif0dma->madr, sif0dma->qwc, (tag[0]>>28)&3, (tag[0]>>31)&1, tag[1], tag[0]); + + if ((psHu32(DMAC_CTRL) & 0x30) != 0 && ((tag[0]>>28)&3) == 0) + psHu32(DMAC_STADR) = sif0dma->madr + (sif0dma->qwc * 16); + notDone = 1; + sif0.chain = 1; + if(tag[0] & 0x40000000) + sif0.end = 1; + + } + } + } + }while(notDone); +} + +__forceinline void SIF1Dma() +{ + int id; + u32 *ptag; + int notDone; + int cycles = 0, psxCycles = 0; + notDone = 1; + do + { + if(eesifbusy[1] == 1) // If EE SIF1 is enabled + { + + if ((psHu32(DMAC_CTRL) & 0xC0) == 0xC0) + SIF_LOG("SIF1 stall control\n"); // STS == fromSIF1 + + if(sif1dma->qwc == 0) // If there's no more to transfer + { + if ((sif1dma->chcr & 0xc) == 0 || sif1.end) // If NORMAL mode or end of CHAIN then stop DMA + { + // Stop & signal interrupts on EE + //sif1dma->chcr &= ~0x100; + //hwDmacIrq(6); + SIF_LOG("EE SIF1 End %x\n", sif1.end); + eesifbusy[1] = 0; + notDone = 0; + CPU_INT(6, cycles*BIAS); + sif1.chain = 0; + sif1.end = 0; + } + else // Chain mode + { + // Process DMA tag at sif1dma->tadr + notDone = 1; + _dmaGetAddr(sif1dma, ptag, sif1dma->tadr, 6); + sif1dma->chcr = ( sif1dma->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); // Copy the tag + sif1dma->qwc = (u16)ptag[0]; + + if (sif1dma->chcr & 0x40) { + SysPrintf("SIF1 TTE\n"); + SIF1write(ptag+2, 2); + } + + sif1.chain = 1; + id = (ptag[0] >> 28) & 0x7; + + switch(id) + { + case 0: // refe + SIF_LOG(" REFE %08X\n", ptag[1]); + sif1.end = 1; + sif1dma->madr = ptag[1]; + sif1dma->tadr += 16; + break; + + case 1: // cnt + SIF_LOG(" CNT\n"); + sif1dma->madr = sif1dma->tadr + 16; + sif1dma->tadr = sif1dma->madr + (sif1dma->qwc << 4); + break; + + case 2: // next + SIF_LOG(" NEXT %08X\n", ptag[1]); + sif1dma->madr = sif1dma->tadr + 16; + sif1dma->tadr = ptag[1]; + break; + + case 3: // ref + case 4: // refs + SIF_LOG(" REF %08X\n", ptag[1]); + sif1dma->madr = ptag[1]; + sif1dma->tadr += 16; + break; + + case 7: // end + SIF_LOG(" END\n"); + sif1.end = 1; + sif1dma->madr = sif1dma->tadr + 16; + sif1dma->tadr = sif1dma->madr + (sif1dma->qwc << 4); + break; + + default: + SysPrintf("Bad addr1 source chain\n"); + } + if ((sif1dma->chcr & 0x80) && (ptag[0] >> 31)) { + SysPrintf("SIF1 TIE\n"); + sif1.end = 1; + } + } + } + else // There's some data ready to transfer into the fifo.. + { + int qwTransfer = sif1dma->qwc; + u32 *data; + + //notDone = 1; + _dmaGetAddr(sif1dma, data, sif1dma->madr, 6); + + if(qwTransfer > (FIFO_SIF1_W-sif1.fifoSize)/4) // Copy part of sif1dma into FIFO + qwTransfer = (FIFO_SIF1_W-sif1.fifoSize)/4; + + SIF1write(data, qwTransfer << 2); + + sif1dma->madr += qwTransfer << 4; + cycles += qwTransfer * BIAS; // fixme : BIAS is factored in above + //cycles += qwTransfer; // 1 cycle per quadword (BIAS is factored later) + sif1dma->qwc -= qwTransfer; + } + } + + if(iopsifbusy[1] == 1) // If IOP SIF enabled and there's something to transfer + { + int size = sif1.counter; + + if(size > 0) // If we're reading something continue to do so + { + /*if(sif1.fifoSize > 0) + {*/ + int readSize = size; + + if(readSize > sif1.fifoSize) readSize = sif1.fifoSize; + + SIF_LOG(" IOP SIF doing transfer %04X to %08X\n", readSize, HW_DMA10_MADR); + + SIF1read((u32*)PSXM(HW_DMA10_MADR), readSize); + psxCpu->Clear(HW_DMA10_MADR, readSize); + psxCycles += readSize / 4; // fixme: should be / 16 + sif1.counter = size-readSize; + HW_DMA10_MADR += readSize << 2; + //notDone = 1; + //} + } + + if(sif1.counter <= 0) + { + if(sif1.tagMode & 0x80) // Stop on tag IRQ + { + // Tag interrupt + SIF_LOG(" IOP SIF interrupt\n"); + //HW_DMA10_CHCR &= ~0x01000000; //reset TR flag + //psxDmaInterrupt2(3); + iopsifbusy[1] = 0; + PSX_INT(IopEvt_SIF1, psxCycles); + //hwIntcIrq(INTC_SBUS); + sif1.tagMode = 0; + notDone = 0; + } + else if(sif1.tagMode & 0x40) // Stop on tag END + { + // End tag. + SIF_LOG(" IOP SIF end\n"); + //HW_DMA10_CHCR &= ~0x01000000; //reset TR flag + //psxDmaInterrupt2(3); + iopsifbusy[1] = 0; + PSX_INT(IopEvt_SIF1, psxCycles); + //hwIntcIrq(INTC_SBUS); + sif1.tagMode = 0; + notDone = 0; + } + else if(sif1.fifoSize >= 4) // Read a tag + { + struct sifData d; + SIF1read((u32*)&d, 4); + SIF_LOG(" IOP SIF dest chain tag madr:%08X wc:%04X id:%X irq:%d\n", d.data & 0xffffff, d.words, (d.data>>28)&7, (d.data>>31)&1); + HW_DMA10_MADR = d.data & 0xffffff; + sif1.counter = d.words; + sif1.tagMode = (d.data >> 24) & 0xFF; + notDone = 1; + } + } + } + } while (notDone); +} + +__forceinline void sif0Interrupt() { + + HW_DMA9_CHCR &= ~0x01000000; + psxDmaInterrupt2(2); + //hwIntcIrq(INTC_SBUS); +} + +__forceinline void sif1Interrupt() { + + HW_DMA10_CHCR &= ~0x01000000; //reset TR flag + psxDmaInterrupt2(3); + //hwIntcIrq(INTC_SBUS); +} + +__forceinline void EEsif0Interrupt() { + sif0dma->chcr &= ~0x100; + hwDmacIrq(DMAC_SIF0); +} + +__forceinline void EEsif1Interrupt() { + hwDmacIrq(DMAC_SIF1); + sif1dma->chcr &= ~0x100; +} + +__forceinline void dmaSIF0() { + SIF_LOG("EE: dmaSIF0 chcr = %lx, madr = %lx, qwc = %lx, tadr = %lx\n", + sif0dma->chcr, sif0dma->madr, sif0dma->qwc, sif0dma->tadr); + + if (sif0.fifoReadPos != sif0.fifoWritePos) { + SIF_LOG("warning, sif0.fifoReadPos != sif0.fifoWritePos\n"); + } +// if(sif0dma->qwc > 0 & (sif0dma->chcr & 0x4) == 0x4) { +// sif0dma->chcr &= ~4; //Halflife sets a QWC amount in chain mode, no tadr set. +// SysPrintf("yo\n"); +// } + + psHu32(0x1000F240) |= 0x2000; + eesifbusy[0] = 1; + if(eesifbusy[0] == 1 && iopsifbusy[0] == 1) { + FreezeXMMRegs(1); + hwIntcIrq(INTC_SBUS); + SIF0Dma(); + psHu32(0x1000F240) &= ~0x20; + psHu32(0x1000F240) &= ~0x2000; + FreezeXMMRegs(0); + } +} + +__forceinline void dmaSIF1() { + SIF_LOG("EE: dmaSIF1 chcr = %lx, madr = %lx, qwc = %lx, tadr = %lx\n", + sif1dma->chcr, sif1dma->madr, sif1dma->qwc, sif1dma->tadr); + + if (sif1.fifoReadPos != sif1.fifoWritePos) { + SIF_LOG("warning, sif1.fifoReadPos != sif1.fifoWritePos\n"); + } + +// if(sif1dma->qwc > 0 & (sif1dma->chcr & 0x4) == 0x4) { +// sif1dma->chcr &= ~4; //Halflife sets a QWC amount in chain mode, no tadr set. +// SysPrintf("yo2\n"); +// } + + psHu32(0x1000F240) |= 0x4000; + eesifbusy[1] = 1; + if(eesifbusy[1] == 1 && iopsifbusy[1] == 1) { + FreezeXMMRegs(1); + SIF1Dma(); + psHu32(0x1000F240) &= ~0x40; + psHu32(0x1000F240) &= ~0x100; + psHu32(0x1000F240) &= ~0x4000; + FreezeXMMRegs(0); + } + +} + +__forceinline void dmaSIF2() { + SIF_LOG("dmaSIF2 chcr = %lx, madr = %lx, qwc = %lx\n", + sif2dma->chcr, sif2dma->madr, sif2dma->qwc); + + sif2dma->chcr&= ~0x100; + hwDmacIrq(7); + SysPrintf("*PCSX2*: dmaSIF2\n"); +} + + +void SaveState::sifFreeze() { + Freeze(sif0); + Freeze(sif1); + + if( GetVersion() >= 0x0012 ) + { + Freeze(eesifbusy); + Freeze(iopsifbusy); + } + else if( IsLoading() ) + { + // Old savestate, inferior data so... + // Take an educated guess on what they should be. Or well, set to 1 because + // it more or less forces them to "kick" + + iopsifbusy[0] = eesifbusy[0] = 1; + iopsifbusy[1] = eesifbusy[1] = 1; + } +} diff --git a/pcsx2/Sif.h b/pcsx2/Sif.h new file mode 100644 index 0000000000..e1608973e0 --- /dev/null +++ b/pcsx2/Sif.h @@ -0,0 +1,51 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __SIF_H__ +#define __SIF_H__ + +#include "Common.h" + +struct sifData{ + int data, + words, + count, + addr; +}; + +extern int eeSifTransfer; + +extern DMACh *sif0ch; +extern DMACh *sif1ch; +extern DMACh *sif2ch; + +extern void sifInit(); +extern void SIF0Dma(); +extern void SIF1Dma(); +extern void dmaSIF0(); +extern void dmaSIF1(); +extern void dmaSIF2(); +extern void sif1Interrupt(); +extern void sif0Interrupt(); +extern void EEsif1Interrupt(); +extern void EEsif0Interrupt(); +extern int EEsif2Interrupt(); +int sifFreeze(gzFile f, int Mode); + + +#endif /* __SIF_H__ */ diff --git a/pcsx2/Sifcmd.h b/pcsx2/Sifcmd.h new file mode 100644 index 0000000000..8734ab2ac7 --- /dev/null +++ b/pcsx2/Sifcmd.h @@ -0,0 +1,193 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __SIFCMD_H__ +#define __SIFCMD_H__ + +/* from sifcmd.h */ + +#define SYSTEM_CMD 0x80000000 + +struct t_sif_cmd_header +{ + u32 size; + void *dest; + int command; + u32 unknown; +}; + +struct t_sif_dma_transfer +{ + void *src, + *dest; + int size; + int attr; +}; + +struct t_sif_handler +{ + void (*handler) ( void *a, void *b); + void *buff; +}; + +#define SYSTEM_CMD_CHANGE_SADDR 0x80000000 +#define SYSTEM_CMD_INIT_CMD 0x80000002 +struct t_sif_saddr{ + struct t_sif_cmd_header hdr; //+00 + void *newaddr; //+10 +}; //=14 + +#define SYSTEM_CMD_SET_SREG 0x80000001 +struct t_sif_sreg{ + struct t_sif_cmd_header hdr; //+00 + int index; //+10 + unsigned int value; //+14 +}; //=18 + +#define SYSTEM_CMD_RESET 0x80000003 +struct t_sif_reset{ + struct t_sif_cmd_header hdr; //+00 + int size, //+10 + flag; //+14 + char data[80]; //+18 +}; //=68 + +/* end of sifcmd.h */ + +/* from sifsrpc.h */ + +struct t_sif_rpc_rend +{ + struct t_sif_cmd_header sifcmd; + int rec_id; /* 04 */ + void *pkt_addr; /* 05 */ + int rpc_id; /* 06 */ + + struct t_rpc_client_data *client; /* 7 */ + u32 command; /* 8 */ + struct t_rpc_server_data *server; /* 9 */ + void *buff, /* 10 */ + *buff2; /* 11 */ +}; + +struct t_sif_rpc_other_data +{ + struct t_sif_cmd_header sifcmd; + int rec_id; /* 04 */ + void *pkt_addr; /* 05 */ + int rpc_id; /* 06 */ + + struct t_rpc_receive_data *receive; /* 07 */ + void *src; /* 08 */ + void *dest; /* 09 */ + int size; /* 10 */ +}; + +struct t_sif_rpc_bind +{ + struct t_sif_cmd_header sifcmd; + int rec_id; /* 04 */ + void *pkt_addr; /* 05 */ + int rpc_id; /* 06 */ + struct t_rpc_client_data *client; /* 07 */ + int rpc_number; /* 08 */ +}; + +struct t_sif_rpc_call +{ + struct t_sif_cmd_header sifcmd; + int rec_id; /* 04 */ + void *pkt_addr; /* 05 */ + int rpc_id; /* 06 */ + struct t_rpc_client_data *client; /* 07 */ + int rpc_number; /* 08 */ + int send_size; /* 09 */ + void *receive; /* 10 */ + int rec_size; /* 11 */ + int has_async_ef; /* 12 */ + struct t_rpc_server_data *server; /* 13 */ +}; + +struct t_rpc_server_data +{ + int command; /* 04 00 */ + + void * (*func)(u32, void *, int); /* 05 01 */ + void *buff; /* 06 02 */ + int size; /* 07 03 */ + + void * (*func2)(u32, void *, int); /* 08 04 */ + void *buff2; /* 09 05 */ + int size2; /* 10 06 */ + + struct t_rpc_client_data *client; /* 11 07 */ + void *pkt_addr; /* 12 08 */ + int rpc_number; /* 13 09 */ + + void *receive; /* 14 10 */ + int rec_size; /* 15 11 */ + int has_async_ef; /* 16 12 */ + int rec_id; /* 17 13 */ + + struct t_rpc_server_data *link; /* 18 14 */ + struct r_rpc_server_data *next; /* 19 15 */ + struct t_rpc_data_queue *queued_object; /* 20 16 */ +}; + + +struct t_rpc_header +{ + void *pkt_addr; /* 04 00 */ + u32 rpc_id; /* 05 01 */ + int sema_id; /* 06 02 */ + u32 mode; /* 07 03 */ +}; + + +struct t_rpc_client_data +{ + struct t_rpc_header hdr; + u32 command; /* 04 08 */ + void *buff, /* 05 09 */ + *buff2; /* 06 10 */ + void (*end_function) ( void *); /* 07 11 */ + void *end_param; /* 08 12*/ + struct t_rpc_server_data *server; /* 09 13 */ +}; + +struct t_rpc_receive_data +{ + struct t_rpc_header hdr; + void *src, /* 04 */ + *dest; /* 05 */ + int size; /* 06 */ +}; + +struct t_rpc_data_queue +{ + int thread_id, /* 00 */ + active; /* 01 */ + struct t_rpc_server_data *svdata_ref, /* 02 */ + *start, /* 03 */ + *end; /* 04 */ + struct t_rpc_data_queue *next; /* 05 */ +}; + +/* end of sifrpc.h */ + +#endif//__SIFCMD_H__ diff --git a/pcsx2/Sio.cpp b/pcsx2/Sio.cpp new file mode 100644 index 0000000000..2087540bd1 --- /dev/null +++ b/pcsx2/Sio.cpp @@ -0,0 +1,661 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" +#include "Paths.h" + +_sio sio; + +static FILE * MemoryCard1, * MemoryCard2; + +static const u8 cardh[4] = { 0xFF, 0xFF, 0x5a, 0x5d }; + +// Memory Card Specs : Sector size etc. +static const struct mc_command_0x26_tag mc_command_0x26= {'+', 512, 16, 0x4000, 0x52, 0x5A}; + +// SIO Inline'd IRQs : Calls the SIO interrupt handlers directly instead of +// feeding them through the IOP's branch test. (see SIO.H for details) + +#ifdef SIO_INLINE_IRQS +#define SIO_INT() sioInterrupt() +#define SIO_FORCEINLINE +#else +__forceinline void SIO_INT() +{ + if( !(psxRegs.interrupt & (1<(sio.buf); + sio.buf[3] = sio.terminator; + sio.buf[2] = '+'; + sio.mcdst = 99; + sio2.packet.recvVal3 = 0x8c; + break; + case 0x12: // RESET + sio.bufcount = 8; + memset8_obj<0xff>(sio.buf); + sio.buf[3] = sio.terminator; + sio.buf[2] = '+'; + sio.mcdst = 99; + + sio2.packet.recvVal3 = 0x8c; + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0x81: // COMMIT + sio.bufcount = 8; + memset8_obj<0xff>(sio.buf); + sio.mcdst = 99; + sio.buf[3] = sio.terminator; + sio.buf[2] = '+'; + sio2.packet.recvVal3 = 0x8c; + if(value == 0x81) { + if(sio.mc_command==0x42) + sio2.packet.recvVal1 = 0x1600; // Writing + else if(sio.mc_command==0x43) sio2.packet.recvVal1 = 0x1700; // Reading + } + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0x21: + case 0x22: + case 0x23: // SECTOR SET + sio.bufcount = 8; sio.mcdst = 99; sio.sector=0; sio.k=0; + memset8_obj<0xff>(sio.buf); + sio2.packet.recvVal3 = 0x8c; + sio.buf[8]=sio.terminator; + sio.buf[7]='+'; + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0x24: + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0x25: + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0x26: + sio.bufcount = 12; sio.mcdst = 99; sio2.packet.recvVal3 = 0x83; + memset8_obj<0xff>(sio.buf); + memcpy(&sio.buf[2], &mc_command_0x26, sizeof(mc_command_0x26)); + sio.buf[12]=sio.terminator; + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0x27: + case 0x28: + case 0xBF: + sio.bufcount = 4; sio.mcdst = 99; sio2.packet.recvVal3 = 0x8b; + memset8_obj<0xff>(sio.buf); + sio.buf[4]=sio.terminator; + sio.buf[3]='+'; + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0x42: // WRITE + case 0x43: // READ + case 0x82: + if(value==0x82 && sio.lastsector==sio.sector) sio.mode = 2; + if(value==0x42) sio.mode = 0; + if(value==0x43) sio.lastsector = sio.sector; // Reading + + sio.bufcount =133; sio.mcdst = 99; + memset8_obj<0xff>(sio.buf); + sio.buf[133]=sio.terminator; + sio.buf[132]='+'; + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0xf0: + case 0xf1: + case 0xf2: + sio.mcdst = 99; + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0xf3: + case 0xf7: + sio.bufcount = 4; sio.mcdst = 99; + memset8_obj<0xff>(sio.buf); + sio.buf[4]=sio.terminator; + sio.buf[3]='+'; + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0x52: + sio.rdwr = 1; memset8_obj<0xff>(sio.buf); + sio.buf[sio.bufcount]=sio.terminator; sio.buf[sio.bufcount-1]='+'; + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + case 0x57: + sio.rdwr = 2; memset8_obj<0xff>(sio.buf); + sio.buf[sio.bufcount]=sio.terminator; sio.buf[sio.bufcount-1]='+'; + MEMCARDS_LOG("MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + break; + default: + sio.mcdst = 0; + memset8_obj<0xff>(sio.buf); + sio.buf[sio.bufcount]=sio.terminator; sio.buf[sio.bufcount-1]='+'; + MEMCARDS_LOG("Unknown MC(%d) command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + } + sio.mc_command=value; + return; + // FURTHER PROCESSING OF THE MEMORY CARD COMMANDS + case 99: + sio.packetsize++; + sio.parp++; + switch(sio.mc_command) + { + // SET_ERASE_PAGE; the next erase commands will *clear* data starting with the page set here + case 0x21: + // SET_WRITE_PAGE; the next write commands will commit data starting with the page set here + case 0x22: + // SET_READ_PAGE; the next read commands will return data starting with the page set here + case 0x23: + if (sio.parp==2)sio.sector|=(value & 0xFF)<< 0; + if (sio.parp==3)sio.sector|=(value & 0xFF)<< 8; + if (sio.parp==4)sio.sector|=(value & 0xFF)<<16; + if (sio.parp==5)sio.sector|=(value & 0xFF)<<24; + if (sio.parp==6) + { + if (sio_xor((u8 *)&sio.sector, 4) == value) + MEMCARDS_LOG("MC(%d) SET PAGE sio.sector 0x%04X\n", + ((sio.CtrlReg&0x2000)>>13)+1, sio.sector); + else + MEMCARDS_LOG("MC(%d) SET PAGE XOR value ERROR 0x%02X != ^0x%02X\n", + ((sio.CtrlReg&0x2000)>>13)+1, value, sio_xor((u8 *)&sio.sector, 4)); + } + break; + + // SET_TERMINATOR; reads the new terminator code + case 0x27: + if(sio.parp==2) { + sio.terminator = value; + sio.buf[4] = value; + MEMCARDS_LOG("MC(%d) SET TERMINATOR command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + + } + break; + + // GET_TERMINATOR; puts in position 3 the current terminator code and in 4 the default one + // depending on the param + case 0x28: + if(sio.parp == 2) { + sio.buf[2] = '+'; + sio.buf[3] = sio.terminator; + + //if(value == 0) sio.buf[4] = 0xFF; + sio.buf[4] = 0x55; + MEMCARDS_LOG("MC(%d) GET TERMINATOR command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + } + break; + // WRITE DATA + case 0x42: + if (sio.parp==2) { + sio.bufcount=5+value; + memset8_obj<0xff>(sio.buf); + sio.buf[sio.bufcount-1]='+'; + sio.buf[sio.bufcount]=sio.terminator; + MEMCARDS_LOG("MC(%d) WRITE command 0x%02X\n\n\n\n\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + } + else + if ((sio.parp>2) && (sio.parp>13)+1, value); + } else + if (sio.parp==sio.bufcount-2) { + if (sio_xor(&sio.buf[3], sio.bufcount-5)==value) { + _SaveMcd(&sio.buf[3], (512+16)*sio.sector+sio.k, sio.bufcount-5); + sio.buf[sio.bufcount-1]=value; + sio.k+=sio.bufcount-5; + }else { + MEMCARDS_LOG("MC(%d) write XOR value error 0x%02X != ^0x%02X\n", + ((sio.CtrlReg&0x2000)>>13)+1, value, sio_xor(&sio.buf[3], sio.bufcount-5)); + } + } + break; + // READ DATA + case 0x43: + if (sio.parp==2){ + //int i; + sio.bufcount=value+5; + sio.buf[3]='+'; + MEMCARDS_LOG("MC(%d) READ command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + _ReadMcd(&sio.buf[4], (512+16)*sio.sector+sio.k, value); + /*if(sio.mode==2) + { + int j; + for(j=0; j < value; j++) + sio.buf[4+j] = ~sio.buf[4+j]; + }*/ + + sio.k+=value; + sio.buf[sio.bufcount-1]=sio_xor(&sio.buf[4], value); + sio.buf[sio.bufcount]=sio.terminator; + } + break; + // INTERNAL ERASE + case 0x82: + if(sio.parp==2) { + sio.buf[2]='+'; + sio.buf[3]=sio.terminator; + //if (sio.k != 0 || (sio.sector & 0xf) != 0) + // Console::Notice("saving : odd position for erase."); + + _EraseMCDBlock((512+16)*(sio.sector&~0xf)); + + /* memset(sio.buf, -1, 256); + _SaveMcd(sio.buf, (512+16)*sio.sector, 256); + _SaveMcd(sio.buf, (512+16)*sio.sector+256, 256); + _SaveMcd(sio.buf, (512+16)*sio.sector+512, 16); + sio.buf[2]='+'; + sio.buf[3]=sio.terminator;*/ + //sio.buf[sio.bufcount] = sio.terminator; + MEMCARDS_LOG("MC(%d) INTERNAL ERASE command 0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + } + break; + // CARD AUTHENTICATION CHECKS + case 0xF0: + if (sio.parp==2) + { + MEMCARDS_LOG("MC(%d) CARD AUTH :0x%02X\n", ((sio.CtrlReg&0x2000)>>13)+1, value); + switch(value){ + case 1: + case 2: + case 4: + case 15: + case 17: + case 19: + sio.bufcount=13; + memset8_obj<0xff>(sio.buf); + sio.buf[12] = 0; // Xor value of data from index 4 to 11 + sio.buf[3]='+'; + sio.buf[13] = sio.terminator; + break; + case 6: + case 7: + case 11: + sio.bufcount=13; + memset8_obj<0xff>(sio.buf); + sio.buf[12]='+'; + sio.buf[13] = sio.terminator; + break; + default: + sio.bufcount=4; + memset8_obj<0xff>(sio.buf); + sio.buf[3]='+'; + sio.buf[4] = sio.terminator; + } + } + break; + } + if (sio.bufcount<=sio.parp) sio.mcdst = 0; + return; + } + + switch (sio.mtapst) + { + case 0x1: + sio.packetsize++; + sio.parp = 1; + SIO_INT(); + switch(value) { + case 0x12: sio.mtapst = 2; sio.bufcount = 5; break; + case 0x13: sio.mtapst = 2; sio.bufcount = 5; break; + case 0x21: sio.mtapst = 2; sio.bufcount = 6; break; + } + sio.buf[sio.bufcount]='Z'; + sio.buf[sio.bufcount-1]='+'; + return; + case 0x2: + sio.packetsize++; + sio.parp++; + if (sio.bufcount<=sio.parp) sio.mcdst = 0; + SIO_INT(); + return; + } + + if(sio.count == 1 || way == 0) InitializeSIO(value); +} + +void InitializeSIO(u8 value) +{ + switch (value) { + case 0x01: // start pad + sio.StatReg &= ~TX_EMPTY; // Now the Buffer is not empty + sio.StatReg |= RX_RDY; // Transfer is Ready + + switch (sio.CtrlReg&0x2002) { + case 0x0002: sio.buf[0] = PAD1startPoll(1); break; + case 0x2002: sio.buf[0] = PAD2startPoll(2); break; + } + + sio.bufcount = 2; + sio.parp = 0; + sio.padst = 1; + sio.packetsize = 1; + sio.count = 0; + sio2.packet.recvVal1 = 0x1100; // Pad is present + SIO_INT(); + return; + + case 0x21: // start mtap + sio.StatReg &= ~TX_EMPTY; // Now the Buffer is not empty + sio.StatReg |= RX_RDY; // Transfer is Ready + sio.parp = 0; + sio.packetsize = 1; + sio.mtapst = 1; + sio.count = 0; + sio2.packet.recvVal1 = 0x1D100; // Mtap is not connected :) + SIO_INT(); + return; + + case 0x61: // start remote control sensor + sio.StatReg &= ~TX_EMPTY; // Now the Buffer is not empty + sio.StatReg |= RX_RDY; // Transfer is Ready + sio.parp = 0; + sio.packetsize = 1; + sio.count = 0; + sio2.packet.recvVal1 = 0x1100; // Pad is present + SIO_INT(); + return; + + case 0x81: // start memcard + sio.StatReg &= ~TX_EMPTY; + sio.StatReg |= RX_RDY; + memcpy(sio.buf, cardh, 4); + sio.parp = 0; + sio.bufcount = 8; + sio.mcdst = 1; + sio.packetsize = 1; + sio.rdwr = 0; + sio2.packet.recvVal1 = 0x1100; // Memcards are present + sio.count = 0; + SIO_INT(); + PAD_LOG("START MEMORY CARD\n"); + return; + } +} + +void sioWrite8(u8 value) +{ + SIO_CommandWrite(value,0); +} + +void SIODMAWrite(u8 value) +{ + SIO_CommandWrite(value,1); +} + +void sioWriteCtrl16(u16 value) { + sio.CtrlReg = value & ~RESET_ERR; + if (value & RESET_ERR) sio.StatReg &= ~IRQ; + if ((sio.CtrlReg & SIO_RESET) || (!sio.CtrlReg)) + { + sio.mtapst = 0; sio.padst = 0; sio.mcdst = 0; sio.parp = 0; + sio.StatReg = TX_RDY | TX_EMPTY; + psxRegs.interrupt &= ~(1<(data); // clears to -1's + if(mcd == 1) + { + SeekMcd(MemoryCard1, adr); + fwrite(data, 1, 528*16, MemoryCard1); + } + else + { + SeekMcd(MemoryCard2, adr); + fwrite(data, 1, 528*16, MemoryCard2); + } +} + + +void CreateMcd(char *mcd) { + FILE *fp; + int i=0, j=0; + //int enc[16] = {0x77,0x7f,0x7f,0x77,0x7f,0x7f,0x77,0x7f,0x7f,0x77,0x7f,0x7f,0,0,0,0}; + + fp = fopen(mcd, "wb"); + if (fp == NULL) return; + for(i=0; i < 16384; i++) + { + for(j=0; j < 528; j++) fputc(0xFF,fp); +// for(j=0; j < 16; j++) fputc(enc[j],fp); + } + fclose(fp); +} + + + diff --git a/pcsx2/Sio.h b/pcsx2/Sio.h new file mode 100644 index 0000000000..c895415c4e --- /dev/null +++ b/pcsx2/Sio.h @@ -0,0 +1,134 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + + +#ifndef _SIO_H_ +#define _SIO_H_ + +// SIO IRQ Timings... +// Scheduling ints into the future is a purist approach to emulation, and +// is mostly cosmetic since the emulator itself performs all actions instantly +// (as far as the emulated CPU is concerned). In some cases this can actually +// cause more sync problems than it supposedly solves, due to accumulated +// delays incurred by the recompiler's low cycle update rate and also Pcsx2 +// failing to properly handle pre-emptive DMA/IRQs or cpu exceptions. + +// The SIO is one of these cases, where-by many games seem to be a lot happier +// if the SIO handles its IRQs instantly instead of scheduling them. +// Uncomment the line below for SIO instant-IRQ mode. It improves responsiveness +// considerably, fixes PAD latency problems in some games, and may even reduce the +// chance of saves getting corrupted (untested). But it lacks the purist touch, +// so it's not enabled by default. + +//#define SIO_INLINE_IRQS + + +struct _sio { + u16 StatReg; + u16 ModeReg; + u16 CtrlReg; + u16 BaudReg; + + u8 buf[256]; + u32 bufcount; + u32 parp; + u32 mcdst,rdwr; + u8 adrH,adrL; + u32 padst; + u32 mtapst; + u32 packetsize; + + u8 terminator; + u8 mode; + u8 mc_command; + u32 lastsector; + u32 sector; + u32 k; + u32 count; +}; + +extern _sio sio; + +#define MCD_SIZE (1024 * 8 * 16) +#define MC2_SIZE (1024 * 528 * 16) + +// Status Flags +#define TX_RDY 0x0001 +#define RX_RDY 0x0002 +#define TX_EMPTY 0x0004 +#define PARITY_ERR 0x0008 +#define RX_OVERRUN 0x0010 +#define FRAMING_ERR 0x0020 +#define SYNC_DETECT 0x0040 +#define DSR 0x0080 +#define CTS 0x0100 +#define IRQ 0x0200 + +// Control Flags +#define TX_PERM 0x0001 +#define DTR 0x0002 +#define RX_PERM 0x0004 +#define BREAK 0x0008 +#define RESET_ERR 0x0010 +#define RTS 0x0020 +#define SIO_RESET 0x0040 + +void sioInit(); +void sioShutdown(); +void psxSIOShutdown(); +u8 sioRead8(); +void sioWrite8(u8 value); +void sioWriteCtrl16(u16 value); +extern void sioInterrupt(); +void InitializeSIO(u8 value); + +FILE *LoadMcd(int mcd); +void ReadMcd(int mcd, u8 *data, u32 adr, int size); +void SaveMcd(int mcd, const u8 *data, u32 adr, int size); +void EraseMcd(int mcd, u32 adr); +void CreateMcd(char *mcd); + +struct McdBlock { + char Title[48]; + char ID[14]; + char Name[16]; + int IconCount; + u16 Icon[16*16*3]; + u8 Flags; +}; + +#ifdef _MSC_VER +#pragma pack(1) +#endif +struct mc_command_0x26_tag{ + u8 field_151; //+02 flags + u16 sectorSize; //+03 divide to it + u16 field_2C; //+05 divide to it + u32 mc_size; //+07 + u8 mc_xor; //+0b don't forget to recalculate it!!! + u8 Z; //+0c +#ifdef _MSC_VER +}; +#pragma pack() +#else +} __attribute__((packed)); +#endif + +void GetMcdBlockInfo(int mcd, int block, McdBlock *info); + +#endif diff --git a/pcsx2/SourceLog.cpp b/pcsx2/SourceLog.cpp new file mode 100644 index 0000000000..799453da90 --- /dev/null +++ b/pcsx2/SourceLog.cpp @@ -0,0 +1,177 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#ifdef _WIN32 +#include "RDebug/deci2.h" +#else +#include +#include "DebugTools/Debug.h" +#endif + +#include +#include + +#include "R3000A.h" +#include "iR5900.h" +#include "System.h" + +using namespace R5900; + +FILE *emuLog; + +#ifdef PCSX2_DEVBUILD +u32 varLog; + +// these used by the depreciated _old_Log only +u16 logProtocol; +u8 logSource; +#endif + +int connected=0; + +#define SYNC_LOGGING + +// writes text directly to the logfile, no newlines appended. +void __Log( const char* fmt, ... ) +{ + char tmp[2024]; + + va_list list; + va_start(list, fmt); + + // concatenate the log message after the prefix: + int length = vsprintf(tmp, fmt, list); + va_end( list ); + + assert( length <= 2020 ); + if( length > 2020 ) + { + Msgbox::Alert("Source Log Stack Corruption Detected. Program execution may become unstable."); + // fixme: should throw an exception here once we have proper exception handling implemented. + } + + if (varLog & 0x80000000) // log to console enabled? + { + Console::Write(tmp); + + } + else if( emuLog != NULL ) // manually write to the logfile. + { + fputs( tmp, emuLog ); + //fputs( "\r\n", emuLog ); + fflush( emuLog ); + } +} + +static __forceinline void _vSourceLog( u16 protocol, u8 source, u32 cpuPc, u32 cpuCycle, const char *fmt, va_list list ) +{ + char tmp[2024]; + + int prelength = sprintf( tmp, "%c/%8.8lx %8.8lx: ", source, cpuPc, cpuCycle ); + + // concatenate the log message after the prefix: + int length = vsprintf(&tmp[prelength], fmt, list); + assert( length <= 2020 ); + if( length > 2020 ) + { + Msgbox::Alert("Source Log Stack Corruption Detected. Program execution may become unstable."); + // fixme: should throw an exception here once we have proper exception handling implemented. + } + +#ifdef PCSX2_DEVBUILD +#ifdef _WIN32 + // Send log data to the (remote?) debugger. + if (connected && logProtocol>=0 && logProtocol<0x10) + { + sendTTYP(logProtocol, logSource, tmp); + } +#endif +#endif + + if (varLog & 0x80000000) // log to console enabled? + { + Console::WriteLn(tmp); + + } else if( emuLog != NULL ) // manually write to the logfile. + { + fputs( tmp, emuLog ); + //fputs( "\r\n", emuLog ); + fflush( emuLog ); + } +} + +// Note: This function automatically appends a newline character always! +// (actually it doesn't yet because too much legacy code, will fix soon!) +void SourceLog( u16 protocol, u8 source, u32 cpuPc, u32 cpuCycle, const char *fmt, ...) +{ + va_list list; + va_start(list, fmt); + _vSourceLog( protocol, source, cpuPc, cpuCycle, fmt, list ); + va_end(list); +} + +// Functions with variable argument lists can't be inlined. +#define IMPLEMENT_SOURCE_LOG( unit, source, protocol ) \ + void SrcLog_##unit( const char* fmt, ... ) \ + { \ + va_list list; \ + va_start( list, fmt ); \ + _vSourceLog( protocol, source, \ + (source == 'E') ? cpuRegs.pc : psxRegs.pc, \ + (source == 'E') ? cpuRegs.cycle : psxRegs.cycle, fmt, list ); \ + va_end( list ); \ + } \ + +IMPLEMENT_SOURCE_LOG( EECNT, 'E', 0 ) +IMPLEMENT_SOURCE_LOG( BIOS, 'E', 0 ) + +IMPLEMENT_SOURCE_LOG( CPU, 'E', 1 ) +IMPLEMENT_SOURCE_LOG( FPU, 'E', 1 ) +IMPLEMENT_SOURCE_LOG( MMI, 'E', 1 ) +IMPLEMENT_SOURCE_LOG( COP0, 'E', 1 ) + +IMPLEMENT_SOURCE_LOG( MEM, 'E', 6 ) +IMPLEMENT_SOURCE_LOG( HW, 'E', 6 ) +IMPLEMENT_SOURCE_LOG( DMA, 'E', 5 ) +IMPLEMENT_SOURCE_LOG( ELF, 'E', 7 ) +IMPLEMENT_SOURCE_LOG( VU0, 'E', 2 ) +IMPLEMENT_SOURCE_LOG( VIF, 'E', 3 ) +IMPLEMENT_SOURCE_LOG( SPR, 'E', 7 ) +IMPLEMENT_SOURCE_LOG( GIF, 'E', 4 ) +IMPLEMENT_SOURCE_LOG( SIF, 'E', 9 ) +IMPLEMENT_SOURCE_LOG( IPU, 'E', 8 ) +IMPLEMENT_SOURCE_LOG( VUM, 'E', 2 ) +IMPLEMENT_SOURCE_LOG( RPC, 'E', 9 ) + +IMPLEMENT_SOURCE_LOG( PSXCPU, 'I', 1 ) +IMPLEMENT_SOURCE_LOG( PSXMEM, 'I', 6 ) +IMPLEMENT_SOURCE_LOG( PSXHW, 'I', 2 ) +IMPLEMENT_SOURCE_LOG( PSXBIOS, 'I', 0 ) +IMPLEMENT_SOURCE_LOG( PSXDMA, 'I', 5 ) +IMPLEMENT_SOURCE_LOG( PSXCNT, 'I', 4 ) + +IMPLEMENT_SOURCE_LOG( MEMCARDS, 'I', 7 ) +IMPLEMENT_SOURCE_LOG( PAD, 'I', 7 ) +IMPLEMENT_SOURCE_LOG( GTE, 'I', 3 ) +IMPLEMENT_SOURCE_LOG( CDR, 'I', 8 ) + + + + diff --git a/pcsx2/Stats.cpp b/pcsx2/Stats.cpp new file mode 100644 index 0000000000..4cccfb5d38 --- /dev/null +++ b/pcsx2/Stats.cpp @@ -0,0 +1,72 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include + +#include "Common.h" +#include "PsxCommon.h" +#include "Stats.h" + +#include "Paths.h" + +void statsOpen() { + stats.vsyncCount = 0; + stats.vsyncTime = time(NULL); + stats.eeCycles = 0; + stats.eeSCycle = 0; + stats.iopCycles = 0; + stats.iopSCycle = 0; +} + +void statsClose() { + time_t t; + FILE *f; + + t = time(NULL) - stats.vsyncTime; +#ifdef _WIN32 + f = fopen(LOGS_DIR "\\stats.txt", "w"); +#else + f = fopen(LOGS_DIR "/stats.txt", "w"); +#endif + if (!f) { SysPrintf("Can't open stats.txt\n"); return; } + fprintf(f, "-- PCSX2 v%s statics--\n\n", PCSX2_VERSION); + fprintf(f, "Ran for %d seconds\n", t); + fprintf(f, "Total VSyncs: %d (%s)\n", stats.vsyncCount, Config.PsxType ? "PAL" : "NTSC"); + fprintf(f, "VSyncs per Seconds: %g\n", (double)stats.vsyncCount / t); + fprintf(f, "Total EE Instructions Executed: %lld\n", stats.eeCycles); + fprintf(f, "Total IOP Instructions Executed: %lld\n", stats.iopCycles); + if (!CHECK_EEREC) fprintf(f, "Interpreter Mode\n"); + else fprintf(f, "Recompiler Mode: VUrec1 %s, VUrec0 %s\n", + CHECK_VU1REC ? "Enabled" : "Disabled", CHECK_VU0REC ? "Enabled" : "Disabled"); + fclose(f); +} + +void statsVSync() { + static u64 accum = 0, accumvu1 = 0; + static u32 frame = 0; + + stats.eeCycles+= cpuRegs.cycle - stats.eeSCycle; + stats.eeSCycle = cpuRegs.cycle; + stats.iopCycles+= psxRegs.cycle - stats.iopSCycle; + stats.iopSCycle = psxRegs.cycle; + stats.vsyncCount++; + stats.vif1count = 0; + stats.vu1count = 0; +} diff --git a/pcsx2/Stats.h b/pcsx2/Stats.h new file mode 100644 index 0000000000..1dc1d74f2d --- /dev/null +++ b/pcsx2/Stats.h @@ -0,0 +1,44 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __STATS_H__ +#define __STATS_H__ + +#include + +struct Stats { + time_t vsyncTime; + u32 vsyncCount; + u32 eeCycles; + u32 eeSCycle; + u32 iopCycles; + u32 iopSCycle; + + u32 ticko; + u32 framecount; + u32 vu1count; + u32 vif1count; +}; + +Stats stats; + +void statsOpen(); +void statsClose(); +void statsVSync(); + +#endif /* __STATS_H__ */ diff --git a/pcsx2/StringUtils.h b/pcsx2/StringUtils.h new file mode 100644 index 0000000000..7bc3d074eb --- /dev/null +++ b/pcsx2/StringUtils.h @@ -0,0 +1,75 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef _PCSX2_STRINGUTILS_H_ +#define _PCSX2_STRINGUTILS_H_ + +#include +#include +#include + +// to_string: A utility template for quick and easy inline string type conversion. +// Use to_string(intval), or to_string(float), etc. Anything that the STL itself +// would support should be supported here. :) +template< typename T > +std::string to_string(const T& value) +{ + std::ostringstream oss; + oss << value; + return oss.str(); +} + +// dummy structure used to type-guard the dummy parameter that's been inserted to +// allow us to use the va_list feature on references. +struct _VARG_PARAM +{ + // just some value to make the struct length 32bits instead of 8 bits, so that the + // compiler generates somewhat more efficient code. + uint someval; +}; + +#ifdef _DEBUG + +#define params va_arg_dummy, +#define varg_assert() // jASSUME( dummy == &va_arg_dummy ); +// typedef the Va-Arg value to be a value type in debug builds. The value +// type requires a little more overhead in terms of code generation, but is always +// type-safe. The compiler will generate errors for any forgotten params value. +typedef _VARG_PARAM VARG_PARAM; + +#else + +#define params NULL, // using null is faster / more compact! +#define varg_assert() jASSUME( dummy == NULL ); +// typedef the Va-Arg value to be a pointer in release builds. Pointers +// generate more compact code by a small margin, but aren't entirely type safe since +// the compiler won't generate errors if you pass NULL or other values. +typedef _VARG_PARAM const * VARG_PARAM; + +#endif + +extern const _VARG_PARAM va_arg_dummy; + +extern void ssprintf(std::string& dest, const char* fmt, ...); +extern void ssappendf( std::string& dest, const char* format, ...); +extern void vssprintf(std::string& dest, const char* format, va_list args); +extern void vssappendf(std::string& dest, const char* format, va_list args); + +extern std::string fmt_string( const char* fmt, ... ); +extern std::string vfmt_string( const char* fmt, va_list args ); +#endif diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp new file mode 100644 index 0000000000..9f797eed7a --- /dev/null +++ b/pcsx2/System.cpp @@ -0,0 +1,333 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "VUmicro.h" +#include "Threading.h" + +#include "iR5900.h" +#include "iR3000A.h" +#include "IopMem.h" +#include "iVUzerorec.h" // for SuperVUReset + + +#include "x86/ix86/ix86.h" + +using namespace std; +using namespace Console; + +// disable all session overrides by default... +SessionOverrideFlags g_Session = {false}; + +bool sysInitialized = false; + +namespace Exception +{ + BaseException::~BaseException() throw() {} +} + + +// I can't believe I had to make my own version of trim. C++'s STL is totally whack. +// And I still had to fix it too. I found three samples of trim online and *all* three +// were buggy. People really need to learn to code before they start posting trim +// functions in their blogs. (air) +static void trim( string& line ) +{ + if ( line.empty() ) + return; + + int string_size = line.length(); + int beginning_of_string = 0; + int end_of_string = string_size - 1; + + bool encountered_characters = false; + + // find the start of characters in the string + while ( (beginning_of_string < string_size) && (!encountered_characters) ) + { + if ( (line[ beginning_of_string ] != ' ') && (line[ beginning_of_string ] != '\t') ) + encountered_characters = true; + else + ++beginning_of_string; + } + + // test if no characters were found in the string + if ( beginning_of_string == string_size ) + return; + + encountered_characters = false; + + // find the character in the string + while ( (end_of_string > beginning_of_string) && (!encountered_characters) ) + { + // if a space or tab was found then ignore it + if ( (line[ end_of_string ] != ' ') && (line[ end_of_string ] != '\t') ) + encountered_characters = true; + else + --end_of_string; + } + + // return the original string with all whitespace removed from its beginning and end + // + 1 at the end to add the space for the string delimiter + //line.substr( beginning_of_string, end_of_string - beginning_of_string + 1 ); + line.erase( end_of_string+1, string_size ); + line.erase( 0, beginning_of_string ); +} + + +// This function should be called once during program execution. +void SysDetect() +{ + if( sysInitialized ) return; + sysInitialized = true; + + Notice("PCSX2 " PCSX2_VERSION " - compiled on %s", params __DATE__ ); + Notice("Savestate version: %x", params g_SaveVersion); + + // fixme: This line is here for the purpose of creating external ASM code. Yah. >_< + DevCon::Notice( "EE pc offset: 0x%x, IOP pc offset: 0x%x", params (u32)&cpuRegs.pc - (u32)&cpuRegs, (u32)&psxRegs.pc - (u32)&psxRegs ); + + cpudetectInit(); + + string family( cpuinfo.x86Fam ); + trim( family ); + + SetColor( Console::Color_White ); + + WriteLn( "x86Init:" ); + WriteLn( + "\tCPU vendor name = %s\n" + "\tFamilyID = %x\n" + "\tx86Family = %s\n" + "\tCPU speed = %d.%03d Ghz\n" + "\tCores = %d physical [%d logical]\n" + "\tx86PType = %s\n" + "\tx86Flags = %8.8x %8.8x\n" + "\tx86EFlags = %8.8x\n", params + cpuinfo.x86ID, cpuinfo.x86StepID, family.c_str(), + cpuinfo.cpuspeed / 1000, cpuinfo.cpuspeed%1000, + cpuinfo.PhysicalCores, cpuinfo.LogicalCores, + cpuinfo.x86Type, cpuinfo.x86Flags, cpuinfo.x86Flags2, + cpuinfo.x86EFlags + ); + + WriteLn( "Features:" ); + WriteLn( + "\t%sDetected MMX\n" + "\t%sDetected SSE\n" + "\t%sDetected SSE2\n" + "\t%sDetected SSE3\n" + "\t%sDetected SSE4.1\n", params + cpucaps.hasMultimediaExtensions ? "" : "Not ", + cpucaps.hasStreamingSIMDExtensions ? "" : "Not ", + cpucaps.hasStreamingSIMD2Extensions ? "" : "Not ", + cpucaps.hasStreamingSIMD3Extensions ? "" : "Not ", + cpucaps.hasStreamingSIMD4Extensions ? "" : "Not " + ); + + if ( cpuinfo.x86ID[0] == 'A' ) //AMD cpu + { + WriteLn( " Extended AMD Features:" ); + WriteLn( + "\t%sDetected MMX2\n" + "\t%sDetected 3DNOW\n" + "\t%sDetected 3DNOW2\n", params + cpucaps.hasMultimediaExtensionsExt ? "" : "Not ", + cpucaps.has3DNOWInstructionExtensions ? "" : "Not ", + cpucaps.has3DNOWInstructionExtensionsExt ? "" : "Not " + ); + } + + Console::ClearColor(); +} + +// Allocates memory for all PS2 systems. +bool SysAllocateMem() +{ + // Allocate PS2 system ram space (required by interpreters and recompilers both) + + try + { + memAlloc(); + psxMemAlloc(); + vuMicroMemAlloc(); + } + catch( Exception::OutOfMemory& ex ) + { + // Failures on the core initialization of memory is bad, since it means the emulator is + // completely non-functional. If the failure is in the VM build then we can try running + // the VTLB build instead. If it's the VTLB build then ... ouch. + + // VTLB build must fail outright... + Msgbox::Alert( "Failed to allocate memory needed to run pcsx2.\n\nError: %s", params ex.cMessage() ); + SysShutdownMem(); + + return false; + } + + return true; +} + + +// Allocates memory for all recompilers, and force-disables any recs that fail to initialize. +// This should be done asap, since the recompilers tend to demand a lot of system resources, and prefer +// to have those resources at specific address ranges. The sooner memory is allocated, the better. +// Returns FALSE on *critical* failure (GUI should issue a msg and exit). +void SysAllocateDynarecs() +{ + // Attempt to initialize the recompilers. + // Most users want to use recs anyway, and if they are using interpreters I don't think the + // extra few megs of allocation is going to be an issue. + + try + { + // R5900 and R3000a must be rec-enabled together for now so if either fails they both fail. + recCpu.Allocate(); + psxRec.Allocate(); + } + catch( Exception::BaseException& ex ) + { + Msgbox::Alert( + "The EE/IOP recompiler failed to initialize with the following error:\n\n" + "%s" + "\n\nThe EE/IOP interpreter will be used instead (slow!).", params + ex.cMessage() + ); + + g_Session.ForceDisableEErec = true; + + recCpu.Shutdown(); + psxRec.Shutdown(); + } + + try + { + VU0micro::recAlloc(); + } + catch( Exception::BaseException& ex ) + { + Msgbox::Alert( + "The VU0 recompiler failed to initialize with the following error:\n\n" + "%s" + "\n\nThe VU0 interpreter will be used for this session (may slow down some games).", params + ex.cMessage() + ); + + g_Session.ForceDisableVU0rec = true; + VU0micro::recShutdown(); + } + + try + { + VU1micro::recAlloc(); + } + catch( Exception::BaseException& ex ) + { + Msgbox::Alert( + "The VU1 recompiler failed to initialize with the following error:\n\n" + "%s" + "\n\nThe VU1 interpreter will be used for this session (will slow down most games).", params + ex.cMessage() + ); + + g_Session.ForceDisableVU1rec = true; + VU1micro::recShutdown(); + } + + // If both VUrecs failed, then make sure the SuperVU is totally closed out: + if( !CHECK_VU0REC && !CHECK_VU1REC) + SuperVUDestroy( -1 ); + +} + +// This should be called last thing before Pcsx2 exits. +void SysShutdownMem() +{ + cpuShutdown(); + + vuMicroMemShutdown(); + psxMemShutdown(); + memShutdown(); +} + +// This should generally be called right before calling SysShutdownMem(), although you can optionally +// use it in conjunction with SysAllocDynarecs to allocate/free the dynarec resources on the fly (as +// risky as it might be, since dynarecs could very well fail on the second attempt). +void SysShutdownDynarecs() +{ + // Special SuperVU "complete" terminator. + SuperVUDestroy( -1 ); + + psxRec.Shutdown(); + recCpu.Shutdown(); +} + +// Resets all PS2 cpu execution states, which does not affect that actual PS2 state/condition. +// This can be called at any time outside the context of a Cpu->Execute() block without +// bad things happening (recompilers will slow down for a brief moment since rec code blocks +// are dumped). +// Use this method to reset the recs when important global pointers like the MTGS are re-assigned. +void SysResetExecutionState() +{ + if( CHECK_EEREC ) + { + Cpu = &recCpu; + psxCpu = &psxRec; + } + else + { + Cpu = &intCpu; + psxCpu = &psxInt; + } + + Cpu->Reset(); + psxCpu->Reset(); + + vuMicroCpuReset(); + + // make sure the VU1 doesn't have lingering "skip" enabled. + vu1MicroDisableSkip(); +} + +u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller) +{ + u8 *Mem = (u8*)SysMmap( base, size ); + + if( (Mem == NULL) || (bounds != 0 && (((uptr)Mem + size) > bounds)) ) + { + DevCon::Notice( "First try failed allocating %s at address 0x%x", params caller, base ); + + // memory allocation *must* have the top bit clear, so let's try again + // with NULL (let the OS pick something for us). + + SafeSysMunmap( Mem, size ); + + Mem = (u8*)SysMmap( NULL, size ); + if( bounds != 0 && (((uptr)Mem + size) > bounds) ) + { + DevCon::Error( "Fatal Error:\n\tSecond try failed allocating %s, block ptr 0x%x does not meet required criteria.", params caller, Mem ); + SafeSysMunmap( Mem, size ); + + // returns NULL, caller should throw an exception. + } + } + return Mem; +} + diff --git a/pcsx2/System.h b/pcsx2/System.h new file mode 100644 index 0000000000..b8b97a880a --- /dev/null +++ b/pcsx2/System.h @@ -0,0 +1,213 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __SYSTEM_H__ +#define __SYSTEM_H__ + +#include "PS2Etypes.h" +#include "Exceptions.h" +#include "Paths.h" +#include "MemcpyFast.h" +#include "SafeArray.h" + +void SysDetect(); // Detects cpu type and fills cpuInfo structs. +bool SysInit(); // Init logfiles, directories, critical memory resources, and other OS-specific one-time +void SysReset(); // Resets the various PS2 cpus, sub-systems, and recompilers. +void SysUpdate(); // Called on VBlank (to update i.e. pads) +void SysClose(); // Close mem and plugins + +bool SysAllocateMem(); // allocates memory for all PS2 systems; returns FALSe on critical error. +void SysAllocateDynarecs(); // allocates memory for all dynarecs, and force-disables any failures. +void SysShutdownDynarecs(); +void SysShutdownMem(); +void SysResetExecutionState(); + +void *SysLoadLibrary(const char *lib); // Loads Library +void *SysLoadSym(void *lib, const char *sym); // Loads Symbol from Library +const char *SysLibError(); // Gets previous error loading symbols +void SysCloseLibrary(void *lib); // Closes Library + +// Maps a block of memory for use as a recompiled code buffer. +// The allocated block has code execution privileges. +// Returns NULL on allocation failure. +void *SysMmap(uptr base, u32 size); + +// Maps a block of memory for use as a recompiled code buffer, and ensures that the +// allocation is below a certain memory address (specified in "bounds" parameter). +// The allocated block has code execution privileges. +// Returns NULL on allocation failure. +u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller="Unnamed"); + +// Unmaps a block allocated by SysMmap +void SysMunmap(uptr base, u32 size); + +// Writes text to the console. +// *DEPRECIATED* Use Console namespace methods instead. +void SysPrintf(const char *fmt, ...); // *DEPRECIATED* + +static __forceinline void SysMunmap( void* base, u32 size ) +{ + SysMunmap( (uptr)base, size ); +} + + +#ifdef _MSC_VER +# define PCSX2_MEM_PROTECT_BEGIN() __try { +# define PCSX2_MEM_PROTECT_END() } __except(SysPageFaultExceptionFilter(GetExceptionInformation())) {} +#else +# define PCSX2_MEM_PROTECT_BEGIN() InstallLinuxExceptionHandler() +# define PCSX2_MEM_PROTECT_END() ReleaseLinuxExceptionHandler() +#endif + + +// Console Namespace -- Replacements for SysPrintf. +// SysPrintf is depreciated -- We should phase these in over time. +namespace Console +{ + enum Colors + { + Color_Black = 0, + Color_Red, + Color_Green, + Color_Yellow, + Color_Blue, + Color_Magenta, + Color_Cyan, + Color_White + }; + + // va_args version of WriteLn, mostly for internal use only. + extern void __fastcall _WriteLn( Colors color, const char* fmt, va_list args ); + + extern void Open(); + extern void Close(); + extern void SetTitle( const std::string& title ); + + // Changes the active console color. + // This color will be unset by calls to colored text methods + // such as ErrorMsg and Notice. + extern void __fastcall SetColor( Colors color ); + + // Restores the console color to default (usually low-intensity white on Win32) + extern void ClearColor(); + + // The following Write functions return bool so that we can use macros to exclude + // them from different build types. The return values are always zero. + + // Writes a newline to the console. + extern bool __fastcall Newline(); + + // Writes an unformatted string of text to the console (fast!) + // No newline is appended. + extern bool __fastcall Write( const char* fmt ); + + // Writes an unformatted string of text to the console (fast!) + // A newline is automatically appended. + extern bool __fastcall WriteLn( const char* fmt ); + + // Writes an unformatted string of text to the console (fast!) + // A newline is automatically appended, and the console color reset to default. + extern bool __fastcall WriteLn( Colors color, const char* fmt ); + + // Writes a line of colored text to the console, with automatic newline appendage. + // The console color is reset to default when the operation is complete. + extern bool WriteLn( Colors color, const char* fmt, VARG_PARAM dummy, ... ); + + // Writes a line of colored text to the console (no newline). + // The console color is reset to default when the operation is complete. + extern bool Write( Colors color, const char* fmt, VARG_PARAM dummy, ... ); + extern bool Write( Colors color, const char* fmt ); + + // Writes a formatted message to the console (no newline) + extern bool Write( const char* fmt, VARG_PARAM dummy, ... ); + + // Writes a formatted message to the console, with appended newline. + extern bool WriteLn( const char* fmt, VARG_PARAM dummy, ... ); + + // Displays a message in the console with red emphasis. + // Newline is automatically appended. + extern bool Error( const char* fmt, VARG_PARAM dummy, ... ); + extern bool Error( const char* fmt ); + + // Displays a message in the console with yellow emphasis. + // Newline is automatically appended. + extern bool Notice( const char* fmt, VARG_PARAM dummy, ... ); + extern bool Notice( const char* fmt ); + + // Displays a message in the console with yellow emphasis. + // Newline is automatically appended. + extern bool Status( const char* fmt, VARG_PARAM dummy, ... ); + extern bool Status( const char* fmt ); +} + +// Different types of message boxes that the emulator can employ from the friendly confines +// of it's blissful unawareness of whatever GUI it runs under. :) All message boxes exhibit +// blocking behavior -- they prompt the user for action and only return after the user has +// responded to the prompt. +namespace Msgbox +{ + // Pops up an alert Dialog Box with a singular "OK" button. + // Always returns false. Replacement for SysMessage. + extern bool Alert( const char* fmt, VARG_PARAM dummy, ... ); + extern bool Alert( const char* fmt ); + + // Pops up a dialog box with Ok/Cancel buttons. Returns the result of the inquiry, + // true if OK, false if cancel. + extern bool OkCancel( const char* fmt, VARG_PARAM dummy, ... ); +} + +using Console::Color_Red; +using Console::Color_Green; +using Console::Color_Blue; +using Console::Color_Magenta; +using Console::Color_Cyan; +using Console::Color_Yellow; +using Console::Color_White; + +////////////////////////////////////////////////////////////// +// Dev / Debug conditionals -- +// Consts for using if() statements instead of uglier #ifdef macros. +// Abbreviated macros for dev/debug only consoles and msgboxes. + +#ifdef PCSX2_DEVBUILD + +# define DevCon Console +# define DevMsg MsgBox + static const bool IsDevBuild = true; + +#else + +# define DevCon 0&&Console +# define DevMsg + static const bool IsDevBuild = false; + +#endif + +#ifdef _DEBUG + +# define DbgCon Console + static const bool IsDebugBuild = true; + +#else + +# define DbgCon 0&&Console + static const bool IsDebugBuild = false; + +#endif + +#endif /* __SYSTEM_H__ */ diff --git a/pcsx2/ThreadTools.cpp b/pcsx2/ThreadTools.cpp new file mode 100644 index 0000000000..a911d89889 --- /dev/null +++ b/pcsx2/ThreadTools.cpp @@ -0,0 +1,194 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "Threading.h" + +using namespace Threading; + +namespace Threading +{ + Thread::Thread() : + m_thread() + , m_returncode( 0 ) + , m_terminated( false ) + , m_sigterm( 0 ) + , m_post_event() + { + if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 ) + throw Exception::ThreadCreationError(); + } + + Thread::~Thread() + { + Close(); + } + + void Thread::Close() + { + AtomicExchange( m_sigterm, 1 ); + m_post_event.Post(); + pthread_join( m_thread, NULL ); + } + + int Thread::GetReturnCode() const + { + if( !m_terminated ) + throw std::logic_error( "Thread is still running. No return code is available." ); + + return m_returncode; + } + + WaitEvent::WaitEvent() + { + int err = 0; + + err = pthread_cond_init(&cond, NULL); + err = pthread_mutex_init(&mutex, NULL); + } + + WaitEvent::~WaitEvent() + { + pthread_cond_destroy( &cond ); + pthread_mutex_destroy( &mutex ); + } + + void WaitEvent::Set() + { + pthread_mutex_lock( &mutex ); + pthread_cond_signal( &cond ); + pthread_mutex_unlock( &mutex ); + } + + void WaitEvent::Wait() + { + pthread_mutex_lock( &mutex ); + pthread_cond_wait( &cond, &mutex ); + pthread_mutex_unlock( &mutex ); + } + + Semaphore::Semaphore() + { + sem_init( &sema, false, 0 ); + } + + Semaphore::~Semaphore() + { + sem_destroy( &sema ); + } + + void Semaphore::Post() + { + sem_post( &sema ); + } + + void Semaphore::Post( int multiple ) + { +#if defined(_MSC_VER) + sem_post_multiple( &sema, multiple ); +#endif + } + + void Semaphore::Wait() + { + sem_wait( &sema ); + } + + int Semaphore::Count() + { + int retval; + sem_getvalue( &sema, &retval ); + return retval; + } + + MutexLock::MutexLock() + { + int err = 0; + err = pthread_mutex_init( &mutex, NULL ); + } + + MutexLock::~MutexLock() + { + pthread_mutex_destroy( &mutex ); + } + + void MutexLock::Lock() + { + pthread_mutex_lock( &mutex ); + } + + void MutexLock::Unlock() + { + pthread_mutex_unlock( &mutex ); + } + + ////////////////////////////////////////////////////////////////////// + // define some overloads for InterlockedExchanges + // for commonly used types, like u32 and s32. + + __forceinline void AtomicExchange( volatile u32& Target, u32 value ) + { + pcsx2_InterlockedExchange( (volatile long*)&Target, value ); + } + + __forceinline void AtomicExchangeAdd( volatile u32& Target, u32 value ) + { + pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, value ); + } + + __forceinline void AtomicIncrement( volatile u32& Target ) + { + pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, 1 ); + } + + __forceinline void AtomicDecrement( volatile u32& Target ) + { + pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, -1 ); + } + + __forceinline void AtomicExchange( volatile s32& Target, s32 value ) + { + pcsx2_InterlockedExchange( (volatile long*)&Target, value ); + } + + __forceinline void AtomicExchangeAdd( s32& Target, u32 value ) + { + pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, value ); + } + + __forceinline void AtomicIncrement( volatile s32& Target ) + { + pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, 1 ); + } + + __forceinline void AtomicDecrement( volatile s32& Target ) + { + pcsx2_InterlockedExchangeAdd( (volatile long*)&Target, -1 ); + } + + __forceinline void _AtomicExchangePointer( const void ** target, const void* value ) + { + pcsx2_InterlockedExchange( (volatile long*)target, (long)value ); + } + + __forceinline void _AtomicCompareExchangePointer( const void ** target, const void* value, const void* comparand ) + { + pcsx2_InterlockedCompareExchange( (volatile long*)target, (long)value, (long)comparand ); + } + +} \ No newline at end of file diff --git a/pcsx2/Threading.h b/pcsx2/Threading.h new file mode 100644 index 0000000000..d430aad570 --- /dev/null +++ b/pcsx2/Threading.h @@ -0,0 +1,136 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef _THREADING_H_ +#define _THREADING_H_ + +#include // EBUSY +#include + +#include "PS2Etypes.h" +#include "Exceptions.h" + +namespace Threading +{ + /////////////////////////////////////////////////////////////// + // Define some useful object handles - wait events, mutexes. + + struct WaitEvent + { + pthread_cond_t cond; + pthread_mutex_t mutex; + + WaitEvent(); + ~WaitEvent(); + + void Set(); + void Wait(); + }; + + struct Semaphore + { + sem_t sema; + + Semaphore(); + ~Semaphore(); + + void Post(); + void Post( int multiple ); + void Wait(); + int Count(); + }; + + struct MutexLock + { + pthread_mutex_t mutex; + + MutexLock(); + ~MutexLock(); + + void Lock(); + void Unlock(); + }; + + // Returns the number of available logical CPUs (cores plus + // hyperthreaded cpus) + extern void CountLogicalCores( int LogicalCoresPerPhysicalCPU, int PhysicalCoresPerPhysicalCPU ); + + // Releases a timeslice to other threads. + extern void Timeslice(); + + // For use in spin/wait loops. + extern void SpinWait(); + + class Thread : NoncopyableObject + { + protected: + typedef int (*PlainJoeFP)(); + pthread_t m_thread; + int m_returncode; // value returned from the thread on close. + bool m_terminated; // set true after the thread has been closed. + u32 m_sigterm; // set to true(1) when the thread has been requested to exit. + Semaphore m_post_event; // general wait event that's needed by most threads. + + public: + virtual ~Thread(); + Thread(); + + virtual void Close(); + + // Gets the return code of the thread. + // Throws std::logic_error if the thread has not terminated. + int GetReturnCode() const; + + protected: + // Used to dispatch the thread callback function. + // (handles some thread cleanup on Win32, and is basically a typecast + // on linux). + static void* _internal_callback( void* func ); + + // Implemented by derrived class to handle threading actions! + virtual int Callback()=0; + }; + + // Our fundamental interlocking functions. All other useful interlocks can + // be derrived from these little beasties! + + extern long pcsx2_InterlockedExchange(volatile long* Target, long srcval); + extern long pcsx2_InterlockedCompareExchange( volatile long* target, long srcval, long comp ); + extern long pcsx2_InterlockedExchangeAdd( volatile long* target, long addval ); + + extern void AtomicExchange( volatile u32& Target, u32 value ); + extern void AtomicExchangeAdd( volatile u32& Target, u32 value ); + extern void AtomicIncrement( volatile u32& Target ); + extern void AtomicDecrement( volatile u32& Target ); + extern void AtomicExchange( volatile s32& Target, s32 value ); + extern void AtomicExchangeAdd( volatile s32& Target, u32 value ); + extern void AtomicIncrement( volatile s32& Target ); + extern void AtomicDecrement( volatile s32& Target ); + + extern void _AtomicExchangePointer( const void ** target, const void* value ); + extern void _AtomicCompareExchangePointer( const void ** target, const void* value, const void* comparand ); + + #define AtomicExchangePointer( target, value ) \ + _AtomicExchangePointer( (const void**)(&target), (const void*)(value) ) + + #define AtomicCompareExchangePointer( target, value, comparand ) \ + _AtomicCompareExchangePointer( (const void**)(&target), (const void*)(value), (const void*)(comparand) ) + +} + +#endif diff --git a/pcsx2/VU.h b/pcsx2/VU.h new file mode 100644 index 0000000000..d68e8ffcb1 --- /dev/null +++ b/pcsx2/VU.h @@ -0,0 +1,201 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __VU_H__ +#define __VU_H__ + +#include "Vif.h" + +#define REG_STATUS_FLAG 16 +#define REG_MAC_FLAG 17 +#define REG_CLIP_FLAG 18 +#define REG_ACC_FLAG 19 // dummy flag that indicates that VFACC is written/read (nothing to do with VI[19]) +#define REG_R 20 +#define REG_I 21 +#define REG_Q 22 +#define REG_P 23 // only exists in micromode +#define REG_VF0_FLAG 24 // dummy flag that indicates VF0 is read (nothing to do with VI[24]) +#define REG_TPC 26 +#define REG_CMSAR0 27 +#define REG_FBRST 28 +#define REG_VPU_STAT 29 +#define REG_CMSAR1 31 + +//interpreter hacks, WIP +//#define INT_VUSTALLHACK //some games work without those, big speedup +//#define INT_VUDOUBLEHACK + +enum VUStatus { + VU_Ready = 0, + VU_Run = 1, + VU_Stop = 2, +}; + +union VECTOR { + struct { + float x,y,z,w; + } f; + struct { + u32 x,y,z,w; + } i; + + float F[4]; + + u64 UD[2]; //128 bits + s64 SD[2]; + u32 UL[4]; + s32 SL[4]; + u16 US[8]; + s16 SS[8]; + u8 UC[16]; + s8 SC[16]; +}; + +struct REG_VI { + union { + float F; + s32 SL; + u32 UL; + s16 SS[2]; + u16 US[2]; + s8 SC[4]; + u8 UC[4]; + }; + u32 padding[3]; // needs padding to make them 128bit; VU0 maps VU1's VI regs as 128bits to addr 0x4xx0 in + // VU0 mem, with only lower 16 bits valid, and the upper 112bits are hardwired to 0 (cottonvibes) +}; + +#define VUFLAG_BREAKONMFLAG 0x00000001 +#define VUFLAG_MFLAGSET 0x00000002 + +struct fdivPipe { + int enable; + REG_VI reg; + u32 sCycle; + u32 Cycle; + u32 statusflag; +}; + +struct efuPipe { + int enable; + REG_VI reg; + u32 sCycle; + u32 Cycle; +}; + +struct fmacPipe { + int enable; + int reg; + int xyzw; + u32 sCycle; + u32 Cycle; + u32 macflag; + u32 statusflag; + u32 clipflag; +}; + +struct VURegs { + VECTOR VF[32]; // VF and VI need to be first in this struct for proper mapping + REG_VI VI[32]; // needs to be 128bit x 32 (cottonvibes) + VECTOR ACC; + REG_VI q; + REG_VI p; + + u32 macflag; + u32 statusflag; + u32 clipflag; + + u32 cycle; + u32 flags; + + void (*vuExec)(VURegs*); + VIFregisters *vifRegs; + + u8 *Mem; + u8 *Micro; + + u32 code; + u32 maxmem; + u32 maxmicro; + + u16 branch; + u16 ebit; + u32 branchpc; + + fmacPipe fmac[8]; + fdivPipe fdiv; + efuPipe efu; + + VURegs() : + Mem( NULL ) + , Micro( NULL ) + { + } +}; + +#define VUPIPE_NONE 0 +#define VUPIPE_FMAC 1 +#define VUPIPE_FDIV 2 +#define VUPIPE_EFU 3 +#define VUPIPE_IALU 4 +#define VUPIPE_BRANCH 5 +#define VUPIPE_XGKICK 6 + +#define VUREG_READ 0x1 +#define VUREG_WRITE 0x2 + +struct _VURegsNum { + u8 pipe; // if 0xff, COP2 + u8 VFwrite; + u8 VFwxyzw; + u8 VFr0xyzw; + u8 VFr1xyzw; + u8 VFread0; + u8 VFread1; + u32 VIwrite; + u32 VIread; + int cycles; +}; + +extern VURegs* g_pVU1; +extern PCSX2_ALIGNED16_DECL(VURegs VU0); + +#define VU1 (*g_pVU1) + + +#ifdef _WIN32 +extern __forceinline u32* GET_VU_MEM(VURegs* VU, u32 addr) +#else +static __forceinline u32* GET_VU_MEM(VURegs* VU, u32 addr) +#endif +{ + if( VU == g_pVU1 ) return (u32*)(VU1.Mem+(addr&0x3fff)); + + if( addr >= 0x4000 ) return (u32*)(VU0.Mem+(addr&0x43f0)); // get VF and VI regs (they're mapped to 0x4xx0 in VU0 mem!) + + return (u32*)(VU0.Mem+(addr&0x0fff)); // for addr 0x0000 to 0x4000 just wrap around +} + + +// various fixes to enable per game (all are off by default) +#define VUFIX_SIGNEDZERO 1 +#define VUFIX_EXTRAFLAGS 2 +#define VUFIX_XGKICKDELAY2 4 +extern int g_VUGameFixes; + +#endif /* __VU_H__ */ diff --git a/pcsx2/VU0.cpp b/pcsx2/VU0.cpp new file mode 100644 index 0000000000..fdb41cf4fa --- /dev/null +++ b/pcsx2/VU0.cpp @@ -0,0 +1,364 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +/* TODO + -Fix the flags Proper as they aren't handle now.. + -Add BC Table opcodes + -Add Interlock in QMFC2,QMTC2,CFC2,CTC2 + -Finish instruction set + -Bug Fixes!!! +*/ + +#include "PrecompiledHeader.h" + +#include + +#include "Common.h" +#include "DebugTools/Debug.h" +#include "R5900.h" +#include "R5900OpcodeTables.h" +#include "VUops.h" +#include "VUmicro.h" + +#define _Ft_ _Rt_ +#define _Fs_ _Rd_ +#define _Fd_ _Sa_ + +#define _X (cpuRegs.code>>24) & 0x1 +#define _Y (cpuRegs.code>>23) & 0x1 +#define _Z (cpuRegs.code>>22) & 0x1 +#define _W (cpuRegs.code>>21) & 0x1 + +#define _Fsf_ ((cpuRegs.code >> 21) & 0x03) +#define _Ftf_ ((cpuRegs.code >> 23) & 0x03) + +#include "VUflags.h" + +using namespace R5900; + +PCSX2_ALIGNED16(VURegs VU0); + +void COP2_BC2() { Int_COP2BC2PrintTable[_Rt_]();} +void COP2_SPECIAL() { Int_COP2SPECIAL1PrintTable[_Funct_]();} + +void COP2_SPECIAL2() { + Int_COP2SPECIAL2PrintTable[(cpuRegs.code & 0x3) | ((cpuRegs.code >> 4) & 0x7c)](); +} + +void COP2_Unknown() +{ + CPU_LOG("Unknown COP2 opcode called\n"); +} + +//**************************************************************************** +void _vu0WaitMicro() { + int startcycle; + + if ((VU0.VI[REG_VPU_STAT].UL & 0x1) == 0) { + return; + } + + startcycle = VU0.cycle; + + VU0.flags|= VUFLAG_BREAKONMFLAG; + VU0.flags&= ~VUFLAG_MFLAGSET; + + do { + CpuVU0.ExecuteBlock(); + // knockout kings 2002 loops here + if( VU0.cycle-startcycle > 0x1000 ) { + Console::Notice("VU0 perma-stall, breaking execution..."); // (email zero if gfx are bad) + break; + } + } while ((VU0.VI[REG_VPU_STAT].UL & 0x1) && (VU0.flags & VUFLAG_MFLAGSET) == 0); + + //NEW + cpuRegs.cycle += (VU0.cycle-startcycle)*2; + VU0.flags&= ~VUFLAG_BREAKONMFLAG; +} + +namespace R5900 { +namespace Interpreter{ +namespace OpcodeImpl +{ + void LQC2() { + u32 addr = cpuRegs.GPR.r[_Rs_].UL[0] + (s16)cpuRegs.code; + if (_Ft_) { + memRead128(addr, &VU0.VF[_Ft_].UD[0]); + } else { + u64 val[2]; + memRead128(addr, val); + } + } + + // Asadr.Changed + //TODO: check this + // HUH why ? doesn;t make any sense ... + void SQC2() { + u32 addr = _Imm_ + cpuRegs.GPR.r[_Rs_].UL[0]; + //memWrite64(addr, VU0.VF[_Ft_].UD[0]); + //memWrite64(addr+8,VU0.VF[_Ft_].UD[1]); + memWrite128(addr, &VU0.VF[_Ft_].UD[0]); + } +}}} + + +void QMFC2() { + if (cpuRegs.code & 1) { + _vu0WaitMicro(); + } + if (_Rt_ == 0) return; + cpuRegs.GPR.r[_Rt_].UD[0] = VU0.VF[_Fs_].UD[0]; + cpuRegs.GPR.r[_Rt_].UD[1] = VU0.VF[_Fs_].UD[1]; +} + +void QMTC2() { + if (cpuRegs.code & 1) { + _vu0WaitMicro(); + } + if (_Fs_ == 0) return; + VU0.VF[_Fs_].UD[0] = cpuRegs.GPR.r[_Rt_].UD[0]; + VU0.VF[_Fs_].UD[1] = cpuRegs.GPR.r[_Rt_].UD[1]; +} + +void CFC2() { + if (cpuRegs.code & 1) { + _vu0WaitMicro(); + } + if (_Rt_ == 0) return; + cpuRegs.GPR.r[_Rt_].UL[0] = VU0.VI[_Fs_].UL; + if(VU0.VI[_Fs_].UL & 0x80000000) + cpuRegs.GPR.r[_Rt_].UL[1] = 0xffffffff; + else + cpuRegs.GPR.r[_Rt_].UL[1] = 0; +} + +void CTC2() { + if (cpuRegs.code & 1) { + _vu0WaitMicro(); + } + if (_Fs_ == 0) return; + + switch(_Fs_) { + case REG_MAC_FLAG: // read-only + case REG_TPC: // read-only + case REG_VPU_STAT: // read-only + break; + case REG_FBRST: + VU0.VI[REG_FBRST].UL = cpuRegs.GPR.r[_Rt_].UL[0] & 0x0C0C; + if (cpuRegs.GPR.r[_Rt_].UL[0] & 0x1) { // VU0 Force Break + Console::Error("fixme: VU0 Force Break"); + } + if (cpuRegs.GPR.r[_Rt_].UL[0] & 0x2) { // VU0 Reset + //SysPrintf("fixme: VU0 Reset\n"); + vu0ResetRegs(); + } + if (cpuRegs.GPR.r[_Rt_].UL[0] & 0x100) { // VU1 Force Break + Console::Error("fixme: VU1 Force Break"); + } + if (cpuRegs.GPR.r[_Rt_].UL[0] & 0x200) { // VU1 Reset +// SysPrintf("fixme: VU1 Reset\n"); + vu1ResetRegs(); + } + break; + case REG_CMSAR1: // REG_CMSAR1 + if (!(VU0.VI[REG_VPU_STAT].UL & 0x100) ) { + VU1.VI[REG_TPC].UL = cpuRegs.GPR.r[_Rt_].US[0]; + vu1ExecMicro(VU1.VI[REG_TPC].UL); // Execute VU1 Micro SubRoutine + } + break; + default: + VU0.VI[_Fs_].UL = cpuRegs.GPR.r[_Rt_].UL[0]; + break; + } +} + +//--------------------------------------------------------------------------------------- + + +__forceinline void SYNCMSFLAGS() +{ + VU0.VI[REG_STATUS_FLAG].UL = VU0.statusflag; + VU0.VI[REG_MAC_FLAG].UL = VU0.macflag; +} + +__forceinline void SYNCFDIV() +{ + VU0.VI[REG_Q].UL = VU0.q.UL; + VU0.VI[REG_STATUS_FLAG].UL = VU0.statusflag; +} + +void VABS() { VU0.code = cpuRegs.code; _vuABS(&VU0); } +void VADD() { VU0.code = cpuRegs.code; _vuADD(&VU0); SYNCMSFLAGS(); } +void VADDi() { VU0.code = cpuRegs.code; _vuADDi(&VU0); SYNCMSFLAGS(); } +void VADDq() { VU0.code = cpuRegs.code; _vuADDq(&VU0); SYNCMSFLAGS(); } +void VADDx() { VU0.code = cpuRegs.code; _vuADDx(&VU0); SYNCMSFLAGS(); } +void VADDy() { VU0.code = cpuRegs.code; _vuADDy(&VU0); SYNCMSFLAGS(); } +void VADDz() { VU0.code = cpuRegs.code; _vuADDz(&VU0); SYNCMSFLAGS(); } +void VADDw() { VU0.code = cpuRegs.code; _vuADDw(&VU0); SYNCMSFLAGS(); } +void VADDA() { VU0.code = cpuRegs.code; _vuADDA(&VU0); SYNCMSFLAGS(); } +void VADDAi() { VU0.code = cpuRegs.code; _vuADDAi(&VU0); SYNCMSFLAGS(); } +void VADDAq() { VU0.code = cpuRegs.code; _vuADDAq(&VU0); SYNCMSFLAGS(); } +void VADDAx() { VU0.code = cpuRegs.code; _vuADDAx(&VU0); SYNCMSFLAGS(); } +void VADDAy() { VU0.code = cpuRegs.code; _vuADDAy(&VU0); SYNCMSFLAGS(); } +void VADDAz() { VU0.code = cpuRegs.code; _vuADDAz(&VU0); SYNCMSFLAGS(); } +void VADDAw() { VU0.code = cpuRegs.code; _vuADDAw(&VU0); SYNCMSFLAGS(); } +void VSUB() { VU0.code = cpuRegs.code; _vuSUB(&VU0); SYNCMSFLAGS(); } +void VSUBi() { VU0.code = cpuRegs.code; _vuSUBi(&VU0); SYNCMSFLAGS(); } +void VSUBq() { VU0.code = cpuRegs.code; _vuSUBq(&VU0); SYNCMSFLAGS(); } +void VSUBx() { VU0.code = cpuRegs.code; _vuSUBx(&VU0); SYNCMSFLAGS(); } +void VSUBy() { VU0.code = cpuRegs.code; _vuSUBy(&VU0); SYNCMSFLAGS(); } +void VSUBz() { VU0.code = cpuRegs.code; _vuSUBz(&VU0); SYNCMSFLAGS(); } +void VSUBw() { VU0.code = cpuRegs.code; _vuSUBw(&VU0); SYNCMSFLAGS(); } +void VSUBA() { VU0.code = cpuRegs.code; _vuSUBA(&VU0); SYNCMSFLAGS(); } +void VSUBAi() { VU0.code = cpuRegs.code; _vuSUBAi(&VU0); SYNCMSFLAGS(); } +void VSUBAq() { VU0.code = cpuRegs.code; _vuSUBAq(&VU0); SYNCMSFLAGS(); } +void VSUBAx() { VU0.code = cpuRegs.code; _vuSUBAx(&VU0); SYNCMSFLAGS(); } +void VSUBAy() { VU0.code = cpuRegs.code; _vuSUBAy(&VU0); SYNCMSFLAGS(); } +void VSUBAz() { VU0.code = cpuRegs.code; _vuSUBAz(&VU0); SYNCMSFLAGS(); } +void VSUBAw() { VU0.code = cpuRegs.code; _vuSUBAw(&VU0); SYNCMSFLAGS(); } +void VMUL() { VU0.code = cpuRegs.code; _vuMUL(&VU0); SYNCMSFLAGS(); } +void VMULi() { VU0.code = cpuRegs.code; _vuMULi(&VU0); SYNCMSFLAGS(); } +void VMULq() { VU0.code = cpuRegs.code; _vuMULq(&VU0); SYNCMSFLAGS(); } +void VMULx() { VU0.code = cpuRegs.code; _vuMULx(&VU0); SYNCMSFLAGS(); } +void VMULy() { VU0.code = cpuRegs.code; _vuMULy(&VU0); SYNCMSFLAGS(); } +void VMULz() { VU0.code = cpuRegs.code; _vuMULz(&VU0); SYNCMSFLAGS(); } +void VMULw() { VU0.code = cpuRegs.code; _vuMULw(&VU0); SYNCMSFLAGS(); } +void VMULA() { VU0.code = cpuRegs.code; _vuMULA(&VU0); SYNCMSFLAGS(); } +void VMULAi() { VU0.code = cpuRegs.code; _vuMULAi(&VU0); SYNCMSFLAGS(); } +void VMULAq() { VU0.code = cpuRegs.code; _vuMULAq(&VU0); SYNCMSFLAGS(); } +void VMULAx() { VU0.code = cpuRegs.code; _vuMULAx(&VU0); SYNCMSFLAGS(); } +void VMULAy() { VU0.code = cpuRegs.code; _vuMULAy(&VU0); SYNCMSFLAGS(); } +void VMULAz() { VU0.code = cpuRegs.code; _vuMULAz(&VU0); SYNCMSFLAGS(); } +void VMULAw() { VU0.code = cpuRegs.code; _vuMULAw(&VU0); SYNCMSFLAGS(); } +void VMADD() { VU0.code = cpuRegs.code; _vuMADD(&VU0); SYNCMSFLAGS(); } +void VMADDi() { VU0.code = cpuRegs.code; _vuMADDi(&VU0); SYNCMSFLAGS(); } +void VMADDq() { VU0.code = cpuRegs.code; _vuMADDq(&VU0); SYNCMSFLAGS(); } +void VMADDx() { VU0.code = cpuRegs.code; _vuMADDx(&VU0); SYNCMSFLAGS(); } +void VMADDy() { VU0.code = cpuRegs.code; _vuMADDy(&VU0); SYNCMSFLAGS(); } +void VMADDz() { VU0.code = cpuRegs.code; _vuMADDz(&VU0); SYNCMSFLAGS(); } +void VMADDw() { VU0.code = cpuRegs.code; _vuMADDw(&VU0); SYNCMSFLAGS(); } +void VMADDA() { VU0.code = cpuRegs.code; _vuMADDA(&VU0); SYNCMSFLAGS(); } +void VMADDAi() { VU0.code = cpuRegs.code; _vuMADDAi(&VU0); SYNCMSFLAGS(); } +void VMADDAq() { VU0.code = cpuRegs.code; _vuMADDAq(&VU0); SYNCMSFLAGS(); } +void VMADDAx() { VU0.code = cpuRegs.code; _vuMADDAx(&VU0); SYNCMSFLAGS(); } +void VMADDAy() { VU0.code = cpuRegs.code; _vuMADDAy(&VU0); SYNCMSFLAGS(); } +void VMADDAz() { VU0.code = cpuRegs.code; _vuMADDAz(&VU0); SYNCMSFLAGS(); } +void VMADDAw() { VU0.code = cpuRegs.code; _vuMADDAw(&VU0); SYNCMSFLAGS(); } +void VMSUB() { VU0.code = cpuRegs.code; _vuMSUB(&VU0); SYNCMSFLAGS(); } +void VMSUBi() { VU0.code = cpuRegs.code; _vuMSUBi(&VU0); SYNCMSFLAGS(); } +void VMSUBq() { VU0.code = cpuRegs.code; _vuMSUBq(&VU0); SYNCMSFLAGS(); } +void VMSUBx() { VU0.code = cpuRegs.code; _vuMSUBx(&VU0); SYNCMSFLAGS(); } +void VMSUBy() { VU0.code = cpuRegs.code; _vuMSUBy(&VU0); SYNCMSFLAGS(); } +void VMSUBz() { VU0.code = cpuRegs.code; _vuMSUBz(&VU0); SYNCMSFLAGS(); } +void VMSUBw() { VU0.code = cpuRegs.code; _vuMSUBw(&VU0); SYNCMSFLAGS(); } +void VMSUBA() { VU0.code = cpuRegs.code; _vuMSUBA(&VU0); SYNCMSFLAGS(); } +void VMSUBAi() { VU0.code = cpuRegs.code; _vuMSUBAi(&VU0); SYNCMSFLAGS(); } +void VMSUBAq() { VU0.code = cpuRegs.code; _vuMSUBAq(&VU0); SYNCMSFLAGS(); } +void VMSUBAx() { VU0.code = cpuRegs.code; _vuMSUBAx(&VU0); SYNCMSFLAGS(); } +void VMSUBAy() { VU0.code = cpuRegs.code; _vuMSUBAy(&VU0); SYNCMSFLAGS(); } +void VMSUBAz() { VU0.code = cpuRegs.code; _vuMSUBAz(&VU0); SYNCMSFLAGS(); } +void VMSUBAw() { VU0.code = cpuRegs.code; _vuMSUBAw(&VU0); SYNCMSFLAGS(); } +void VMAX() { VU0.code = cpuRegs.code; _vuMAX(&VU0); } +void VMAXi() { VU0.code = cpuRegs.code; _vuMAXi(&VU0); } +void VMAXx() { VU0.code = cpuRegs.code; _vuMAXx(&VU0); } +void VMAXy() { VU0.code = cpuRegs.code; _vuMAXy(&VU0); } +void VMAXz() { VU0.code = cpuRegs.code; _vuMAXz(&VU0); } +void VMAXw() { VU0.code = cpuRegs.code; _vuMAXw(&VU0); } +void VMINI() { VU0.code = cpuRegs.code; _vuMINI(&VU0); } +void VMINIi() { VU0.code = cpuRegs.code; _vuMINIi(&VU0); } +void VMINIx() { VU0.code = cpuRegs.code; _vuMINIx(&VU0); } +void VMINIy() { VU0.code = cpuRegs.code; _vuMINIy(&VU0); } +void VMINIz() { VU0.code = cpuRegs.code; _vuMINIz(&VU0); } +void VMINIw() { VU0.code = cpuRegs.code; _vuMINIw(&VU0); } +void VOPMULA() { VU0.code = cpuRegs.code; _vuOPMULA(&VU0); SYNCMSFLAGS(); } +void VOPMSUB() { VU0.code = cpuRegs.code; _vuOPMSUB(&VU0); SYNCMSFLAGS(); } +void VNOP() { VU0.code = cpuRegs.code; _vuNOP(&VU0); } +void VFTOI0() { VU0.code = cpuRegs.code; _vuFTOI0(&VU0); } +void VFTOI4() { VU0.code = cpuRegs.code; _vuFTOI4(&VU0); } +void VFTOI12() { VU0.code = cpuRegs.code; _vuFTOI12(&VU0); } +void VFTOI15() { VU0.code = cpuRegs.code; _vuFTOI15(&VU0); } +void VITOF0() { VU0.code = cpuRegs.code; _vuITOF0(&VU0); } +void VITOF4() { VU0.code = cpuRegs.code; _vuITOF4(&VU0); } +void VITOF12() { VU0.code = cpuRegs.code; _vuITOF12(&VU0); } +void VITOF15() { VU0.code = cpuRegs.code; _vuITOF15(&VU0); } +void VCLIPw() { VU0.code = cpuRegs.code; _vuCLIP(&VU0); VU0.VI[REG_CLIP_FLAG].UL = VU0.clipflag; } + +void VDIV() { VU0.code = cpuRegs.code; _vuDIV(&VU0); SYNCFDIV(); } +void VSQRT() { VU0.code = cpuRegs.code; _vuSQRT(&VU0); SYNCFDIV(); } +void VRSQRT() { VU0.code = cpuRegs.code; _vuRSQRT(&VU0); SYNCFDIV(); } +void VIADD() { VU0.code = cpuRegs.code; _vuIADD(&VU0); } +void VIADDI() { VU0.code = cpuRegs.code; _vuIADDI(&VU0); } +void VIADDIU() { VU0.code = cpuRegs.code; _vuIADDIU(&VU0); } +void VIAND() { VU0.code = cpuRegs.code; _vuIAND(&VU0); } +void VIOR() { VU0.code = cpuRegs.code; _vuIOR(&VU0); } +void VISUB() { VU0.code = cpuRegs.code; _vuISUB(&VU0); } +void VISUBIU() { VU0.code = cpuRegs.code; _vuISUBIU(&VU0); } +void VMOVE() { VU0.code = cpuRegs.code; _vuMOVE(&VU0); } +void VMFIR() { VU0.code = cpuRegs.code; _vuMFIR(&VU0); } +void VMTIR() { VU0.code = cpuRegs.code; _vuMTIR(&VU0); } +void VMR32() { VU0.code = cpuRegs.code; _vuMR32(&VU0); } +void VLQ() { VU0.code = cpuRegs.code; _vuLQ(&VU0); } +void VLQD() { VU0.code = cpuRegs.code; _vuLQD(&VU0); } +void VLQI() { VU0.code = cpuRegs.code; _vuLQI(&VU0); } +void VSQ() { VU0.code = cpuRegs.code; _vuSQ(&VU0); } +void VSQD() { VU0.code = cpuRegs.code; _vuSQD(&VU0); } +void VSQI() { VU0.code = cpuRegs.code; _vuSQI(&VU0); } +void VILW() { VU0.code = cpuRegs.code; _vuILW(&VU0); } +void VISW() { VU0.code = cpuRegs.code; _vuISW(&VU0); } +void VILWR() { VU0.code = cpuRegs.code; _vuILWR(&VU0); } +void VISWR() { VU0.code = cpuRegs.code; _vuISWR(&VU0); } +void VRINIT() { VU0.code = cpuRegs.code; _vuRINIT(&VU0); } +void VRGET() { VU0.code = cpuRegs.code; _vuRGET(&VU0); } +void VRNEXT() { VU0.code = cpuRegs.code; _vuRNEXT(&VU0); } +void VRXOR() { VU0.code = cpuRegs.code; _vuRXOR(&VU0); } +void VWAITQ() { VU0.code = cpuRegs.code; _vuWAITQ(&VU0); } +void VFSAND() { VU0.code = cpuRegs.code; _vuFSAND(&VU0); } +void VFSEQ() { VU0.code = cpuRegs.code; _vuFSEQ(&VU0); } +void VFSOR() { VU0.code = cpuRegs.code; _vuFSOR(&VU0); } +void VFSSET() { VU0.code = cpuRegs.code; _vuFSSET(&VU0); } +void VFMAND() { VU0.code = cpuRegs.code; _vuFMAND(&VU0); } +void VFMEQ() { VU0.code = cpuRegs.code; _vuFMEQ(&VU0); } +void VFMOR() { VU0.code = cpuRegs.code; _vuFMOR(&VU0); } +void VFCAND() { VU0.code = cpuRegs.code; _vuFCAND(&VU0); } +void VFCEQ() { VU0.code = cpuRegs.code; _vuFCEQ(&VU0); } +void VFCOR() { VU0.code = cpuRegs.code; _vuFCOR(&VU0); } +void VFCSET() { VU0.code = cpuRegs.code; _vuFCSET(&VU0); } +void VFCGET() { VU0.code = cpuRegs.code; _vuFCGET(&VU0); } +void VXITOP() { VU0.code = cpuRegs.code; _vuXITOP(&VU0); } + +// fixme: Shouldn't anything calling this function be calling vu0WaitMicro instead? +// Meaning that this function stalls, but doesn't increment the cpuRegs.cycle like +// you would think it should. +void vu0Finish() +{ + if( (VU0.VI[REG_VPU_STAT].UL & 0x1) ) { + int i = 0; + + while(i++ < 32) { + CpuVU0.ExecuteBlock(); + if(!(VU0.VI[REG_VPU_STAT].UL & 0x1)) + break; + } + if(VU0.VI[REG_VPU_STAT].UL & 0x1) { + VU0.VI[REG_VPU_STAT].UL &= ~1; + // this log tends to spam a lot (MGS3) + //Console::Notice("vu0Finish > stall aborted by force."); + } + } +} diff --git a/pcsx2/VU0micro.cpp b/pcsx2/VU0micro.cpp new file mode 100644 index 0000000000..02063891ab --- /dev/null +++ b/pcsx2/VU0micro.cpp @@ -0,0 +1,454 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// This module contains code shared by both the dynarec and interpreter versions +// of the VU0 micro. + + +#include "PrecompiledHeader.h" + +#include + +#include "Common.h" +#include "DebugTools/Debug.h" +#include "R5900.h" +#include "iR5900.h" +#include "VUmicro.h" +#include "VUflags.h" +#include "VUops.h" + +#include "iVUzerorec.h" + +using namespace R5900; + +#define VF_VAL(x) ((x==0x80000000)?0:(x)) + +void iDumpVU0Registers() +{ + // fixme: This code is outdated, broken, and lacks printed labels. + // Needs heavy mods to be useful. +#if 0 + int i; + + for(i = 1; i < 32; ++i) { + __Log("v%d: %x %x %x %x, vi: ", i, VF_VAL(VU0.VF[i].UL[3]), VF_VAL(VU0.VF[i].UL[2]), + VF_VAL(VU0.VF[i].UL[1]), VF_VAL(VU0.VF[i].UL[0])); + if( i == REG_Q || i == REG_P ) __Log("%f\n", VU0.VI[i].F); + else if( i == REG_MAC_FLAG ) __Log("%x\n", 0);//VU0.VI[i].UL&0xff); + else if( i == REG_STATUS_FLAG ) __Log("%x\n", 0);//VU0.VI[i].UL&0x03); + else if( i == REG_CLIP_FLAG ) __Log("0\n"); + else __Log("%x\n", VU0.VI[i].UL); + } + __Log("vfACC: %f %f %f %f\n", VU0.ACC.F[3], VU0.ACC.F[2], VU0.ACC.F[1], VU0.ACC.F[0]); +#endif +} + +// This is called by the COP2 as per the CTC instruction +void vu0ResetRegs() +{ + VU0.VI[REG_VPU_STAT].UL &= ~0xff; // stop vu0 + VU0.VI[REG_FBRST].UL &= ~0xff; // stop vu0 + vif0Regs->stat &= ~4; +} + +void VU0MI_XGKICK() { +} + +void VU0MI_XTOP() { +} + +void vu0ExecMicro(u32 addr) { + VUM_LOG("vu0ExecMicro %x\n", addr); + + if(VU0.VI[REG_VPU_STAT].UL & 0x1) { + DevCon::Notice("vu0ExecMicro > Stalling for previous microprogram to finish"); + vu0Finish(); + } + VU0.VI[REG_VPU_STAT].UL|= 0x1; + VU0.VI[REG_VPU_STAT].UL&= ~0xAE; + + if (addr != -1) VU0.VI[REG_TPC].UL = addr; + _vuExecMicroDebug(VU0); + CpuVU0.ExecuteBlock(); + + // If the VU0 program didn't finish then we'll want to finish it up + // pretty soon. This fixes vmhacks in some games (Naruto Ultimate Ninja 2) + if(VU0.VI[REG_VPU_STAT].UL & 0x1) + cpuSetNextBranchDelta( 192 ); // fixme : ideally this should be higher, like 512 or so. +} + +void VU0unknown() { + assert(0); + + CPU_LOG("Unknown VU micromode opcode called\n"); +} + +void VU0regsunknown(_VURegsNum *VUregsn) { + assert(0); + + CPU_LOG("Unknown VU micromode opcode called\n"); +} + +_vuRegsTables(VU0, VU0regs); + + +/****************************************/ +/* VU Micromode Upper instructions */ +/****************************************/ + +void VU0MI_ABS() { _vuABS(&VU0); } +void VU0MI_ADD() { _vuADD(&VU0); } +void VU0MI_ADDi() { _vuADDi(&VU0); } +void VU0MI_ADDq() { _vuADDq(&VU0); } +void VU0MI_ADDx() { _vuADDx(&VU0); } +void VU0MI_ADDy() { _vuADDy(&VU0); } +void VU0MI_ADDz() { _vuADDz(&VU0); } +void VU0MI_ADDw() { _vuADDw(&VU0); } +void VU0MI_ADDA() { _vuADDA(&VU0); } +void VU0MI_ADDAi() { _vuADDAi(&VU0); } +void VU0MI_ADDAq() { _vuADDAq(&VU0); } +void VU0MI_ADDAx() { _vuADDAx(&VU0); } +void VU0MI_ADDAy() { _vuADDAy(&VU0); } +void VU0MI_ADDAz() { _vuADDAz(&VU0); } +void VU0MI_ADDAw() { _vuADDAw(&VU0); } +void VU0MI_SUB() { _vuSUB(&VU0); } +void VU0MI_SUBi() { _vuSUBi(&VU0); } +void VU0MI_SUBq() { _vuSUBq(&VU0); } +void VU0MI_SUBx() { _vuSUBx(&VU0); } +void VU0MI_SUBy() { _vuSUBy(&VU0); } +void VU0MI_SUBz() { _vuSUBz(&VU0); } +void VU0MI_SUBw() { _vuSUBw(&VU0); } +void VU0MI_SUBA() { _vuSUBA(&VU0); } +void VU0MI_SUBAi() { _vuSUBAi(&VU0); } +void VU0MI_SUBAq() { _vuSUBAq(&VU0); } +void VU0MI_SUBAx() { _vuSUBAx(&VU0); } +void VU0MI_SUBAy() { _vuSUBAy(&VU0); } +void VU0MI_SUBAz() { _vuSUBAz(&VU0); } +void VU0MI_SUBAw() { _vuSUBAw(&VU0); } +void VU0MI_MUL() { _vuMUL(&VU0); } +void VU0MI_MULi() { _vuMULi(&VU0); } +void VU0MI_MULq() { _vuMULq(&VU0); } +void VU0MI_MULx() { _vuMULx(&VU0); } +void VU0MI_MULy() { _vuMULy(&VU0); } +void VU0MI_MULz() { _vuMULz(&VU0); } +void VU0MI_MULw() { _vuMULw(&VU0); } +void VU0MI_MULA() { _vuMULA(&VU0); } +void VU0MI_MULAi() { _vuMULAi(&VU0); } +void VU0MI_MULAq() { _vuMULAq(&VU0); } +void VU0MI_MULAx() { _vuMULAx(&VU0); } +void VU0MI_MULAy() { _vuMULAy(&VU0); } +void VU0MI_MULAz() { _vuMULAz(&VU0); } +void VU0MI_MULAw() { _vuMULAw(&VU0); } +void VU0MI_MADD() { _vuMADD(&VU0); } +void VU0MI_MADDi() { _vuMADDi(&VU0); } +void VU0MI_MADDq() { _vuMADDq(&VU0); } +void VU0MI_MADDx() { _vuMADDx(&VU0); } +void VU0MI_MADDy() { _vuMADDy(&VU0); } +void VU0MI_MADDz() { _vuMADDz(&VU0); } +void VU0MI_MADDw() { _vuMADDw(&VU0); } +void VU0MI_MADDA() { _vuMADDA(&VU0); } +void VU0MI_MADDAi() { _vuMADDAi(&VU0); } +void VU0MI_MADDAq() { _vuMADDAq(&VU0); } +void VU0MI_MADDAx() { _vuMADDAx(&VU0); } +void VU0MI_MADDAy() { _vuMADDAy(&VU0); } +void VU0MI_MADDAz() { _vuMADDAz(&VU0); } +void VU0MI_MADDAw() { _vuMADDAw(&VU0); } +void VU0MI_MSUB() { _vuMSUB(&VU0); } +void VU0MI_MSUBi() { _vuMSUBi(&VU0); } +void VU0MI_MSUBq() { _vuMSUBq(&VU0); } +void VU0MI_MSUBx() { _vuMSUBx(&VU0); } +void VU0MI_MSUBy() { _vuMSUBy(&VU0); } +void VU0MI_MSUBz() { _vuMSUBz(&VU0); } +void VU0MI_MSUBw() { _vuMSUBw(&VU0); } +void VU0MI_MSUBA() { _vuMSUBA(&VU0); } +void VU0MI_MSUBAi() { _vuMSUBAi(&VU0); } +void VU0MI_MSUBAq() { _vuMSUBAq(&VU0); } +void VU0MI_MSUBAx() { _vuMSUBAx(&VU0); } +void VU0MI_MSUBAy() { _vuMSUBAy(&VU0); } +void VU0MI_MSUBAz() { _vuMSUBAz(&VU0); } +void VU0MI_MSUBAw() { _vuMSUBAw(&VU0); } +void VU0MI_MAX() { _vuMAX(&VU0); } +void VU0MI_MAXi() { _vuMAXi(&VU0); } +void VU0MI_MAXx() { _vuMAXx(&VU0); } +void VU0MI_MAXy() { _vuMAXy(&VU0); } +void VU0MI_MAXz() { _vuMAXz(&VU0); } +void VU0MI_MAXw() { _vuMAXw(&VU0); } +void VU0MI_MINI() { _vuMINI(&VU0); } +void VU0MI_MINIi() { _vuMINIi(&VU0); } +void VU0MI_MINIx() { _vuMINIx(&VU0); } +void VU0MI_MINIy() { _vuMINIy(&VU0); } +void VU0MI_MINIz() { _vuMINIz(&VU0); } +void VU0MI_MINIw() { _vuMINIw(&VU0); } +void VU0MI_OPMULA() { _vuOPMULA(&VU0); } +void VU0MI_OPMSUB() { _vuOPMSUB(&VU0); } +void VU0MI_NOP() { _vuNOP(&VU0); } +void VU0MI_FTOI0() { _vuFTOI0(&VU0); } +void VU0MI_FTOI4() { _vuFTOI4(&VU0); } +void VU0MI_FTOI12() { _vuFTOI12(&VU0); } +void VU0MI_FTOI15() { _vuFTOI15(&VU0); } +void VU0MI_ITOF0() { _vuITOF0(&VU0); } +void VU0MI_ITOF4() { _vuITOF4(&VU0); } +void VU0MI_ITOF12() { _vuITOF12(&VU0); } +void VU0MI_ITOF15() { _vuITOF15(&VU0); } +void VU0MI_CLIP() { _vuCLIP(&VU0); } + +/*****************************************/ +/* VU Micromode Lower instructions */ +/*****************************************/ + +void VU0MI_DIV() { _vuDIV(&VU0); } +void VU0MI_SQRT() { _vuSQRT(&VU0); } +void VU0MI_RSQRT() { _vuRSQRT(&VU0); } +void VU0MI_IADD() { _vuIADD(&VU0); } +void VU0MI_IADDI() { _vuIADDI(&VU0); } +void VU0MI_IADDIU() { _vuIADDIU(&VU0); } +void VU0MI_IAND() { _vuIAND(&VU0); } +void VU0MI_IOR() { _vuIOR(&VU0); } +void VU0MI_ISUB() { _vuISUB(&VU0); } +void VU0MI_ISUBIU() { _vuISUBIU(&VU0); } +void VU0MI_MOVE() { _vuMOVE(&VU0); } +void VU0MI_MFIR() { _vuMFIR(&VU0); } +void VU0MI_MTIR() { _vuMTIR(&VU0); } +void VU0MI_MR32() { _vuMR32(&VU0); } +void VU0MI_LQ() { _vuLQ(&VU0); } +void VU0MI_LQD() { _vuLQD(&VU0); } +void VU0MI_LQI() { _vuLQI(&VU0); } +void VU0MI_SQ() { _vuSQ(&VU0); } +void VU0MI_SQD() { _vuSQD(&VU0); } +void VU0MI_SQI() { _vuSQI(&VU0); } +void VU0MI_ILW() { _vuILW(&VU0); } +void VU0MI_ISW() { _vuISW(&VU0); } +void VU0MI_ILWR() { _vuILWR(&VU0); } +void VU0MI_ISWR() { _vuISWR(&VU0); } +void VU0MI_RINIT() { _vuRINIT(&VU0); } +void VU0MI_RGET() { _vuRGET(&VU0); } +void VU0MI_RNEXT() { _vuRNEXT(&VU0); } +void VU0MI_RXOR() { _vuRXOR(&VU0); } +void VU0MI_WAITQ() { _vuWAITQ(&VU0); } +void VU0MI_FSAND() { _vuFSAND(&VU0); } +void VU0MI_FSEQ() { _vuFSEQ(&VU0); } +void VU0MI_FSOR() { _vuFSOR(&VU0); } +void VU0MI_FSSET() { _vuFSSET(&VU0); } +void VU0MI_FMAND() { _vuFMAND(&VU0); } +void VU0MI_FMEQ() { _vuFMEQ(&VU0); } +void VU0MI_FMOR() { _vuFMOR(&VU0); } +void VU0MI_FCAND() { _vuFCAND(&VU0); } +void VU0MI_FCEQ() { _vuFCEQ(&VU0); } +void VU0MI_FCOR() { _vuFCOR(&VU0); } +void VU0MI_FCSET() { _vuFCSET(&VU0); } +void VU0MI_FCGET() { _vuFCGET(&VU0); } +void VU0MI_IBEQ() { _vuIBEQ(&VU0); } +void VU0MI_IBGEZ() { _vuIBGEZ(&VU0); } +void VU0MI_IBGTZ() { _vuIBGTZ(&VU0); } +void VU0MI_IBLTZ() { _vuIBLTZ(&VU0); } +void VU0MI_IBLEZ() { _vuIBLEZ(&VU0); } +void VU0MI_IBNE() { _vuIBNE(&VU0); } +void VU0MI_B() { _vuB(&VU0); } +void VU0MI_BAL() { _vuBAL(&VU0); } +void VU0MI_JR() { _vuJR(&VU0); } +void VU0MI_JALR() { _vuJALR(&VU0); } +void VU0MI_MFP() { _vuMFP(&VU0); } +void VU0MI_WAITP() { _vuWAITP(&VU0); } +void VU0MI_ESADD() { _vuESADD(&VU0); } +void VU0MI_ERSADD() { _vuERSADD(&VU0); } +void VU0MI_ELENG() { _vuELENG(&VU0); } +void VU0MI_ERLENG() { _vuERLENG(&VU0); } +void VU0MI_EATANxy() { _vuEATANxy(&VU0); } +void VU0MI_EATANxz() { _vuEATANxz(&VU0); } +void VU0MI_ESUM() { _vuESUM(&VU0); } +void VU0MI_ERCPR() { _vuERCPR(&VU0); } +void VU0MI_ESQRT() { _vuESQRT(&VU0); } +void VU0MI_ERSQRT() { _vuERSQRT(&VU0); } +void VU0MI_ESIN() { _vuESIN(&VU0); } +void VU0MI_EATAN() { _vuEATAN(&VU0); } +void VU0MI_EEXP() { _vuEEXP(&VU0); } +void VU0MI_XITOP() { _vuXITOP(&VU0); } + +/****************************************/ +/* VU Micromode Upper instructions */ +/****************************************/ + +void VU0regsMI_ABS(_VURegsNum *VUregsn) { _vuRegsABS(&VU0, VUregsn); } +void VU0regsMI_ADD(_VURegsNum *VUregsn) { _vuRegsADD(&VU0, VUregsn); } +void VU0regsMI_ADDi(_VURegsNum *VUregsn) { _vuRegsADDi(&VU0, VUregsn); } +void VU0regsMI_ADDq(_VURegsNum *VUregsn) { _vuRegsADDq(&VU0, VUregsn); } +void VU0regsMI_ADDx(_VURegsNum *VUregsn) { _vuRegsADDx(&VU0, VUregsn); } +void VU0regsMI_ADDy(_VURegsNum *VUregsn) { _vuRegsADDy(&VU0, VUregsn); } +void VU0regsMI_ADDz(_VURegsNum *VUregsn) { _vuRegsADDz(&VU0, VUregsn); } +void VU0regsMI_ADDw(_VURegsNum *VUregsn) { _vuRegsADDw(&VU0, VUregsn); } +void VU0regsMI_ADDA(_VURegsNum *VUregsn) { _vuRegsADDA(&VU0, VUregsn); } +void VU0regsMI_ADDAi(_VURegsNum *VUregsn) { _vuRegsADDAi(&VU0, VUregsn); } +void VU0regsMI_ADDAq(_VURegsNum *VUregsn) { _vuRegsADDAq(&VU0, VUregsn); } +void VU0regsMI_ADDAx(_VURegsNum *VUregsn) { _vuRegsADDAx(&VU0, VUregsn); } +void VU0regsMI_ADDAy(_VURegsNum *VUregsn) { _vuRegsADDAy(&VU0, VUregsn); } +void VU0regsMI_ADDAz(_VURegsNum *VUregsn) { _vuRegsADDAz(&VU0, VUregsn); } +void VU0regsMI_ADDAw(_VURegsNum *VUregsn) { _vuRegsADDAw(&VU0, VUregsn); } +void VU0regsMI_SUB(_VURegsNum *VUregsn) { _vuRegsSUB(&VU0, VUregsn); } +void VU0regsMI_SUBi(_VURegsNum *VUregsn) { _vuRegsSUBi(&VU0, VUregsn); } +void VU0regsMI_SUBq(_VURegsNum *VUregsn) { _vuRegsSUBq(&VU0, VUregsn); } +void VU0regsMI_SUBx(_VURegsNum *VUregsn) { _vuRegsSUBx(&VU0, VUregsn); } +void VU0regsMI_SUBy(_VURegsNum *VUregsn) { _vuRegsSUBy(&VU0, VUregsn); } +void VU0regsMI_SUBz(_VURegsNum *VUregsn) { _vuRegsSUBz(&VU0, VUregsn); } +void VU0regsMI_SUBw(_VURegsNum *VUregsn) { _vuRegsSUBw(&VU0, VUregsn); } +void VU0regsMI_SUBA(_VURegsNum *VUregsn) { _vuRegsSUBA(&VU0, VUregsn); } +void VU0regsMI_SUBAi(_VURegsNum *VUregsn) { _vuRegsSUBAi(&VU0, VUregsn); } +void VU0regsMI_SUBAq(_VURegsNum *VUregsn) { _vuRegsSUBAq(&VU0, VUregsn); } +void VU0regsMI_SUBAx(_VURegsNum *VUregsn) { _vuRegsSUBAx(&VU0, VUregsn); } +void VU0regsMI_SUBAy(_VURegsNum *VUregsn) { _vuRegsSUBAy(&VU0, VUregsn); } +void VU0regsMI_SUBAz(_VURegsNum *VUregsn) { _vuRegsSUBAz(&VU0, VUregsn); } +void VU0regsMI_SUBAw(_VURegsNum *VUregsn) { _vuRegsSUBAw(&VU0, VUregsn); } +void VU0regsMI_MUL(_VURegsNum *VUregsn) { _vuRegsMUL(&VU0, VUregsn); } +void VU0regsMI_MULi(_VURegsNum *VUregsn) { _vuRegsMULi(&VU0, VUregsn); } +void VU0regsMI_MULq(_VURegsNum *VUregsn) { _vuRegsMULq(&VU0, VUregsn); } +void VU0regsMI_MULx(_VURegsNum *VUregsn) { _vuRegsMULx(&VU0, VUregsn); } +void VU0regsMI_MULy(_VURegsNum *VUregsn) { _vuRegsMULy(&VU0, VUregsn); } +void VU0regsMI_MULz(_VURegsNum *VUregsn) { _vuRegsMULz(&VU0, VUregsn); } +void VU0regsMI_MULw(_VURegsNum *VUregsn) { _vuRegsMULw(&VU0, VUregsn); } +void VU0regsMI_MULA(_VURegsNum *VUregsn) { _vuRegsMULA(&VU0, VUregsn); } +void VU0regsMI_MULAi(_VURegsNum *VUregsn) { _vuRegsMULAi(&VU0, VUregsn); } +void VU0regsMI_MULAq(_VURegsNum *VUregsn) { _vuRegsMULAq(&VU0, VUregsn); } +void VU0regsMI_MULAx(_VURegsNum *VUregsn) { _vuRegsMULAx(&VU0, VUregsn); } +void VU0regsMI_MULAy(_VURegsNum *VUregsn) { _vuRegsMULAy(&VU0, VUregsn); } +void VU0regsMI_MULAz(_VURegsNum *VUregsn) { _vuRegsMULAz(&VU0, VUregsn); } +void VU0regsMI_MULAw(_VURegsNum *VUregsn) { _vuRegsMULAw(&VU0, VUregsn); } +void VU0regsMI_MADD(_VURegsNum *VUregsn) { _vuRegsMADD(&VU0, VUregsn); } +void VU0regsMI_MADDi(_VURegsNum *VUregsn) { _vuRegsMADDi(&VU0, VUregsn); } +void VU0regsMI_MADDq(_VURegsNum *VUregsn) { _vuRegsMADDq(&VU0, VUregsn); } +void VU0regsMI_MADDx(_VURegsNum *VUregsn) { _vuRegsMADDx(&VU0, VUregsn); } +void VU0regsMI_MADDy(_VURegsNum *VUregsn) { _vuRegsMADDy(&VU0, VUregsn); } +void VU0regsMI_MADDz(_VURegsNum *VUregsn) { _vuRegsMADDz(&VU0, VUregsn); } +void VU0regsMI_MADDw(_VURegsNum *VUregsn) { _vuRegsMADDw(&VU0, VUregsn); } +void VU0regsMI_MADDA(_VURegsNum *VUregsn) { _vuRegsMADDA(&VU0, VUregsn); } +void VU0regsMI_MADDAi(_VURegsNum *VUregsn) { _vuRegsMADDAi(&VU0, VUregsn); } +void VU0regsMI_MADDAq(_VURegsNum *VUregsn) { _vuRegsMADDAq(&VU0, VUregsn); } +void VU0regsMI_MADDAx(_VURegsNum *VUregsn) { _vuRegsMADDAx(&VU0, VUregsn); } +void VU0regsMI_MADDAy(_VURegsNum *VUregsn) { _vuRegsMADDAy(&VU0, VUregsn); } +void VU0regsMI_MADDAz(_VURegsNum *VUregsn) { _vuRegsMADDAz(&VU0, VUregsn); } +void VU0regsMI_MADDAw(_VURegsNum *VUregsn) { _vuRegsMADDAw(&VU0, VUregsn); } +void VU0regsMI_MSUB(_VURegsNum *VUregsn) { _vuRegsMSUB(&VU0, VUregsn); } +void VU0regsMI_MSUBi(_VURegsNum *VUregsn) { _vuRegsMSUBi(&VU0, VUregsn); } +void VU0regsMI_MSUBq(_VURegsNum *VUregsn) { _vuRegsMSUBq(&VU0, VUregsn); } +void VU0regsMI_MSUBx(_VURegsNum *VUregsn) { _vuRegsMSUBx(&VU0, VUregsn); } +void VU0regsMI_MSUBy(_VURegsNum *VUregsn) { _vuRegsMSUBy(&VU0, VUregsn); } +void VU0regsMI_MSUBz(_VURegsNum *VUregsn) { _vuRegsMSUBz(&VU0, VUregsn); } +void VU0regsMI_MSUBw(_VURegsNum *VUregsn) { _vuRegsMSUBw(&VU0, VUregsn); } +void VU0regsMI_MSUBA(_VURegsNum *VUregsn) { _vuRegsMSUBA(&VU0, VUregsn); } +void VU0regsMI_MSUBAi(_VURegsNum *VUregsn) { _vuRegsMSUBAi(&VU0, VUregsn); } +void VU0regsMI_MSUBAq(_VURegsNum *VUregsn) { _vuRegsMSUBAq(&VU0, VUregsn); } +void VU0regsMI_MSUBAx(_VURegsNum *VUregsn) { _vuRegsMSUBAx(&VU0, VUregsn); } +void VU0regsMI_MSUBAy(_VURegsNum *VUregsn) { _vuRegsMSUBAy(&VU0, VUregsn); } +void VU0regsMI_MSUBAz(_VURegsNum *VUregsn) { _vuRegsMSUBAz(&VU0, VUregsn); } +void VU0regsMI_MSUBAw(_VURegsNum *VUregsn) { _vuRegsMSUBAw(&VU0, VUregsn); } +void VU0regsMI_MAX(_VURegsNum *VUregsn) { _vuRegsMAX(&VU0, VUregsn); } +void VU0regsMI_MAXi(_VURegsNum *VUregsn) { _vuRegsMAXi(&VU0, VUregsn); } +void VU0regsMI_MAXx(_VURegsNum *VUregsn) { _vuRegsMAXx(&VU0, VUregsn); } +void VU0regsMI_MAXy(_VURegsNum *VUregsn) { _vuRegsMAXy(&VU0, VUregsn); } +void VU0regsMI_MAXz(_VURegsNum *VUregsn) { _vuRegsMAXz(&VU0, VUregsn); } +void VU0regsMI_MAXw(_VURegsNum *VUregsn) { _vuRegsMAXw(&VU0, VUregsn); } +void VU0regsMI_MINI(_VURegsNum *VUregsn) { _vuRegsMINI(&VU0, VUregsn); } +void VU0regsMI_MINIi(_VURegsNum *VUregsn) { _vuRegsMINIi(&VU0, VUregsn); } +void VU0regsMI_MINIx(_VURegsNum *VUregsn) { _vuRegsMINIx(&VU0, VUregsn); } +void VU0regsMI_MINIy(_VURegsNum *VUregsn) { _vuRegsMINIy(&VU0, VUregsn); } +void VU0regsMI_MINIz(_VURegsNum *VUregsn) { _vuRegsMINIz(&VU0, VUregsn); } +void VU0regsMI_MINIw(_VURegsNum *VUregsn) { _vuRegsMINIw(&VU0, VUregsn); } +void VU0regsMI_OPMULA(_VURegsNum *VUregsn) { _vuRegsOPMULA(&VU0, VUregsn); } +void VU0regsMI_OPMSUB(_VURegsNum *VUregsn) { _vuRegsOPMSUB(&VU0, VUregsn); } +void VU0regsMI_NOP(_VURegsNum *VUregsn) { _vuRegsNOP(&VU0, VUregsn); } +void VU0regsMI_FTOI0(_VURegsNum *VUregsn) { _vuRegsFTOI0(&VU0, VUregsn); } +void VU0regsMI_FTOI4(_VURegsNum *VUregsn) { _vuRegsFTOI4(&VU0, VUregsn); } +void VU0regsMI_FTOI12(_VURegsNum *VUregsn) { _vuRegsFTOI12(&VU0, VUregsn); } +void VU0regsMI_FTOI15(_VURegsNum *VUregsn) { _vuRegsFTOI15(&VU0, VUregsn); } +void VU0regsMI_ITOF0(_VURegsNum *VUregsn) { _vuRegsITOF0(&VU0, VUregsn); } +void VU0regsMI_ITOF4(_VURegsNum *VUregsn) { _vuRegsITOF4(&VU0, VUregsn); } +void VU0regsMI_ITOF12(_VURegsNum *VUregsn) { _vuRegsITOF12(&VU0, VUregsn); } +void VU0regsMI_ITOF15(_VURegsNum *VUregsn) { _vuRegsITOF15(&VU0, VUregsn); } +void VU0regsMI_CLIP(_VURegsNum *VUregsn) { _vuRegsCLIP(&VU0, VUregsn); } + +/*****************************************/ +/* VU Micromode Lower instructions */ +/*****************************************/ + +void VU0regsMI_DIV(_VURegsNum *VUregsn) { _vuRegsDIV(&VU0, VUregsn); } +void VU0regsMI_SQRT(_VURegsNum *VUregsn) { _vuRegsSQRT(&VU0, VUregsn); } +void VU0regsMI_RSQRT(_VURegsNum *VUregsn) { _vuRegsRSQRT(&VU0, VUregsn); } +void VU0regsMI_IADD(_VURegsNum *VUregsn) { _vuRegsIADD(&VU0, VUregsn); } +void VU0regsMI_IADDI(_VURegsNum *VUregsn) { _vuRegsIADDI(&VU0, VUregsn); } +void VU0regsMI_IADDIU(_VURegsNum *VUregsn) { _vuRegsIADDIU(&VU0, VUregsn); } +void VU0regsMI_IAND(_VURegsNum *VUregsn) { _vuRegsIAND(&VU0, VUregsn); } +void VU0regsMI_IOR(_VURegsNum *VUregsn) { _vuRegsIOR(&VU0, VUregsn); } +void VU0regsMI_ISUB(_VURegsNum *VUregsn) { _vuRegsISUB(&VU0, VUregsn); } +void VU0regsMI_ISUBIU(_VURegsNum *VUregsn) { _vuRegsISUBIU(&VU0, VUregsn); } +void VU0regsMI_MOVE(_VURegsNum *VUregsn) { _vuRegsMOVE(&VU0, VUregsn); } +void VU0regsMI_MFIR(_VURegsNum *VUregsn) { _vuRegsMFIR(&VU0, VUregsn); } +void VU0regsMI_MTIR(_VURegsNum *VUregsn) { _vuRegsMTIR(&VU0, VUregsn); } +void VU0regsMI_MR32(_VURegsNum *VUregsn) { _vuRegsMR32(&VU0, VUregsn); } +void VU0regsMI_LQ(_VURegsNum *VUregsn) { _vuRegsLQ(&VU0, VUregsn); } +void VU0regsMI_LQD(_VURegsNum *VUregsn) { _vuRegsLQD(&VU0, VUregsn); } +void VU0regsMI_LQI(_VURegsNum *VUregsn) { _vuRegsLQI(&VU0, VUregsn); } +void VU0regsMI_SQ(_VURegsNum *VUregsn) { _vuRegsSQ(&VU0, VUregsn); } +void VU0regsMI_SQD(_VURegsNum *VUregsn) { _vuRegsSQD(&VU0, VUregsn); } +void VU0regsMI_SQI(_VURegsNum *VUregsn) { _vuRegsSQI(&VU0, VUregsn); } +void VU0regsMI_ILW(_VURegsNum *VUregsn) { _vuRegsILW(&VU0, VUregsn); } +void VU0regsMI_ISW(_VURegsNum *VUregsn) { _vuRegsISW(&VU0, VUregsn); } +void VU0regsMI_ILWR(_VURegsNum *VUregsn) { _vuRegsILWR(&VU0, VUregsn); } +void VU0regsMI_ISWR(_VURegsNum *VUregsn) { _vuRegsISWR(&VU0, VUregsn); } +void VU0regsMI_RINIT(_VURegsNum *VUregsn) { _vuRegsRINIT(&VU0, VUregsn); } +void VU0regsMI_RGET(_VURegsNum *VUregsn) { _vuRegsRGET(&VU0, VUregsn); } +void VU0regsMI_RNEXT(_VURegsNum *VUregsn) { _vuRegsRNEXT(&VU0, VUregsn); } +void VU0regsMI_RXOR(_VURegsNum *VUregsn) { _vuRegsRXOR(&VU0, VUregsn); } +void VU0regsMI_WAITQ(_VURegsNum *VUregsn) { _vuRegsWAITQ(&VU0, VUregsn); } +void VU0regsMI_FSAND(_VURegsNum *VUregsn) { _vuRegsFSAND(&VU0, VUregsn); } +void VU0regsMI_FSEQ(_VURegsNum *VUregsn) { _vuRegsFSEQ(&VU0, VUregsn); } +void VU0regsMI_FSOR(_VURegsNum *VUregsn) { _vuRegsFSOR(&VU0, VUregsn); } +void VU0regsMI_FSSET(_VURegsNum *VUregsn) { _vuRegsFSSET(&VU0, VUregsn); } +void VU0regsMI_FMAND(_VURegsNum *VUregsn) { _vuRegsFMAND(&VU0, VUregsn); } +void VU0regsMI_FMEQ(_VURegsNum *VUregsn) { _vuRegsFMEQ(&VU0, VUregsn); } +void VU0regsMI_FMOR(_VURegsNum *VUregsn) { _vuRegsFMOR(&VU0, VUregsn); } +void VU0regsMI_FCAND(_VURegsNum *VUregsn) { _vuRegsFCAND(&VU0, VUregsn); } +void VU0regsMI_FCEQ(_VURegsNum *VUregsn) { _vuRegsFCEQ(&VU0, VUregsn); } +void VU0regsMI_FCOR(_VURegsNum *VUregsn) { _vuRegsFCOR(&VU0, VUregsn); } +void VU0regsMI_FCSET(_VURegsNum *VUregsn) { _vuRegsFCSET(&VU0, VUregsn); } +void VU0regsMI_FCGET(_VURegsNum *VUregsn) { _vuRegsFCGET(&VU0, VUregsn); } +void VU0regsMI_IBEQ(_VURegsNum *VUregsn) { _vuRegsIBEQ(&VU0, VUregsn); } +void VU0regsMI_IBGEZ(_VURegsNum *VUregsn) { _vuRegsIBGEZ(&VU0, VUregsn); } +void VU0regsMI_IBGTZ(_VURegsNum *VUregsn) { _vuRegsIBGTZ(&VU0, VUregsn); } +void VU0regsMI_IBLTZ(_VURegsNum *VUregsn) { _vuRegsIBLTZ(&VU0, VUregsn); } +void VU0regsMI_IBLEZ(_VURegsNum *VUregsn) { _vuRegsIBLEZ(&VU0, VUregsn); } +void VU0regsMI_IBNE(_VURegsNum *VUregsn) { _vuRegsIBNE(&VU0, VUregsn); } +void VU0regsMI_B(_VURegsNum *VUregsn) { _vuRegsB(&VU0, VUregsn); } +void VU0regsMI_BAL(_VURegsNum *VUregsn) { _vuRegsBAL(&VU0, VUregsn); } +void VU0regsMI_JR(_VURegsNum *VUregsn) { _vuRegsJR(&VU0, VUregsn); } +void VU0regsMI_JALR(_VURegsNum *VUregsn) { _vuRegsJALR(&VU0, VUregsn); } +void VU0regsMI_MFP(_VURegsNum *VUregsn) { _vuRegsMFP(&VU0, VUregsn); } +void VU0regsMI_WAITP(_VURegsNum *VUregsn) { _vuRegsWAITP(&VU0, VUregsn); } +void VU0regsMI_ESADD(_VURegsNum *VUregsn) { _vuRegsESADD(&VU0, VUregsn); } +void VU0regsMI_ERSADD(_VURegsNum *VUregsn) { _vuRegsERSADD(&VU0, VUregsn); } +void VU0regsMI_ELENG(_VURegsNum *VUregsn) { _vuRegsELENG(&VU0, VUregsn); } +void VU0regsMI_ERLENG(_VURegsNum *VUregsn) { _vuRegsERLENG(&VU0, VUregsn); } +void VU0regsMI_EATANxy(_VURegsNum *VUregsn) { _vuRegsEATANxy(&VU0, VUregsn); } +void VU0regsMI_EATANxz(_VURegsNum *VUregsn) { _vuRegsEATANxz(&VU0, VUregsn); } +void VU0regsMI_ESUM(_VURegsNum *VUregsn) { _vuRegsESUM(&VU0, VUregsn); } +void VU0regsMI_ERCPR(_VURegsNum *VUregsn) { _vuRegsERCPR(&VU0, VUregsn); } +void VU0regsMI_ESQRT(_VURegsNum *VUregsn) { _vuRegsESQRT(&VU0, VUregsn); } +void VU0regsMI_ERSQRT(_VURegsNum *VUregsn) { _vuRegsERSQRT(&VU0, VUregsn); } +void VU0regsMI_ESIN(_VURegsNum *VUregsn) { _vuRegsESIN(&VU0, VUregsn); } +void VU0regsMI_EATAN(_VURegsNum *VUregsn) { _vuRegsEATAN(&VU0, VUregsn); } +void VU0regsMI_EEXP(_VURegsNum *VUregsn) { _vuRegsEEXP(&VU0, VUregsn); } +void VU0regsMI_XITOP(_VURegsNum *VUregsn) { _vuRegsXITOP(&VU0, VUregsn); } +void VU0regsMI_XGKICK(_VURegsNum *VUregsn) { _vuRegsXGKICK(&VU0, VUregsn); } +void VU0regsMI_XTOP(_VURegsNum *VUregsn) { _vuRegsXTOP(&VU0, VUregsn); } diff --git a/pcsx2/VU0microInterp.cpp b/pcsx2/VU0microInterp.cpp new file mode 100644 index 0000000000..bee2122011 --- /dev/null +++ b/pcsx2/VU0microInterp.cpp @@ -0,0 +1,253 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "DebugTools/Debug.h" +#include "VUmicro.h" + +extern void _vuFlushAll(VURegs* VU); + +_vuTables(VU0, VU0); + +void _vu0ExecUpper(VURegs* VU, u32 *ptr) { + VU->code = ptr[1]; + IdebugUPPER(VU0); + VU0_UPPER_OPCODE[VU->code & 0x3f](); +} + +void _vu0ExecLower(VURegs* VU, u32 *ptr) { + VU->code = ptr[0]; + IdebugLOWER(VU0); + VU0_LOWER_OPCODE[VU->code >> 25](); +} + +int vu0branch = 0; +static void _vu0Exec(VURegs* VU) +{ + _VURegsNum lregs; + _VURegsNum uregs; + VECTOR _VF; + VECTOR _VFc; + REG_VI _VI; + REG_VI _VIc; + u32 *ptr; + int vfreg; + int vireg; + int discard=0; + + if(VU0.VI[REG_TPC].UL >= VU0.maxmicro){ +#ifdef CPU_LOG + SysPrintf("VU0 memory overflow!!: %x\n", VU->VI[REG_TPC].UL); +#endif + VU0.VI[REG_VPU_STAT].UL&= ~0x1; + VU->cycle++; + return; + } + + ptr = (u32*)&VU->Micro[VU->VI[REG_TPC].UL]; + VU->VI[REG_TPC].UL+=8; + + if (ptr[1] & 0x40000000) { + VU->ebit = 2; + } + if (ptr[1] & 0x20000000) { /* M flag */ + VU->flags|= VUFLAG_MFLAGSET; +// SysPrintf("fixme: M flag set\n"); + } + if (ptr[1] & 0x10000000) { /* D flag */ + if (VU0.VI[REG_FBRST].UL & 0x4) { + VU0.VI[REG_VPU_STAT].UL|= 0x2; + hwIntcIrq(INTC_VU0); + } + } + if (ptr[1] & 0x08000000) { /* T flag */ + if (VU0.VI[REG_FBRST].UL & 0x8) { + VU0.VI[REG_VPU_STAT].UL|= 0x4; + hwIntcIrq(INTC_VU0); + } + } + + VU->code = ptr[1]; + VU0regs_UPPER_OPCODE[VU->code & 0x3f](&uregs); +#ifndef INT_VUSTALLHACK + _vuTestUpperStalls(VU, &uregs); +#endif + + /* check upper flags */ + if (ptr[1] & 0x80000000) { /* I flag */ + _vu0ExecUpper(VU, ptr); + + VU->VI[REG_I].UL = ptr[0]; + memset(&lregs, 0, sizeof(lregs)); + } else { + VU->code = ptr[0]; + VU0regs_LOWER_OPCODE[VU->code >> 25](&lregs); +#ifndef INT_VUSTALLHACK + _vuTestLowerStalls(VU, &lregs); +#endif + + vu0branch = lregs.pipe == VUPIPE_BRANCH; + + vfreg = 0; vireg = 0; + if (uregs.VFwrite) { + if (lregs.VFwrite == uregs.VFwrite) { +// SysPrintf("*PCSX2*: Warning, VF write to the same reg in both lower/upper cycle\n"); + discard = 1; + } + if (lregs.VFread0 == uregs.VFwrite || + lregs.VFread1 == uregs.VFwrite) { +// SysPrintf("saving reg %d at pc=%x\n", i, VU->VI[REG_TPC].UL); + _VF = VU->VF[uregs.VFwrite]; + vfreg = uregs.VFwrite; + } + } + if (uregs.VIread & (1 << REG_CLIP_FLAG)) { + if (lregs.VIwrite & (1 << REG_CLIP_FLAG)) { + SysPrintf("*PCSX2*: Warning, VI write to the same reg in both lower/upper cycle\n"); + discard = 1; + } + if (lregs.VIread & (1 << REG_CLIP_FLAG)) { + _VI = VU0.VI[REG_CLIP_FLAG]; + vireg = REG_CLIP_FLAG; + } + } + + _vu0ExecUpper(VU, ptr); + + if (discard == 0) { + if (vfreg) { + _VFc = VU->VF[vfreg]; + VU->VF[vfreg] = _VF; + } + if (vireg) { + _VIc = VU->VI[vireg]; + VU->VI[vireg] = _VI; + } + + _vu0ExecLower(VU, ptr); + + if (vfreg) { + VU->VF[vfreg] = _VFc; + } + if (vireg) { + VU->VI[vireg] = _VIc; + } + } + } + _vuAddUpperStalls(VU, &uregs); + + if (!(ptr[1] & 0x80000000)) + _vuAddLowerStalls(VU, &lregs); + + _vuTestPipes(VU); + + if (VU->branch > 0) { + VU->branch--; + if (VU->branch == 0) { + VU->VI[REG_TPC].UL = VU->branchpc; + } + } + + if( VU->ebit > 0 ) { + if( VU->ebit-- == 1 ) { + _vuFlushAll(VU); + VU0.VI[REG_VPU_STAT].UL&= ~0x1; /* E flag */ + vif0Regs->stat&= ~0x4; + } + } +} + +void vu0Exec(VURegs* VU) +{ + if (VU->VI[REG_TPC].UL >= VU->maxmicro) { +#ifdef CPU_LOG + SysPrintf("VU0 memory overflow!!: %x\n", VU->VI[REG_TPC].UL); +#endif + VU0.VI[REG_VPU_STAT].UL&= ~0x1; + } else { + _vu0Exec(VU); + } + VU->cycle++; + + if (VU->VI[0].UL != 0) DbgCon::Error("VI[0] != 0!!!!\n"); + if (VU->VF[0].f.x != 0.0f) DbgCon::Error("VF[0].x != 0.0!!!!\n"); + if (VU->VF[0].f.y != 0.0f) DbgCon::Error("VF[0].y != 0.0!!!!\n"); + if (VU->VF[0].f.z != 0.0f) DbgCon::Error("VF[0].z != 0.0!!!!\n"); + if (VU->VF[0].f.w != 1.0f) DbgCon::Error("VF[0].w != 1.0!!!!\n"); +} + +namespace VU0micro +{ + void intAlloc() + { + } + + void intShutdown() + { + } + + void __fastcall intClear(u32 Addr, u32 Size) + { + } + + static void intReset() + { + } + + static void intStep() + { + vu0Exec( &VU0 ); + } + + static void intExecuteBlock() + { + int i; + + #ifdef _DEBUG + int prevbranch; + #endif + + for (i = 128; i--;) { + + if ((VU0.VI[REG_VPU_STAT].UL & 0x1) == 0) + break; + + #ifdef _DEBUG + prevbranch = vu0branch; + #endif + vu0Exec(&VU0); + } + + if( i < 0 && (VU0.branch || VU0.ebit) ) { + // execute one more + vu0Exec(&VU0); + } + } +} + +using namespace VU0micro; + +const VUmicroCpu intVU0 = +{ + intReset +, intStep +, intExecuteBlock +, intClear +}; diff --git a/pcsx2/VU1micro.cpp b/pcsx2/VU1micro.cpp new file mode 100644 index 0000000000..54fdd032a5 --- /dev/null +++ b/pcsx2/VU1micro.cpp @@ -0,0 +1,454 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// This module contains code shared by both the dynarec and interpreter versions +// of the VU0 micro. + +#include "PrecompiledHeader.h" + +#include + +#include "Common.h" +#include "VU.h" +#include "VUops.h" +#include "VUmicro.h" +#include "iVUmicro.h" +#include "iVUzerorec.h" + +VURegs* g_pVU1; + +#ifdef _DEBUG +u32 vudump = 0; +#endif + +#define VF_VAL(x) ((x==0x80000000)?0:(x)) + +void iDumpVU1Registers() +{ + // fixme: This code is outdated, broken, and lacks printed labels. + // Needs heavy mods to be useful. +#if 0 + int i; +// static int icount = 0; +// __Log("%x\n", icount); + for(i = 1; i < 32; ++i) { +// __Log("v%d: w%f(%x) z%f(%x) y%f(%x) x%f(%x), vi: ", i, VU1.VF[i].F[3], VU1.VF[i].UL[3], VU1.VF[i].F[2], VU1.VF[i].UL[2], +// VU1.VF[i].F[1], VU1.VF[i].UL[1], VU1.VF[i].F[0], VU1.VF[i].UL[0]); + //__Log("v%d: %f %f %f %f, vi: ", i, VU1.VF[i].F[3], VU1.VF[i].F[2], VU1.VF[i].F[1], VU1.VF[i].F[0]); + __Log("v%d: %x %x %x %x, vi: ", i, VF_VAL(VU1.VF[i].UL[3]), VF_VAL(VU1.VF[i].UL[2]), VF_VAL(VU1.VF[i].UL[1]), VF_VAL(VU1.VF[i].UL[0])); + if( i == REG_Q || i == REG_P ) __Log("%f\n", VU1.VI[i].F); + //else __Log("%x\n", VU1.VI[i].UL); + else __Log("%x\n", (i==REG_STATUS_FLAG||i==REG_MAC_FLAG||i==REG_CLIP_FLAG)?0:VU1.VI[i].UL); + } + __Log("vfACC: %f %f %f %f\n", VU1.ACC.F[3], VU1.ACC.F[2], VU1.ACC.F[1], VU1.ACC.F[0]); +#endif +} + +// This is called by the COP2 as per the CTC instruction +void vu1ResetRegs() +{ + VU0.VI[REG_VPU_STAT].UL &= ~0xff00; // stop vu1 + VU0.VI[REG_FBRST].UL &= ~0xff00; // stop vu1 + vif1Regs->stat &= ~4; +} + +static int count; + +void vu1ExecMicro(u32 addr) +{ + while(VU0.VI[REG_VPU_STAT].UL & 0x100) + { + VUM_LOG("vu1ExecMicro > Stalling until current microprogram finishes"); + CpuVU1.ExecuteBlock(); + } + + VUM_LOG("vu1ExecMicro %x\n", addr); + VUM_LOG("vu1ExecMicro %x (count=%d)\n", addr, count++); + + VU0.VI[REG_VPU_STAT].UL|= 0x100; + VU0.VI[REG_VPU_STAT].UL&= ~0x7E000; + vif1Regs->stat|= 0x4; + if (addr != -1) VU1.VI[REG_TPC].UL = addr; + _vuExecMicroDebug(VU1); + + CpuVU1.ExecuteBlock(); +} + +_vuRegsTables(VU1, VU1regs); + +void VU1unknown() { + //assert(0); + CPU_LOG("Unknown VU micromode opcode called\n"); +} + +void VU1regsunknown(_VURegsNum *VUregsn) { + //assert(0); + CPU_LOG("Unknown VU micromode opcode called\n"); +} + + + +/****************************************/ +/* VU Micromode Upper instructions */ +/****************************************/ + +void VU1MI_ABS() { _vuABS(&VU1); } +void VU1MI_ADD() { _vuADD(&VU1); } +void VU1MI_ADDi() { _vuADDi(&VU1); } +void VU1MI_ADDq() { _vuADDq(&VU1); } +void VU1MI_ADDx() { _vuADDx(&VU1); } +void VU1MI_ADDy() { _vuADDy(&VU1); } +void VU1MI_ADDz() { _vuADDz(&VU1); } +void VU1MI_ADDw() { _vuADDw(&VU1); } +void VU1MI_ADDA() { _vuADDA(&VU1); } +void VU1MI_ADDAi() { _vuADDAi(&VU1); } +void VU1MI_ADDAq() { _vuADDAq(&VU1); } +void VU1MI_ADDAx() { _vuADDAx(&VU1); } +void VU1MI_ADDAy() { _vuADDAy(&VU1); } +void VU1MI_ADDAz() { _vuADDAz(&VU1); } +void VU1MI_ADDAw() { _vuADDAw(&VU1); } +void VU1MI_SUB() { _vuSUB(&VU1); } +void VU1MI_SUBi() { _vuSUBi(&VU1); } +void VU1MI_SUBq() { _vuSUBq(&VU1); } +void VU1MI_SUBx() { _vuSUBx(&VU1); } +void VU1MI_SUBy() { _vuSUBy(&VU1); } +void VU1MI_SUBz() { _vuSUBz(&VU1); } +void VU1MI_SUBw() { _vuSUBw(&VU1); } +void VU1MI_SUBA() { _vuSUBA(&VU1); } +void VU1MI_SUBAi() { _vuSUBAi(&VU1); } +void VU1MI_SUBAq() { _vuSUBAq(&VU1); } +void VU1MI_SUBAx() { _vuSUBAx(&VU1); } +void VU1MI_SUBAy() { _vuSUBAy(&VU1); } +void VU1MI_SUBAz() { _vuSUBAz(&VU1); } +void VU1MI_SUBAw() { _vuSUBAw(&VU1); } +void VU1MI_MUL() { _vuMUL(&VU1); } +void VU1MI_MULi() { _vuMULi(&VU1); } +void VU1MI_MULq() { _vuMULq(&VU1); } +void VU1MI_MULx() { _vuMULx(&VU1); } +void VU1MI_MULy() { _vuMULy(&VU1); } +void VU1MI_MULz() { _vuMULz(&VU1); } +void VU1MI_MULw() { _vuMULw(&VU1); } +void VU1MI_MULA() { _vuMULA(&VU1); } +void VU1MI_MULAi() { _vuMULAi(&VU1); } +void VU1MI_MULAq() { _vuMULAq(&VU1); } +void VU1MI_MULAx() { _vuMULAx(&VU1); } +void VU1MI_MULAy() { _vuMULAy(&VU1); } +void VU1MI_MULAz() { _vuMULAz(&VU1); } +void VU1MI_MULAw() { _vuMULAw(&VU1); } +void VU1MI_MADD() { _vuMADD(&VU1); } +void VU1MI_MADDi() { _vuMADDi(&VU1); } +void VU1MI_MADDq() { _vuMADDq(&VU1); } +void VU1MI_MADDx() { _vuMADDx(&VU1); } +void VU1MI_MADDy() { _vuMADDy(&VU1); } +void VU1MI_MADDz() { _vuMADDz(&VU1); } +void VU1MI_MADDw() { _vuMADDw(&VU1); } +void VU1MI_MADDA() { _vuMADDA(&VU1); } +void VU1MI_MADDAi() { _vuMADDAi(&VU1); } +void VU1MI_MADDAq() { _vuMADDAq(&VU1); } +void VU1MI_MADDAx() { _vuMADDAx(&VU1); } +void VU1MI_MADDAy() { _vuMADDAy(&VU1); } +void VU1MI_MADDAz() { _vuMADDAz(&VU1); } +void VU1MI_MADDAw() { _vuMADDAw(&VU1); } +void VU1MI_MSUB() { _vuMSUB(&VU1); } +void VU1MI_MSUBi() { _vuMSUBi(&VU1); } +void VU1MI_MSUBq() { _vuMSUBq(&VU1); } +void VU1MI_MSUBx() { _vuMSUBx(&VU1); } +void VU1MI_MSUBy() { _vuMSUBy(&VU1); } +void VU1MI_MSUBz() { _vuMSUBz(&VU1); } +void VU1MI_MSUBw() { _vuMSUBw(&VU1); } +void VU1MI_MSUBA() { _vuMSUBA(&VU1); } +void VU1MI_MSUBAi() { _vuMSUBAi(&VU1); } +void VU1MI_MSUBAq() { _vuMSUBAq(&VU1); } +void VU1MI_MSUBAx() { _vuMSUBAx(&VU1); } +void VU1MI_MSUBAy() { _vuMSUBAy(&VU1); } +void VU1MI_MSUBAz() { _vuMSUBAz(&VU1); } +void VU1MI_MSUBAw() { _vuMSUBAw(&VU1); } +void VU1MI_MAX() { _vuMAX(&VU1); } +void VU1MI_MAXi() { _vuMAXi(&VU1); } +void VU1MI_MAXx() { _vuMAXx(&VU1); } +void VU1MI_MAXy() { _vuMAXy(&VU1); } +void VU1MI_MAXz() { _vuMAXz(&VU1); } +void VU1MI_MAXw() { _vuMAXw(&VU1); } +void VU1MI_MINI() { _vuMINI(&VU1); } +void VU1MI_MINIi() { _vuMINIi(&VU1); } +void VU1MI_MINIx() { _vuMINIx(&VU1); } +void VU1MI_MINIy() { _vuMINIy(&VU1); } +void VU1MI_MINIz() { _vuMINIz(&VU1); } +void VU1MI_MINIw() { _vuMINIw(&VU1); } +void VU1MI_OPMULA() { _vuOPMULA(&VU1); } +void VU1MI_OPMSUB() { _vuOPMSUB(&VU1); } +void VU1MI_NOP() { _vuNOP(&VU1); } +void VU1MI_FTOI0() { _vuFTOI0(&VU1); } +void VU1MI_FTOI4() { _vuFTOI4(&VU1); } +void VU1MI_FTOI12() { _vuFTOI12(&VU1); } +void VU1MI_FTOI15() { _vuFTOI15(&VU1); } +void VU1MI_ITOF0() { _vuITOF0(&VU1); } +void VU1MI_ITOF4() { _vuITOF4(&VU1); } +void VU1MI_ITOF12() { _vuITOF12(&VU1); } +void VU1MI_ITOF15() { _vuITOF15(&VU1); } +void VU1MI_CLIP() { _vuCLIP(&VU1); } + +/*****************************************/ +/* VU Micromode Lower instructions */ +/*****************************************/ + +void VU1MI_DIV() { _vuDIV(&VU1); } +void VU1MI_SQRT() { _vuSQRT(&VU1); } +void VU1MI_RSQRT() { _vuRSQRT(&VU1); } +void VU1MI_IADD() { _vuIADD(&VU1); } +void VU1MI_IADDI() { _vuIADDI(&VU1); } +void VU1MI_IADDIU() { _vuIADDIU(&VU1); } +void VU1MI_IAND() { _vuIAND(&VU1); } +void VU1MI_IOR() { _vuIOR(&VU1); } +void VU1MI_ISUB() { _vuISUB(&VU1); } +void VU1MI_ISUBIU() { _vuISUBIU(&VU1); } +void VU1MI_MOVE() { _vuMOVE(&VU1); } +void VU1MI_MFIR() { _vuMFIR(&VU1); } +void VU1MI_MTIR() { _vuMTIR(&VU1); } +void VU1MI_MR32() { _vuMR32(&VU1); } +void VU1MI_LQ() { _vuLQ(&VU1); } +void VU1MI_LQD() { _vuLQD(&VU1); } +void VU1MI_LQI() { _vuLQI(&VU1); } +void VU1MI_SQ() { _vuSQ(&VU1); } +void VU1MI_SQD() { _vuSQD(&VU1); } +void VU1MI_SQI() { _vuSQI(&VU1); } +void VU1MI_ILW() { _vuILW(&VU1); } +void VU1MI_ISW() { _vuISW(&VU1); } +void VU1MI_ILWR() { _vuILWR(&VU1); } +void VU1MI_ISWR() { _vuISWR(&VU1); } +void VU1MI_RINIT() { _vuRINIT(&VU1); } +void VU1MI_RGET() { _vuRGET(&VU1); } +void VU1MI_RNEXT() { _vuRNEXT(&VU1); } +void VU1MI_RXOR() { _vuRXOR(&VU1); } +void VU1MI_WAITQ() { _vuWAITQ(&VU1); } +void VU1MI_FSAND() { _vuFSAND(&VU1); } +void VU1MI_FSEQ() { _vuFSEQ(&VU1); } +void VU1MI_FSOR() { _vuFSOR(&VU1); } +void VU1MI_FSSET() { _vuFSSET(&VU1); } +void VU1MI_FMAND() { _vuFMAND(&VU1); } +void VU1MI_FMEQ() { _vuFMEQ(&VU1); } +void VU1MI_FMOR() { _vuFMOR(&VU1); } +void VU1MI_FCAND() { _vuFCAND(&VU1); } +void VU1MI_FCEQ() { _vuFCEQ(&VU1); } +void VU1MI_FCOR() { _vuFCOR(&VU1); } +void VU1MI_FCSET() { _vuFCSET(&VU1); } +void VU1MI_FCGET() { _vuFCGET(&VU1); } +void VU1MI_IBEQ() { _vuIBEQ(&VU1); } +void VU1MI_IBGEZ() { _vuIBGEZ(&VU1); } +void VU1MI_IBGTZ() { _vuIBGTZ(&VU1); } +void VU1MI_IBLTZ() { _vuIBLTZ(&VU1); } +void VU1MI_IBLEZ() { _vuIBLEZ(&VU1); } +void VU1MI_IBNE() { _vuIBNE(&VU1); } +void VU1MI_B() { _vuB(&VU1); } +void VU1MI_BAL() { _vuBAL(&VU1); } +void VU1MI_JR() { _vuJR(&VU1); } +void VU1MI_JALR() { _vuJALR(&VU1); } +void VU1MI_MFP() { _vuMFP(&VU1); } +void VU1MI_WAITP() { _vuWAITP(&VU1); } +void VU1MI_ESADD() { _vuESADD(&VU1); } +void VU1MI_ERSADD() { _vuERSADD(&VU1); } +void VU1MI_ELENG() { _vuELENG(&VU1); } +void VU1MI_ERLENG() { _vuERLENG(&VU1); } +void VU1MI_EATANxy() { _vuEATANxy(&VU1); } +void VU1MI_EATANxz() { _vuEATANxz(&VU1); } +void VU1MI_ESUM() { _vuESUM(&VU1); } +void VU1MI_ERCPR() { _vuERCPR(&VU1); } +void VU1MI_ESQRT() { _vuESQRT(&VU1); } +void VU1MI_ERSQRT() { _vuERSQRT(&VU1); } +void VU1MI_ESIN() { _vuESIN(&VU1); } +void VU1MI_EATAN() { _vuEATAN(&VU1); } +void VU1MI_EEXP() { _vuEEXP(&VU1); } +void VU1MI_XITOP() { _vuXITOP(&VU1); } +void VU1MI_XGKICK() { _vuXGKICK(&VU1); } +void VU1MI_XTOP() { _vuXTOP(&VU1); } + + + +/****************************************/ +/* VU Micromode Upper instructions */ +/****************************************/ + +void VU1regsMI_ABS(_VURegsNum *VUregsn) { _vuRegsABS(&VU1, VUregsn); } +void VU1regsMI_ADD(_VURegsNum *VUregsn) { _vuRegsADD(&VU1, VUregsn); } +void VU1regsMI_ADDi(_VURegsNum *VUregsn) { _vuRegsADDi(&VU1, VUregsn); } +void VU1regsMI_ADDq(_VURegsNum *VUregsn) { _vuRegsADDq(&VU1, VUregsn); } +void VU1regsMI_ADDx(_VURegsNum *VUregsn) { _vuRegsADDx(&VU1, VUregsn); } +void VU1regsMI_ADDy(_VURegsNum *VUregsn) { _vuRegsADDy(&VU1, VUregsn); } +void VU1regsMI_ADDz(_VURegsNum *VUregsn) { _vuRegsADDz(&VU1, VUregsn); } +void VU1regsMI_ADDw(_VURegsNum *VUregsn) { _vuRegsADDw(&VU1, VUregsn); } +void VU1regsMI_ADDA(_VURegsNum *VUregsn) { _vuRegsADDA(&VU1, VUregsn); } +void VU1regsMI_ADDAi(_VURegsNum *VUregsn) { _vuRegsADDAi(&VU1, VUregsn); } +void VU1regsMI_ADDAq(_VURegsNum *VUregsn) { _vuRegsADDAq(&VU1, VUregsn); } +void VU1regsMI_ADDAx(_VURegsNum *VUregsn) { _vuRegsADDAx(&VU1, VUregsn); } +void VU1regsMI_ADDAy(_VURegsNum *VUregsn) { _vuRegsADDAy(&VU1, VUregsn); } +void VU1regsMI_ADDAz(_VURegsNum *VUregsn) { _vuRegsADDAz(&VU1, VUregsn); } +void VU1regsMI_ADDAw(_VURegsNum *VUregsn) { _vuRegsADDAw(&VU1, VUregsn); } +void VU1regsMI_SUB(_VURegsNum *VUregsn) { _vuRegsSUB(&VU1, VUregsn); } +void VU1regsMI_SUBi(_VURegsNum *VUregsn) { _vuRegsSUBi(&VU1, VUregsn); } +void VU1regsMI_SUBq(_VURegsNum *VUregsn) { _vuRegsSUBq(&VU1, VUregsn); } +void VU1regsMI_SUBx(_VURegsNum *VUregsn) { _vuRegsSUBx(&VU1, VUregsn); } +void VU1regsMI_SUBy(_VURegsNum *VUregsn) { _vuRegsSUBy(&VU1, VUregsn); } +void VU1regsMI_SUBz(_VURegsNum *VUregsn) { _vuRegsSUBz(&VU1, VUregsn); } +void VU1regsMI_SUBw(_VURegsNum *VUregsn) { _vuRegsSUBw(&VU1, VUregsn); } +void VU1regsMI_SUBA(_VURegsNum *VUregsn) { _vuRegsSUBA(&VU1, VUregsn); } +void VU1regsMI_SUBAi(_VURegsNum *VUregsn) { _vuRegsSUBAi(&VU1, VUregsn); } +void VU1regsMI_SUBAq(_VURegsNum *VUregsn) { _vuRegsSUBAq(&VU1, VUregsn); } +void VU1regsMI_SUBAx(_VURegsNum *VUregsn) { _vuRegsSUBAx(&VU1, VUregsn); } +void VU1regsMI_SUBAy(_VURegsNum *VUregsn) { _vuRegsSUBAy(&VU1, VUregsn); } +void VU1regsMI_SUBAz(_VURegsNum *VUregsn) { _vuRegsSUBAz(&VU1, VUregsn); } +void VU1regsMI_SUBAw(_VURegsNum *VUregsn) { _vuRegsSUBAw(&VU1, VUregsn); } +void VU1regsMI_MUL(_VURegsNum *VUregsn) { _vuRegsMUL(&VU1, VUregsn); } +void VU1regsMI_MULi(_VURegsNum *VUregsn) { _vuRegsMULi(&VU1, VUregsn); } +void VU1regsMI_MULq(_VURegsNum *VUregsn) { _vuRegsMULq(&VU1, VUregsn); } +void VU1regsMI_MULx(_VURegsNum *VUregsn) { _vuRegsMULx(&VU1, VUregsn); } +void VU1regsMI_MULy(_VURegsNum *VUregsn) { _vuRegsMULy(&VU1, VUregsn); } +void VU1regsMI_MULz(_VURegsNum *VUregsn) { _vuRegsMULz(&VU1, VUregsn); } +void VU1regsMI_MULw(_VURegsNum *VUregsn) { _vuRegsMULw(&VU1, VUregsn); } +void VU1regsMI_MULA(_VURegsNum *VUregsn) { _vuRegsMULA(&VU1, VUregsn); } +void VU1regsMI_MULAi(_VURegsNum *VUregsn) { _vuRegsMULAi(&VU1, VUregsn); } +void VU1regsMI_MULAq(_VURegsNum *VUregsn) { _vuRegsMULAq(&VU1, VUregsn); } +void VU1regsMI_MULAx(_VURegsNum *VUregsn) { _vuRegsMULAx(&VU1, VUregsn); } +void VU1regsMI_MULAy(_VURegsNum *VUregsn) { _vuRegsMULAy(&VU1, VUregsn); } +void VU1regsMI_MULAz(_VURegsNum *VUregsn) { _vuRegsMULAz(&VU1, VUregsn); } +void VU1regsMI_MULAw(_VURegsNum *VUregsn) { _vuRegsMULAw(&VU1, VUregsn); } +void VU1regsMI_MADD(_VURegsNum *VUregsn) { _vuRegsMADD(&VU1, VUregsn); } +void VU1regsMI_MADDi(_VURegsNum *VUregsn) { _vuRegsMADDi(&VU1, VUregsn); } +void VU1regsMI_MADDq(_VURegsNum *VUregsn) { _vuRegsMADDq(&VU1, VUregsn); } +void VU1regsMI_MADDx(_VURegsNum *VUregsn) { _vuRegsMADDx(&VU1, VUregsn); } +void VU1regsMI_MADDy(_VURegsNum *VUregsn) { _vuRegsMADDy(&VU1, VUregsn); } +void VU1regsMI_MADDz(_VURegsNum *VUregsn) { _vuRegsMADDz(&VU1, VUregsn); } +void VU1regsMI_MADDw(_VURegsNum *VUregsn) { _vuRegsMADDw(&VU1, VUregsn); } +void VU1regsMI_MADDA(_VURegsNum *VUregsn) { _vuRegsMADDA(&VU1, VUregsn); } +void VU1regsMI_MADDAi(_VURegsNum *VUregsn) { _vuRegsMADDAi(&VU1, VUregsn); } +void VU1regsMI_MADDAq(_VURegsNum *VUregsn) { _vuRegsMADDAq(&VU1, VUregsn); } +void VU1regsMI_MADDAx(_VURegsNum *VUregsn) { _vuRegsMADDAx(&VU1, VUregsn); } +void VU1regsMI_MADDAy(_VURegsNum *VUregsn) { _vuRegsMADDAy(&VU1, VUregsn); } +void VU1regsMI_MADDAz(_VURegsNum *VUregsn) { _vuRegsMADDAz(&VU1, VUregsn); } +void VU1regsMI_MADDAw(_VURegsNum *VUregsn) { _vuRegsMADDAw(&VU1, VUregsn); } +void VU1regsMI_MSUB(_VURegsNum *VUregsn) { _vuRegsMSUB(&VU1, VUregsn); } +void VU1regsMI_MSUBi(_VURegsNum *VUregsn) { _vuRegsMSUBi(&VU1, VUregsn); } +void VU1regsMI_MSUBq(_VURegsNum *VUregsn) { _vuRegsMSUBq(&VU1, VUregsn); } +void VU1regsMI_MSUBx(_VURegsNum *VUregsn) { _vuRegsMSUBx(&VU1, VUregsn); } +void VU1regsMI_MSUBy(_VURegsNum *VUregsn) { _vuRegsMSUBy(&VU1, VUregsn); } +void VU1regsMI_MSUBz(_VURegsNum *VUregsn) { _vuRegsMSUBz(&VU1, VUregsn); } +void VU1regsMI_MSUBw(_VURegsNum *VUregsn) { _vuRegsMSUBw(&VU1, VUregsn); } +void VU1regsMI_MSUBA(_VURegsNum *VUregsn) { _vuRegsMSUBA(&VU1, VUregsn); } +void VU1regsMI_MSUBAi(_VURegsNum *VUregsn) { _vuRegsMSUBAi(&VU1, VUregsn); } +void VU1regsMI_MSUBAq(_VURegsNum *VUregsn) { _vuRegsMSUBAq(&VU1, VUregsn); } +void VU1regsMI_MSUBAx(_VURegsNum *VUregsn) { _vuRegsMSUBAx(&VU1, VUregsn); } +void VU1regsMI_MSUBAy(_VURegsNum *VUregsn) { _vuRegsMSUBAy(&VU1, VUregsn); } +void VU1regsMI_MSUBAz(_VURegsNum *VUregsn) { _vuRegsMSUBAz(&VU1, VUregsn); } +void VU1regsMI_MSUBAw(_VURegsNum *VUregsn) { _vuRegsMSUBAw(&VU1, VUregsn); } +void VU1regsMI_MAX(_VURegsNum *VUregsn) { _vuRegsMAX(&VU1, VUregsn); } +void VU1regsMI_MAXi(_VURegsNum *VUregsn) { _vuRegsMAXi(&VU1, VUregsn); } +void VU1regsMI_MAXx(_VURegsNum *VUregsn) { _vuRegsMAXx(&VU1, VUregsn); } +void VU1regsMI_MAXy(_VURegsNum *VUregsn) { _vuRegsMAXy(&VU1, VUregsn); } +void VU1regsMI_MAXz(_VURegsNum *VUregsn) { _vuRegsMAXz(&VU1, VUregsn); } +void VU1regsMI_MAXw(_VURegsNum *VUregsn) { _vuRegsMAXw(&VU1, VUregsn); } +void VU1regsMI_MINI(_VURegsNum *VUregsn) { _vuRegsMINI(&VU1, VUregsn); } +void VU1regsMI_MINIi(_VURegsNum *VUregsn) { _vuRegsMINIi(&VU1, VUregsn); } +void VU1regsMI_MINIx(_VURegsNum *VUregsn) { _vuRegsMINIx(&VU1, VUregsn); } +void VU1regsMI_MINIy(_VURegsNum *VUregsn) { _vuRegsMINIy(&VU1, VUregsn); } +void VU1regsMI_MINIz(_VURegsNum *VUregsn) { _vuRegsMINIz(&VU1, VUregsn); } +void VU1regsMI_MINIw(_VURegsNum *VUregsn) { _vuRegsMINIw(&VU1, VUregsn); } +void VU1regsMI_OPMULA(_VURegsNum *VUregsn) { _vuRegsOPMULA(&VU1, VUregsn); } +void VU1regsMI_OPMSUB(_VURegsNum *VUregsn) { _vuRegsOPMSUB(&VU1, VUregsn); } +void VU1regsMI_NOP(_VURegsNum *VUregsn) { _vuRegsNOP(&VU1, VUregsn); } +void VU1regsMI_FTOI0(_VURegsNum *VUregsn) { _vuRegsFTOI0(&VU1, VUregsn); } +void VU1regsMI_FTOI4(_VURegsNum *VUregsn) { _vuRegsFTOI4(&VU1, VUregsn); } +void VU1regsMI_FTOI12(_VURegsNum *VUregsn) { _vuRegsFTOI12(&VU1, VUregsn); } +void VU1regsMI_FTOI15(_VURegsNum *VUregsn) { _vuRegsFTOI15(&VU1, VUregsn); } +void VU1regsMI_ITOF0(_VURegsNum *VUregsn) { _vuRegsITOF0(&VU1, VUregsn); } +void VU1regsMI_ITOF4(_VURegsNum *VUregsn) { _vuRegsITOF4(&VU1, VUregsn); } +void VU1regsMI_ITOF12(_VURegsNum *VUregsn) { _vuRegsITOF12(&VU1, VUregsn); } +void VU1regsMI_ITOF15(_VURegsNum *VUregsn) { _vuRegsITOF15(&VU1, VUregsn); } +void VU1regsMI_CLIP(_VURegsNum *VUregsn) { _vuRegsCLIP(&VU1, VUregsn); } + +/*****************************************/ +/* VU Micromode Lower instructions */ +/*****************************************/ + +void VU1regsMI_DIV(_VURegsNum *VUregsn) { _vuRegsDIV(&VU1, VUregsn); } +void VU1regsMI_SQRT(_VURegsNum *VUregsn) { _vuRegsSQRT(&VU1, VUregsn); } +void VU1regsMI_RSQRT(_VURegsNum *VUregsn) { _vuRegsRSQRT(&VU1, VUregsn); } +void VU1regsMI_IADD(_VURegsNum *VUregsn) { _vuRegsIADD(&VU1, VUregsn); } +void VU1regsMI_IADDI(_VURegsNum *VUregsn) { _vuRegsIADDI(&VU1, VUregsn); } +void VU1regsMI_IADDIU(_VURegsNum *VUregsn) { _vuRegsIADDIU(&VU1, VUregsn); } +void VU1regsMI_IAND(_VURegsNum *VUregsn) { _vuRegsIAND(&VU1, VUregsn); } +void VU1regsMI_IOR(_VURegsNum *VUregsn) { _vuRegsIOR(&VU1, VUregsn); } +void VU1regsMI_ISUB(_VURegsNum *VUregsn) { _vuRegsISUB(&VU1, VUregsn); } +void VU1regsMI_ISUBIU(_VURegsNum *VUregsn) { _vuRegsISUBIU(&VU1, VUregsn); } +void VU1regsMI_MOVE(_VURegsNum *VUregsn) { _vuRegsMOVE(&VU1, VUregsn); } +void VU1regsMI_MFIR(_VURegsNum *VUregsn) { _vuRegsMFIR(&VU1, VUregsn); } +void VU1regsMI_MTIR(_VURegsNum *VUregsn) { _vuRegsMTIR(&VU1, VUregsn); } +void VU1regsMI_MR32(_VURegsNum *VUregsn) { _vuRegsMR32(&VU1, VUregsn); } +void VU1regsMI_LQ(_VURegsNum *VUregsn) { _vuRegsLQ(&VU1, VUregsn); } +void VU1regsMI_LQD(_VURegsNum *VUregsn) { _vuRegsLQD(&VU1, VUregsn); } +void VU1regsMI_LQI(_VURegsNum *VUregsn) { _vuRegsLQI(&VU1, VUregsn); } +void VU1regsMI_SQ(_VURegsNum *VUregsn) { _vuRegsSQ(&VU1, VUregsn); } +void VU1regsMI_SQD(_VURegsNum *VUregsn) { _vuRegsSQD(&VU1, VUregsn); } +void VU1regsMI_SQI(_VURegsNum *VUregsn) { _vuRegsSQI(&VU1, VUregsn); } +void VU1regsMI_ILW(_VURegsNum *VUregsn) { _vuRegsILW(&VU1, VUregsn); } +void VU1regsMI_ISW(_VURegsNum *VUregsn) { _vuRegsISW(&VU1, VUregsn); } +void VU1regsMI_ILWR(_VURegsNum *VUregsn) { _vuRegsILWR(&VU1, VUregsn); } +void VU1regsMI_ISWR(_VURegsNum *VUregsn) { _vuRegsISWR(&VU1, VUregsn); } +void VU1regsMI_RINIT(_VURegsNum *VUregsn) { _vuRegsRINIT(&VU1, VUregsn); } +void VU1regsMI_RGET(_VURegsNum *VUregsn) { _vuRegsRGET(&VU1, VUregsn); } +void VU1regsMI_RNEXT(_VURegsNum *VUregsn) { _vuRegsRNEXT(&VU1, VUregsn); } +void VU1regsMI_RXOR(_VURegsNum *VUregsn) { _vuRegsRXOR(&VU1, VUregsn); } +void VU1regsMI_WAITQ(_VURegsNum *VUregsn) { _vuRegsWAITQ(&VU1, VUregsn); } +void VU1regsMI_FSAND(_VURegsNum *VUregsn) { _vuRegsFSAND(&VU1, VUregsn); } +void VU1regsMI_FSEQ(_VURegsNum *VUregsn) { _vuRegsFSEQ(&VU1, VUregsn); } +void VU1regsMI_FSOR(_VURegsNum *VUregsn) { _vuRegsFSOR(&VU1, VUregsn); } +void VU1regsMI_FSSET(_VURegsNum *VUregsn) { _vuRegsFSSET(&VU1, VUregsn); } +void VU1regsMI_FMAND(_VURegsNum *VUregsn) { _vuRegsFMAND(&VU1, VUregsn); } +void VU1regsMI_FMEQ(_VURegsNum *VUregsn) { _vuRegsFMEQ(&VU1, VUregsn); } +void VU1regsMI_FMOR(_VURegsNum *VUregsn) { _vuRegsFMOR(&VU1, VUregsn); } +void VU1regsMI_FCAND(_VURegsNum *VUregsn) { _vuRegsFCAND(&VU1, VUregsn); } +void VU1regsMI_FCEQ(_VURegsNum *VUregsn) { _vuRegsFCEQ(&VU1, VUregsn); } +void VU1regsMI_FCOR(_VURegsNum *VUregsn) { _vuRegsFCOR(&VU1, VUregsn); } +void VU1regsMI_FCSET(_VURegsNum *VUregsn) { _vuRegsFCSET(&VU1, VUregsn); } +void VU1regsMI_FCGET(_VURegsNum *VUregsn) { _vuRegsFCGET(&VU1, VUregsn); } +void VU1regsMI_IBEQ(_VURegsNum *VUregsn) { _vuRegsIBEQ(&VU1, VUregsn); } +void VU1regsMI_IBGEZ(_VURegsNum *VUregsn) { _vuRegsIBGEZ(&VU1, VUregsn); } +void VU1regsMI_IBGTZ(_VURegsNum *VUregsn) { _vuRegsIBGTZ(&VU1, VUregsn); } +void VU1regsMI_IBLTZ(_VURegsNum *VUregsn) { _vuRegsIBLTZ(&VU1, VUregsn); } +void VU1regsMI_IBLEZ(_VURegsNum *VUregsn) { _vuRegsIBLEZ(&VU1, VUregsn); } +void VU1regsMI_IBNE(_VURegsNum *VUregsn) { _vuRegsIBNE(&VU1, VUregsn); } +void VU1regsMI_B(_VURegsNum *VUregsn) { _vuRegsB(&VU1, VUregsn); } +void VU1regsMI_BAL(_VURegsNum *VUregsn) { _vuRegsBAL(&VU1, VUregsn); } +void VU1regsMI_JR(_VURegsNum *VUregsn) { _vuRegsJR(&VU1, VUregsn); } +void VU1regsMI_JALR(_VURegsNum *VUregsn) { _vuRegsJALR(&VU1, VUregsn); } +void VU1regsMI_MFP(_VURegsNum *VUregsn) { _vuRegsMFP(&VU1, VUregsn); } +void VU1regsMI_WAITP(_VURegsNum *VUregsn) { _vuRegsWAITP(&VU1, VUregsn); } +void VU1regsMI_ESADD(_VURegsNum *VUregsn) { _vuRegsESADD(&VU1, VUregsn); } +void VU1regsMI_ERSADD(_VURegsNum *VUregsn) { _vuRegsERSADD(&VU1, VUregsn); } +void VU1regsMI_ELENG(_VURegsNum *VUregsn) { _vuRegsELENG(&VU1, VUregsn); } +void VU1regsMI_ERLENG(_VURegsNum *VUregsn) { _vuRegsERLENG(&VU1, VUregsn); } +void VU1regsMI_EATANxy(_VURegsNum *VUregsn) { _vuRegsEATANxy(&VU1, VUregsn); } +void VU1regsMI_EATANxz(_VURegsNum *VUregsn) { _vuRegsEATANxz(&VU1, VUregsn); } +void VU1regsMI_ESUM(_VURegsNum *VUregsn) { _vuRegsESUM(&VU1, VUregsn); } +void VU1regsMI_ERCPR(_VURegsNum *VUregsn) { _vuRegsERCPR(&VU1, VUregsn); } +void VU1regsMI_ESQRT(_VURegsNum *VUregsn) { _vuRegsESQRT(&VU1, VUregsn); } +void VU1regsMI_ERSQRT(_VURegsNum *VUregsn) { _vuRegsERSQRT(&VU1, VUregsn); } +void VU1regsMI_ESIN(_VURegsNum *VUregsn) { _vuRegsESIN(&VU1, VUregsn); } +void VU1regsMI_EATAN(_VURegsNum *VUregsn) { _vuRegsEATAN(&VU1, VUregsn); } +void VU1regsMI_EEXP(_VURegsNum *VUregsn) { _vuRegsEEXP(&VU1, VUregsn); } +void VU1regsMI_XITOP(_VURegsNum *VUregsn) { _vuRegsXITOP(&VU1, VUregsn); } +void VU1regsMI_XGKICK(_VURegsNum *VUregsn) { _vuRegsXGKICK(&VU1, VUregsn); } +void VU1regsMI_XTOP(_VURegsNum *VUregsn) { _vuRegsXTOP(&VU1, VUregsn); } diff --git a/pcsx2/VU1microInterp.cpp b/pcsx2/VU1microInterp.cpp new file mode 100644 index 0000000000..08329cff81 --- /dev/null +++ b/pcsx2/VU1microInterp.cpp @@ -0,0 +1,237 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "DebugTools/Debug.h" +#include "VUmicro.h" + +extern void _vuFlushAll(VURegs* VU); + +_vuTables(VU1, VU1); + +void _vu1ExecUpper(VURegs* VU, u32 *ptr) { + VU->code = ptr[1]; + IdebugUPPER(VU1); + VU1_UPPER_OPCODE[VU->code & 0x3f](); +} + +void _vu1ExecLower(VURegs* VU, u32 *ptr) { + VU->code = ptr[0]; + IdebugLOWER(VU1); + VU1_LOWER_OPCODE[VU->code >> 25](); +} + +int vu1branch = 0; + +static void _vu1Exec(VURegs* VU) +{ + _VURegsNum lregs; + _VURegsNum uregs; + VECTOR _VF; + VECTOR _VFc; + REG_VI _VI; + REG_VI _VIc; + u32 *ptr; + int vfreg; + int vireg; + int discard=0; + + if(VU->VI[REG_TPC].UL >= VU->maxmicro){ + CPU_LOG("VU1 memory overflow!!: %x\n", VU->VI[REG_TPC].UL); + VU->VI[REG_TPC].UL &= 0x3FFF; + /*VU0.VI[REG_VPU_STAT].UL&= ~0x100; + VU->cycle++; + return;*/ + } + ptr = (u32*)&VU->Micro[VU->VI[REG_TPC].UL]; + VU->VI[REG_TPC].UL+=8; + + if (ptr[1] & 0x40000000) { /* E flag */ + VU->ebit = 2; + } + if (ptr[1] & 0x10000000) { /* D flag */ + if (VU0.VI[REG_FBRST].UL & 0x400) { + VU0.VI[REG_VPU_STAT].UL|= 0x200; + hwIntcIrq(INTC_VU1); + } + } + if (ptr[1] & 0x08000000) { /* T flag */ + if (VU0.VI[REG_FBRST].UL & 0x800) { + VU0.VI[REG_VPU_STAT].UL|= 0x400; + hwIntcIrq(INTC_VU1); + } + } + + VUM_LOG("VU->cycle = %d (flags st=%x;mac=%x;clip=%x,q=%f)\n", VU->cycle, VU->statusflag, VU->macflag, VU->clipflag, VU->q.F); + + VU->code = ptr[1]; + VU1regs_UPPER_OPCODE[VU->code & 0x3f](&uregs); +#ifndef INT_VUSTALLHACK + _vuTestUpperStalls(VU, &uregs); +#endif + + /* check upper flags */ + if (ptr[1] & 0x80000000) { /* I flag */ + _vu1ExecUpper(VU, ptr); + + VU->VI[REG_I].UL = ptr[0]; + } else { + VU->code = ptr[0]; + VU1regs_LOWER_OPCODE[VU->code >> 25](&lregs); +#ifndef INT_VUSTALLHACK + _vuTestLowerStalls(VU, &lregs); +#endif + + vu1branch = lregs.pipe == VUPIPE_BRANCH; + + vfreg = 0; vireg = 0; + if (uregs.VFwrite) { + if (lregs.VFwrite == uregs.VFwrite) { +// SysPrintf("*PCSX2*: Warning, VF write to the same reg in both lower/upper cycle\n"); + discard = 1; + } + if (lregs.VFread0 == uregs.VFwrite || + lregs.VFread1 == uregs.VFwrite) { +// SysPrintf("saving reg %d at pc=%x\n", i, VU->VI[REG_TPC].UL); + _VF = VU->VF[uregs.VFwrite]; + vfreg = uregs.VFwrite; + } + } + if (uregs.VIread & (1 << REG_CLIP_FLAG)) { + if (lregs.VIwrite & (1 << REG_CLIP_FLAG)) { + SysPrintf("*PCSX2*: Warning, VI write to the same reg in both lower/upper cycle\n"); + discard = 1; + } + if (lregs.VIread & (1 << REG_CLIP_FLAG)) { + _VI = VU->VI[REG_CLIP_FLAG]; + vireg = REG_CLIP_FLAG; + } + } + + _vu1ExecUpper(VU, ptr); + + if (discard == 0) { + if (vfreg) { + _VFc = VU->VF[vfreg]; + VU->VF[vfreg] = _VF; + } + if (vireg) { + _VIc = VU->VI[vireg]; + VU->VI[vireg] = _VI; + } + + _vu1ExecLower(VU, ptr); + + if (vfreg) { + VU->VF[vfreg] = _VFc; + } + if (vireg) { + VU->VI[vireg] = _VIc; + } + } + } + _vuAddUpperStalls(VU, &uregs); + _vuAddLowerStalls(VU, &lregs); + + _vuTestPipes(VU); + + if (VU->branch > 0) { + if (VU->branch-- == 1) { + VU->VI[REG_TPC].UL = VU->branchpc; + } + } + + if( VU->ebit > 0 ) { + if( VU->ebit-- == 1 ) { + _vuFlushAll(VU); + VU0.VI[REG_VPU_STAT].UL&= ~0x100; + vif1Regs->stat&= ~0x4; + } + } +} + +void vu1Exec(VURegs* VU) +{ + _vu1Exec(VU); + VU->cycle++; + + if (VU->VI[0].UL != 0) DbgCon::Error("VI[0] != 0!!!!\n"); + if (VU->VF[0].f.x != 0.0f) DbgCon::Error("VF[0].x != 0.0!!!!\n"); + if (VU->VF[0].f.y != 0.0f) DbgCon::Error("VF[0].y != 0.0!!!!\n"); + if (VU->VF[0].f.z != 0.0f) DbgCon::Error("VF[0].z != 0.0!!!!\n"); + if (VU->VF[0].f.w != 1.0f) DbgCon::Error("VF[0].w != 1.0!!!!\n"); +} + +namespace VU1micro +{ + void intAlloc() + { + } + + void __fastcall intClear(u32 Addr, u32 Size) + { + } + + void intShutdown() + { + } + + static void intReset() + { + } + + static void intStep() + { + vu1Exec( &VU1 ); + } + + static void intExecuteBlock() + { + int i; + #ifdef _DEBUG + int prevbranch; + #endif + + for (i = 128; i--;) { + if ((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0) + break; + + #ifdef _DEBUG + prevbranch = vu1branch; + #endif + vu1Exec(&VU1); + } + + if( i < 0 && (VU1.branch || VU1.ebit) ) { + // execute one more + vu1Exec(&VU1); + } + } +} + +using namespace VU1micro; + +const VUmicroCpu intVU1 = +{ + intReset +, intStep +, intExecuteBlock +, intClear +}; diff --git a/pcsx2/VUflags.cpp b/pcsx2/VUflags.cpp new file mode 100644 index 0000000000..84b43aad05 --- /dev/null +++ b/pcsx2/VUflags.cpp @@ -0,0 +1,124 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include +#include + +#include "Common.h" +#include "VUmicro.h" +/*****************************************/ +/* NEW FLAGS */ //By asadr. Thnkx F|RES :p +/*****************************************/ + + +__inline void vuUpdateDI(VURegs * VU) { +// u32 Flag_S = 0; +// u32 Flag_I = 0; +// u32 Flag_D = 0; +// +// /* +// FLAG D - I +// */ +// Flag_I = (VU->statusflag >> 4) & 0x1; +// Flag_D = (VU->statusflag >> 5) & 0x1; +// +// VU->statusflag|= (Flag_I | (VU0.VI[REG_STATUS_FLAG].US[0] >> 4)) << 10; +// VU->statusflag|= (Flag_D | (VU0.VI[REG_STATUS_FLAG].US[0] >> 5)) << 11; +} + +__forceinline u32 VU_MAC_UPDATE( int shift, VURegs * VU, float f) +{ + u32 v = *(u32*)&f; + int exp = (v >> 23) & 0xff; + u32 s = v & 0x80000000; + + if (s) + VU->macflag |= 0x0010<macflag &= ~(0x0010<macflag = (VU->macflag & ~(0x1100<macflag = (VU->macflag&~(0x1000<macflag = (VU->macflag&~(0x0100<macflag = (VU->macflag & ~(0x1101<macflag&= ~(0x1111<<3); +} + +__forceinline void VU_MACy_CLEAR(VURegs * VU) +{ + VU->macflag&= ~(0x1111<<2); +} + +__forceinline void VU_MACz_CLEAR(VURegs * VU) +{ + VU->macflag&= ~(0x1111<<1); +} + +__forceinline void VU_MACw_CLEAR(VURegs * VU) +{ + VU->macflag&= ~(0x1111<<0); +} + +void VU_STAT_UPDATE(VURegs * VU) { + int newflag = 0 ; + if (VU->macflag & 0x000F) newflag = 0x1; + if (VU->macflag & 0x00F0) newflag |= 0x2; + if (VU->macflag & 0x0F00) newflag |= 0x4; + if (VU->macflag & 0xF000) newflag |= 0x8; + VU->statusflag = (VU->statusflag&0xc30)|newflag|((VU->statusflag&0xf)<<6); +} diff --git a/pcsx2/VUflags.h b/pcsx2/VUflags.h new file mode 100644 index 0000000000..cd6dcca5b7 --- /dev/null +++ b/pcsx2/VUflags.h @@ -0,0 +1,39 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __VUFLAGS_H__ +#define __VUFLAGS_H__ + +#include "VU.h" + +void vuUpdateDI(VURegs * VU); +__forceinline u32 VU_MAC_UPDATE( int shift, VURegs * VU, float f); +__forceinline u32 VU_MACx_UPDATE(VURegs * VU, float x); +__forceinline u32 VU_MACy_UPDATE(VURegs * VU, float y); +__forceinline u32 VU_MACz_UPDATE(VURegs * VU, float z); +__forceinline u32 VU_MACw_UPDATE(VURegs * VU, float w); +__forceinline void VU_MACx_CLEAR(VURegs * VU); +__forceinline void VU_MACy_CLEAR(VURegs * VU); +__forceinline void VU_MACz_CLEAR(VURegs * VU); +__forceinline void VU_MACw_CLEAR(VURegs * VU); +void VU_STAT_UPDATE(VURegs * VU); + + +#endif + + diff --git a/pcsx2/VUmicro.h b/pcsx2/VUmicro.h new file mode 100644 index 0000000000..3c7199a8c5 --- /dev/null +++ b/pcsx2/VUmicro.h @@ -0,0 +1,1314 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __VUMICRO_H__ +#define __VUMICRO_H__ + +#include "VU.h" + +struct VUmicroCpu +{ + void (*Reset)(); + void (*Step)(); + void (*ExecuteBlock)(); // VUs should support block-level execution only. + void (__fastcall *Clear)(u32 Addr, u32 Size); +}; + +extern VUmicroCpu CpuVU0; +extern const VUmicroCpu intVU0; +extern const VUmicroCpu recVU0; + +extern VUmicroCpu CpuVU1; +extern const VUmicroCpu intVU1; +extern const VUmicroCpu recVU1; + +namespace VU0micro +{ + extern void recAlloc(); + extern void recShutdown(); + extern void __fastcall recClear(u32 Addr, u32 Size); + + // Note: Interpreter functions are dummies -- they don't actually do anything. + extern void intAlloc(); + extern void intShutdown(); + extern void __fastcall intClear(u32 Addr, u32 Size); +} + +namespace VU1micro +{ + extern void recAlloc(); + extern void recShutdown(); + extern void __fastcall recClear(u32 Addr, u32 Size); + + // Note: Interpreter functions are dummies -- they don't actually do anything. + extern void intAlloc(); + extern void intShutdown(); + extern void __fastcall intClear(u32 Addr, u32 Size); +} + +///////////////////////////////////////////////////////////////// +// These functions initialize memory for both VUs. +// +void vuMicroMemAlloc(); +void vuMicroMemShutdown(); +void vuMicroMemReset(); + +// Resets VUs and assigns the cpuVU0 / cpuVU1 pointers as according to +// the CHECK_VU0REC / CHECK_VU1REC config options. +void vuMicroCpuReset(); + +///////////////////////////////////////////////////////////////// +// Everything else does stuff on a per-VU basis. +// +void iDumpVU0Registers(); +void iDumpVU1Registers(); + + +extern void (*VU0_LOWER_OPCODE[128])(); +extern void (*VU0_UPPER_OPCODE[64])(); + +extern void (*VU0_UPPER_FD_00_TABLE[32])(); +extern void (*VU0_UPPER_FD_01_TABLE[32])(); +extern void (*VU0_UPPER_FD_10_TABLE[32])(); +extern void (*VU0_UPPER_FD_11_TABLE[32])(); + +extern void (*VU0regs_LOWER_OPCODE[128])(_VURegsNum *VUregsn); +extern void (*VU0regs_UPPER_OPCODE[64])(_VURegsNum *VUregsn); + +extern void (*VU0regs_UPPER_FD_00_TABLE[32])(_VURegsNum *VUregsn); +extern void (*VU0regs_UPPER_FD_01_TABLE[32])(_VURegsNum *VUregsn); +extern void (*VU0regs_UPPER_FD_10_TABLE[32])(_VURegsNum *VUregsn); +extern void (*VU0regs_UPPER_FD_11_TABLE[32])(_VURegsNum *VUregsn); + +extern void (*VU1_LOWER_OPCODE[128])(); +extern void (*VU1_UPPER_OPCODE[64])(); + +extern void (*VU1_UPPER_FD_00_TABLE[32])(); +extern void (*VU1_UPPER_FD_01_TABLE[32])(); +extern void (*VU1_UPPER_FD_10_TABLE[32])(); +extern void (*VU1_UPPER_FD_11_TABLE[32])(); + +extern void (*VU1regs_LOWER_OPCODE[128])(_VURegsNum *VUregsn); +extern void (*VU1regs_UPPER_OPCODE[64])(_VURegsNum *VUregsn); + +extern void (*VU1regs_UPPER_FD_00_TABLE[32])(_VURegsNum *VUregsn); +extern void (*VU1regs_UPPER_FD_01_TABLE[32])(_VURegsNum *VUregsn); +extern void (*VU1regs_UPPER_FD_10_TABLE[32])(_VURegsNum *VUregsn); +extern void (*VU1regs_UPPER_FD_11_TABLE[32])(_VURegsNum *VUregsn); + +// VU0 +extern void vu0ResetRegs(); +extern void vu0ExecMicro(u32 addr); +extern void vu0Exec(VURegs* VU); +extern void vu0Finish(); +extern void recResetVU0( void ); + +// VU1 +extern void vu1ResetRegs(); +extern void vu1ExecMicro(u32 addr); +extern void vu1Exec(VURegs* VU); + +extern void vu1MicroEnableSkip(); +extern void vu1MicroDisableSkip(); +extern bool vu1MicroIsSkipping(); + +void VU0_UPPER_FD_00(); +void VU0_UPPER_FD_01(); +void VU0_UPPER_FD_10(); +void VU0_UPPER_FD_11(); + +void VU0LowerOP(); +void VU0LowerOP_T3_00(); +void VU0LowerOP_T3_01(); +void VU0LowerOP_T3_10(); +void VU0LowerOP_T3_11(); + +void VU0unknown(); + +void VU1_UPPER_FD_00(); +void VU1_UPPER_FD_01(); +void VU1_UPPER_FD_10(); +void VU1_UPPER_FD_11(); + +void VU1LowerOP(); +void VU1LowerOP_T3_00(); +void VU1LowerOP_T3_01(); +void VU1LowerOP_T3_10(); +void VU1LowerOP_T3_11(); + +void VU1unknown(); + +void VU0regs_UPPER_FD_00(_VURegsNum *VUregsn); +void VU0regs_UPPER_FD_01(_VURegsNum *VUregsn); +void VU0regs_UPPER_FD_10(_VURegsNum *VUregsn); +void VU0regs_UPPER_FD_11(_VURegsNum *VUregsn); + +void VU0regsLowerOP(_VURegsNum *VUregsn); +void VU0regsLowerOP_T3_00(_VURegsNum *VUregsn); +void VU0regsLowerOP_T3_01(_VURegsNum *VUregsn); +void VU0regsLowerOP_T3_10(_VURegsNum *VUregsn); +void VU0regsLowerOP_T3_11(_VURegsNum *VUregsn); + +void VU0regsunknown(_VURegsNum *VUregsn); + +void VU1regs_UPPER_FD_00(_VURegsNum *VUregsn); +void VU1regs_UPPER_FD_01(_VURegsNum *VUregsn); +void VU1regs_UPPER_FD_10(_VURegsNum *VUregsn); +void VU1regs_UPPER_FD_11(_VURegsNum *VUregsn); + +void VU1regsLowerOP(_VURegsNum *VUregsn); +void VU1regsLowerOP_T3_00(_VURegsNum *VUregsn); +void VU1regsLowerOP_T3_01(_VURegsNum *VUregsn); +void VU1regsLowerOP_T3_10(_VURegsNum *VUregsn); +void VU1regsLowerOP_T3_11(_VURegsNum *VUregsn); + +void VU1regsunknown(_VURegsNum *VUregsn); + +/***************************************** + VU0 Micromode Upper instructions +*****************************************/ + +void VU0MI_ABS(); +void VU0MI_ADD(); +void VU0MI_ADDi(); +void VU0MI_ADDq(); +void VU0MI_ADDx(); +void VU0MI_ADDy(); +void VU0MI_ADDz(); +void VU0MI_ADDw(); +void VU0MI_ADDA(); +void VU0MI_ADDAi(); +void VU0MI_ADDAq(); +void VU0MI_ADDAx(); +void VU0MI_ADDAy(); +void VU0MI_ADDAz(); +void VU0MI_ADDAw(); +void VU0MI_SUB(); +void VU0MI_SUBi(); +void VU0MI_SUBq(); +void VU0MI_SUBx(); +void VU0MI_SUBy(); +void VU0MI_SUBz(); +void VU0MI_SUBw(); +void VU0MI_SUBA(); +void VU0MI_SUBAi(); +void VU0MI_SUBAq(); +void VU0MI_SUBAx(); +void VU0MI_SUBAy(); +void VU0MI_SUBAz(); +void VU0MI_SUBAw(); +void VU0MI_MUL(); +void VU0MI_MULi(); +void VU0MI_MULq(); +void VU0MI_MULx(); +void VU0MI_MULy(); +void VU0MI_MULz(); +void VU0MI_MULw(); +void VU0MI_MULA(); +void VU0MI_MULAi(); +void VU0MI_MULAq(); +void VU0MI_MULAx(); +void VU0MI_MULAy(); +void VU0MI_MULAz(); +void VU0MI_MULAw(); +void VU0MI_MADD(); +void VU0MI_MADDi(); +void VU0MI_MADDq(); +void VU0MI_MADDx(); +void VU0MI_MADDy(); +void VU0MI_MADDz(); +void VU0MI_MADDw(); +void VU0MI_MADDA(); +void VU0MI_MADDAi(); +void VU0MI_MADDAq(); +void VU0MI_MADDAx(); +void VU0MI_MADDAy(); +void VU0MI_MADDAz(); +void VU0MI_MADDAw(); +void VU0MI_MSUB(); +void VU0MI_MSUBi(); +void VU0MI_MSUBq(); +void VU0MI_MSUBx(); +void VU0MI_MSUBy(); +void VU0MI_MSUBz(); +void VU0MI_MSUBw(); +void VU0MI_MSUBA(); +void VU0MI_MSUBAi(); +void VU0MI_MSUBAq(); +void VU0MI_MSUBAx(); +void VU0MI_MSUBAy(); +void VU0MI_MSUBAz(); +void VU0MI_MSUBAw(); +void VU0MI_MAX(); +void VU0MI_MAXi(); +void VU0MI_MAXx(); +void VU0MI_MAXy(); +void VU0MI_MAXz(); +void VU0MI_MAXw(); +void VU0MI_MINI(); +void VU0MI_MINIi(); +void VU0MI_MINIx(); +void VU0MI_MINIy(); +void VU0MI_MINIz(); +void VU0MI_MINIw(); +void VU0MI_OPMULA(); +void VU0MI_OPMSUB(); +void VU0MI_NOP(); +void VU0MI_FTOI0(); +void VU0MI_FTOI4(); +void VU0MI_FTOI12(); +void VU0MI_FTOI15(); +void VU0MI_ITOF0(); +void VU0MI_ITOF4(); +void VU0MI_ITOF12(); +void VU0MI_ITOF15(); +void VU0MI_CLIP(); + +/***************************************** + VU0 Micromode Lower instructions +*****************************************/ + +void VU0MI_DIV(); +void VU0MI_SQRT(); +void VU0MI_RSQRT(); +void VU0MI_IADD(); +void VU0MI_IADDI(); +void VU0MI_IADDIU(); +void VU0MI_IAND(); +void VU0MI_IOR(); +void VU0MI_ISUB(); +void VU0MI_ISUBIU(); +void VU0MI_MOVE(); +void VU0MI_MFIR(); +void VU0MI_MTIR(); +void VU0MI_MR32(); +void VU0MI_LQ(); +void VU0MI_LQD(); +void VU0MI_LQI(); +void VU0MI_SQ(); +void VU0MI_SQD(); +void VU0MI_SQI(); +void VU0MI_ILW(); +void VU0MI_ISW(); +void VU0MI_ILWR(); +void VU0MI_ISWR(); +void VU0MI_LOI(); +void VU0MI_RINIT(); +void VU0MI_RGET(); +void VU0MI_RNEXT(); +void VU0MI_RXOR(); +void VU0MI_WAITQ(); +void VU0MI_FSAND(); +void VU0MI_FSEQ(); +void VU0MI_FSOR(); +void VU0MI_FSSET(); +void VU0MI_FMAND(); +void VU0MI_FMEQ(); +void VU0MI_FMOR(); +void VU0MI_FCAND(); +void VU0MI_FCEQ(); +void VU0MI_FCOR(); +void VU0MI_FCSET(); +void VU0MI_FCGET(); +void VU0MI_IBEQ(); +void VU0MI_IBGEZ(); +void VU0MI_IBGTZ(); +void VU0MI_IBLEZ(); +void VU0MI_IBLTZ(); +void VU0MI_IBNE(); +void VU0MI_B(); +void VU0MI_BAL(); +void VU0MI_JR(); +void VU0MI_JALR(); +void VU0MI_MFP(); +void VU0MI_WAITP(); +void VU0MI_ESADD(); +void VU0MI_ERSADD(); +void VU0MI_ELENG(); +void VU0MI_ERLENG(); +void VU0MI_EATANxy(); +void VU0MI_EATANxz(); +void VU0MI_ESUM(); +void VU0MI_ERCPR(); +void VU0MI_ESQRT(); +void VU0MI_ERSQRT(); +void VU0MI_ESIN(); +void VU0MI_EATAN(); +void VU0MI_EEXP(); +void VU0MI_XGKICK(); +void VU0MI_XTOP(); +void VU0MI_XITOP(); + +/***************************************** + VU1 Micromode Upper instructions +*****************************************/ + +void VU0regsMI_ABS(_VURegsNum *VUregsn); +void VU0regsMI_ADD(_VURegsNum *VUregsn); +void VU0regsMI_ADDi(_VURegsNum *VUregsn); +void VU0regsMI_ADDq(_VURegsNum *VUregsn); +void VU0regsMI_ADDx(_VURegsNum *VUregsn); +void VU0regsMI_ADDy(_VURegsNum *VUregsn); +void VU0regsMI_ADDz(_VURegsNum *VUregsn); +void VU0regsMI_ADDw(_VURegsNum *VUregsn); +void VU0regsMI_ADDA(_VURegsNum *VUregsn); +void VU0regsMI_ADDAi(_VURegsNum *VUregsn); +void VU0regsMI_ADDAq(_VURegsNum *VUregsn); +void VU0regsMI_ADDAx(_VURegsNum *VUregsn); +void VU0regsMI_ADDAy(_VURegsNum *VUregsn); +void VU0regsMI_ADDAz(_VURegsNum *VUregsn); +void VU0regsMI_ADDAw(_VURegsNum *VUregsn); +void VU0regsMI_SUB(_VURegsNum *VUregsn); +void VU0regsMI_SUBi(_VURegsNum *VUregsn); +void VU0regsMI_SUBq(_VURegsNum *VUregsn); +void VU0regsMI_SUBx(_VURegsNum *VUregsn); +void VU0regsMI_SUBy(_VURegsNum *VUregsn); +void VU0regsMI_SUBz(_VURegsNum *VUregsn); +void VU0regsMI_SUBw(_VURegsNum *VUregsn); +void VU0regsMI_SUBA(_VURegsNum *VUregsn); +void VU0regsMI_SUBAi(_VURegsNum *VUregsn); +void VU0regsMI_SUBAq(_VURegsNum *VUregsn); +void VU0regsMI_SUBAx(_VURegsNum *VUregsn); +void VU0regsMI_SUBAy(_VURegsNum *VUregsn); +void VU0regsMI_SUBAz(_VURegsNum *VUregsn); +void VU0regsMI_SUBAw(_VURegsNum *VUregsn); +void VU0regsMI_MUL(_VURegsNum *VUregsn); +void VU0regsMI_MULi(_VURegsNum *VUregsn); +void VU0regsMI_MULq(_VURegsNum *VUregsn); +void VU0regsMI_MULx(_VURegsNum *VUregsn); +void VU0regsMI_MULy(_VURegsNum *VUregsn); +void VU0regsMI_MULz(_VURegsNum *VUregsn); +void VU0regsMI_MULw(_VURegsNum *VUregsn); +void VU0regsMI_MULA(_VURegsNum *VUregsn); +void VU0regsMI_MULAi(_VURegsNum *VUregsn); +void VU0regsMI_MULAq(_VURegsNum *VUregsn); +void VU0regsMI_MULAx(_VURegsNum *VUregsn); +void VU0regsMI_MULAy(_VURegsNum *VUregsn); +void VU0regsMI_MULAz(_VURegsNum *VUregsn); +void VU0regsMI_MULAw(_VURegsNum *VUregsn); +void VU0regsMI_MADD(_VURegsNum *VUregsn); +void VU0regsMI_MADDi(_VURegsNum *VUregsn); +void VU0regsMI_MADDq(_VURegsNum *VUregsn); +void VU0regsMI_MADDx(_VURegsNum *VUregsn); +void VU0regsMI_MADDy(_VURegsNum *VUregsn); +void VU0regsMI_MADDz(_VURegsNum *VUregsn); +void VU0regsMI_MADDw(_VURegsNum *VUregsn); +void VU0regsMI_MADDA(_VURegsNum *VUregsn); +void VU0regsMI_MADDAi(_VURegsNum *VUregsn); +void VU0regsMI_MADDAq(_VURegsNum *VUregsn); +void VU0regsMI_MADDAx(_VURegsNum *VUregsn); +void VU0regsMI_MADDAy(_VURegsNum *VUregsn); +void VU0regsMI_MADDAz(_VURegsNum *VUregsn); +void VU0regsMI_MADDAw(_VURegsNum *VUregsn); +void VU0regsMI_MSUB(_VURegsNum *VUregsn); +void VU0regsMI_MSUBi(_VURegsNum *VUregsn); +void VU0regsMI_MSUBq(_VURegsNum *VUregsn); +void VU0regsMI_MSUBx(_VURegsNum *VUregsn); +void VU0regsMI_MSUBy(_VURegsNum *VUregsn); +void VU0regsMI_MSUBz(_VURegsNum *VUregsn); +void VU0regsMI_MSUBw(_VURegsNum *VUregsn); +void VU0regsMI_MSUBA(_VURegsNum *VUregsn); +void VU0regsMI_MSUBAi(_VURegsNum *VUregsn); +void VU0regsMI_MSUBAq(_VURegsNum *VUregsn); +void VU0regsMI_MSUBAx(_VURegsNum *VUregsn); +void VU0regsMI_MSUBAy(_VURegsNum *VUregsn); +void VU0regsMI_MSUBAz(_VURegsNum *VUregsn); +void VU0regsMI_MSUBAw(_VURegsNum *VUregsn); +void VU0regsMI_MAX(_VURegsNum *VUregsn); +void VU0regsMI_MAXi(_VURegsNum *VUregsn); +void VU0regsMI_MAXx(_VURegsNum *VUregsn); +void VU0regsMI_MAXy(_VURegsNum *VUregsn); +void VU0regsMI_MAXz(_VURegsNum *VUregsn); +void VU0regsMI_MAXw(_VURegsNum *VUregsn); +void VU0regsMI_MINI(_VURegsNum *VUregsn); +void VU0regsMI_MINIi(_VURegsNum *VUregsn); +void VU0regsMI_MINIx(_VURegsNum *VUregsn); +void VU0regsMI_MINIy(_VURegsNum *VUregsn); +void VU0regsMI_MINIz(_VURegsNum *VUregsn); +void VU0regsMI_MINIw(_VURegsNum *VUregsn); +void VU0regsMI_OPMULA(_VURegsNum *VUregsn); +void VU0regsMI_OPMSUB(_VURegsNum *VUregsn); +void VU0regsMI_NOP(_VURegsNum *VUregsn); +void VU0regsMI_FTOI0(_VURegsNum *VUregsn); +void VU0regsMI_FTOI4(_VURegsNum *VUregsn); +void VU0regsMI_FTOI12(_VURegsNum *VUregsn); +void VU0regsMI_FTOI15(_VURegsNum *VUregsn); +void VU0regsMI_ITOF0(_VURegsNum *VUregsn); +void VU0regsMI_ITOF4(_VURegsNum *VUregsn); +void VU0regsMI_ITOF12(_VURegsNum *VUregsn); +void VU0regsMI_ITOF15(_VURegsNum *VUregsn); +void VU0regsMI_CLIP(_VURegsNum *VUregsn); + +/***************************************** + VU0 Micromode Lower instructions +*****************************************/ + +void VU0regsMI_DIV(_VURegsNum *VUregsn); +void VU0regsMI_SQRT(_VURegsNum *VUregsn); +void VU0regsMI_RSQRT(_VURegsNum *VUregsn); +void VU0regsMI_IADD(_VURegsNum *VUregsn); +void VU0regsMI_IADDI(_VURegsNum *VUregsn); +void VU0regsMI_IADDIU(_VURegsNum *VUregsn); +void VU0regsMI_IAND(_VURegsNum *VUregsn); +void VU0regsMI_IOR(_VURegsNum *VUregsn); +void VU0regsMI_ISUB(_VURegsNum *VUregsn); +void VU0regsMI_ISUBIU(_VURegsNum *VUregsn); +void VU0regsMI_MOVE(_VURegsNum *VUregsn); +void VU0regsMI_MFIR(_VURegsNum *VUregsn); +void VU0regsMI_MTIR(_VURegsNum *VUregsn); +void VU0regsMI_MR32(_VURegsNum *VUregsn); +void VU0regsMI_LQ(_VURegsNum *VUregsn); +void VU0regsMI_LQD(_VURegsNum *VUregsn); +void VU0regsMI_LQI(_VURegsNum *VUregsn); +void VU0regsMI_SQ(_VURegsNum *VUregsn); +void VU0regsMI_SQD(_VURegsNum *VUregsn); +void VU0regsMI_SQI(_VURegsNum *VUregsn); +void VU0regsMI_ILW(_VURegsNum *VUregsn); +void VU0regsMI_ISW(_VURegsNum *VUregsn); +void VU0regsMI_ILWR(_VURegsNum *VUregsn); +void VU0regsMI_ISWR(_VURegsNum *VUregsn); +void VU0regsMI_LOI(_VURegsNum *VUregsn); +void VU0regsMI_RINIT(_VURegsNum *VUregsn); +void VU0regsMI_RGET(_VURegsNum *VUregsn); +void VU0regsMI_RNEXT(_VURegsNum *VUregsn); +void VU0regsMI_RXOR(_VURegsNum *VUregsn); +void VU0regsMI_WAITQ(_VURegsNum *VUregsn); +void VU0regsMI_FSAND(_VURegsNum *VUregsn); +void VU0regsMI_FSEQ(_VURegsNum *VUregsn); +void VU0regsMI_FSOR(_VURegsNum *VUregsn); +void VU0regsMI_FSSET(_VURegsNum *VUregsn); +void VU0regsMI_FMAND(_VURegsNum *VUregsn); +void VU0regsMI_FMEQ(_VURegsNum *VUregsn); +void VU0regsMI_FMOR(_VURegsNum *VUregsn); +void VU0regsMI_FCAND(_VURegsNum *VUregsn); +void VU0regsMI_FCEQ(_VURegsNum *VUregsn); +void VU0regsMI_FCOR(_VURegsNum *VUregsn); +void VU0regsMI_FCSET(_VURegsNum *VUregsn); +void VU0regsMI_FCGET(_VURegsNum *VUregsn); +void VU0regsMI_IBEQ(_VURegsNum *VUregsn); +void VU0regsMI_IBGEZ(_VURegsNum *VUregsn); +void VU0regsMI_IBGTZ(_VURegsNum *VUregsn); +void VU0regsMI_IBLTZ(_VURegsNum *VUregsn); +void VU0regsMI_IBLEZ(_VURegsNum *VUregsn); +void VU0regsMI_IBNE(_VURegsNum *VUregsn); +void VU0regsMI_B(_VURegsNum *VUregsn); +void VU0regsMI_BAL(_VURegsNum *VUregsn); +void VU0regsMI_JR(_VURegsNum *VUregsn); +void VU0regsMI_JALR(_VURegsNum *VUregsn); +void VU0regsMI_MFP(_VURegsNum *VUregsn); +void VU0regsMI_WAITP(_VURegsNum *VUregsn); +void VU0regsMI_ESADD(_VURegsNum *VUregsn); +void VU0regsMI_ERSADD(_VURegsNum *VUregsn); +void VU0regsMI_ELENG(_VURegsNum *VUregsn); +void VU0regsMI_ERLENG(_VURegsNum *VUregsn); +void VU0regsMI_EATANxy(_VURegsNum *VUregsn); +void VU0regsMI_EATANxz(_VURegsNum *VUregsn); +void VU0regsMI_ESUM(_VURegsNum *VUregsn); +void VU0regsMI_ERCPR(_VURegsNum *VUregsn); +void VU0regsMI_ESQRT(_VURegsNum *VUregsn); +void VU0regsMI_ERSQRT(_VURegsNum *VUregsn); +void VU0regsMI_ESIN(_VURegsNum *VUregsn); +void VU0regsMI_EATAN(_VURegsNum *VUregsn); +void VU0regsMI_EEXP(_VURegsNum *VUregsn); +void VU0regsMI_XGKICK(_VURegsNum *VUregsn); +void VU0regsMI_XTOP(_VURegsNum *VUregsn); +void VU0regsMI_XITOP(_VURegsNum *VUregsn); + +/***************************************** + VU1 Micromode Upper instructions +*****************************************/ + +void VU1MI_ABS(); +void VU1MI_ADD(); +void VU1MI_ADDi(); +void VU1MI_ADDq(); +void VU1MI_ADDx(); +void VU1MI_ADDy(); +void VU1MI_ADDz(); +void VU1MI_ADDw(); +void VU1MI_ADDA(); +void VU1MI_ADDAi(); +void VU1MI_ADDAq(); +void VU1MI_ADDAx(); +void VU1MI_ADDAy(); +void VU1MI_ADDAz(); +void VU1MI_ADDAw(); +void VU1MI_SUB(); +void VU1MI_SUBi(); +void VU1MI_SUBq(); +void VU1MI_SUBx(); +void VU1MI_SUBy(); +void VU1MI_SUBz(); +void VU1MI_SUBw(); +void VU1MI_SUBA(); +void VU1MI_SUBAi(); +void VU1MI_SUBAq(); +void VU1MI_SUBAx(); +void VU1MI_SUBAy(); +void VU1MI_SUBAz(); +void VU1MI_SUBAw(); +void VU1MI_MUL(); +void VU1MI_MULi(); +void VU1MI_MULq(); +void VU1MI_MULx(); +void VU1MI_MULy(); +void VU1MI_MULz(); +void VU1MI_MULw(); +void VU1MI_MULA(); +void VU1MI_MULAi(); +void VU1MI_MULAq(); +void VU1MI_MULAx(); +void VU1MI_MULAy(); +void VU1MI_MULAz(); +void VU1MI_MULAw(); +void VU1MI_MADD(); +void VU1MI_MADDi(); +void VU1MI_MADDq(); +void VU1MI_MADDx(); +void VU1MI_MADDy(); +void VU1MI_MADDz(); +void VU1MI_MADDw(); +void VU1MI_MADDA(); +void VU1MI_MADDAi(); +void VU1MI_MADDAq(); +void VU1MI_MADDAx(); +void VU1MI_MADDAy(); +void VU1MI_MADDAz(); +void VU1MI_MADDAw(); +void VU1MI_MSUB(); +void VU1MI_MSUBi(); +void VU1MI_MSUBq(); +void VU1MI_MSUBx(); +void VU1MI_MSUBy(); +void VU1MI_MSUBz(); +void VU1MI_MSUBw(); +void VU1MI_MSUBA(); +void VU1MI_MSUBAi(); +void VU1MI_MSUBAq(); +void VU1MI_MSUBAx(); +void VU1MI_MSUBAy(); +void VU1MI_MSUBAz(); +void VU1MI_MSUBAw(); +void VU1MI_MAX(); +void VU1MI_MAXi(); +void VU1MI_MAXx(); +void VU1MI_MAXy(); +void VU1MI_MAXz(); +void VU1MI_MAXw(); +void VU1MI_MINI(); +void VU1MI_MINIi(); +void VU1MI_MINIx(); +void VU1MI_MINIy(); +void VU1MI_MINIz(); +void VU1MI_MINIw(); +void VU1MI_OPMULA(); +void VU1MI_OPMSUB(); +void VU1MI_NOP(); +void VU1MI_FTOI0(); +void VU1MI_FTOI4(); +void VU1MI_FTOI12(); +void VU1MI_FTOI15(); +void VU1MI_ITOF0(); +void VU1MI_ITOF4(); +void VU1MI_ITOF12(); +void VU1MI_ITOF15(); +void VU1MI_CLIP(); + +/***************************************** + VU1 Micromode Lower instructions +*****************************************/ + +void VU1MI_DIV(); +void VU1MI_SQRT(); +void VU1MI_RSQRT(); +void VU1MI_IADD(); +void VU1MI_IADDI(); +void VU1MI_IADDIU(); +void VU1MI_IAND(); +void VU1MI_IOR(); +void VU1MI_ISUB(); +void VU1MI_ISUBIU(); +void VU1MI_MOVE(); +void VU1MI_MFIR(); +void VU1MI_MTIR(); +void VU1MI_MR32(); +void VU1MI_LQ(); +void VU1MI_LQD(); +void VU1MI_LQI(); +void VU1MI_SQ(); +void VU1MI_SQD(); +void VU1MI_SQI(); +void VU1MI_ILW(); +void VU1MI_ISW(); +void VU1MI_ILWR(); +void VU1MI_ISWR(); +void VU1MI_LOI(); +void VU1MI_RINIT(); +void VU1MI_RGET(); +void VU1MI_RNEXT(); +void VU1MI_RXOR(); +void VU1MI_WAITQ(); +void VU1MI_FSAND(); +void VU1MI_FSEQ(); +void VU1MI_FSOR(); +void VU1MI_FSSET(); +void VU1MI_FMAND(); +void VU1MI_FMEQ(); +void VU1MI_FMOR(); +void VU1MI_FCAND(); +void VU1MI_FCEQ(); +void VU1MI_FCOR(); +void VU1MI_FCSET(); +void VU1MI_FCGET(); +void VU1MI_IBEQ(); +void VU1MI_IBGEZ(); +void VU1MI_IBGTZ(); +void VU1MI_IBLTZ(); +void VU1MI_IBLEZ(); +void VU1MI_IBNE(); +void VU1MI_B(); +void VU1MI_BAL(); +void VU1MI_JR(); +void VU1MI_JALR(); +void VU1MI_MFP(); +void VU1MI_WAITP(); +void VU1MI_ESADD(); +void VU1MI_ERSADD(); +void VU1MI_ELENG(); +void VU1MI_ERLENG(); +void VU1MI_EATANxy(); +void VU1MI_EATANxz(); +void VU1MI_ESUM(); +void VU1MI_ERCPR(); +void VU1MI_ESQRT(); +void VU1MI_ERSQRT(); +void VU1MI_ESIN(); +void VU1MI_EATAN(); +void VU1MI_EEXP(); +void VU1MI_XGKICK(); +void VU1MI_XTOP(); +void VU1MI_XITOP(); + +/***************************************** + VU1 Micromode Upper instructions +*****************************************/ + +void VU1regsMI_ABS(_VURegsNum *VUregsn); +void VU1regsMI_ADD(_VURegsNum *VUregsn); +void VU1regsMI_ADDi(_VURegsNum *VUregsn); +void VU1regsMI_ADDq(_VURegsNum *VUregsn); +void VU1regsMI_ADDx(_VURegsNum *VUregsn); +void VU1regsMI_ADDy(_VURegsNum *VUregsn); +void VU1regsMI_ADDz(_VURegsNum *VUregsn); +void VU1regsMI_ADDw(_VURegsNum *VUregsn); +void VU1regsMI_ADDA(_VURegsNum *VUregsn); +void VU1regsMI_ADDAi(_VURegsNum *VUregsn); +void VU1regsMI_ADDAq(_VURegsNum *VUregsn); +void VU1regsMI_ADDAx(_VURegsNum *VUregsn); +void VU1regsMI_ADDAy(_VURegsNum *VUregsn); +void VU1regsMI_ADDAz(_VURegsNum *VUregsn); +void VU1regsMI_ADDAw(_VURegsNum *VUregsn); +void VU1regsMI_SUB(_VURegsNum *VUregsn); +void VU1regsMI_SUBi(_VURegsNum *VUregsn); +void VU1regsMI_SUBq(_VURegsNum *VUregsn); +void VU1regsMI_SUBx(_VURegsNum *VUregsn); +void VU1regsMI_SUBy(_VURegsNum *VUregsn); +void VU1regsMI_SUBz(_VURegsNum *VUregsn); +void VU1regsMI_SUBw(_VURegsNum *VUregsn); +void VU1regsMI_SUBA(_VURegsNum *VUregsn); +void VU1regsMI_SUBAi(_VURegsNum *VUregsn); +void VU1regsMI_SUBAq(_VURegsNum *VUregsn); +void VU1regsMI_SUBAx(_VURegsNum *VUregsn); +void VU1regsMI_SUBAy(_VURegsNum *VUregsn); +void VU1regsMI_SUBAz(_VURegsNum *VUregsn); +void VU1regsMI_SUBAw(_VURegsNum *VUregsn); +void VU1regsMI_MUL(_VURegsNum *VUregsn); +void VU1regsMI_MULi(_VURegsNum *VUregsn); +void VU1regsMI_MULq(_VURegsNum *VUregsn); +void VU1regsMI_MULx(_VURegsNum *VUregsn); +void VU1regsMI_MULy(_VURegsNum *VUregsn); +void VU1regsMI_MULz(_VURegsNum *VUregsn); +void VU1regsMI_MULw(_VURegsNum *VUregsn); +void VU1regsMI_MULA(_VURegsNum *VUregsn); +void VU1regsMI_MULAi(_VURegsNum *VUregsn); +void VU1regsMI_MULAq(_VURegsNum *VUregsn); +void VU1regsMI_MULAx(_VURegsNum *VUregsn); +void VU1regsMI_MULAy(_VURegsNum *VUregsn); +void VU1regsMI_MULAz(_VURegsNum *VUregsn); +void VU1regsMI_MULAw(_VURegsNum *VUregsn); +void VU1regsMI_MADD(_VURegsNum *VUregsn); +void VU1regsMI_MADDi(_VURegsNum *VUregsn); +void VU1regsMI_MADDq(_VURegsNum *VUregsn); +void VU1regsMI_MADDx(_VURegsNum *VUregsn); +void VU1regsMI_MADDy(_VURegsNum *VUregsn); +void VU1regsMI_MADDz(_VURegsNum *VUregsn); +void VU1regsMI_MADDw(_VURegsNum *VUregsn); +void VU1regsMI_MADDA(_VURegsNum *VUregsn); +void VU1regsMI_MADDAi(_VURegsNum *VUregsn); +void VU1regsMI_MADDAq(_VURegsNum *VUregsn); +void VU1regsMI_MADDAx(_VURegsNum *VUregsn); +void VU1regsMI_MADDAy(_VURegsNum *VUregsn); +void VU1regsMI_MADDAz(_VURegsNum *VUregsn); +void VU1regsMI_MADDAw(_VURegsNum *VUregsn); +void VU1regsMI_MSUB(_VURegsNum *VUregsn); +void VU1regsMI_MSUBi(_VURegsNum *VUregsn); +void VU1regsMI_MSUBq(_VURegsNum *VUregsn); +void VU1regsMI_MSUBx(_VURegsNum *VUregsn); +void VU1regsMI_MSUBy(_VURegsNum *VUregsn); +void VU1regsMI_MSUBz(_VURegsNum *VUregsn); +void VU1regsMI_MSUBw(_VURegsNum *VUregsn); +void VU1regsMI_MSUBA(_VURegsNum *VUregsn); +void VU1regsMI_MSUBAi(_VURegsNum *VUregsn); +void VU1regsMI_MSUBAq(_VURegsNum *VUregsn); +void VU1regsMI_MSUBAx(_VURegsNum *VUregsn); +void VU1regsMI_MSUBAy(_VURegsNum *VUregsn); +void VU1regsMI_MSUBAz(_VURegsNum *VUregsn); +void VU1regsMI_MSUBAw(_VURegsNum *VUregsn); +void VU1regsMI_MAX(_VURegsNum *VUregsn); +void VU1regsMI_MAXi(_VURegsNum *VUregsn); +void VU1regsMI_MAXx(_VURegsNum *VUregsn); +void VU1regsMI_MAXy(_VURegsNum *VUregsn); +void VU1regsMI_MAXz(_VURegsNum *VUregsn); +void VU1regsMI_MAXw(_VURegsNum *VUregsn); +void VU1regsMI_MINI(_VURegsNum *VUregsn); +void VU1regsMI_MINIi(_VURegsNum *VUregsn); +void VU1regsMI_MINIx(_VURegsNum *VUregsn); +void VU1regsMI_MINIy(_VURegsNum *VUregsn); +void VU1regsMI_MINIz(_VURegsNum *VUregsn); +void VU1regsMI_MINIw(_VURegsNum *VUregsn); +void VU1regsMI_OPMULA(_VURegsNum *VUregsn); +void VU1regsMI_OPMSUB(_VURegsNum *VUregsn); +void VU1regsMI_NOP(_VURegsNum *VUregsn); +void VU1regsMI_FTOI0(_VURegsNum *VUregsn); +void VU1regsMI_FTOI4(_VURegsNum *VUregsn); +void VU1regsMI_FTOI12(_VURegsNum *VUregsn); +void VU1regsMI_FTOI15(_VURegsNum *VUregsn); +void VU1regsMI_ITOF0(_VURegsNum *VUregsn); +void VU1regsMI_ITOF4(_VURegsNum *VUregsn); +void VU1regsMI_ITOF12(_VURegsNum *VUregsn); +void VU1regsMI_ITOF15(_VURegsNum *VUregsn); +void VU1regsMI_CLIP(_VURegsNum *VUregsn); + +/***************************************** + VU1 Micromode Lower instructions +*****************************************/ + +void VU1regsMI_DIV(_VURegsNum *VUregsn); +void VU1regsMI_SQRT(_VURegsNum *VUregsn); +void VU1regsMI_RSQRT(_VURegsNum *VUregsn); +void VU1regsMI_IADD(_VURegsNum *VUregsn); +void VU1regsMI_IADDI(_VURegsNum *VUregsn); +void VU1regsMI_IADDIU(_VURegsNum *VUregsn); +void VU1regsMI_IAND(_VURegsNum *VUregsn); +void VU1regsMI_IOR(_VURegsNum *VUregsn); +void VU1regsMI_ISUB(_VURegsNum *VUregsn); +void VU1regsMI_ISUBIU(_VURegsNum *VUregsn); +void VU1regsMI_MOVE(_VURegsNum *VUregsn); +void VU1regsMI_MFIR(_VURegsNum *VUregsn); +void VU1regsMI_MTIR(_VURegsNum *VUregsn); +void VU1regsMI_MR32(_VURegsNum *VUregsn); +void VU1regsMI_LQ(_VURegsNum *VUregsn); +void VU1regsMI_LQD(_VURegsNum *VUregsn); +void VU1regsMI_LQI(_VURegsNum *VUregsn); +void VU1regsMI_SQ(_VURegsNum *VUregsn); +void VU1regsMI_SQD(_VURegsNum *VUregsn); +void VU1regsMI_SQI(_VURegsNum *VUregsn); +void VU1regsMI_ILW(_VURegsNum *VUregsn); +void VU1regsMI_ISW(_VURegsNum *VUregsn); +void VU1regsMI_ILWR(_VURegsNum *VUregsn); +void VU1regsMI_ISWR(_VURegsNum *VUregsn); +void VU1regsMI_LOI(_VURegsNum *VUregsn); +void VU1regsMI_RINIT(_VURegsNum *VUregsn); +void VU1regsMI_RGET(_VURegsNum *VUregsn); +void VU1regsMI_RNEXT(_VURegsNum *VUregsn); +void VU1regsMI_RXOR(_VURegsNum *VUregsn); +void VU1regsMI_WAITQ(_VURegsNum *VUregsn); +void VU1regsMI_FSAND(_VURegsNum *VUregsn); +void VU1regsMI_FSEQ(_VURegsNum *VUregsn); +void VU1regsMI_FSOR(_VURegsNum *VUregsn); +void VU1regsMI_FSSET(_VURegsNum *VUregsn); +void VU1regsMI_FMAND(_VURegsNum *VUregsn); +void VU1regsMI_FMEQ(_VURegsNum *VUregsn); +void VU1regsMI_FMOR(_VURegsNum *VUregsn); +void VU1regsMI_FCAND(_VURegsNum *VUregsn); +void VU1regsMI_FCEQ(_VURegsNum *VUregsn); +void VU1regsMI_FCOR(_VURegsNum *VUregsn); +void VU1regsMI_FCSET(_VURegsNum *VUregsn); +void VU1regsMI_FCGET(_VURegsNum *VUregsn); +void VU1regsMI_IBEQ(_VURegsNum *VUregsn); +void VU1regsMI_IBGEZ(_VURegsNum *VUregsn); +void VU1regsMI_IBGTZ(_VURegsNum *VUregsn); +void VU1regsMI_IBLTZ(_VURegsNum *VUregsn); +void VU1regsMI_IBLEZ(_VURegsNum *VUregsn); +void VU1regsMI_IBNE(_VURegsNum *VUregsn); +void VU1regsMI_B(_VURegsNum *VUregsn); +void VU1regsMI_BAL(_VURegsNum *VUregsn); +void VU1regsMI_JR(_VURegsNum *VUregsn); +void VU1regsMI_JALR(_VURegsNum *VUregsn); +void VU1regsMI_MFP(_VURegsNum *VUregsn); +void VU1regsMI_WAITP(_VURegsNum *VUregsn); +void VU1regsMI_ESADD(_VURegsNum *VUregsn); +void VU1regsMI_ERSADD(_VURegsNum *VUregsn); +void VU1regsMI_ELENG(_VURegsNum *VUregsn); +void VU1regsMI_ERLENG(_VURegsNum *VUregsn); +void VU1regsMI_EATANxy(_VURegsNum *VUregsn); +void VU1regsMI_EATANxz(_VURegsNum *VUregsn); +void VU1regsMI_ESUM(_VURegsNum *VUregsn); +void VU1regsMI_ERCPR(_VURegsNum *VUregsn); +void VU1regsMI_ESQRT(_VURegsNum *VUregsn); +void VU1regsMI_ERSQRT(_VURegsNum *VUregsn); +void VU1regsMI_ESIN(_VURegsNum *VUregsn); +void VU1regsMI_EATAN(_VURegsNum *VUregsn); +void VU1regsMI_EEXP(_VURegsNum *VUregsn); +void VU1regsMI_XGKICK(_VURegsNum *VUregsn); +void VU1regsMI_XTOP(_VURegsNum *VUregsn); +void VU1regsMI_XITOP(_VURegsNum *VUregsn); + +/***************************************** + VU Micromode Tables/Opcodes defs macros +*****************************************/ + + +#define _vuTables(VU, PREFIX) \ + \ +void (*PREFIX##_LOWER_OPCODE[128])() = { \ + PREFIX##MI_LQ , PREFIX##MI_SQ , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_ILW , PREFIX##MI_ISW , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_IADDIU, PREFIX##MI_ISUBIU, PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_FCEQ , PREFIX##MI_FCSET , PREFIX##MI_FCAND, PREFIX##MI_FCOR, /* 0x10 */ \ + PREFIX##MI_FSEQ , PREFIX##MI_FSSET , PREFIX##MI_FSAND, PREFIX##MI_FSOR, \ + PREFIX##MI_FMEQ , PREFIX##unknown , PREFIX##MI_FMAND, PREFIX##MI_FMOR, \ + PREFIX##MI_FCGET , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_B , PREFIX##MI_BAL , PREFIX##unknown , PREFIX##unknown, /* 0x20 */ \ + PREFIX##MI_JR , PREFIX##MI_JALR , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_IBEQ , PREFIX##MI_IBNE , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_IBLTZ , PREFIX##MI_IBGTZ , PREFIX##MI_IBLEZ, PREFIX##MI_IBGEZ, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x30 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##LowerOP , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x40*/ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x50 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x60 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x70 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ +}; \ + \ + void (*PREFIX##LowerOP_T3_00_OPCODE[32])() = { \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_MOVE , PREFIX##MI_LQI , PREFIX##MI_DIV , PREFIX##MI_MTIR, \ + PREFIX##MI_RNEXT , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x10 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##MI_MFP , PREFIX##MI_XTOP , PREFIX##MI_XGKICK, \ + PREFIX##MI_ESADD , PREFIX##MI_EATANxy, PREFIX##MI_ESQRT, PREFIX##MI_ESIN, \ +}; \ + \ + void (*PREFIX##LowerOP_T3_01_OPCODE[32])() = { \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_MR32 , PREFIX##MI_SQI , PREFIX##MI_SQRT , PREFIX##MI_MFIR, \ + PREFIX##MI_RGET , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x10 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##MI_XITOP, PREFIX##unknown, \ + PREFIX##MI_ERSADD, PREFIX##MI_EATANxz, PREFIX##MI_ERSQRT, PREFIX##MI_EATAN, \ +}; \ + \ + void (*PREFIX##LowerOP_T3_10_OPCODE[32])() = { \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##MI_LQD , PREFIX##MI_RSQRT, PREFIX##MI_ILWR, \ + PREFIX##MI_RINIT , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x10 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_ELENG , PREFIX##MI_ESUM , PREFIX##MI_ERCPR, PREFIX##MI_EEXP, \ +}; \ + \ + void (*PREFIX##LowerOP_T3_11_OPCODE[32])() = { \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##MI_SQD , PREFIX##MI_WAITQ, PREFIX##MI_ISWR, \ + PREFIX##MI_RXOR , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x10 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_ERLENG, PREFIX##unknown , PREFIX##MI_WAITP, PREFIX##unknown, \ +}; \ + \ + void (*PREFIX##LowerOP_OPCODE[64])() = { \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x10 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x20 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_IADD , PREFIX##MI_ISUB , PREFIX##MI_IADDI, PREFIX##unknown, /* 0x30 */ \ + PREFIX##MI_IAND , PREFIX##MI_IOR , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##LowerOP_T3_00, PREFIX##LowerOP_T3_01, PREFIX##LowerOP_T3_10, PREFIX##LowerOP_T3_11, \ +}; \ + \ + void (*PREFIX##_UPPER_OPCODE[64])() = { \ + PREFIX##MI_ADDx , PREFIX##MI_ADDy , PREFIX##MI_ADDz , PREFIX##MI_ADDw, \ + PREFIX##MI_SUBx , PREFIX##MI_SUBy , PREFIX##MI_SUBz , PREFIX##MI_SUBw, \ + PREFIX##MI_MADDx , PREFIX##MI_MADDy , PREFIX##MI_MADDz , PREFIX##MI_MADDw, \ + PREFIX##MI_MSUBx , PREFIX##MI_MSUBy , PREFIX##MI_MSUBz , PREFIX##MI_MSUBw, \ + PREFIX##MI_MAXx , PREFIX##MI_MAXy , PREFIX##MI_MAXz , PREFIX##MI_MAXw, /* 0x10 */ \ + PREFIX##MI_MINIx , PREFIX##MI_MINIy , PREFIX##MI_MINIz , PREFIX##MI_MINIw, \ + PREFIX##MI_MULx , PREFIX##MI_MULy , PREFIX##MI_MULz , PREFIX##MI_MULw, \ + PREFIX##MI_MULq , PREFIX##MI_MAXi , PREFIX##MI_MULi , PREFIX##MI_MINIi, \ + PREFIX##MI_ADDq , PREFIX##MI_MADDq , PREFIX##MI_ADDi , PREFIX##MI_MADDi, /* 0x20 */ \ + PREFIX##MI_SUBq , PREFIX##MI_MSUBq , PREFIX##MI_SUBi , PREFIX##MI_MSUBi, \ + PREFIX##MI_ADD , PREFIX##MI_MADD , PREFIX##MI_MUL , PREFIX##MI_MAX, \ + PREFIX##MI_SUB , PREFIX##MI_MSUB , PREFIX##MI_OPMSUB, PREFIX##MI_MINI, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x30 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##_UPPER_FD_00, PREFIX##_UPPER_FD_01, PREFIX##_UPPER_FD_10, PREFIX##_UPPER_FD_11, \ +}; \ + \ + void (*PREFIX##_UPPER_FD_00_TABLE[32])() = { \ + PREFIX##MI_ADDAx, PREFIX##MI_SUBAx , PREFIX##MI_MADDAx, PREFIX##MI_MSUBAx, \ + PREFIX##MI_ITOF0, PREFIX##MI_FTOI0, PREFIX##MI_MULAx , PREFIX##MI_MULAq , \ + PREFIX##MI_ADDAq, PREFIX##MI_SUBAq, PREFIX##MI_ADDA , PREFIX##MI_SUBA , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ +}; \ + \ + void (*PREFIX##_UPPER_FD_01_TABLE[32])() = { \ + PREFIX##MI_ADDAy , PREFIX##MI_SUBAy , PREFIX##MI_MADDAy, PREFIX##MI_MSUBAy, \ + PREFIX##MI_ITOF4 , PREFIX##MI_FTOI4 , PREFIX##MI_MULAy , PREFIX##MI_ABS , \ + PREFIX##MI_MADDAq, PREFIX##MI_MSUBAq, PREFIX##MI_MADDA , PREFIX##MI_MSUBA , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ +}; \ + \ + void (*PREFIX##_UPPER_FD_10_TABLE[32])() = { \ + PREFIX##MI_ADDAz , PREFIX##MI_SUBAz , PREFIX##MI_MADDAz, PREFIX##MI_MSUBAz, \ + PREFIX##MI_ITOF12, PREFIX##MI_FTOI12, PREFIX##MI_MULAz , PREFIX##MI_MULAi , \ + PREFIX##MI_ADDAi, PREFIX##MI_SUBAi , PREFIX##MI_MULA , PREFIX##MI_OPMULA, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ +}; \ + \ + void (*PREFIX##_UPPER_FD_11_TABLE[32])() = { \ + PREFIX##MI_ADDAw , PREFIX##MI_SUBAw , PREFIX##MI_MADDAw, PREFIX##MI_MSUBAw, \ + PREFIX##MI_ITOF15, PREFIX##MI_FTOI15, PREFIX##MI_MULAw , PREFIX##MI_CLIP , \ + PREFIX##MI_MADDAi, PREFIX##MI_MSUBAi, PREFIX##unknown , PREFIX##MI_NOP , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ +}; \ + \ + \ + \ + void PREFIX##_UPPER_FD_00() { \ + PREFIX##_UPPER_FD_00_TABLE[(VU.code >> 6) & 0x1f ](); \ +} \ + \ + void PREFIX##_UPPER_FD_01() { \ + PREFIX##_UPPER_FD_01_TABLE[(VU.code >> 6) & 0x1f](); \ +} \ + \ + void PREFIX##_UPPER_FD_10() { \ + PREFIX##_UPPER_FD_10_TABLE[(VU.code >> 6) & 0x1f](); \ +} \ + \ + void PREFIX##_UPPER_FD_11() { \ + PREFIX##_UPPER_FD_11_TABLE[(VU.code >> 6) & 0x1f](); \ +} \ + \ + void PREFIX##LowerOP() { \ + PREFIX##LowerOP_OPCODE[VU.code & 0x3f](); \ +} \ + \ + void PREFIX##LowerOP_T3_00() { \ + PREFIX##LowerOP_T3_00_OPCODE[(VU.code >> 6) & 0x1f](); \ +} \ + \ + void PREFIX##LowerOP_T3_01() { \ + PREFIX##LowerOP_T3_01_OPCODE[(VU.code >> 6) & 0x1f](); \ +} \ + \ + void PREFIX##LowerOP_T3_10() { \ + PREFIX##LowerOP_T3_10_OPCODE[(VU.code >> 6) & 0x1f](); \ +} \ + \ + void PREFIX##LowerOP_T3_11() { \ + PREFIX##LowerOP_T3_11_OPCODE[(VU.code >> 6) & 0x1f](); \ +} + +#define _vuRegsTables(VU, PREFIX) \ + \ +void (*PREFIX##_LOWER_OPCODE[128])(_VURegsNum *VUregsn) = { \ + PREFIX##MI_LQ , PREFIX##MI_SQ , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_ILW , PREFIX##MI_ISW , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_IADDIU, PREFIX##MI_ISUBIU, PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_FCEQ , PREFIX##MI_FCSET , PREFIX##MI_FCAND, PREFIX##MI_FCOR, /* 0x10 */ \ + PREFIX##MI_FSEQ , PREFIX##MI_FSSET , PREFIX##MI_FSAND, PREFIX##MI_FSOR, \ + PREFIX##MI_FMEQ , PREFIX##unknown , PREFIX##MI_FMAND, PREFIX##MI_FMOR, \ + PREFIX##MI_FCGET , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_B , PREFIX##MI_BAL , PREFIX##unknown , PREFIX##unknown, /* 0x20 */ \ + PREFIX##MI_JR , PREFIX##MI_JALR , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_IBEQ , PREFIX##MI_IBNE , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_IBLTZ , PREFIX##MI_IBGTZ , PREFIX##MI_IBLEZ, PREFIX##MI_IBGEZ, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x30 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##LowerOP , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x40*/ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x50 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x60 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x70 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ +}; \ + \ + void (*PREFIX##LowerOP_T3_00_OPCODE[32])(_VURegsNum *VUregsn) = { \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_MOVE , PREFIX##MI_LQI , PREFIX##MI_DIV , PREFIX##MI_MTIR, \ + PREFIX##MI_RNEXT , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x10 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##MI_MFP , PREFIX##MI_XTOP , PREFIX##MI_XGKICK, \ + PREFIX##MI_ESADD , PREFIX##MI_EATANxy, PREFIX##MI_ESQRT, PREFIX##MI_ESIN, \ +}; \ + \ + void (*PREFIX##LowerOP_T3_01_OPCODE[32])(_VURegsNum *VUregsn) = { \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_MR32 , PREFIX##MI_SQI , PREFIX##MI_SQRT , PREFIX##MI_MFIR, \ + PREFIX##MI_RGET , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x10 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##MI_XITOP, PREFIX##unknown, \ + PREFIX##MI_ERSADD, PREFIX##MI_EATANxz, PREFIX##MI_ERSQRT, PREFIX##MI_EATAN, \ +}; \ + \ + void (*PREFIX##LowerOP_T3_10_OPCODE[32])(_VURegsNum *VUregsn) = { \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##MI_LQD , PREFIX##MI_RSQRT, PREFIX##MI_ILWR, \ + PREFIX##MI_RINIT , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x10 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_ELENG , PREFIX##MI_ESUM , PREFIX##MI_ERCPR, PREFIX##MI_EEXP, \ +}; \ + \ + void (*PREFIX##LowerOP_T3_11_OPCODE[32])(_VURegsNum *VUregsn) = { \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##MI_SQD , PREFIX##MI_WAITQ, PREFIX##MI_ISWR, \ + PREFIX##MI_RXOR , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x10 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_ERLENG, PREFIX##unknown , PREFIX##MI_WAITP, PREFIX##unknown, \ +}; \ + \ + void (*PREFIX##LowerOP_OPCODE[64])(_VURegsNum *VUregsn) = { \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x10 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x20 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##MI_IADD , PREFIX##MI_ISUB , PREFIX##MI_IADDI, PREFIX##unknown, /* 0x30 */ \ + PREFIX##MI_IAND , PREFIX##MI_IOR , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##LowerOP_T3_00, PREFIX##LowerOP_T3_01, PREFIX##LowerOP_T3_10, PREFIX##LowerOP_T3_11, \ +}; \ + \ + void (*PREFIX##_UPPER_OPCODE[64])(_VURegsNum *VUregsn) = { \ + PREFIX##MI_ADDx , PREFIX##MI_ADDy , PREFIX##MI_ADDz , PREFIX##MI_ADDw, \ + PREFIX##MI_SUBx , PREFIX##MI_SUBy , PREFIX##MI_SUBz , PREFIX##MI_SUBw, \ + PREFIX##MI_MADDx , PREFIX##MI_MADDy , PREFIX##MI_MADDz , PREFIX##MI_MADDw, \ + PREFIX##MI_MSUBx , PREFIX##MI_MSUBy , PREFIX##MI_MSUBz , PREFIX##MI_MSUBw, \ + PREFIX##MI_MAXx , PREFIX##MI_MAXy , PREFIX##MI_MAXz , PREFIX##MI_MAXw, /* 0x10 */ \ + PREFIX##MI_MINIx , PREFIX##MI_MINIy , PREFIX##MI_MINIz , PREFIX##MI_MINIw, \ + PREFIX##MI_MULx , PREFIX##MI_MULy , PREFIX##MI_MULz , PREFIX##MI_MULw, \ + PREFIX##MI_MULq , PREFIX##MI_MAXi , PREFIX##MI_MULi , PREFIX##MI_MINIi, \ + PREFIX##MI_ADDq , PREFIX##MI_MADDq , PREFIX##MI_ADDi , PREFIX##MI_MADDi, /* 0x20 */ \ + PREFIX##MI_SUBq , PREFIX##MI_MSUBq , PREFIX##MI_SUBi , PREFIX##MI_MSUBi, \ + PREFIX##MI_ADD , PREFIX##MI_MADD , PREFIX##MI_MUL , PREFIX##MI_MAX, \ + PREFIX##MI_SUB , PREFIX##MI_MSUB , PREFIX##MI_OPMSUB, PREFIX##MI_MINI, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, /* 0x30 */ \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown, \ + PREFIX##_UPPER_FD_00, PREFIX##_UPPER_FD_01, PREFIX##_UPPER_FD_10, PREFIX##_UPPER_FD_11, \ +}; \ + \ + void (*PREFIX##_UPPER_FD_00_TABLE[32])(_VURegsNum *VUregsn) = { \ + PREFIX##MI_ADDAx, PREFIX##MI_SUBAx , PREFIX##MI_MADDAx, PREFIX##MI_MSUBAx, \ + PREFIX##MI_ITOF0, PREFIX##MI_FTOI0, PREFIX##MI_MULAx , PREFIX##MI_MULAq , \ + PREFIX##MI_ADDAq, PREFIX##MI_SUBAq, PREFIX##MI_ADDA , PREFIX##MI_SUBA , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ +}; \ + \ + void (*PREFIX##_UPPER_FD_01_TABLE[32])(_VURegsNum *VUregsn) = { \ + PREFIX##MI_ADDAy , PREFIX##MI_SUBAy , PREFIX##MI_MADDAy, PREFIX##MI_MSUBAy, \ + PREFIX##MI_ITOF4 , PREFIX##MI_FTOI4 , PREFIX##MI_MULAy , PREFIX##MI_ABS , \ + PREFIX##MI_MADDAq, PREFIX##MI_MSUBAq, PREFIX##MI_MADDA , PREFIX##MI_MSUBA , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ +}; \ + \ + void (*PREFIX##_UPPER_FD_10_TABLE[32])(_VURegsNum *VUregsn) = { \ + PREFIX##MI_ADDAz , PREFIX##MI_SUBAz , PREFIX##MI_MADDAz, PREFIX##MI_MSUBAz, \ + PREFIX##MI_ITOF12, PREFIX##MI_FTOI12, PREFIX##MI_MULAz , PREFIX##MI_MULAi , \ + PREFIX##MI_ADDAi, PREFIX##MI_SUBAi , PREFIX##MI_MULA , PREFIX##MI_OPMULA, \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ +}; \ + \ + void (*PREFIX##_UPPER_FD_11_TABLE[32])(_VURegsNum *VUregsn) = { \ + PREFIX##MI_ADDAw , PREFIX##MI_SUBAw , PREFIX##MI_MADDAw, PREFIX##MI_MSUBAw, \ + PREFIX##MI_ITOF15, PREFIX##MI_FTOI15, PREFIX##MI_MULAw , PREFIX##MI_CLIP , \ + PREFIX##MI_MADDAi, PREFIX##MI_MSUBAi, PREFIX##unknown , PREFIX##MI_NOP , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ + PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , PREFIX##unknown , \ +}; \ + \ + \ + \ + void PREFIX##_UPPER_FD_00(_VURegsNum *VUregsn) { \ + PREFIX##_UPPER_FD_00_TABLE[(VU.code >> 6) & 0x1f ](VUregsn); \ +} \ + \ + void PREFIX##_UPPER_FD_01(_VURegsNum *VUregsn) { \ + PREFIX##_UPPER_FD_01_TABLE[(VU.code >> 6) & 0x1f](VUregsn); \ +} \ + \ + void PREFIX##_UPPER_FD_10(_VURegsNum *VUregsn) { \ + PREFIX##_UPPER_FD_10_TABLE[(VU.code >> 6) & 0x1f](VUregsn); \ +} \ + \ + void PREFIX##_UPPER_FD_11(_VURegsNum *VUregsn) { \ + PREFIX##_UPPER_FD_11_TABLE[(VU.code >> 6) & 0x1f](VUregsn); \ +} \ + \ + void PREFIX##LowerOP(_VURegsNum *VUregsn) { \ + PREFIX##LowerOP_OPCODE[VU.code & 0x3f](VUregsn); \ +} \ + \ + void PREFIX##LowerOP_T3_00(_VURegsNum *VUregsn) { \ + PREFIX##LowerOP_T3_00_OPCODE[(VU.code >> 6) & 0x1f](VUregsn); \ +} \ + \ + void PREFIX##LowerOP_T3_01(_VURegsNum *VUregsn) { \ + PREFIX##LowerOP_T3_01_OPCODE[(VU.code >> 6) & 0x1f](VUregsn); \ +} \ + \ + void PREFIX##LowerOP_T3_10(_VURegsNum *VUregsn) { \ + PREFIX##LowerOP_T3_10_OPCODE[(VU.code >> 6) & 0x1f](VUregsn); \ +} \ + \ + void PREFIX##LowerOP_T3_11(_VURegsNum *VUregsn) { \ + PREFIX##LowerOP_T3_11_OPCODE[(VU.code >> 6) & 0x1f](VUregsn); \ +} + + +#ifdef VUM_LOG + +#define IdebugUPPER(VU) \ + VUM_LOG("%s\n", dis##VU##MicroUF(VU.code, VU.VI[REG_TPC].UL)); +#define IdebugLOWER(VU) \ + VUM_LOG("%s\n", dis##VU##MicroLF(VU.code, VU.VI[REG_TPC].UL)); + +#else + +#define IdebugUPPER(VU) +#define IdebugLOWER(VU) + +#endif + +#ifdef VUM_LOG +#define _vuExecMicroDebug(VU) \ + VUM_LOG("_vuExecMicro: %8.8x\n", VU.VI[REG_TPC].UL); +#else +#define _vuExecMicroDebug(VU) +#endif + +#include "VUops.h" + +#endif + diff --git a/pcsx2/VUmicroMem.cpp b/pcsx2/VUmicroMem.cpp new file mode 100644 index 0000000000..bfd2010ac6 --- /dev/null +++ b/pcsx2/VUmicroMem.cpp @@ -0,0 +1,197 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include + +#include "Common.h" +#include "R5900.h" +#include "VUmicro.h" + +#include "iVUzerorec.h" + +// The following CpuVU objects are value types instead of handles or pointers because they are +// modified on the fly to implement VU1 Skip. + +VUmicroCpu CpuVU0; // contains a working copy of the VU0 cpu functions/API +VUmicroCpu CpuVU1; // contains a working copy of the VU1 cpu functions/API + +static void DummyExecuteVU1Block(void) +{ + VU0.VI[ REG_VPU_STAT ].UL &= ~0x100; + VU1.vifRegs->stat &= ~4; // also reset the bit (grandia 3 works) +} + +void vu1MicroEnableSkip() +{ + CpuVU1.ExecuteBlock = DummyExecuteVU1Block; +} + +void vu1MicroDisableSkip() +{ + CpuVU1.ExecuteBlock = CHECK_VU1REC ? recVU1.ExecuteBlock : intVU1.ExecuteBlock; +} + +bool vu1MicroIsSkipping() +{ + return CpuVU1.ExecuteBlock == DummyExecuteVU1Block; +} + +void vuMicroCpuReset() +{ + CpuVU0 = CHECK_VU0REC ? recVU0 : intVU0; + CpuVU1 = CHECK_VU1REC ? recVU1 : intVU1; + CpuVU0.Reset(); + CpuVU1.Reset(); + + // SuperVUreset will do nothing is none of the recs are initialized. + // But it's needed if one or the other is initialized. + SuperVUReset(-1); +} + +static u8* m_vuAllMem = NULL; +static const uint m_vuMemSize = + 0x4000+0x800 + // VU0 memory and VU1 registers + 0x1000 + // VU0micro memory + 0x4000 + // VU1 memory + 0x4000 + // VU1micro memory + 0x4000; // HACKFIX (see below) + +// HACKFIX! (air) +// The VIFdma1 has a nasty habit of transferring data into the 4k page of memory above +// the VU1. (oops!!) This happens to be recLUT most of the time, which causes rapid death +// of our emulator. So we allocate some extra space here to keep VIF1 a little happier. + +// fixme - When the VIF is fixed, remove the third +0x4000 above. :) + +void vuMicroMemAlloc() +{ + if( m_vuAllMem == NULL ) + m_vuAllMem = vtlb_malloc( m_vuMemSize, 16, 0x28000000 ); + + if( m_vuAllMem == NULL ) + throw Exception::OutOfMemory( "vuMicroMemInit > Failed to allocate VUmicro memory." ); + + jASSUME( sizeof( VURegs ) <= 0x800 ); + + u8* curpos = m_vuAllMem; + VU0.Mem = curpos; curpos += 0x4000; + g_pVU1 = (VURegs*)curpos; curpos += 0x800; + VU0.Micro = curpos; curpos += 0x1000; + VU1.Mem = curpos; curpos += 0x4000; + VU1.Micro = curpos; //curpos += 0x4000; +} + +void vuMicroMemShutdown() +{ + // -- VTLB Memory Allocation -- + + vtlb_free( m_vuAllMem, m_vuMemSize ); + m_vuAllMem = NULL; + g_pVU1 = NULL; +} + +void vuMicroMemReset() +{ + jASSUME( VU0.Mem != NULL ); + jASSUME( VU1.Mem != NULL ); + + memMapVUmicro(); + + // === VU0 Initialization === + memzero_obj(VU0.ACC); + memzero_obj(VU0.VF); + memzero_obj(VU0.VI); + VU0.VF[0].f.x = 0.0f; + VU0.VF[0].f.y = 0.0f; + VU0.VF[0].f.z = 0.0f; + VU0.VF[0].f.w = 1.0f; + VU0.VI[0].UL = 0; + memzero_ptr<4*1024>(VU0.Mem); + memzero_ptr<4*1024>(VU0.Micro); + + /* this is kinda tricky, maxmem is set to 0x4400 here, + tho it's not 100% accurate, since the mem goes from + 0x0000 - 0x1000 (Mem) and 0x4000 - 0x4400 (VU1 Regs), + i guess it shouldn't be a problem, + at least hope so :) (linuz) + */ + VU0.maxmem = 0x4400-4; + VU0.maxmicro = 4*1024-4; + VU0.vuExec = vu0Exec; + VU0.vifRegs = vif0Regs; + + // === VU1 Initialization === + memzero_obj(VU1.ACC); + memzero_obj(VU1.VF); + memzero_obj(VU1.VI); + VU1.VF[0].f.x = 0.0f; + VU1.VF[0].f.y = 0.0f; + VU1.VF[0].f.z = 0.0f; + VU1.VF[0].f.w = 1.0f; + VU1.VI[0].UL = 0; + memzero_ptr<16*1024>(VU1.Mem); + memzero_ptr<16*1024>(VU1.Micro); + + VU1.maxmem = -1;//16*1024-4; + VU1.maxmicro = 16*1024-4; +// VU1.VF = (VECTOR*)(VU0.Mem + 0x4000); +// VU1.VI = (REG_VI*)(VU0.Mem + 0x4200); + VU1.vuExec = vu1Exec; + VU1.vifRegs = vif1Regs; +} + +void SaveState::vuMicroFreeze() +{ + jASSUME( VU0.Mem != NULL ); + jASSUME( VU1.Mem != NULL ); + + Freeze(VU0.ACC); + Freeze(VU0.code); + FreezeMem(VU0.Mem, 4*1024); + FreezeMem(VU0.Micro, 4*1024); + + Freeze(VU0.VF); + if( GetVersion() >= 0x0012 ) + Freeze(VU0.VI); + else + { + // Old versions stored the VIregs as 32 bit values... + memzero_obj( VU0.VI ); + for(int i=0; i<32; i++ ) + Freeze( VU0.VI[i].UL ); + } + + Freeze(VU1.ACC); + Freeze(VU1.code); + FreezeMem(VU1.Mem, 16*1024); + FreezeMem(VU1.Micro, 16*1024); + + Freeze(VU1.VF); + if( GetVersion() >= 0x0012 ) + Freeze(VU1.VI); + else + { + // Old versions stored the VIregs as 32 bit values... + memzero_obj( VU1.VI ); + for(int i=0; i<32; i++ ) + Freeze( VU1.VI[i].UL ); + } + +} diff --git a/pcsx2/VUops.cpp b/pcsx2/VUops.cpp new file mode 100644 index 0000000000..bc33358014 --- /dev/null +++ b/pcsx2/VUops.cpp @@ -0,0 +1,2929 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include + +#include "Common.h" +#include "VUmicro.h" +#include "VUflags.h" +#include "VUops.h" +#include "GS.h" + +//Lower/Upper instructions can use that.. +#define _Ft_ ((VU->code >> 16) & 0x1F) // The rt part of the instruction register +#define _Fs_ ((VU->code >> 11) & 0x1F) // The rd part of the instruction register +#define _Fd_ ((VU->code >> 6) & 0x1F) // The sa part of the instruction register + +#define _X ((VU->code>>24) & 0x1) +#define _Y ((VU->code>>23) & 0x1) +#define _Z ((VU->code>>22) & 0x1) +#define _W ((VU->code>>21) & 0x1) + +#define _XYZW ((VU->code>>21) & 0xF) + +#define _Fsf_ ((VU->code >> 21) & 0x03) +#define _Ftf_ ((VU->code >> 23) & 0x03) + +#define _Imm11_ (s32)(VU->code & 0x400 ? 0xfffffc00 | (VU->code & 0x3ff) : VU->code & 0x3ff) +#define _UImm11_ (s32)(VU->code & 0x7ff) + + +VECTOR RDzero; + +void _vuFMACflush(VURegs * VU) { + int i; + + for (i=0; i<8; i++) { + if (VU->fmac[i].enable == 0) continue; + + if ((VU->cycle - VU->fmac[i].sCycle) >= VU->fmac[i].Cycle) { + VUM_LOG("flushing FMAC pipe[%d] (macflag=%x)\n", i, VU->fmac[i].macflag); + + VU->fmac[i].enable = 0; + VU->VI[REG_MAC_FLAG].UL = VU->fmac[i].macflag; + VU->VI[REG_STATUS_FLAG].UL = VU->fmac[i].statusflag; + VU->VI[REG_CLIP_FLAG].UL = VU->fmac[i].clipflag; + } + } +} + +void _vuFDIVflush(VURegs * VU) { + if (VU->fdiv.enable == 0) return; + + if ((VU->cycle - VU->fdiv.sCycle) >= VU->fdiv.Cycle) { + VUM_LOG("flushing FDIV pipe\n"); + + VU->fdiv.enable = 0; + VU->VI[REG_Q].UL = VU->fdiv.reg.UL; + VU->VI[REG_STATUS_FLAG].UL = VU->fdiv.statusflag; + } +} + +void _vuEFUflush(VURegs * VU) { + if (VU->efu.enable == 0) return; + + if ((VU->cycle - VU->efu.sCycle) >= VU->efu.Cycle) { +// VUM_LOG("flushing EFU pipe\n"); + + VU->efu.enable = 0; + VU->VI[REG_P].UL = VU->efu.reg.UL; + } +} + +// called at end of program +void _vuFlushAll(VURegs* VU) +{ + int nRepeat = 1, i; + + do { + nRepeat = 0; + + for (i=0; i<8; i++) { + if (VU->fmac[i].enable == 0) continue; + + nRepeat = 1; + + if ((VU->cycle - VU->fmac[i].sCycle) >= VU->fmac[i].Cycle) { + VUM_LOG("flushing FMAC pipe[%d] (macflag=%x)\n", i, VU->fmac[i].macflag); + + VU->fmac[i].enable = 0; + VU->VI[REG_MAC_FLAG].UL = VU->fmac[i].macflag; + VU->VI[REG_STATUS_FLAG].UL = VU->fmac[i].statusflag; + VU->VI[REG_CLIP_FLAG].UL = VU->fmac[i].clipflag; + } + } + + if (VU->fdiv.enable ) { + + nRepeat = 1; + + if ((VU->cycle - VU->fdiv.sCycle) >= VU->fdiv.Cycle) { + VUM_LOG("flushing FDIV pipe\n"); + + nRepeat = 1; + VU->fdiv.enable = 0; + VU->VI[REG_Q].UL = VU->fdiv.reg.UL; + VU->VI[REG_STATUS_FLAG].UL = VU->fdiv.statusflag; + } + } + + if (VU->efu.enable) { + + nRepeat = 1; + + if ((VU->cycle - VU->efu.sCycle) >= VU->efu.Cycle) { + // VUM_LOG("flushing EFU pipe\n"); + + nRepeat = 1; + VU->efu.enable = 0; + VU->VI[REG_P].UL = VU->efu.reg.UL; + } + } + + VU->cycle++; + + } while(nRepeat); +} + +void _vuTestPipes(VURegs * VU) { + _vuFMACflush(VU); + _vuFDIVflush(VU); + _vuEFUflush(VU); +} + +void _vuFMACTestStall(VURegs * VU, int reg, int xyzw) { + int cycle; + int i; + + for (i=0; i<8; i++) { + if (VU->fmac[i].enable == 0) continue; + if (VU->fmac[i].reg == reg && + VU->fmac[i].xyzw & xyzw) break; + } + + if (i == 8) return; + + cycle = VU->fmac[i].Cycle - (VU->cycle - VU->fmac[i].sCycle) + 1; // add 1 delay! (fixes segaclassics bad geom) + VU->fmac[i].enable = 0; + VU->VI[REG_MAC_FLAG].UL = VU->fmac[i].macflag; + VU->VI[REG_STATUS_FLAG].UL = VU->fmac[i].statusflag; + VU->VI[REG_CLIP_FLAG].UL = VU->fmac[i].clipflag; + VUM_LOG("FMAC[%d] stall %d\n", i, cycle); + + VU->cycle+= cycle; + _vuTestPipes(VU); +} + +void _vuFMACAdd(VURegs * VU, int reg, int xyzw) { + int i; + + /* find a free fmac pipe */ + for (i=0; i<8; i++) { + if (VU->fmac[i].enable == 1) continue; + break; + } + if (i==8) { +// SysPrintf("*PCSX2*: error , out of fmacs %d\n", VU->cycle); + } + + VUM_LOG("adding FMAC pipe[%d]; xyzw=%x\n", i, xyzw); + + VU->fmac[i].enable = 1; + VU->fmac[i].sCycle = VU->cycle; + VU->fmac[i].Cycle = 3; + VU->fmac[i].reg = reg; + VU->fmac[i].xyzw = xyzw; + VU->fmac[i].macflag = VU->macflag; + VU->fmac[i].statusflag = VU->statusflag; + VU->fmac[i].clipflag = VU->clipflag; +} + +void _vuFDIVAdd(VURegs * VU, int cycles) { + VUM_LOG("adding FDIV pipe\n"); + + VU->fdiv.enable = 1; + VU->fdiv.sCycle = VU->cycle; + VU->fdiv.Cycle = cycles; + VU->fdiv.reg.F = VU->q.F; + VU->fdiv.statusflag = VU->statusflag; +} + +void _vuEFUAdd(VURegs * VU, int cycles) { +// VUM_LOG("adding EFU pipe\n"); + + VU->efu.enable = 1; + VU->efu.sCycle = VU->cycle; + VU->efu.Cycle = cycles; + VU->efu.reg.F = VU->p.F; +} + +void _vuFlushFDIV(VURegs * VU) { + int cycle; + + if (VU->fdiv.enable == 0) return; + + cycle = VU->fdiv.Cycle - (VU->cycle - VU->fdiv.sCycle); + VUM_LOG("waiting FDIV pipe %d\n", cycle); + + VU->fdiv.enable = 0; + VU->cycle+= cycle; + VU->VI[REG_Q].UL = VU->fdiv.reg.UL; + VU->VI[REG_STATUS_FLAG].UL = VU->fdiv.statusflag; +} + +void _vuFlushEFU(VURegs * VU) { + int cycle; + + if (VU->efu.enable == 0) return; + + cycle = VU->efu.Cycle - (VU->cycle - VU->efu.sCycle); +// VUM_LOG("waiting EFU pipe %d\n", cycle); + + VU->efu.enable = 0; + VU->cycle+= cycle; + VU->VI[REG_P].UL = VU->efu.reg.UL; +} + +void _vuTestFMACStalls(VURegs * VU, _VURegsNum *VUregsn) { + if (VUregsn->VFread0) { + _vuFMACTestStall(VU, VUregsn->VFread0, VUregsn->VFr0xyzw); + } + if (VUregsn->VFread1) { + _vuFMACTestStall(VU, VUregsn->VFread1, VUregsn->VFr1xyzw); + } +} + +void _vuAddFMACStalls(VURegs * VU, _VURegsNum *VUregsn) { + if (VUregsn->VFwrite) { + _vuFMACAdd(VU, VUregsn->VFwrite, VUregsn->VFwxyzw); + } else + if (VUregsn->VIwrite & (1 << REG_CLIP_FLAG)) { + _vuFMACAdd(VU, -REG_CLIP_FLAG, 0); + } else { + _vuFMACAdd(VU, 0, 0); + } +} + +void _vuTestFDIVStalls(VURegs * VU, _VURegsNum *VUregsn) { +// _vuTestFMACStalls(VURegs * VU, _VURegsNum *VUregsn); + _vuFlushFDIV(VU); +} + +void _vuAddFDIVStalls(VURegs * VU, _VURegsNum *VUregsn) { + if (VUregsn->VIwrite & (1 << REG_Q)) { + _vuFDIVAdd(VU, VUregsn->cycles); + } +} + + +void _vuTestEFUStalls(VURegs * VU, _VURegsNum *VUregsn) { +// _vuTestFMACStalls(VURegs * VU, _VURegsNum *VUregsn); + _vuFlushEFU(VU); +} + +void _vuAddEFUStalls(VURegs * VU, _VURegsNum *VUregsn) { + if (VUregsn->VIwrite & (1 << REG_P)) { + _vuEFUAdd(VU, VUregsn->cycles); + } +} + +void _vuTestUpperStalls(VURegs * VU, _VURegsNum *VUregsn) { + switch (VUregsn->pipe) { + case VUPIPE_FMAC: _vuTestFMACStalls(VU, VUregsn); break; + } +} + +void _vuTestLowerStalls(VURegs * VU, _VURegsNum *VUregsn) { + switch (VUregsn->pipe) { + case VUPIPE_FMAC: _vuTestFMACStalls(VU, VUregsn); break; + case VUPIPE_FDIV: _vuTestFDIVStalls(VU, VUregsn); break; + case VUPIPE_EFU: _vuTestEFUStalls(VU, VUregsn); break; + } +} + +void _vuAddUpperStalls(VURegs * VU, _VURegsNum *VUregsn) { + switch (VUregsn->pipe) { + case VUPIPE_FMAC: _vuAddFMACStalls(VU, VUregsn); break; + } +} + +void _vuAddLowerStalls(VURegs * VU, _VURegsNum *VUregsn) { + switch (VUregsn->pipe) { + case VUPIPE_FMAC: _vuAddFMACStalls(VU, VUregsn); break; + case VUPIPE_FDIV: _vuAddFDIVStalls(VU, VUregsn); break; + case VUPIPE_EFU: _vuAddEFUStalls(VU, VUregsn); break; + } +} + + +/******************************/ +/* VU Upper instructions */ +/******************************/ +#ifndef INT_VUDOUBLEHACK +static u32 d; +float vuDouble(u32 f) +{ + switch(f & 0x7f800000){ + case 0x0: + f &= 0x80000000; + return *(float*)&f; + break; + case 0x7f800000: + d = (f & 0x80000000)|0x7f7fffff; + return *(float*)&d; + break; + default: + return *(float*)&f; + break; + } +} +#else +float vuDouble(u32 f) +{ + return *(float*)&f; +} +#endif + +void _vuABS(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X){ VU->VF[_Ft_].f.x = fabs(vuDouble(VU->VF[_Fs_].i.x)); } + if (_Y){ VU->VF[_Ft_].f.y = fabs(vuDouble(VU->VF[_Fs_].i.y)); } + if (_Z){ VU->VF[_Ft_].f.z = fabs(vuDouble(VU->VF[_Fs_].i.z)); } + if (_W){ VU->VF[_Ft_].f.w = fabs(vuDouble(VU->VF[_Fs_].i.w)); } +}/*Reworked from define to function. asadr*/ + + +void _vuADD(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + vuDouble(VU->VF[_Ft_].i.x)); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + vuDouble(VU->VF[_Ft_].i.y)); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + vuDouble(VU->VF[_Ft_].i.z)); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + vuDouble(VU->VF[_Ft_].i.w)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + + +void _vuADDi(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + vuDouble(VU->VI[REG_I].UL));} else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + vuDouble(VU->VI[REG_I].UL));} else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + vuDouble(VU->VI[REG_I].UL));} else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + vuDouble(VU->VI[REG_I].UL));} else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDq(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + vuDouble(VU->VI[REG_Q].UL)); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + vuDouble(VU->VI[REG_Q].UL)); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + vuDouble(VU->VI[REG_Q].UL)); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + vuDouble(VU->VI[REG_Q].UL)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + + +void _vuADDx(VURegs * VU) { + float ftx; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftx=vuDouble(VU->VF[_Ft_].i.x); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + ftx); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + ftx); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + ftx); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + ftx); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDy(VURegs * VU) { + float fty; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + fty=vuDouble(VU->VF[_Ft_].i.y); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + fty);} else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + fty);} else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + fty);} else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + fty);} else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDz(VURegs * VU) { + float ftz; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftz=vuDouble(VU->VF[_Ft_].i.z); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + ftz); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + ftz); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + ftz); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + ftz); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDw(VURegs * VU) { + float ftw; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftw=vuDouble(VU->VF[_Ft_].i.w); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + ftw); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + ftw); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + ftw); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + ftw); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDA(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + vuDouble(VU->VF[_Ft_].i.x)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + vuDouble(VU->VF[_Ft_].i.y)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + vuDouble(VU->VF[_Ft_].i.z)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + vuDouble(VU->VF[_Ft_].i.w)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDAi(VURegs * VU) { + float ti = vuDouble(VU->VI[REG_I].UL); + + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + ti); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + ti); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + ti); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + ti); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDAq(VURegs * VU) { + float tf = vuDouble(VU->VI[REG_Q].UL); + + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + tf); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + tf); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + tf); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + tf); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDAx(VURegs * VU) { + float tx = vuDouble(VU->VF[_Ft_].i.x); + + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + tx); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + tx); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + tx); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + tx); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDAy(VURegs * VU) { + float ty = vuDouble(VU->VF[_Ft_].i.y); + + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + ty); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + ty); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + ty); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + ty); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDAz(VURegs * VU) { + float tz = vuDouble(VU->VF[_Ft_].i.z); + + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + tz); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + tz); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + tz); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + tz); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + +void _vuADDAw(VURegs * VU) { + float tw = vuDouble(VU->VF[_Ft_].i.w); + + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) + tw); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) + tw); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) + tw); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) + tw); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*Reworked from define to function. asadr*/ + + +void _vuSUB(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - vuDouble(VU->VF[_Ft_].i.x)); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - vuDouble(VU->VF[_Ft_].i.y)); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - vuDouble(VU->VF[_Ft_].i.z)); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - vuDouble(VU->VF[_Ft_].i.w)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBi(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - vuDouble(VU->VI[REG_I].UL)); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - vuDouble(VU->VI[REG_I].UL)); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - vuDouble(VU->VI[REG_I].UL)); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - vuDouble(VU->VI[REG_I].UL)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBq(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - vuDouble(VU->VI[REG_Q].UL)); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - vuDouble(VU->VI[REG_Q].UL)); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - vuDouble(VU->VI[REG_Q].UL)); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - vuDouble(VU->VI[REG_Q].UL)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBx(VURegs * VU) { + float ftx; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftx=vuDouble(VU->VF[_Ft_].i.x); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - ftx); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - ftx); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - ftx); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - ftx); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBy(VURegs * VU) { + float fty; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + fty=vuDouble(VU->VF[_Ft_].i.y); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - fty); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - fty); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - fty); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - fty); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBz(VURegs * VU) { + float ftz; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftz=vuDouble(VU->VF[_Ft_].i.z); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - ftz); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - ftz); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - ftz); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - ftz); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBw(VURegs * VU) { + float ftw; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftw=vuDouble(VU->VF[_Ft_].i.w); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - ftw); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - ftw); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - ftw); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - ftw); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + + +void _vuSUBA(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - vuDouble(VU->VF[_Ft_].i.x)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - vuDouble(VU->VF[_Ft_].i.y)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - vuDouble(VU->VF[_Ft_].i.z)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - vuDouble(VU->VF[_Ft_].i.w)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBAi(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - vuDouble(VU->VI[REG_I].UL)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - vuDouble(VU->VI[REG_I].UL)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - vuDouble(VU->VI[REG_I].UL)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - vuDouble(VU->VI[REG_I].UL)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBAq(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - vuDouble(VU->VI[REG_Q].UL)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - vuDouble(VU->VI[REG_Q].UL)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - vuDouble(VU->VI[REG_Q].UL)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - vuDouble(VU->VI[REG_Q].UL)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBAx(VURegs * VU) { + float tx = vuDouble(VU->VF[_Ft_].i.x); + + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - tx); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - tx); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - tx); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - tx); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBAy(VURegs * VU) { + float ty = vuDouble(VU->VF[_Ft_].i.y); + + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - ty); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - ty); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - ty); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - ty); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBAz(VURegs * VU) { + float tz = vuDouble(VU->VF[_Ft_].i.z); + + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - tz); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - tz); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - tz); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - tz); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuSUBAw(VURegs * VU) { + float tw = vuDouble(VU->VF[_Ft_].i.w); + + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) - tw); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) - tw); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) - tw); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) - tw); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}//updated 10/05/03 shadow + +void _vuMUL(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.x)); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.y)); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.z)); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.w)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +/* No need to presave I reg in ti. asadr */ +void _vuMULi(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VI[REG_I].UL)); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VI[REG_I].UL)); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VI[REG_I].UL)); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VI[REG_I].UL)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +void _vuMULq(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VI[REG_Q].UL)); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VI[REG_Q].UL)); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VI[REG_Q].UL)); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VI[REG_Q].UL)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +void _vuMULx(VURegs * VU) { + float ftx; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftx=vuDouble(VU->VF[_Ft_].i.x); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * ftx); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * ftx); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * ftx); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * ftx); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + + +void _vuMULy(VURegs * VU) { + float fty; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + fty=vuDouble(VU->VF[_Ft_].i.y); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * fty); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * fty); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * fty); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * fty); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +void _vuMULz(VURegs * VU) { + float ftz; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftz=vuDouble(VU->VF[_Ft_].i.z); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * ftz); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * ftz); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * ftz); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * ftz); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +void _vuMULw(VURegs * VU) { + float ftw; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftw=vuDouble(VU->VF[_Ft_].i.w); + if (_X){ dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * ftw); } else VU_MACx_CLEAR(VU); + if (_Y){ dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * ftw); } else VU_MACy_CLEAR(VU); + if (_Z){ dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * ftw); } else VU_MACz_CLEAR(VU); + if (_W){ dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * ftw); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + + +void _vuMULA(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.x)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.y)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.z)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.w)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +/* No need to presave I reg in ti. asadr */ +void _vuMULAi(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VI[REG_I].UL)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VI[REG_I].UL)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VI[REG_I].UL)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VI[REG_I].UL)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +/* No need to presave Q reg in ti. asadr */ +void _vuMULAq(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VI[REG_Q].UL)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VI[REG_Q].UL)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VI[REG_Q].UL)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VI[REG_Q].UL)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +/* No need to presave X reg in ti. asadr */ +void _vuMULAx(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.x)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.x)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.x)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.x)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +void _vuMULAy(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.y)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.y)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.y)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.y)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +void _vuMULAz(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.z)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.z)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.z)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.z)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +void _vuMULAw(VURegs * VU) { + if (_X){ VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.w)); } else VU_MACx_CLEAR(VU); + if (_Y){ VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.w)); } else VU_MACy_CLEAR(VU); + if (_Z){ VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.w)); } else VU_MACz_CLEAR(VU); + if (_W){ VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.w)); } else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 8/05/03 shadow */ + +void _vuMADD(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + ( vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.x))); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + ( vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.y))); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + ( vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.z))); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + ( vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.w))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 10/05/03 shadow */ + + +void _vuMADDi(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + (vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VI[REG_I].UL))); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + (vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VI[REG_I].UL))); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + (vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VI[REG_I].UL))); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + (vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VI[REG_I].UL))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 10/05/03 shadow */ + +/* No need to presave . asadr */ +void _vuMADDq(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + (vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VI[REG_Q].UL))); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + (vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VI[REG_Q].UL))); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + (vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VI[REG_Q].UL))); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + (vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VI[REG_Q].UL))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 10/05/03 shadow */ + +void _vuMADDx(VURegs * VU) { + float ftx; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftx=vuDouble(VU->VF[_Ft_].i.x); + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + (vuDouble(VU->VF[_Fs_].i.x) * ftx)); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + (vuDouble(VU->VF[_Fs_].i.y) * ftx)); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + (vuDouble(VU->VF[_Fs_].i.z) * ftx)); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + (vuDouble(VU->VF[_Fs_].i.w) * ftx)); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 10/05/03 shadow */ + +void _vuMADDy(VURegs * VU) { + float fty; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + fty=vuDouble(VU->VF[_Ft_].i.y); + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + (vuDouble(VU->VF[_Fs_].i.x) * fty)); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + (vuDouble(VU->VF[_Fs_].i.y) * fty)); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + (vuDouble(VU->VF[_Fs_].i.z) * fty)); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + (vuDouble(VU->VF[_Fs_].i.w) * fty)); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 10/05/03 shadow */ + +void _vuMADDz(VURegs * VU) { + float ftz; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftz=vuDouble(VU->VF[_Ft_].i.z); + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + (vuDouble(VU->VF[_Fs_].i.x) * ftz)); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + (vuDouble(VU->VF[_Fs_].i.y) * ftz)); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + (vuDouble(VU->VF[_Fs_].i.z) * ftz)); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + (vuDouble(VU->VF[_Fs_].i.w) * ftz)); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 10/05/03 shadow */ + +void _vuMADDw(VURegs * VU) { + float ftw; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftw=vuDouble(VU->VF[_Ft_].i.w); + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + (vuDouble(VU->VF[_Fs_].i.x) * ftw)); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + (vuDouble(VU->VF[_Fs_].i.y) * ftw)); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + (vuDouble(VU->VF[_Fs_].i.z) * ftw)); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + (vuDouble(VU->VF[_Fs_].i.w) * ftw)); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 10/05/03 shadow */ + +void _vuMADDA(VURegs * VU) { + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + (vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.x))); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + (vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.y))); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + (vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.z))); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + (vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.w))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last updated 10/05/03 shadow*/ + +void _vuMADDAi(VURegs * VU) { + float ti = vuDouble(VU->VI[REG_I].UL); + + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + ( vuDouble(VU->VF[_Fs_].i.x) * ti)); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + ( vuDouble(VU->VF[_Fs_].i.y) * ti)); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + ( vuDouble(VU->VF[_Fs_].i.z) * ti)); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + ( vuDouble(VU->VF[_Fs_].i.w) * ti)); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last updated 10/05/03 shadow*/ + +void _vuMADDAq(VURegs * VU) { + float tq = vuDouble(VU->VI[REG_Q].UL); + + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + ( vuDouble(VU->VF[_Fs_].i.x) * tq)); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + ( vuDouble(VU->VF[_Fs_].i.y) * tq)); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + ( vuDouble(VU->VF[_Fs_].i.z) * tq)); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + ( vuDouble(VU->VF[_Fs_].i.w) * tq)); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last update 10/05/03 shadow*/ + +void _vuMADDAx(VURegs * VU) { + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + ( vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.x))); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + ( vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.x))); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + ( vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.x))); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + ( vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.x))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last update 11/05/03 shadow*/ + +void _vuMADDAy(VURegs * VU) { + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + ( vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.y))); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + ( vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.y))); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + ( vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.y))); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + ( vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.y))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last update 11/05/03 shadow*/ + +void _vuMADDAz(VURegs * VU) { + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + ( vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.z))); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + ( vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.z))); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + ( vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.z))); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + ( vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.z))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last update 11/05/03 shadow*/ + +void _vuMADDAw(VURegs * VU) { + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) + ( vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.w))); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) + ( vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.w))); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) + ( vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.w))); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) + ( vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.w))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last update 11/05/03 shadow*/ + +void _vuMSUB(VURegs * VU) { + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.x))); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.y))); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.z))); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.w))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 11/05/03 shadow */ + +void _vuMSUBi(VURegs * VU) { + float ti = vuDouble(VU->VI[REG_I].UL); + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * ti ) ); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * ti ) ); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * ti ) ); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * ti ) ); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 11/05/03 shadow */ + +void _vuMSUBq(VURegs * VU) { + float tq = vuDouble(VU->VI[REG_Q].UL); + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * tq ) ); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * tq ) ); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * tq ) ); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * tq ) ); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 11/05/03 shadow */ + + +void _vuMSUBx(VURegs * VU) { + float ftx; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftx=vuDouble(VU->VF[_Ft_].i.x); + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * ftx ) ); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * ftx ) ); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * ftx ) ); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * ftx ) ); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 11/05/03 shadow */ + + +void _vuMSUBy(VURegs * VU) { + float fty; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + fty=vuDouble(VU->VF[_Ft_].i.y); + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * fty ) ); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * fty ) ); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * fty ) ); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * fty ) ); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 11/05/03 shadow */ + + +void _vuMSUBz(VURegs * VU) { + float ftz; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftz=vuDouble(VU->VF[_Ft_].i.z); + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * ftz ) ); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * ftz ) ); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * ftz ) ); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * ftz ) ); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 11/05/03 shadow */ + +void _vuMSUBw(VURegs * VU) { + float ftw; + VECTOR * dst; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftw=vuDouble(VU->VF[_Ft_].i.w); + if (_X) dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * ftw ) ); else VU_MACx_CLEAR(VU); + if (_Y) dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * ftw ) ); else VU_MACy_CLEAR(VU); + if (_Z) dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * ftw ) ); else VU_MACz_CLEAR(VU); + if (_W) dst->i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * ftw ) ); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/* last update 11/05/03 shadow */ + + +void _vuMSUBA(VURegs * VU) { + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.x))); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.y))); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.z))); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VF[_Ft_].i.w))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last updated 11/05/03 shadow*/ + +void _vuMSUBAi(VURegs * VU) { + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VI[REG_I].UL))); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VI[REG_I].UL))); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VI[REG_I].UL))); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VI[REG_I].UL))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last updated 11/05/03 shadow*/ + +void _vuMSUBAq(VURegs * VU) { + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VI[REG_Q].UL))); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VI[REG_Q].UL))); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VI[REG_Q].UL))); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * vuDouble(VU->VI[REG_Q].UL))); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last updated 11/05/03 shadow*/ + +void _vuMSUBAx(VURegs * VU) { + float tx = vuDouble(VU->VF[_Ft_].i.x); + + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * tx)); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * tx)); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * tx)); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * tx)); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last updated 11/05/03 shadow*/ + +void _vuMSUBAy(VURegs * VU) { + float ty = vuDouble(VU->VF[_Ft_].i.y); + + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * ty)); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * ty)); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * ty)); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * ty)); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last updated 11/05/03 shadow*/ + +void _vuMSUBAz(VURegs * VU) { + float tz = vuDouble(VU->VF[_Ft_].i.z); + + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * tz)); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * tz)); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * tz)); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * tz)); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last updated 11/05/03 shadow*/ + +void _vuMSUBAw(VURegs * VU) { + float tw = vuDouble(VU->VF[_Ft_].i.w); + + if (_X) VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - ( vuDouble(VU->VF[_Fs_].i.x) * tw)); else VU_MACx_CLEAR(VU); + if (_Y) VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - ( vuDouble(VU->VF[_Fs_].i.y) * tw)); else VU_MACy_CLEAR(VU); + if (_Z) VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - ( vuDouble(VU->VF[_Fs_].i.z) * tw)); else VU_MACz_CLEAR(VU); + if (_W) VU->ACC.i.w = VU_MACw_UPDATE(VU, vuDouble(VU->ACC.i.w) - ( vuDouble(VU->VF[_Fs_].i.w) * tw)); else VU_MACw_CLEAR(VU); + VU_STAT_UPDATE(VU); +}/*last updated 11/05/03 shadow*/ + +u32 _MAX(u32 a, u32 b) { + if (a & 0x80000000) { // -a + if (b & 0x80000000) { // -b + return (a & 0x7fffffff) > (b & 0x7fffffff) ? b : a; + } else { // +b + return b; + } + } else { // +a + if (b & 0x80000000) { // -b + return a; + } else { // +b + return (a & 0x7fffffff) > (b & 0x7fffffff) ? a : b; + } + } + + return 0; +} + +void _vuMAX(VURegs * VU) { + if (_Fd_ == 0) return; + + /* ft is bc */ + if (_X) VU->VF[_Fd_].i.x = _MAX(VU->VF[_Fs_].i.x, (s32)VU->VF[_Ft_].i.x); + if (_Y) VU->VF[_Fd_].i.y = _MAX(VU->VF[_Fs_].i.y, (s32)VU->VF[_Ft_].i.y); + if (_Z) VU->VF[_Fd_].i.z = _MAX(VU->VF[_Fs_].i.z, (s32)VU->VF[_Ft_].i.z); + if (_W) VU->VF[_Fd_].i.w = _MAX(VU->VF[_Fs_].i.w, (s32)VU->VF[_Ft_].i.w); +}//checked 13/05/03 shadow + +void _vuMAXi(VURegs * VU) { + if (_Fd_ == 0) return; + + /* ft is bc */ + if (_X) VU->VF[_Fd_].i.x = _MAX(VU->VF[_Fs_].i.x, VU->VI[REG_I].UL); + if (_Y) VU->VF[_Fd_].i.y = _MAX(VU->VF[_Fs_].i.y, VU->VI[REG_I].UL); + if (_Z) VU->VF[_Fd_].i.z = _MAX(VU->VF[_Fs_].i.z, VU->VI[REG_I].UL); + if (_W) VU->VF[_Fd_].i.w = _MAX(VU->VF[_Fs_].i.w, VU->VI[REG_I].UL); +}//checked 13/05/03 shadow + +void _vuMAXx(VURegs * VU) { + s32 ftx; + if (_Fd_ == 0) return; + + ftx=(s32)VU->VF[_Ft_].i.x; + if (_X) VU->VF[_Fd_].i.x = _MAX(VU->VF[_Fs_].i.x, ftx); + if (_Y) VU->VF[_Fd_].i.y = _MAX(VU->VF[_Fs_].i.y, ftx); + if (_Z) VU->VF[_Fd_].i.z = _MAX(VU->VF[_Fs_].i.z, ftx); + if (_W) VU->VF[_Fd_].i.w = _MAX(VU->VF[_Fs_].i.w, ftx); +} +//checked 13/05/03 shadow + +void _vuMAXy(VURegs * VU) { + s32 fty; + if (_Fd_ == 0) return; + + fty=(s32)VU->VF[_Ft_].i.y; + if (_X) VU->VF[_Fd_].i.x = _MAX(VU->VF[_Fs_].i.x, fty); + if (_Y) VU->VF[_Fd_].i.y = _MAX(VU->VF[_Fs_].i.y, fty); + if (_Z) VU->VF[_Fd_].i.z = _MAX(VU->VF[_Fs_].i.z, fty); + if (_W) VU->VF[_Fd_].i.w = _MAX(VU->VF[_Fs_].i.w, fty); +}//checked 13/05/03 shadow + +void _vuMAXz(VURegs * VU) { + s32 ftz; + if (_Fd_ == 0) return; + + ftz=(s32)VU->VF[_Ft_].i.z; + if (_X) VU->VF[_Fd_].i.x = _MAX(VU->VF[_Fs_].i.x, ftz); + if (_Y) VU->VF[_Fd_].i.y = _MAX(VU->VF[_Fs_].i.y, ftz); + if (_Z) VU->VF[_Fd_].i.z = _MAX(VU->VF[_Fs_].i.z, ftz); + if (_W) VU->VF[_Fd_].i.w = _MAX(VU->VF[_Fs_].i.w, ftz); +} + +void _vuMAXw(VURegs * VU) { + s32 ftw; + if (_Fd_ == 0) return; + + ftw=(s32)VU->VF[_Ft_].i.w; + if (_X) VU->VF[_Fd_].i.x = _MAX(VU->VF[_Fs_].i.x, ftw); + if (_Y) VU->VF[_Fd_].i.y = _MAX(VU->VF[_Fs_].i.y, ftw); + if (_Z) VU->VF[_Fd_].i.z = _MAX(VU->VF[_Fs_].i.z, ftw); + if (_W) VU->VF[_Fd_].i.w = _MAX(VU->VF[_Fs_].i.w, ftw); +} + +u32 _MINI(u32 a, u32 b) { + if (a & 0x80000000) { // -a + if (b & 0x80000000) { // -b + return (a & 0x7fffffff) < (b & 0x7fffffff) ? b : a; + } else { // +b + return a; + } + } else { // +a + if (b & 0x80000000) { // -b + return b; + } else { // +b + return (a & 0x7fffffff) < (b & 0x7fffffff) ? a : b; + } + } + + return 0; +} + +void _vuMINI(VURegs * VU) { + if (_Fd_ == 0) return; + + /* ft is bc */ + if (_X) VU->VF[_Fd_].i.x = _MINI(VU->VF[_Fs_].i.x, (s32)VU->VF[_Ft_].i.x); + if (_Y) VU->VF[_Fd_].i.y = _MINI(VU->VF[_Fs_].i.y, (s32)VU->VF[_Ft_].i.y); + if (_Z) VU->VF[_Fd_].i.z = _MINI(VU->VF[_Fs_].i.z, (s32)VU->VF[_Ft_].i.z); + if (_W) VU->VF[_Fd_].i.w = _MINI(VU->VF[_Fs_].i.w, (s32)VU->VF[_Ft_].i.w); +}//checked 13/05/03 shadow + +void _vuMINIi(VURegs * VU) { + if (_Fd_ == 0) return; + + /* ft is bc */ + if (_X) VU->VF[_Fd_].i.x = _MINI(VU->VF[_Fs_].i.x, VU->VI[REG_I].UL); + if (_Y) VU->VF[_Fd_].i.y = _MINI(VU->VF[_Fs_].i.y, VU->VI[REG_I].UL); + if (_Z) VU->VF[_Fd_].i.z = _MINI(VU->VF[_Fs_].i.z, VU->VI[REG_I].UL); + if (_W) VU->VF[_Fd_].i.w = _MINI(VU->VF[_Fs_].i.w, VU->VI[REG_I].UL); +}//checked 13/05/03 shadow + +void _vuMINIx(VURegs * VU) { + s32 ftx; + if (_Fd_ == 0) return; + + ftx=(s32)VU->VF[_Ft_].i.x; + if (_X) VU->VF[_Fd_].i.x = _MINI(VU->VF[_Fs_].i.x, ftx); + if (_Y) VU->VF[_Fd_].i.y = _MINI(VU->VF[_Fs_].i.y, ftx); + if (_Z) VU->VF[_Fd_].i.z = _MINI(VU->VF[_Fs_].i.z, ftx); + if (_W) VU->VF[_Fd_].i.w = _MINI(VU->VF[_Fs_].i.w, ftx); +} +//checked 13/05/03 shadow + +void _vuMINIy(VURegs * VU) { + s32 fty; + if (_Fd_ == 0) return; + + fty=(s32)VU->VF[_Ft_].i.y; + if (_X) VU->VF[_Fd_].i.x = _MINI(VU->VF[_Fs_].i.x, fty); + if (_Y) VU->VF[_Fd_].i.y = _MINI(VU->VF[_Fs_].i.y, fty); + if (_Z) VU->VF[_Fd_].i.z = _MINI(VU->VF[_Fs_].i.z, fty); + if (_W) VU->VF[_Fd_].i.w = _MINI(VU->VF[_Fs_].i.w, fty); +}//checked 13/05/03 shadow + +void _vuMINIz(VURegs * VU) { + s32 ftz; + if (_Fd_ == 0) return; + + ftz=(s32)VU->VF[_Ft_].i.z; + if (_X) VU->VF[_Fd_].i.x = _MINI(VU->VF[_Fs_].i.x, ftz); + if (_Y) VU->VF[_Fd_].i.y = _MINI(VU->VF[_Fs_].i.y, ftz); + if (_Z) VU->VF[_Fd_].i.z = _MINI(VU->VF[_Fs_].i.z, ftz); + if (_W) VU->VF[_Fd_].i.w = _MINI(VU->VF[_Fs_].i.w, ftz); +} + +void _vuMINIw(VURegs * VU) { + s32 ftw; + if (_Fd_ == 0) return; + + ftw=(s32)VU->VF[_Ft_].i.w; + if (_X) VU->VF[_Fd_].i.x = _MINI(VU->VF[_Fs_].i.x, ftw); + if (_Y) VU->VF[_Fd_].i.y = _MINI(VU->VF[_Fs_].i.y, ftw); + if (_Z) VU->VF[_Fd_].i.z = _MINI(VU->VF[_Fs_].i.z, ftw); + if (_W) VU->VF[_Fd_].i.w = _MINI(VU->VF[_Fs_].i.w, ftw); +} + +void _vuOPMULA(VURegs * VU) { + VU->ACC.i.x = VU_MACx_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Ft_].i.z)); + VU->ACC.i.y = VU_MACy_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Ft_].i.x)); + VU->ACC.i.z = VU_MACz_UPDATE(VU, vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Ft_].i.y)); + VU_STAT_UPDATE(VU); +}/*last updated 8/05/03 shadow*/ + +void _vuOPMSUB(VURegs * VU) { + VECTOR * dst; + float ftx, fty, ftz; + float fsx, fsy, fsz; + if (_Fd_ == 0) dst = &RDzero; + else dst = &VU->VF[_Fd_]; + + ftx = vuDouble(VU->VF[_Ft_].i.x); fty = vuDouble(VU->VF[_Ft_].i.y); ftz = vuDouble(VU->VF[_Ft_].i.z); + fsx = vuDouble(VU->VF[_Fs_].i.x); fsy = vuDouble(VU->VF[_Fs_].i.y); fsz = vuDouble(VU->VF[_Fs_].i.z); + dst->i.x = VU_MACx_UPDATE(VU, vuDouble(VU->ACC.i.x) - fsy * ftz); + dst->i.y = VU_MACy_UPDATE(VU, vuDouble(VU->ACC.i.y) - fsz * ftx); + dst->i.z = VU_MACz_UPDATE(VU, vuDouble(VU->ACC.i.z) - fsx * fty); + VU_STAT_UPDATE(VU); +}/*last updated 8/05/03 shadow*/ + +void _vuNOP(VURegs * VU) { +} + +void _vuFTOI0(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].SL[0] = (s32)vuDouble(VU->VF[_Fs_].i.x); + if (_Y) VU->VF[_Ft_].SL[1] = (s32)vuDouble(VU->VF[_Fs_].i.y); + if (_Z) VU->VF[_Ft_].SL[2] = (s32)vuDouble(VU->VF[_Fs_].i.z); + if (_W) VU->VF[_Ft_].SL[3] = (s32)vuDouble(VU->VF[_Fs_].i.w); +} + +void _vuFTOI4(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].SL[0] = float_to_int4(vuDouble(VU->VF[_Fs_].i.x)); + if (_Y) VU->VF[_Ft_].SL[1] = float_to_int4(vuDouble(VU->VF[_Fs_].i.y)); + if (_Z) VU->VF[_Ft_].SL[2] = float_to_int4(vuDouble(VU->VF[_Fs_].i.z)); + if (_W) VU->VF[_Ft_].SL[3] = float_to_int4(vuDouble(VU->VF[_Fs_].i.w)); +} + +void _vuFTOI12(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].SL[0] = float_to_int12(vuDouble(VU->VF[_Fs_].i.x)); + if (_Y) VU->VF[_Ft_].SL[1] = float_to_int12(vuDouble(VU->VF[_Fs_].i.y)); + if (_Z) VU->VF[_Ft_].SL[2] = float_to_int12(vuDouble(VU->VF[_Fs_].i.z)); + if (_W) VU->VF[_Ft_].SL[3] = float_to_int12(vuDouble(VU->VF[_Fs_].i.w)); +} + +void _vuFTOI15(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].SL[0] = float_to_int15(vuDouble(VU->VF[_Fs_].i.x)); + if (_Y) VU->VF[_Ft_].SL[1] = float_to_int15(vuDouble(VU->VF[_Fs_].i.y)); + if (_Z) VU->VF[_Ft_].SL[2] = float_to_int15(vuDouble(VU->VF[_Fs_].i.z)); + if (_W) VU->VF[_Ft_].SL[3] = float_to_int15(vuDouble(VU->VF[_Fs_].i.w)); +} + +void _vuITOF0(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].f.x = (float)VU->VF[_Fs_].SL[0]; + if (_Y) VU->VF[_Ft_].f.y = (float)VU->VF[_Fs_].SL[1]; + if (_Z) VU->VF[_Ft_].f.z = (float)VU->VF[_Fs_].SL[2]; + if (_W) VU->VF[_Ft_].f.w = (float)VU->VF[_Fs_].SL[3]; +} + +void _vuITOF4(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].f.x = int4_to_float(VU->VF[_Fs_].SL[0]); + if (_Y) VU->VF[_Ft_].f.y = int4_to_float(VU->VF[_Fs_].SL[1]); + if (_Z) VU->VF[_Ft_].f.z = int4_to_float(VU->VF[_Fs_].SL[2]); + if (_W) VU->VF[_Ft_].f.w = int4_to_float(VU->VF[_Fs_].SL[3]); +} + +void _vuITOF12(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].f.x = int12_to_float(VU->VF[_Fs_].SL[0]); + if (_Y) VU->VF[_Ft_].f.y = int12_to_float(VU->VF[_Fs_].SL[1]); + if (_Z) VU->VF[_Ft_].f.z = int12_to_float(VU->VF[_Fs_].SL[2]); + if (_W) VU->VF[_Ft_].f.w = int12_to_float(VU->VF[_Fs_].SL[3]); +} + +void _vuITOF15(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].f.x = int15_to_float(VU->VF[_Fs_].SL[0]); + if (_Y) VU->VF[_Ft_].f.y = int15_to_float(VU->VF[_Fs_].SL[1]); + if (_Z) VU->VF[_Ft_].f.z = int15_to_float(VU->VF[_Fs_].SL[2]); + if (_W) VU->VF[_Ft_].f.w = int15_to_float(VU->VF[_Fs_].SL[3]); +} + +/* Different type of clipping by presaving w. asadr */ +void _vuCLIP(VURegs * VU) { + float value = fabs(vuDouble(VU->VF[_Ft_].i.w)); + + VU->clipflag <<= 6; + if ( vuDouble(VU->VF[_Fs_].i.x) > +value ) VU->clipflag|= 0x01; + if ( vuDouble(VU->VF[_Fs_].i.x) < -value ) VU->clipflag|= 0x02; + if ( vuDouble(VU->VF[_Fs_].i.y) > +value ) VU->clipflag|= 0x04; + if ( vuDouble(VU->VF[_Fs_].i.y) < -value ) VU->clipflag|= 0x08; + if ( vuDouble(VU->VF[_Fs_].i.z) > +value ) VU->clipflag|= 0x10; + if ( vuDouble(VU->VF[_Fs_].i.z) < -value ) VU->clipflag|= 0x20; + VU->clipflag = VU->clipflag & 0xFFFFFF; + VU->VI[REG_CLIP_FLAG].UL = VU->clipflag; + + +}/*last update 16/07/05 refraction - Needs checking */ + + +/******************************/ +/* VU Lower instructions */ +/******************************/ + +void _vuDIV(VURegs * VU) { + float ft = vuDouble(VU->VF[_Ft_].UL[_Ftf_]); + float fs = vuDouble(VU->VF[_Fs_].UL[_Fsf_]); + +// _vuFMACTestStall(VU, _Fs_); _vuFMACTestStall(VU, _Ft_); + VU->statusflag = (VU->statusflag&0xfcf)|((VU->statusflag&0x30)<<6); + + if (ft == 0.0) { + if (fs == 0.0) { + VU->statusflag |= 0x10; + } else { + VU->statusflag |= 0x20; + } + if ((VU->VF[_Ft_].UL[_Ftf_] & 0x80000000) ^ + (VU->VF[_Fs_].UL[_Fsf_] & 0x80000000)) { + VU->q.UL = 0xFF7FFFFF; + } else { + VU->q.UL = 0x7F7FFFFF; + } + } else { + VU->q.F = fs / ft; + VU->q.F = vuDouble(VU->q.UL); + } +} //last update 15/01/06 zerofrog + +void _vuSQRT(VURegs * VU) { + float ft = vuDouble(VU->VF[_Ft_].UL[_Ftf_]); + +// _vuFMACTestStall(VU, _Ft_); + VU->statusflag = (VU->statusflag&0xfcf)|((VU->statusflag&0x30)<<6); + + if (ft < 0.0 ) + VU->statusflag |= 0x10; + VU->q.F = sqrt(fabs(ft)); + VU->q.F = vuDouble(VU->q.UL); +} //last update 15/01/06 zerofrog + + +/* Eminent Bug - Dvisior == 0 Check Missing ( D Flag Not Set ) */ +/* REFIXED....ASADR; rerefixed....zerofrog */ +void _vuRSQRT(VURegs * VU) { + float ft = vuDouble(VU->VF[_Ft_].UL[_Ftf_]); + float fs = vuDouble(VU->VF[_Fs_].UL[_Fsf_]); + float temp; + +// _vuFMACTestStall(VU, _Fs_); _vuFMACTestStall(VU, _Ft_); + VU->statusflag = (VU->statusflag&0xfcf)|((VU->statusflag&0x30)<<6); + + if ( ft == 0.0 ) { + VU->statusflag |= 0x20; + + if( fs != 0 ) { + if ((VU->VF[_Ft_].UL[_Ftf_] & 0x80000000) ^ + (VU->VF[_Fs_].UL[_Fsf_] & 0x80000000)) { + VU->q.UL = 0xFF7FFFFF; + } else { + VU->q.UL = 0x7F7FFFFF; + } + } + else { + if ((VU->VF[_Ft_].UL[_Ftf_] & 0x80000000) ^ + (VU->VF[_Fs_].UL[_Fsf_] & 0x80000000)) { + VU->q.UL = 0x80000000; + } else { + VU->q.UL = 0; + } + + VU->statusflag |= 0x10; + } + + } else { + if (ft < 0.0) { + VU->statusflag |= 0x10; + } + + temp = sqrt(fabs(ft)); + VU->q.F = fs / temp; + VU->q.F = vuDouble(VU->q.UL); + } +} //last update 15/01/06 zerofrog + + +void _vuIADDI(VURegs * VU) { + s16 imm = ((VU->code >> 6) & 0x1f); + imm = ((imm & 0x10 ? 0xfff0 : 0) | (imm & 0xf)); + if(_Ft_ == 0) return; + VU->VI[_Ft_].SS[0] = VU->VI[_Fs_].SS[0] + imm; +}//last checked 17/05/03 shadow NOTE: not quite sure about that + +void _vuIADDIU(VURegs * VU) { + if(_Ft_ == 0) return; + VU->VI[_Ft_].SS[0] = VU->VI[_Fs_].SS[0] + (((VU->code >> 10) & 0x7800) | (VU->code & 0x7ff)); +}//last checked 17/05/03 shadow + +void _vuIADD(VURegs * VU) { + if(_Fd_ == 0) return; + VU->VI[_Fd_].SS[0] = VU->VI[_Fs_].SS[0] + VU->VI[_Ft_].SS[0]; +}//last checked 17/05/03 shadow + +void _vuIAND(VURegs * VU) { + if(_Fd_ == 0) return; + VU->VI[_Fd_].US[0] = VU->VI[_Fs_].US[0] & VU->VI[_Ft_].US[0]; +}//last checked 17/05/03 shadow + +void _vuIOR(VURegs * VU) { + if(_Fd_ == 0) return; + VU->VI[_Fd_].US[0] = VU->VI[_Fs_].US[0] | VU->VI[_Ft_].US[0]; +} + +void _vuISUB(VURegs * VU) { + if(_Fd_ == 0) return; + VU->VI[_Fd_].SS[0] = VU->VI[_Fs_].SS[0] - VU->VI[_Ft_].SS[0]; +} + +void _vuISUBIU(VURegs * VU) { + if(_Ft_ == 0) return; + VU->VI[_Ft_].SS[0] = VU->VI[_Fs_].SS[0] - (((VU->code >> 10) & 0x7800) | (VU->code & 0x7ff)); +} + +void _vuMOVE(VURegs * VU) { + if(_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].UL[0] = VU->VF[_Fs_].UL[0]; + if (_Y) VU->VF[_Ft_].UL[1] = VU->VF[_Fs_].UL[1]; + if (_Z) VU->VF[_Ft_].UL[2] = VU->VF[_Fs_].UL[2]; + if (_W) VU->VF[_Ft_].UL[3] = VU->VF[_Fs_].UL[3]; +}//last checked 17/05/03 shadow + +void _vuMFIR(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].SL[0] = (s32)VU->VI[_Fs_].SS[0]; + if (_Y) VU->VF[_Ft_].SL[1] = (s32)VU->VI[_Fs_].SS[0]; + if (_Z) VU->VF[_Ft_].SL[2] = (s32)VU->VI[_Fs_].SS[0]; + if (_W) VU->VF[_Ft_].SL[3] = (s32)VU->VI[_Fs_].SS[0]; +} + +// Big bug!!! mov from fs to ft not ft to fs. asadr +void _vuMTIR(VURegs * VU) { + if(_Ft_ == 0) return; + VU->VI[_Ft_].US[0] = *(u16*)&VU->VF[_Fs_].F[_Fsf_]; +} + +void _vuMR32(VURegs * VU) { + u32 tx; + if (_Ft_ == 0) return; + + tx = VU->VF[_Fs_].i.x; + if (_X) VU->VF[_Ft_].i.x = VU->VF[_Fs_].i.y; + if (_Y) VU->VF[_Ft_].i.y = VU->VF[_Fs_].i.z; + if (_Z) VU->VF[_Ft_].i.z = VU->VF[_Fs_].i.w; + if (_W) VU->VF[_Ft_].i.w = tx; +}//last updated 23/10/03 linuzappz + +void _vuLQ(VURegs * VU) { + s16 imm; + u16 addr; + u32 *ptr; + + if (_Ft_ == 0) return; + + imm = (VU->code & 0x400) ? (VU->code & 0x3ff) | 0xfc00 : (VU->code & 0x3ff); + addr = (imm + VU->VI[_Fs_].SS[0]) * 16; + + ptr = (u32*)GET_VU_MEM(VU, addr); + if (_X) VU->VF[_Ft_].UL[0] = ptr[0]; + if (_Y) VU->VF[_Ft_].UL[1] = ptr[1]; + if (_Z) VU->VF[_Ft_].UL[2] = ptr[2]; + if (_W) VU->VF[_Ft_].UL[3] = ptr[3]; +} + +void _vuLQD( VURegs * VU ) { + u32 addr; + u32 *ptr; + + if (_Fs_ != 0) VU->VI[_Fs_].US[0]--; + if (_Ft_ == 0) return; + + addr = VU->VI[_Fs_].US[0] * 16; + ptr = (u32*)GET_VU_MEM(VU, addr); + if (_X) VU->VF[_Ft_].UL[0] = ptr[0]; + if (_Y) VU->VF[_Ft_].UL[1] = ptr[1]; + if (_Z) VU->VF[_Ft_].UL[2] = ptr[2]; + if (_W) VU->VF[_Ft_].UL[3] = ptr[3]; +} + +void _vuLQI(VURegs * VU) { + if (_Ft_) { + u32 addr; + u32 *ptr; + + addr = VU->VI[_Fs_].US[0] * 16; + ptr = (u32*)GET_VU_MEM(VU, addr); + if (_X) VU->VF[_Ft_].UL[0] = ptr[0]; + if (_Y) VU->VF[_Ft_].UL[1] = ptr[1]; + if (_Z) VU->VF[_Ft_].UL[2] = ptr[2]; + if (_W) VU->VF[_Ft_].UL[3] = ptr[3]; + } + if (_Fs_ != 0) VU->VI[_Fs_].US[0]++; +} + +/* addr is now signed. Asadr */ +void _vuSQ(VURegs * VU) { + s16 imm; + u16 addr; + u32 *ptr; + + imm = (VU->code & 0x400) ? (VU->code & 0x3ff) | 0xfc00 : (VU->code & 0x3ff); + addr = (imm + VU->VI[_Ft_].SS[0]) * 16; + ptr = (u32*)GET_VU_MEM(VU, addr); + if (_X) ptr[0] = VU->VF[_Fs_].UL[0]; + if (_Y) ptr[1] = VU->VF[_Fs_].UL[1]; + if (_Z) ptr[2] = VU->VF[_Fs_].UL[2]; + if (_W) ptr[3] = VU->VF[_Fs_].UL[3]; +} + +void _vuSQD(VURegs * VU) { + u32 addr; + u32 *ptr; + + if(_Ft_ != 0) VU->VI[_Ft_].US[0]--; + addr = VU->VI[_Ft_].US[0] * 16; + ptr = (u32*)GET_VU_MEM(VU, addr); + if (_X) ptr[0] = VU->VF[_Fs_].UL[0]; + if (_Y) ptr[1] = VU->VF[_Fs_].UL[1]; + if (_Z) ptr[2] = VU->VF[_Fs_].UL[2]; + if (_W) ptr[3] = VU->VF[_Fs_].UL[3]; +} + +void _vuSQI(VURegs * VU) { + u32 addr; + u32 *ptr; + + addr = VU->VI[_Ft_].US[0] * 16; + ptr = (u32*)GET_VU_MEM(VU, addr); + if (_X) ptr[0] = VU->VF[_Fs_].UL[0]; + if (_Y) ptr[1] = VU->VF[_Fs_].UL[1]; + if (_Z) ptr[2] = VU->VF[_Fs_].UL[2]; + if (_W) ptr[3] = VU->VF[_Fs_].UL[3]; + if(_Ft_ != 0) VU->VI[_Ft_].US[0]++; +} + +/* addr now signed. asadr */ +void _vuILW(VURegs * VU) { + s16 imm; + u16 addr; + u16 *ptr; + if (_Ft_ == 0) return; + + imm = (VU->code & 0x400) ? (VU->code & 0x3ff) | 0xfc00 : (VU->code & 0x3ff); + addr = (imm + VU->VI[_Fs_].SS[0]) * 16; + ptr = (u16*)GET_VU_MEM(VU, addr); + if (_X) VU->VI[_Ft_].US[0] = ptr[0]; + if (_Y) VU->VI[_Ft_].US[0] = ptr[2]; + if (_Z) VU->VI[_Ft_].US[0] = ptr[4]; + if (_W) VU->VI[_Ft_].US[0] = ptr[6]; +} + +void _vuISW(VURegs * VU) { + s16 imm; + u16 addr; + u16 *ptr; + + imm = (VU->code & 0x400) ? (VU->code & 0x3ff) | 0xfc00 : (VU->code & 0x3ff); + addr = (imm + VU->VI[_Fs_].SS[0]) * 16; + ptr = (u16*)GET_VU_MEM(VU, addr); + if (_X) { ptr[0] = VU->VI[_Ft_].US[0]; ptr[1] = 0; } + if (_Y) { ptr[2] = VU->VI[_Ft_].US[0]; ptr[3] = 0; } + if (_Z) { ptr[4] = VU->VI[_Ft_].US[0]; ptr[5] = 0; } + if (_W) { ptr[6] = VU->VI[_Ft_].US[0]; ptr[7] = 0; } +} + +void _vuILWR(VURegs * VU) { + u32 addr; + u16 *ptr; + if (_Ft_ == 0) return; + + addr = VU->VI[_Fs_].US[0] * 16; + ptr = (u16*)GET_VU_MEM(VU, addr); + if (_X) VU->VI[_Ft_].US[0] = ptr[0]; + if (_Y) VU->VI[_Ft_].US[0] = ptr[2]; + if (_Z) VU->VI[_Ft_].US[0] = ptr[4]; + if (_W) VU->VI[_Ft_].US[0] = ptr[6]; +} + +void _vuISWR(VURegs * VU) { + u32 addr; + u16 *ptr; + + addr = VU->VI[_Fs_].US[0] * 16; + ptr = (u16*)GET_VU_MEM(VU, addr); + if (_X) { ptr[0] = VU->VI[_Ft_].US[0]; ptr[1] = 0; } + if (_Y) { ptr[2] = VU->VI[_Ft_].US[0]; ptr[3] = 0; } + if (_Z) { ptr[4] = VU->VI[_Ft_].US[0]; ptr[5] = 0; } + if (_W) { ptr[6] = VU->VI[_Ft_].US[0]; ptr[7] = 0; } +} + +/* code contributed by _Riff_ + +The following code implements a Galois form M-series LFSR that can be configured to have a width from 0 to 32. +A Galois field can be represented as G(X) = g_m * X^m + g_(m-1) * X^(m-1) + ... + g_1 * X^1 + g0. +A Galois form M-Series LFSR represents a Galois field where g0 = g_m = 1 and the generated set contains 2^M - 1 values. +In modulo-2 arithmetic, addition is replaced by XOR and multiplication is replaced by AND. +The code is written in such a way that the polynomial lsb (g0) should be set to 0 and g_m is not represented. +As an example for setting the polynomial variable correctly, the 23-bit M-series generating polynomial X^23+X^14 + would be specified as (1 << 14). +*/ + + +//The two-tap 23 stage M-series polynomials are x23+x18 and x23+x14 ((1 << 18) and (1 << 14), respectively). +//The reverse sequences can be generated by x23+x(23-18) and x23+x(23-14) ((1 << 9) and (1 << 5), respectively) +u32 poly = 1 << 5; + +void SetPoly(u32 newPoly) { + poly = poly & ~1; +} + +void AdvanceLFSR(VURegs * VU) { + // code from www.project-fao.org + int x = (VU->VI[REG_R].UL >> 4) & 1; + int y = (VU->VI[REG_R].UL >> 22) & 1; + VU->VI[REG_R].UL <<= 1; + VU->VI[REG_R].UL ^= x ^ y; + VU->VI[REG_R].UL = (VU->VI[REG_R].UL&0x7fffff)|0x3f800000; +} +// old +// u32 lfsr = VU->VI[REG_R].UL & 0x007FFFFF; +// u32 oldlfsr = lfsr; +// lfsr <<= 1; +// if (oldlfsr & 0x00400000) { +// lfsr ^= poly; +// lfsr |= 1; +// } +// +// VU->VI[REG_R].UL = 0x3F800000 | (lfsr & 0x007FFFFF); + +void _vuRINIT(VURegs * VU) { + VU->VI[REG_R].UL = 0x3F800000 | (VU->VF[_Fs_].UL[_Fsf_] & 0x007FFFFF); +} + +void _vuRGET(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].UL[0] = VU->VI[REG_R].UL; + if (_Y) VU->VF[_Ft_].UL[1] = VU->VI[REG_R].UL; + if (_Z) VU->VF[_Ft_].UL[2] = VU->VI[REG_R].UL; + if (_W) VU->VF[_Ft_].UL[3] = VU->VI[REG_R].UL; +} + +void _vuRNEXT(VURegs * VU) { + if (_Ft_ == 0) return; + AdvanceLFSR(VU); + if (_X) VU->VF[_Ft_].UL[0] = VU->VI[REG_R].UL; + if (_Y) VU->VF[_Ft_].UL[1] = VU->VI[REG_R].UL; + if (_Z) VU->VF[_Ft_].UL[2] = VU->VI[REG_R].UL; + if (_W) VU->VF[_Ft_].UL[3] = VU->VI[REG_R].UL; +} + +void _vuRXOR(VURegs * VU) { + VU->VI[REG_R].UL = 0x3F800000 | ((VU->VI[REG_R].UL ^ VU->VF[_Fs_].UL[_Fsf_]) & 0x007FFFFF); +} + +void _vuWAITQ(VURegs * VU) { +} + +void _vuFSAND(VURegs * VU) { + u16 imm; + imm = (((VU->code >> 21 ) & 0x1) << 11) | (VU->code & 0x7ff); + if(_Ft_ == 0) return; + VU->VI[_Ft_].US[0] = (VU->VI[REG_STATUS_FLAG].US[0] & 0xFFF) & imm; +} + +void _vuFSEQ(VURegs * VU) { + u16 imm; + imm = (((VU->code >> 21 ) & 0x1) << 11) | (VU->code & 0x7ff); + if(_Ft_ == 0) return; + if((VU->VI[REG_STATUS_FLAG].US[0] & 0xFFF) == imm) VU->VI[_Ft_].US[0] = 1; + else VU->VI[_Ft_].US[0] = 0; +} + +void _vuFSOR(VURegs * VU) { + u16 imm; + imm = (((VU->code >> 21 ) & 0x1) << 11) | (VU->code & 0x7ff); + if(_Ft_ == 0) return; + VU->VI[_Ft_].US[0] = (VU->VI[REG_STATUS_FLAG].US[0] & 0xFFF) | imm; +} + +void _vuFSSET(VURegs * VU) { + u16 imm = 0; + imm = (((VU->code >> 21 ) & 0x1) << 11) | (VU->code & 0x7FF); + VU->statusflag = (imm & 0xFC0) | (VU->VI[REG_STATUS_FLAG].US[0] & 0x3F); +} + +void _vuFMAND(VURegs * VU) { + if(_Ft_ == 0) return; + VU->VI[_Ft_].US[0] = VU->VI[_Fs_].US[0] & (VU->VI[REG_MAC_FLAG].UL & 0xFFFF); +} + +void _vuFMEQ(VURegs * VU) { + if(_Ft_ == 0) return; + if((VU->VI[REG_MAC_FLAG].UL & 0xFFFF) == VU->VI[_Fs_].US[0]){ + VU->VI[_Ft_].US[0] =1;} else { VU->VI[_Ft_].US[0] =0; } +} + +void _vuFMOR(VURegs * VU) { + if(_Ft_ == 0) return; + VU->VI[_Ft_].US[0] = (VU->VI[REG_MAC_FLAG].UL & 0xFFFF) | VU->VI[_Fs_].US[0]; +} + +void _vuFCAND(VURegs * VU) { + if((VU->VI[REG_CLIP_FLAG].UL & 0xFFFFFF) & (VU->code & 0xFFFFFF)) VU->VI[1].US[0] = 1; + else VU->VI[1].US[0] = 0; +} + +void _vuFCEQ(VURegs * VU) { + if((VU->VI[REG_CLIP_FLAG].UL & 0xFFFFFF) == (VU->code & 0xFFFFFF)) VU->VI[1].US[0] = 1; + else VU->VI[1].US[0] = 0; +} + +void _vuFCOR(VURegs * VU) { + u32 hold = (VU->VI[REG_CLIP_FLAG].UL & 0xFFFFFF) | ( VU->code & 0xFFFFFF); + if(hold == 0xFFFFFF) VU->VI[1].US[0] = 1; + else VU->VI[1].US[0] = 0; +} + +void _vuFCSET(VURegs * VU) { + VU->clipflag = (u32) (VU->code & 0xFFFFFF); + VU->VI[REG_CLIP_FLAG].UL = (u32) (VU->code & 0xFFFFFF); +} + +void _vuFCGET(VURegs * VU) { + if(_Ft_ == 0) return; + VU->VI[_Ft_].US[0] = VU->VI[REG_CLIP_FLAG].UL & 0x0FFF; +} + +s32 _branchAddr(VURegs * VU) { + s32 bpc = VU->VI[REG_TPC].SL + ( _Imm11_ * 8 ); + //if (bpc < 0) bpc = VU->VI[REG_TPC].SL + _UImm11_ * 8; + bpc&= (VU == &VU1) ? 0x3fff : 0x0fff; + return bpc; +} + +void _setBranch(VURegs * VU, u32 bpc) { + VU->branch = 2; + VU->branchpc = bpc; +// VU->vuExec(VU); +// VU->VI[REG_TPC].UL = bpc; +} + +void _vuIBEQ(VURegs * VU) { + if (VU->VI[_Ft_].US[0] == VU->VI[_Fs_].US[0]) { + s32 bpc = _branchAddr(VU); + _setBranch(VU, bpc); + } +} + +void _vuIBGEZ(VURegs * VU) { + if (VU->VI[_Fs_].SS[0] >= 0) { + s32 bpc = _branchAddr(VU); + _setBranch(VU, bpc); + } +} + +void _vuIBGTZ(VURegs * VU) { + if (VU->VI[_Fs_].SS[0] > 0) { + s32 bpc = _branchAddr(VU); + _setBranch(VU, bpc); + } +} + +void _vuIBLEZ(VURegs * VU) { + if (VU->VI[_Fs_].SS[0] <= 0) { + s32 bpc = _branchAddr(VU); + _setBranch(VU, bpc); + } +} + +void _vuIBLTZ(VURegs * VU) { + if (VU->VI[_Fs_].SS[0] < 0) { + s32 bpc = _branchAddr(VU); + _setBranch(VU, bpc); + } +} + +void _vuIBNE(VURegs * VU) { + if (VU->VI[_Ft_].US[0] != VU->VI[_Fs_].US[0]) { + s32 bpc = _branchAddr(VU); + _setBranch(VU, bpc); + } +} + +void _vuB(VURegs * VU) { + s32 bpc = _branchAddr(VU); + _setBranch(VU, bpc); +} + +void _vuBAL(VURegs * VU) { + s32 bpc = _branchAddr(VU); + + if (_Ft_) { + VU->VI[_Ft_].US[0] = (VU->VI[REG_TPC].UL + 8)/8; + } + + _setBranch(VU, bpc); +} + +void _vuJR(VURegs * VU) { + u32 bpc = VU->VI[_Fs_].US[0] * 8; + _setBranch(VU, bpc); +} + +void _vuJALR(VURegs * VU) { + u32 bpc = VU->VI[_Fs_].US[0] * 8; + if (_Ft_) { + VU->VI[_Ft_].US[0] = (VU->VI[REG_TPC].UL + 8)/8; + } + + _setBranch(VU, bpc); +} + +void _vuMFP(VURegs * VU) { + if (_Ft_ == 0) return; + + if (_X) VU->VF[_Ft_].i.x = VU->VI[REG_P].UL; + if (_Y) VU->VF[_Ft_].i.y = VU->VI[REG_P].UL; + if (_Z) VU->VF[_Ft_].i.z = VU->VI[REG_P].UL; + if (_W) VU->VF[_Ft_].i.w = VU->VI[REG_P].UL; +} + +void _vuWAITP(VURegs * VU) { +} + +void _vuESADD(VURegs * VU) { + float p = vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Fs_].i.x) + vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Fs_].i.y) + vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Fs_].i.z); + VU->p.F = p; +} + +void _vuERSADD(VURegs * VU) { + float p = (vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Fs_].i.x)) + (vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Fs_].i.y)) + (vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Fs_].i.z)); + if (p != 0.0) + p = 1.0f / p; + VU->p.F = p; +} + +/* Fixed. Could have caused crash due to value being -ve for sqrt *asadr */ +void _vuELENG(VURegs * VU) { + float p = vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Fs_].i.x) + vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Fs_].i.y) + vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Fs_].i.z); + if(p >= 0){ + p = sqrt(p); + } + VU->p.F = p; +} + +/* Fixed. Could have caused crash due to divisor being = 0 *asadr */ +void _vuERLENG(VURegs * VU) { + float p = vuDouble(VU->VF[_Fs_].i.x) * vuDouble(VU->VF[_Fs_].i.x) + vuDouble(VU->VF[_Fs_].i.y) * vuDouble(VU->VF[_Fs_].i.y) + vuDouble(VU->VF[_Fs_].i.z) * vuDouble(VU->VF[_Fs_].i.z); + if (p >= 0) { + p = sqrt(p); + if (p != 0) { + p = 1.0f / p; + } + } + VU->p.F = p; +} + +/* Fixed. Could have caused crash due to divisor being = 0 *asadr */ +void _vuEATANxy(VURegs * VU) { + float p = 0; + if(vuDouble(VU->VF[_Fs_].i.x) != 0) { + p = atan2(vuDouble(VU->VF[_Fs_].i.y), vuDouble(VU->VF[_Fs_].i.x)); + } + VU->p.F = p; +} + +/* Fixed. Could have caused crash due to divisor being = 0 *asadr */ +void _vuEATANxz(VURegs * VU) { + float p = 0; + if(vuDouble(VU->VF[_Fs_].i.x) != 0) { + p = atan2(vuDouble(VU->VF[_Fs_].i.z), vuDouble(VU->VF[_Fs_].i.x)); + } + VU->p.F = p; +} + +void _vuESUM(VURegs * VU) { + float p = vuDouble(VU->VF[_Fs_].i.x) + vuDouble(VU->VF[_Fs_].i.y) + vuDouble(VU->VF[_Fs_].i.z) + vuDouble(VU->VF[_Fs_].i.w); + VU->p.F = p; +} + +/* Fixed. Could have caused crash due to divisor being = 0 *asadr */ +void _vuERCPR(VURegs * VU) { + float p = vuDouble(VU->VF[_Fs_].UL[_Fsf_]); + if (p != 0){ + p = 1.0 / p; + } + VU->p.F = p; +} + +/* Fixed. Could have caused crash due to Value being -ve for sqrt *asadr */ +void _vuESQRT(VURegs * VU) { + float p = vuDouble(VU->VF[_Fs_].UL[_Fsf_]); + if (p >= 0){ + p = sqrt(p); + } + VU->p.F = p; +} + +/* Fixed. Could have caused crash due to divisor being = 0 *asadr */ +void _vuERSQRT(VURegs * VU) { + float p = vuDouble(VU->VF[_Fs_].UL[_Fsf_]); + if (p >= 0) { + p = sqrt(p); + if (p) { + p = 1.0f / p; + } + } + VU->p.F = p; +} + +void _vuESIN(VURegs * VU) { + float p = sin(vuDouble(VU->VF[_Fs_].UL[_Fsf_])); + VU->p.F = p; +} + +void _vuEATAN(VURegs * VU) { + float p = atan(vuDouble(VU->VF[_Fs_].UL[_Fsf_])); + VU->p.F = p; +} + +void _vuEEXP(VURegs * VU) { + float p = exp(-(vuDouble(VU->VF[_Fs_].UL[_Fsf_]))); + VU->p.F = p; +} + +void _vuXITOP(VURegs * VU) { + if (_Ft_ == 0) return; + VU->VI[_Ft_].US[0] = VU->vifRegs->itop; +} + +void _vuXGKICK(VURegs * VU) +{ + // flush all pipelines first (in the right order) + _vuFlushAll(VU); + GSGIFTRANSFER1((u32*)VU->Mem, (VU->VI[_Fs_].US[0]*16) & 0x3fff); +} + +void _vuXTOP(VURegs * VU) { + if(_Ft_ == 0) return; + VU->VI[_Ft_].US[0] = (u16)VU->vifRegs->top; +} + +#define GET_VF0_FLAG(reg) (((reg)==0)?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = _Fd_; \ + VUregsn->VFwxyzw = _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = 0; \ + VUregsn->VIwrite = 0; \ + VUregsn->VIread = (1 << REG_I)|(ACC?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = _Fd_; \ + VUregsn->VFwxyzw = _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = 0; \ + VUregsn->VIwrite = 0; \ + VUregsn->VIread = (1 << REG_Q)|(ACC?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = _Fd_; \ + VUregsn->VFwxyzw = _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = _Ft_; \ + VUregsn->VFr1xyzw= _XYZW; \ + VUregsn->VIwrite = 0; \ + VUregsn->VIread = (ACC?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = _Fd_; \ + VUregsn->VFwxyzw = _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = _Ft_; \ + VUregsn->VFr1xyzw= xyzw; \ + VUregsn->VIwrite = 0; \ + VUregsn->VIread = (ACC?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = 0; \ + VUregsn->VFwxyzw= _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = 0; \ + VUregsn->VIwrite = (1<VIread = (1 << REG_I)|GET_VF0_FLAG(_Fs_)|((readacc||_XYZW!=15)?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = 0; \ + VUregsn->VFwxyzw= _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = 0; \ + VUregsn->VIwrite = (1<VIread = (1 << REG_Q)|GET_VF0_FLAG(_Fs_)|((readacc||_XYZW!=15)?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = 0; \ + VUregsn->VFwxyzw= _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = _Ft_; \ + VUregsn->VFr1xyzw= _XYZW; \ + VUregsn->VIwrite = (1<VIread = GET_VF0_FLAG(_Fs_)|GET_VF0_FLAG(_Ft_)|((readacc||_XYZW!=15)?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = 0; \ + VUregsn->VFwxyzw= _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = _Ft_; \ + VUregsn->VFr1xyzw= xyzw; \ + VUregsn->VIwrite = (1<VIread = GET_VF0_FLAG(_Fs_)|GET_VF0_FLAG(_Ft_)|((readacc||_XYZW!=15)?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = _Ft_; \ + VUregsn->VFwxyzw = _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = 0; \ + VUregsn->VFr1xyzw = 0xff; \ + VUregsn->VIwrite = 0; \ + VUregsn->VIread = (_Ft_ ? GET_VF0_FLAG(_Fs_) : 0); \ +} + +#define VUREGS_IDISIT(OP) \ +void _vuRegs##OP(VURegs * VU, _VURegsNum *VUregsn) { \ + VUregsn->pipe = VUPIPE_IALU; \ + VUregsn->VFwrite = 0; \ + VUregsn->VFread0 = 0; \ + VUregsn->VFread1 = 0; \ + VUregsn->VIwrite = 1 << _Fd_; \ + VUregsn->VIread = (1 << _Fs_) | (1 << _Ft_); \ +} + +#define VUREGS_ITIS(OP) \ +void _vuRegs##OP(VURegs * VU, _VURegsNum *VUregsn) { \ + VUregsn->pipe = VUPIPE_IALU; \ + VUregsn->VFwrite = 0; \ + VUregsn->VFread0 = 0; \ + VUregsn->VFread1 = 0; \ + VUregsn->VIwrite = 1 << _Ft_; \ + VUregsn->VIread = 1 << _Fs_; \ +} + +#define VUREGS_PFS(OP, _cycles) \ +void _vuRegs##OP(VURegs * VU, _VURegsNum *VUregsn) { \ + VUregsn->pipe = VUPIPE_EFU; \ + VUregsn->VFwrite = 0; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = 0; \ + VUregsn->VIwrite = 1 << REG_P; \ + VUregsn->VIread = GET_VF0_FLAG(_Fs_); \ + VUregsn->cycles = _cycles; \ +} + + + +VUREGS_FTFS(ABS); + +VUREGS_FDFSFT(ADD, 0); +VUREGS_FDFSI(ADDi, 0); +VUREGS_FDFSQ(ADDq, 0); +VUREGS_FDFSFTx(ADDx, 0); +VUREGS_FDFSFTy(ADDy, 0); +VUREGS_FDFSFTz(ADDz, 0); +VUREGS_FDFSFTw(ADDw, 0); + +VUREGS_ACCFSFT(ADDA, 0); +VUREGS_ACCFSI(ADDAi, 0); +VUREGS_ACCFSQ(ADDAq, 0); +VUREGS_ACCFSFTx(ADDAx, 0); +VUREGS_ACCFSFTy(ADDAy, 0); +VUREGS_ACCFSFTz(ADDAz, 0); +VUREGS_ACCFSFTw(ADDAw, 0); + +VUREGS_FDFSFT(SUB, 0); +VUREGS_FDFSI(SUBi, 0); +VUREGS_FDFSQ(SUBq, 0); +VUREGS_FDFSFTx(SUBx, 0); +VUREGS_FDFSFTy(SUBy, 0); +VUREGS_FDFSFTz(SUBz, 0); +VUREGS_FDFSFTw(SUBw, 0); + +VUREGS_ACCFSFT(SUBA, 0); +VUREGS_ACCFSI(SUBAi, 0); +VUREGS_ACCFSQ(SUBAq, 0); +VUREGS_ACCFSFTx(SUBAx, 0); +VUREGS_ACCFSFTy(SUBAy, 0); +VUREGS_ACCFSFTz(SUBAz, 0); +VUREGS_ACCFSFTw(SUBAw, 0); + +#define VUREGS_FDFSFTxyzw_MUL(OP, ACC, xyzw) \ +void _vuRegs##OP(VURegs * VU, _VURegsNum *VUregsn) { \ + if( _Ft_ == 0 && xyzw > 1 && _XYZW == 0xf ) { /* resetting to 0 */ \ + VUregsn->pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = ACC?0:_Fd_; \ + VUregsn->VFwxyzw = _XYZW; \ + VUregsn->VFread0 = 0; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = 0; \ + VUregsn->VFr1xyzw= xyzw; \ + VUregsn->VIwrite = (ACC?(1<VIread = (ACC&&(_XYZW!=15))?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = ACC?0:_Fd_; \ + VUregsn->VFwxyzw = _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = _Ft_; \ + VUregsn->VFr1xyzw= xyzw; \ + VUregsn->VIwrite = (ACC?(1<VIread = GET_VF0_FLAG(_Fs_)|((ACC&&(_XYZW!=15))?(1<pipe = VUPIPE_FMAC; \ + VUregsn->VFwrite = _Fd_; \ + VUregsn->VFwxyzw = _XYZW; \ + VUregsn->VFread0 = _Fs_; \ + VUregsn->VFr0xyzw= _XYZW; \ + VUregsn->VFread1 = _Ft_; \ + VUregsn->VFr1xyzw= xyzw; \ + VUregsn->VIwrite = 0; \ + VUregsn->VIread = (1<pipe = VUPIPE_FMAC; + VUregsn->VFwrite = _Fd_; + VUregsn->VFwxyzw = _XYZW; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= _XYZW; + VUregsn->VFread1 = _Ft_; + VUregsn->VFr1xyzw= 1; + VUregsn->VIwrite = 0; + VUregsn->VIread = (1<VIread &= ~(1<VIread &= ~(1<VIread &= ~(1<VIread &= ~(1<pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFwxyzw= 0xE; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= 0xE; + VUregsn->VFread1 = _Ft_; + VUregsn->VFr1xyzw= 0xE; + VUregsn->VIwrite = 1<VIread = GET_VF0_FLAG(_Fs_)|GET_VF0_FLAG(_Ft_)|(1<pipe = VUPIPE_FMAC; + VUregsn->VFwrite = _Fd_; + VUregsn->VFwxyzw= 0xE; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= 0xE; + VUregsn->VFread1 = _Ft_; + VUregsn->VFr1xyzw= 0xE; + VUregsn->VIwrite = 0; + VUregsn->VIread = GET_VF0_FLAG(_Fs_)|GET_VF0_FLAG(_Ft_)|(1<pipe = VUPIPE_NONE; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 0; +} + +VUREGS_FTFS(FTOI0); +VUREGS_FTFS(FTOI4); +VUREGS_FTFS(FTOI12); +VUREGS_FTFS(FTOI15); +VUREGS_FTFS(ITOF0); +VUREGS_FTFS(ITOF4); +VUREGS_FTFS(ITOF12); +VUREGS_FTFS(ITOF15); + +void _vuRegsCLIP(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= 0xE; + VUregsn->VFread1 = _Ft_; + VUregsn->VFr1xyzw= 0x1; + VUregsn->VIwrite = 1 << REG_CLIP_FLAG; + VUregsn->VIread = GET_VF0_FLAG(_Fs_)|GET_VF0_FLAG(_Ft_)|(1 << REG_CLIP_FLAG); +} + +/******************************/ +/* VU Lower instructions */ +/******************************/ + +void _vuRegsDIV(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FDIV; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= 1 << (3-_Fsf_); + VUregsn->VFread1 = _Ft_; + VUregsn->VFr1xyzw= 1 << (3-_Ftf_); + VUregsn->VIwrite = 1 << REG_Q; + VUregsn->VIread = GET_VF0_FLAG(_Fs_)|GET_VF0_FLAG(_Ft_); + VUregsn->cycles = 6; +} + +void _vuRegsSQRT(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FDIV; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFr0xyzw = 0; + VUregsn->VFread1 = _Ft_; + VUregsn->VFr1xyzw = 1 << (3-_Ftf_); + VUregsn->VIwrite = 1 << REG_Q; + VUregsn->VIread = GET_VF0_FLAG(_Ft_); + VUregsn->cycles = 6; +} + +void _vuRegsRSQRT(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FDIV; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= 1 << (3-_Fsf_); + VUregsn->VFread1 = _Ft_; + VUregsn->VFr1xyzw= 1 << (3-_Ftf_); + VUregsn->VIwrite = 1 << REG_Q; + VUregsn->VIread = GET_VF0_FLAG(_Fs_)|GET_VF0_FLAG(_Ft_); + VUregsn->cycles = 12; +} + +VUREGS_ITIS(IADDI); +VUREGS_ITIS(IADDIU); +VUREGS_IDISIT(IADD); +VUREGS_IDISIT(IAND); +VUREGS_IDISIT(IOR); +VUREGS_IDISIT(ISUB); +VUREGS_ITIS(ISUBIU); + +VUREGS_FTFS(MOVE); + +void _vuRegsMFIR(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = _Ft_; + VUregsn->VFwxyzw = _XYZW; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsMTIR(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= _XYZW; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = GET_VF0_FLAG(_Fs_); +} + +VUREGS_FTFS(MR32); + +void _vuRegsLQ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = _Ft_; + VUregsn->VFwxyzw = _XYZW; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsLQD(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = _Ft_; + VUregsn->VFwxyzw = _XYZW; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Fs_; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsLQI(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = _Ft_; + VUregsn->VFwxyzw = _XYZW; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Fs_; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsSQ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= _XYZW; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = (1 << _Ft_)|GET_VF0_FLAG(_Fs_); +} + +void _vuRegsSQD(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= _XYZW; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = (1 << _Ft_)|GET_VF0_FLAG(_Fs_); +} + +void _vuRegsSQI(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= _XYZW; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = (1 << _Ft_)|GET_VF0_FLAG(_Fs_); +} + +void _vuRegsILW(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_IALU; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsISW(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_IALU; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = (1 << _Fs_) | (1 << _Ft_); +} + +void _vuRegsILWR(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_IALU; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = (1 << _Ft_); + VUregsn->VIread = (1 << _Fs_); +} + +void _vuRegsISWR(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_IALU; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = (1 << _Fs_) | (1 << _Ft_); +} + +void _vuRegsRINIT(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= 1 << (3-_Fsf_); + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << REG_R; + VUregsn->VIread = GET_VF0_FLAG(_Fs_); +} + +void _vuRegsRGET(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = _Ft_; + VUregsn->VFwxyzw = _XYZW; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 1 << REG_R; +} + +void _vuRegsRNEXT(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = _Ft_; + VUregsn->VFwxyzw = _XYZW; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << REG_R; + VUregsn->VIread = 1 << REG_R; +} + +void _vuRegsRXOR(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= 1 << (3-_Fsf_); + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << REG_R; + VUregsn->VIread = (1 << REG_R)|GET_VF0_FLAG(_Fs_); +} + +void _vuRegsWAITQ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FDIV; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 0; +} + +void _vuRegsFSAND(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = 1 << REG_STATUS_FLAG; +} + +void _vuRegsFSEQ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = 1 << REG_STATUS_FLAG; +} + +void _vuRegsFSOR(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = 1 << REG_STATUS_FLAG; +} + +void _vuRegsFSSET(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << REG_STATUS_FLAG; + VUregsn->VIread = 0;//1 << REG_STATUS_FLAG; this kills speed +} + +void _vuRegsFMAND(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = (1 << REG_MAC_FLAG) | (1 << _Fs_); +} + +void _vuRegsFMEQ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = (1 << REG_MAC_FLAG) | (1 << _Fs_); +} + +void _vuRegsFMOR(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = (1 << REG_MAC_FLAG) | (1 << _Fs_); +} + +void _vuRegsFCAND(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << 1; + VUregsn->VIread = 1 << REG_CLIP_FLAG; +} + +void _vuRegsFCEQ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << 1; + VUregsn->VIread = 1 << REG_CLIP_FLAG; +} + +void _vuRegsFCOR(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << 1; + VUregsn->VIread = 1 << REG_CLIP_FLAG; +} + +void _vuRegsFCSET(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << REG_CLIP_FLAG; + VUregsn->VIread = 0; +} + +void _vuRegsFCGET(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = 1 << REG_CLIP_FLAG; +} + +void _vuRegsIBEQ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_BRANCH; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = (1 << _Fs_) | (1 << _Ft_); +} + +void _vuRegsIBGEZ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_BRANCH; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsIBGTZ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_BRANCH; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsIBLEZ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_BRANCH; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsIBLTZ(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_BRANCH; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsIBNE(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_BRANCH; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = (1 << _Fs_) | (1 << _Ft_); +} + +void _vuRegsB(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_BRANCH; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 0; +} + +void _vuRegsBAL(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_BRANCH; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = 0; +} + +void _vuRegsJR(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_BRANCH; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsJALR(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_BRANCH; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsMFP(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_FMAC; + VUregsn->VFwrite = _Ft_; + VUregsn->VFwxyzw = _XYZW; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 1 << REG_P; +} + +void _vuRegsWAITP(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_EFU; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 0; +} + +VUREGS_PFS(ESADD, 10); +VUREGS_PFS(ERSADD, 17); +VUREGS_PFS(ELENG, 17); +VUREGS_PFS(ERLENG, 23); +VUREGS_PFS(EATANxy, 53); +VUREGS_PFS(EATANxz, 53); +VUREGS_PFS(ESUM, 11); +VUREGS_PFS(ERCPR, 11); +VUREGS_PFS(ESQRT, 11); +VUREGS_PFS(ERSQRT, 17); +VUREGS_PFS(ESIN, 28); +VUREGS_PFS(EATAN, 53); +VUREGS_PFS(EEXP, 43); + +void _vuRegsXITOP(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_IALU; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = 0; +} + +void _vuRegsXGKICK(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_XGKICK; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 0; + VUregsn->VIread = 1 << _Fs_; +} + +void _vuRegsXTOP(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->pipe = VUPIPE_IALU; + VUregsn->VFwrite = 0; + VUregsn->VFread0 = 0; + VUregsn->VFread1 = 0; + VUregsn->VIwrite = 1 << _Ft_; + VUregsn->VIread = 0; +} diff --git a/pcsx2/VUops.h b/pcsx2/VUops.h new file mode 100644 index 0000000000..748ccbc38c --- /dev/null +++ b/pcsx2/VUops.h @@ -0,0 +1,399 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __VU1OPS_H__ +#define __VU1OPS_H__ + +#include "VU.h" + +extern __forceinline u32 VU_MAC_UPDATE( int shift, VURegs * VU, float f); +extern __forceinline u32 VU_MACx_UPDATE(VURegs * VU, float x); +extern __forceinline u32 VU_MACy_UPDATE(VURegs * VU, float y); +extern __forceinline u32 VU_MACz_UPDATE(VURegs * VU, float z); +extern __forceinline u32 VU_MACw_UPDATE(VURegs * VU, float w); +extern __forceinline void VU_MACx_CLEAR(VURegs * VU); +extern __forceinline void VU_MACy_CLEAR(VURegs * VU); +extern __forceinline void VU_MACz_CLEAR(VURegs * VU); +extern __forceinline void VU_MACw_CLEAR(VURegs * VU); + +#define float_to_int4(x) (s32)((float)x * (1.0f / 0.0625f)) +#define float_to_int12(x) (s32)((float)x * (1.0f / 0.000244140625f)) +#define float_to_int15(x) (s32)((float)x * (1.0f / 0.000030517578125)) + +#define int4_to_float(x) (float)((float)x * 0.0625f) +#define int12_to_float(x) (float)((float)x * 0.000244140625f) +#define int15_to_float(x) (float)((float)x * 0.000030517578125) + +#define MAC_Reset( VU ) VU->VI[REG_MAC_FLAG].UL = VU->VI[REG_MAC_FLAG].UL & (~0xFFFF) + +void _vuSetCycleFlags(VURegs * VU); +void _vuFlushFDIV(VURegs * VU); +void _vuFlushEFU(VURegs * VU); +void _vuTestPipes(VURegs * VU); +void _vuTestUpperStalls(VURegs * VU, _VURegsNum *VUregsn); +void _vuTestLowerStalls(VURegs * VU, _VURegsNum *VUregsn); +void _vuAddUpperStalls(VURegs * VU, _VURegsNum *VUregsn); +void _vuAddLowerStalls(VURegs * VU, _VURegsNum *VUregsn); + +/******************************/ +/* VU Upper instructions */ +/******************************/ + +void _vuABS(VURegs * VU); +void _vuADD(VURegs * VU); +void _vuADDi(VURegs * VU); +void _vuADDq(VURegs * VU); +void _vuADDx(VURegs * VU); +void _vuADDy(VURegs * VU); +void _vuADDz(VURegs * VU); +void _vuADDw(VURegs * VU); +void _vuADDA(VURegs * VU); +void _vuADDAi(VURegs * VU); +void _vuADDAq(VURegs * VU); +void _vuADDAx(VURegs * VU); +void _vuADDAy(VURegs * VU); +void _vuADDAz(VURegs * VU); +void _vuADDAw(VURegs * VU); +void _vuSUB(VURegs * VU); +void _vuSUBi(VURegs * VU); +void _vuSUBq(VURegs * VU); +void _vuSUBx(VURegs * VU); +void _vuSUBy(VURegs * VU); +void _vuSUBz(VURegs * VU); +void _vuSUBw(VURegs * VU); +void _vuSUBA(VURegs * VU); +void _vuSUBAi(VURegs * VU); +void _vuSUBAq(VURegs * VU); +void _vuSUBAx(VURegs * VU); +void _vuSUBAy(VURegs * VU); +void _vuSUBAz(VURegs * VU); +void _vuSUBAw(VURegs * VU); +void _vuMUL(VURegs * VU); +void _vuMULi(VURegs * VU); +void _vuMULq(VURegs * VU); +void _vuMULx(VURegs * VU); +void _vuMULy(VURegs * VU); +void _vuMULz(VURegs * VU); +void _vuMULw(VURegs * VU); +void _vuMULA(VURegs * VU); +void _vuMULAi(VURegs * VU); +void _vuMULAq(VURegs * VU); +void _vuMULAx(VURegs * VU); +void _vuMULAy(VURegs * VU); +void _vuMULAz(VURegs * VU); +void _vuMULAw(VURegs * VU); +void _vuMADD(VURegs * VU) ; +void _vuMADDi(VURegs * VU); +void _vuMADDq(VURegs * VU); +void _vuMADDx(VURegs * VU); +void _vuMADDy(VURegs * VU); +void _vuMADDz(VURegs * VU); +void _vuMADDw(VURegs * VU); +void _vuMADDA(VURegs * VU); +void _vuMADDAi(VURegs * VU); +void _vuMADDAq(VURegs * VU); +void _vuMADDAx(VURegs * VU); +void _vuMADDAy(VURegs * VU); +void _vuMADDAz(VURegs * VU); +void _vuMADDAw(VURegs * VU); +void _vuMSUB(VURegs * VU); +void _vuMSUBi(VURegs * VU); +void _vuMSUBq(VURegs * VU); +void _vuMSUBx(VURegs * VU); +void _vuMSUBy(VURegs * VU); +void _vuMSUBz(VURegs * VU) ; +void _vuMSUBw(VURegs * VU) ; +void _vuMSUBA(VURegs * VU); +void _vuMSUBAi(VURegs * VU); +void _vuMSUBAq(VURegs * VU); +void _vuMSUBAx(VURegs * VU); +void _vuMSUBAy(VURegs * VU); +void _vuMSUBAz(VURegs * VU); +void _vuMSUBAw(VURegs * VU); +void _vuMAX(VURegs * VU); +void _vuMAXi(VURegs * VU); +void _vuMAXx(VURegs * VU); +void _vuMAXy(VURegs * VU); +void _vuMAXz(VURegs * VU); +void _vuMAXw(VURegs * VU); +void _vuMINI(VURegs * VU); +void _vuMINIi(VURegs * VU); +void _vuMINIx(VURegs * VU); +void _vuMINIy(VURegs * VU); +void _vuMINIz(VURegs * VU); +void _vuMINIw(VURegs * VU); +void _vuOPMULA(VURegs * VU); +void _vuOPMSUB(VURegs * VU); +void _vuNOP(VURegs * VU); +void _vuFTOI0(VURegs * VU); +void _vuFTOI4(VURegs * VU); +void _vuFTOI12(VURegs * VU); +void _vuFTOI15(VURegs * VU); +void _vuITOF0(VURegs * VU) ; +void _vuITOF4(VURegs * VU) ; +void _vuITOF12(VURegs * VU); +void _vuITOF15(VURegs * VU); +void _vuCLIP(VURegs * VU); +/******************************/ +/* VU Lower instructions */ +/******************************/ +void _vuDIV(VURegs * VU); +void _vuSQRT(VURegs * VU); +void _vuRSQRT(VURegs * VU); +void _vuIADDI(VURegs * VU); +void _vuIADDIU(VURegs * VU); +void _vuIADD(VURegs * VU); +void _vuIAND(VURegs * VU); +void _vuIOR(VURegs * VU); +void _vuISUB(VURegs * VU); +void _vuISUBIU(VURegs * VU); +void _vuMOVE(VURegs * VU); +void _vuMFIR(VURegs * VU); +void _vuMTIR(VURegs * VU); +void _vuMR32(VURegs * VU); +void _vuLQ(VURegs * VU) ; +void _vuLQD(VURegs * VU); +void _vuLQI(VURegs * VU); +void _vuSQ(VURegs * VU); +void _vuSQD(VURegs * VU); +void _vuSQI(VURegs * VU); +void _vuILW(VURegs * VU); +void _vuISW(VURegs * VU); +void _vuILWR(VURegs * VU); +void _vuISWR(VURegs * VU); +void _vuLOI(VURegs * VU); +void _vuRINIT(VURegs * VU); +void _vuRGET(VURegs * VU); +void _vuRNEXT(VURegs * VU); +void _vuRXOR(VURegs * VU); +void _vuWAITQ(VURegs * VU); +void _vuFSAND(VURegs * VU); +void _vuFSEQ(VURegs * VU); +void _vuFSOR(VURegs * VU); +void _vuFSSET(VURegs * VU); +void _vuFMAND(VURegs * VU); +void _vuFMEQ(VURegs * VU); +void _vuFMOR(VURegs * VU); +void _vuFCAND(VURegs * VU); +void _vuFCEQ(VURegs * VU); +void _vuFCOR(VURegs * VU); +void _vuFCSET(VURegs * VU); +void _vuFCGET(VURegs * VU); +void _vuIBEQ(VURegs * VU); +void _vuIBGEZ(VURegs * VU); +void _vuIBGTZ(VURegs * VU); +void _vuIBLEZ(VURegs * VU); +void _vuIBLTZ(VURegs * VU); +void _vuIBNE(VURegs * VU); +void _vuB(VURegs * VU); +void _vuBAL(VURegs * VU); +void _vuJR(VURegs * VU); +void _vuJALR(VURegs * VU); +void _vuMFP(VURegs * VU); +void _vuWAITP(VURegs * VU); +void _vuESADD(VURegs * VU); +void _vuERSADD(VURegs * VU); +void _vuELENG(VURegs * VU); +void _vuERLENG(VURegs * VU); +void _vuEATANxy(VURegs * VU); +void _vuEATANxz(VURegs * VU); +void _vuESUM(VURegs * VU); +void _vuERCPR(VURegs * VU); +void _vuESQRT(VURegs * VU); +void _vuERSQRT(VURegs * VU); +void _vuESIN(VURegs * VU); +void _vuEATAN(VURegs * VU); +void _vuEEXP(VURegs * VU); +void _vuXITOP(VURegs * VU); +void _vuXGKICK(VURegs * VU); +void _vuXTOP(VURegs * VU); + +/******************************/ +/* VU Upper instructions */ +/******************************/ + +void _vuRegsABS(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADD(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDq(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDA(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDAi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDAq(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDAx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDAy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDAz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsADDAw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUB(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBq(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBA(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBAi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBAq(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBAx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBAy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBAz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSUBAw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMUL(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULq(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULA(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULAi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULAq(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULAx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULAy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULAz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMULAw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADD(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDq(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDA(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDAi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDAq(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDAx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDAy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDAz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMADDAw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUB(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBq(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBA(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBAi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBAq(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBAx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBAy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBAz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMSUBAw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMAX(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMAXi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMAXx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMAXy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMAXz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMAXw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMINI(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMINIi(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMINIx(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMINIy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMINIz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMINIw(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsOPMULA(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsOPMSUB(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsNOP(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFTOI0(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFTOI4(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFTOI12(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFTOI15(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsITOF0(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsITOF4(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsITOF12(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsITOF15(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsCLIP(VURegs * VU, _VURegsNum *VUregsn); +/******************************/ +/* VU Lower instructions */ +/******************************/ +void _vuRegsDIV(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSQRT(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsRSQRT(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIADDI(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIADDIU(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIADD(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIAND(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIOR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsISUB(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsISUBIU(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMOVE(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMFIR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMTIR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMR32(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsLQ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsLQD(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsLQI(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSQ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSQD(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsSQI(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsILW(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsISW(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsILWR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsISWR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsLOI(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsRINIT(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsRGET(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsRNEXT(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsRXOR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsWAITQ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFSAND(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFSEQ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFSOR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFSSET(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFMAND(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFMEQ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFMOR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFCAND(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFCEQ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFCOR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFCSET(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsFCGET(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIBEQ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIBGEZ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIBGTZ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIBLEZ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIBLTZ(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsIBNE(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsB(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsBAL(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsJR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsJALR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsMFP(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsWAITP(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsESADD(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsERSADD(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsELENG(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsERLENG(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsEATANxy(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsEATANxz(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsESUM(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsERCPR(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsESQRT(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsERSQRT(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsESIN(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsEATAN(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsEEXP(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsXITOP(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsXGKICK(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsXTOP(VURegs * VU, _VURegsNum *VUregsn); + +#endif diff --git a/pcsx2/Vif.cpp b/pcsx2/Vif.cpp new file mode 100644 index 0000000000..04ecc3a468 --- /dev/null +++ b/pcsx2/Vif.cpp @@ -0,0 +1,608 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include + +#include "Common.h" +#include "ix86/ix86.h" +#include "VUmicro.h" + +#include "Vif.h" +#include "VifDma.h" + +#include + +VIFregisters *_vifRegs; +u32* _vifMaskRegs = NULL; +PCSX2_ALIGNED16(u32 g_vifRow0[4]); +PCSX2_ALIGNED16(u32 g_vifCol0[4]); +PCSX2_ALIGNED16(u32 g_vifRow1[4]); +PCSX2_ALIGNED16(u32 g_vifCol1[4]); +u32* _vifRow = NULL, *_vifCol = NULL; + +vifStruct *_vif; + +static int n; + +__forceinline static int _limit( int a, int max ) +{ + return ( a > max ? max : a ); +} + +#define _UNPACKpart( offnum, func ) \ + if ( ( size > 0 ) && ( _vifRegs->offset == offnum ) ) { \ + func; \ + size--; \ + _vifRegs->offset++; \ + } + +#define _UNPACKpart_nosize( offnum, func ) \ + if ( ( _vifRegs->offset == offnum ) ) { \ + func; \ + _vifRegs->offset++; \ + } + +static void writeX( u32 *dest, u32 data ) { + if (_vifRegs->code & 0x10000000) { + switch ( _vif->cl ) { + case 0: n = (_vifRegs->mask) & 0x3; break; + case 1: n = (_vifRegs->mask >> 8) & 0x3; break; + case 2: n = (_vifRegs->mask >> 16) & 0x3; break; + default: n = (_vifRegs->mask >> 24) & 0x3; break; + } + } else n = 0; + + switch ( n ) { + case 0: + if((_vif->cmd & 0x6F) == 0x6f) { + *dest = data; + break; + } + if (_vifRegs->mode == 1) { + *dest = data + _vifRegs->r0; + } else + if (_vifRegs->mode == 2) { + _vifRegs->r0 = data + _vifRegs->r0; + *dest = _vifRegs->r0; + } else { + *dest = data; + } + break; + case 1: *dest = _vifRegs->r0; break; + case 2: + switch ( _vif->cl ) { + case 0: *dest = _vifRegs->c0; break; + case 1: *dest = _vifRegs->c1; break; + case 2: *dest = _vifRegs->c2; break; + default: *dest = _vifRegs->c3; break; + } + break; + } +// VIF_LOG("writeX %8.8x : Mode %d, r0 = %x, data %8.8x\n", *dest,_vifRegs->mode,_vifRegs->r0,data); +} + +static void writeY( u32 *dest, u32 data ) { + if (_vifRegs->code & 0x10000000) { + switch ( _vif->cl ) { + case 0: n = (_vifRegs->mask >> 2) & 0x3; break; + case 1: n = (_vifRegs->mask >> 10) & 0x3; break; + case 2: n = (_vifRegs->mask >> 18) & 0x3; break; + default: n = (_vifRegs->mask >> 26) & 0x3; break; + } + } else n = 0; + + switch ( n ) { + case 0: + if((_vif->cmd & 0x6F) == 0x6f) { + *dest = data; + break; + } + if (_vifRegs->mode == 1) { + *dest = data + _vifRegs->r1; + } else + if (_vifRegs->mode == 2) { + _vifRegs->r1 = data + _vifRegs->r1; + *dest = _vifRegs->r1; + } else { + *dest = data; + } + break; + case 1: *dest = _vifRegs->r1; break; + case 2: + switch ( _vif->cl ) { + case 0: *dest = _vifRegs->c0; break; + case 1: *dest = _vifRegs->c1; break; + case 2: *dest = _vifRegs->c2; break; + default: *dest = _vifRegs->c3; break; + } + break; + } +// VIF_LOG("writeY %8.8x : Mode %d, r1 = %x, data %8.8x\n", *dest,_vifRegs->mode,_vifRegs->r1,data); +} + +static void writeZ( u32 *dest, u32 data ) { + if (_vifRegs->code & 0x10000000) { + switch ( _vif->cl ) { + case 0: n = (_vifRegs->mask >> 4) & 0x3; break; + case 1: n = (_vifRegs->mask >> 12) & 0x3; break; + case 2: n = (_vifRegs->mask >> 20) & 0x3; break; + default: n = (_vifRegs->mask >> 28) & 0x3; break; + } + } else n = 0; + + switch ( n ) { + case 0: + if((_vif->cmd & 0x6F) == 0x6f) { + *dest = data; + break; + } + if (_vifRegs->mode == 1) { + *dest = data + _vifRegs->r2; + } else + if (_vifRegs->mode == 2) { + _vifRegs->r2 = data + _vifRegs->r2; + *dest = _vifRegs->r2; + } else { + *dest = data; + } + break; + case 1: *dest = _vifRegs->r2; break; + case 2: + switch ( _vif->cl ) { + case 0: *dest = _vifRegs->c0; break; + case 1: *dest = _vifRegs->c1; break; + case 2: *dest = _vifRegs->c2; break; + default: *dest = _vifRegs->c3; break; + } + break; + } +// VIF_LOG("writeZ %8.8x : Mode %d, r2 = %x, data %8.8x\n", *dest,_vifRegs->mode,_vifRegs->r2,data); +} + +static void writeW( u32 *dest, u32 data ) { + if (_vifRegs->code & 0x10000000) { + switch ( _vif->cl ) { + case 0: n = (_vifRegs->mask >> 6) & 0x3; break; + case 1: n = (_vifRegs->mask >> 14) & 0x3; break; + case 2: n = (_vifRegs->mask >> 22) & 0x3; break; + default: n = (_vifRegs->mask >> 30) & 0x3; break; + } + } else n = 0; + + switch ( n ) { + case 0: + if((_vif->cmd & 0x6F) == 0x6f) { + *dest = data; + break; + } + if (_vifRegs->mode == 1) { + *dest = data + _vifRegs->r3; + } else + if (_vifRegs->mode == 2) { + _vifRegs->r3 = data + _vifRegs->r3; + *dest = _vifRegs->r3; + } else { + *dest = data; + } + break; + case 1: *dest = _vifRegs->r3; break; + case 2: + switch ( _vif->cl ) { + case 0: *dest = _vifRegs->c0; break; + case 1: *dest = _vifRegs->c1; break; + case 2: *dest = _vifRegs->c2; break; + default: *dest = _vifRegs->c3; break; + } + break; + } +// VIF_LOG("writeW %8.8x : Mode %d, r3 = %x, data %8.8x\n", *dest,_vifRegs->mode,_vifRegs->r3,data); +} + +void UNPACK_S_32(u32 *dest, u32 *data, int size) { + _UNPACKpart(0, writeX(dest++, *data) ); + _UNPACKpart(1, writeY(dest++, *data) ); + _UNPACKpart(2, writeZ(dest++, *data) ); + _UNPACKpart(3, writeW(dest , *data) ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_S_16s( u32 *dest, u32 *data, int size) { + s16 *sdata = (s16*)data; + _UNPACKpart(0, writeX(dest++, *sdata) ); + _UNPACKpart(1, writeY(dest++, *sdata) ); + _UNPACKpart(2, writeZ(dest++, *sdata) ); + _UNPACKpart(3, writeW(dest , *sdata) ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_S_16u( u32 *dest, u32 *data, int size) { + u16 *sdata = (u16*)data; + _UNPACKpart(0, writeX(dest++, *sdata) ); + _UNPACKpart(1, writeY(dest++, *sdata) ); + _UNPACKpart(2, writeZ(dest++, *sdata) ); + _UNPACKpart(3, writeW(dest , *sdata) ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_S_8s(u32 *dest, u32 *data, int size) { + s8 *cdata = (s8*)data; + _UNPACKpart(0, writeX(dest++, *cdata) ); + _UNPACKpart(1, writeY(dest++, *cdata) ); + _UNPACKpart(2, writeZ(dest++, *cdata) ); + _UNPACKpart(3, writeW(dest , *cdata) ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_S_8u(u32 *dest, u32 *data, int size) { + u8 *cdata = (u8*)data; + _UNPACKpart(0, writeX(dest++, *cdata) ); + _UNPACKpart(1, writeY(dest++, *cdata) ); + _UNPACKpart(2, writeZ(dest++, *cdata) ); + _UNPACKpart(3, writeW(dest , *cdata) ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V2_32( u32 *dest, u32 *data, int size ) { + _UNPACKpart(0, writeX(dest++, *data++)); + _UNPACKpart(1, writeY(dest++, *data--)); + _UNPACKpart_nosize(2, writeZ(dest++, *data)); + _UNPACKpart_nosize(3, writeW(dest , 0)); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; + +} + +void UNPACK_V2_16s(u32 *dest, u32 *data, int size) { + s16 *sdata = (s16*)data; + _UNPACKpart(0, writeX(dest++, *sdata++)); + _UNPACKpart(1, writeY(dest++, *sdata--)); + _UNPACKpart_nosize(2,writeZ(dest++, *sdata++)); + _UNPACKpart_nosize(3,writeW(dest , *sdata)); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V2_16u(u32 *dest, u32 *data, int size) { + u16 *sdata = (u16*)data; + _UNPACKpart(0, writeX(dest++, *sdata++)); + _UNPACKpart(1, writeY(dest++, *sdata--)); + _UNPACKpart_nosize(2,writeZ(dest++, *sdata++)); + _UNPACKpart_nosize(3,writeW(dest , *sdata)); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V2_8s(u32 *dest, u32 *data, int size) { + s8 *cdata = (s8*)data; + _UNPACKpart(0, writeX(dest++, *cdata++)); + _UNPACKpart(1, writeY(dest++, *cdata--)); + _UNPACKpart_nosize(2,writeZ(dest++, *cdata++)); + _UNPACKpart_nosize(3,writeW(dest , *cdata)); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V2_8u(u32 *dest, u32 *data, int size) { + u8 *cdata = (u8*)data; + _UNPACKpart(0, writeX(dest++, *cdata++)); + _UNPACKpart(1, writeY(dest++, *cdata--)); + _UNPACKpart_nosize(2,writeZ(dest++, *cdata++)); + _UNPACKpart_nosize(3,writeW(dest , *cdata)); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V3_32(u32 *dest, u32 *data, int size) { + _UNPACKpart(0, writeX(dest++, *data++); ); + _UNPACKpart(1, writeY(dest++, *data++); ); + _UNPACKpart(2, writeZ(dest++, *data++); ); + _UNPACKpart_nosize(3, writeW(dest, *data); ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V3_16s(u32 *dest, u32 *data, int size) { + s16 *sdata = (s16*)data; + _UNPACKpart(0, writeX(dest++, *sdata++)); + _UNPACKpart(1, writeY(dest++, *sdata++)); + _UNPACKpart(2, writeZ(dest++, *sdata++)); + _UNPACKpart_nosize(3,writeW(dest, *sdata)); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V3_16u(u32 *dest, u32 *data, int size) { + u16 *sdata = (u16*)data; + _UNPACKpart(0, writeX(dest++, *sdata++)); + _UNPACKpart(1, writeY(dest++, *sdata++)); + _UNPACKpart(2, writeZ(dest++, *sdata++)); + _UNPACKpart_nosize(3,writeW(dest, *sdata)); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V3_8s(u32 *dest, u32 *data, int size) { + s8 *cdata = (s8*)data; + _UNPACKpart(0, writeX(dest++, *cdata++)); + _UNPACKpart(1, writeY(dest++, *cdata++)); + _UNPACKpart(2, writeZ(dest++, *cdata++)); + _UNPACKpart_nosize(3,writeW(dest, *cdata)); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V3_8u(u32 *dest, u32 *data, int size) { + u8 *cdata = (u8*)data; + _UNPACKpart(0, writeX(dest++, *cdata++)); + _UNPACKpart(1, writeY(dest++, *cdata++)); + _UNPACKpart(2, writeZ(dest++, *cdata++)); + _UNPACKpart_nosize(3,writeW(dest, *cdata)); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V4_32( u32 *dest, u32 *data , int size) { + _UNPACKpart(0, writeX(dest++, *data++) ); + _UNPACKpart(1, writeY(dest++, *data++) ); + _UNPACKpart(2, writeZ(dest++, *data++) ); + _UNPACKpart(3, writeW(dest , *data ) ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V4_16s(u32 *dest, u32 *data, int size) { + s16 *sdata = (s16*)data; + _UNPACKpart(0, writeX(dest++, *sdata++) ); + _UNPACKpart(1, writeY(dest++, *sdata++) ); + _UNPACKpart(2, writeZ(dest++, *sdata++) ); + _UNPACKpart(3, writeW(dest , *sdata ) ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V4_16u(u32 *dest, u32 *data, int size) { + u16 *sdata = (u16*)data; + _UNPACKpart(0, writeX(dest++, *sdata++) ); + _UNPACKpart(1, writeY(dest++, *sdata++) ); + _UNPACKpart(2, writeZ(dest++, *sdata++) ); + _UNPACKpart(3, writeW(dest , *sdata ) ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V4_8s(u32 *dest, u32 *data, int size) { + s8 *cdata = (s8*)data; + _UNPACKpart(0, writeX(dest++, *cdata++) ); + _UNPACKpart(1, writeY(dest++, *cdata++) ); + _UNPACKpart(2, writeZ(dest++, *cdata++) ); + _UNPACKpart(3, writeW(dest , *cdata ) ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V4_8u(u32 *dest, u32 *data, int size) { + u8 *cdata = (u8*)data; + _UNPACKpart(0, writeX(dest++, *cdata++) ); + _UNPACKpart(1, writeY(dest++, *cdata++) ); + _UNPACKpart(2, writeZ(dest++, *cdata++) ); + _UNPACKpart(3, writeW(dest , *cdata ) ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +void UNPACK_V4_5(u32 *dest, u32 *data, int size) { + + _UNPACKpart(0, writeX(dest++, (*data & 0x001f) << 3); ); + _UNPACKpart(1, writeY(dest++, (*data & 0x03e0) >> 2); ); + _UNPACKpart(2, writeZ(dest++, (*data & 0x7c00) >> 7); ); + _UNPACKpart(3, writeW(dest , (*data & 0x8000) >> 8); ); + if (_vifRegs->offset == 4) _vifRegs->offset = 0; +} + +static int cycles; +extern int g_vifCycles; +u16 vifqwc = 0; +static __forceinline int mfifoVIF1rbTransfer() { + u32 maddr = psHu32(DMAC_RBOR); + u32 ret, msize = psHu32(DMAC_RBOR) + psHu32(DMAC_RBSR) + 16; + u16 mfifoqwc = std::min(vif1ch->qwc, vifqwc); + u32 *src; + + /* Check if the transfer should wrap around the ring buffer */ + if ((vif1ch->madr+(mfifoqwc << 4)) > (msize)) { + int s1 = ((msize) - vif1ch->madr) >> 2; + + SPR_LOG("Split MFIFO\n"); + + /* it does, so first copy 's1' bytes from 'addr' to 'data' */ + src = (u32*)PSM(vif1ch->madr); + if (src == NULL) return -1; + if(vif1.vifstalled == 1){ + ret = VIF1transfer(src+vif1.irqoffset, s1-vif1.irqoffset, 0); + } + else + ret = VIF1transfer(src, s1, 0); + if(ret == -2) return ret; + + /* and second copy 's2' bytes from 'maddr' to '&data[s1]' */ + vif1ch->madr = maddr; + + src = (u32*)PSM(maddr); + if (src == NULL) return -1; + ret = VIF1transfer(src, ((mfifoqwc << 2) - s1), 0); + } else + { + SPR_LOG("Direct MFIFO\n"); + + /* it doesn't, so just transfer 'qwc*4' words */ + src = (u32*)PSM(vif1ch->madr); + if (src == NULL) return -1; + if(vif1.vifstalled == 1) + ret = VIF1transfer(src+vif1.irqoffset, mfifoqwc*4-vif1.irqoffset, 0); + else + ret = VIF1transfer(src, mfifoqwc << 2, 0); + if(ret == -2) return ret; + } + + + return ret; +} + +static __forceinline int mfifoVIF1chain() { + int ret; + + /* Is QWC = 0? if so there is nothing to transfer */ + if (vif1ch->qwc == 0 && vif1.vifstalled == 0) return 0; + + + if (vif1ch->madr >= psHu32(DMAC_RBOR) && + vif1ch->madr <= (psHu32(DMAC_RBOR)+psHu32(DMAC_RBSR))) { + u16 startqwc = vif1ch->qwc; + ret = mfifoVIF1rbTransfer(); + vifqwc -= startqwc - vif1ch->qwc; + } else { + u32 *pMem = (u32*)dmaGetAddr(vif1ch->madr); + SPR_LOG("Non-MFIFO Location\n"); + + if (pMem == NULL) return -1; + if(vif1.vifstalled == 1){ + ret = VIF1transfer(pMem+vif1.irqoffset, vif1ch->qwc*4-vif1.irqoffset, 0); + }else + ret = VIF1transfer(pMem, vif1ch->qwc << 2, 0); + } + + return ret; +} + +#define spr0 ((DMACh*)&PS2MEM_HW[0xD000]) + +void mfifoVIF1transfer(int qwc) { + u32 *ptag; + int id; + int ret, temp; + + g_vifCycles = 0; + + if(qwc > 0){ + vifqwc += qwc; + SPR_LOG("Added %x qw to mfifo, total now %x\n", qwc, vifqwc); + if((vif1ch->chcr & 0x100) == 0 || vif1.vifstalled == 1) return; + } + + if(vif1ch->qwc == 0){ + ptag = (u32*)dmaGetAddr(vif1ch->tadr); + + if (vif1ch->chcr & 0x40) { + if( vif1.stallontag == 1) ret = VIF1transfer(ptag+(2+vif1.irqoffset), 2-vif1.irqoffset, 1); //Transfer Tag on Stall + else ret = VIF1transfer(ptag+2, 2, 1); //Transfer Tag + if (ret == -2) { + VIF_LOG("MFIFO Stallon tag\n"); + + vif1.stallontag = 1; + CPU_INT(10,cycles+g_vifCycles); + return; //IRQ set by VIFTransfer + } + } + + id = (ptag[0] >> 28) & 0x7; + vif1ch->qwc = (ptag[0] & 0xffff); + vif1ch->madr = ptag[1]; + cycles += 2; + + vif1ch->chcr = ( vif1ch->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); + + SPR_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx mfifo qwc = %x spr0 madr = %x\n", + ptag[1], ptag[0], vif1ch->qwc, id, vif1ch->madr, vif1ch->tadr, vifqwc, spr0->madr); + vifqwc--; + + switch (id) { + case 0: // Refe - Transfer Packet According to ADDR field + vif1ch->tadr = psHu32(DMAC_RBOR) + ((vif1ch->tadr + 16) & psHu32(DMAC_RBSR)); + vif1.done = 2; //End Transfer + break; + + case 1: // CNT - Transfer QWC following the tag. + vif1ch->madr = psHu32(DMAC_RBOR) + ((vif1ch->tadr + 16) & psHu32(DMAC_RBSR)); //Set MADR to QW after Tag + vif1ch->tadr = psHu32(DMAC_RBOR) + ((vif1ch->madr + (vif1ch->qwc << 4)) & psHu32(DMAC_RBSR)); //Set TADR to QW following the data + vif1.done = 0; + break; + + case 2: // Next - Transfer QWC following tag. TADR = ADDR + temp = vif1ch->madr; //Temporarily Store ADDR + vif1ch->madr = psHu32(DMAC_RBOR) + ((vif1ch->tadr + 16) & psHu32(DMAC_RBSR)); //Set MADR to QW following the tag + vif1ch->tadr = temp; //Copy temporarily stored ADDR to Tag + if((temp & psHu32(DMAC_RBSR)) != psHu32(DMAC_RBOR)) SysPrintf("Next tag = %x outside ring %x size %x\n", temp, psHu32(DMAC_RBOR), psHu32(DMAC_RBSR)); + vif1.done = 0; + break; + + case 3: // Ref - Transfer QWC from ADDR field + case 4: // Refs - Transfer QWC from ADDR field (Stall Control) + vif1ch->tadr = psHu32(DMAC_RBOR) + ((vif1ch->tadr + 16) & psHu32(DMAC_RBSR)); //Set TADR to next tag + vif1.done = 0; + break; + + case 7: // End - Transfer QWC following the tag + vif1ch->madr = psHu32(DMAC_RBOR) + ((vif1ch->tadr + 16) & psHu32(DMAC_RBSR)); //Set MADR to data following the tag + vif1ch->tadr = psHu32(DMAC_RBOR) + ((vif1ch->madr + (vif1ch->qwc << 4)) & psHu32(DMAC_RBSR)); //Set TADR to QW following the data + vif1.done = 2; //End Transfer + break; + } + + if ((vif1ch->chcr & 0x80) && (ptag[0] >> 31)) { + VIF_LOG("dmaIrq Set\n"); + vif1.done = 2; + } + } + ret = mfifoVIF1chain(); + if (ret == -1) { + SysPrintf("VIF dmaChain error size=%d, madr=%lx, tadr=%lx\n", + vif1ch->qwc, vif1ch->madr, vif1ch->tadr); + vif1.done = 1; + CPU_INT(10,g_vifCycles); + } + if(ret == -2){ + VIF_LOG("MFIFO Stall\n"); + CPU_INT(10,g_vifCycles); + return; + } + + if(vif1.done == 2 && vif1ch->qwc == 0) vif1.done = 1; + CPU_INT(10,g_vifCycles); + SPR_LOG("mfifoVIF1transfer end %x madr %x, tadr %x vifqwc %x\n", vif1ch->chcr, vif1ch->madr, vif1ch->tadr, vifqwc); +} + +void vifMFIFOInterrupt() +{ + if(vif1.irq && vif1.tag.size == 0) { + vif1Regs->stat|= VIF1_STAT_INT; + hwIntcIrq(5); + --vif1.irq; + if(vif1Regs->stat & (VIF1_STAT_VSS|VIF1_STAT_VIS|VIF1_STAT_VFS)) + { + vif1Regs->stat&= ~0x1F000000; // FQC=0 + vif1ch->chcr &= ~0x100; + return; + } + } + + if(vif1.done != 1) { + if(vifqwc <= 0){ + //SysPrintf("Empty\n"); + hwDmacIrq(14); + return; + } + mfifoVIF1transfer(0); + return; + } + + //if(vifqwc > 0)SysPrintf("VIF MFIFO ending with stuff in it %x\n", vifqwc); + vifqwc = 0; + vif1.done = 0; + vif1ch->chcr &= ~0x100; + hwDmacIrq(DMAC_VIF1); + VIF_LOG("vif mfifo dma end\n"); + + vif1Regs->stat&= ~0x1F000000; // FQC=0 +// } +} diff --git a/pcsx2/Vif.h b/pcsx2/Vif.h new file mode 100644 index 0000000000..866195b32e --- /dev/null +++ b/pcsx2/Vif.h @@ -0,0 +1,113 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __VIF_H__ +#define __VIF_H__ + +struct vifCycle { + u8 cl, wl; + u8 pad[2]; +}; + +struct VIFregisters { + u32 stat; + u32 pad0[3]; + u32 fbrst; + u32 pad1[3]; + u32 err; + u32 pad2[3]; + u32 mark; + u32 pad3[3]; + vifCycle cycle; //data write cycle + u32 pad4[3]; + u32 mode; + u32 pad5[3]; + u32 num; + u32 pad6[3]; + u32 mask; + u32 pad7[3]; + u32 code; + u32 pad8[3]; + u32 itops; + u32 pad9[3]; + u32 base; // Not used in VIF0 + u32 pad10[3]; + u32 ofst; // Not used in VIF0 + u32 pad11[3]; + u32 tops; // Not used in VIF0 + u32 pad12[3]; + u32 itop; + u32 pad13[3]; + u32 top; // Not used in VIF0 + u32 pad14[3]; + u32 mskpath3; + u32 pad15[3]; + u32 r0; // row0 register + u32 pad16[3]; + u32 r1; // row1 register + u32 pad17[3]; + u32 r2; // row2 register + u32 pad18[3]; + u32 r3; // row3 register + u32 pad19[3]; + u32 c0; // col0 register + u32 pad20[3]; + u32 c1; // col1 register + u32 pad21[3]; + u32 c2; // col2 register + u32 pad22[3]; + u32 c3; // col3 register + u32 pad23[3]; + u32 offset; // internal UNPACK offset + u32 addr; +}; + +extern "C" +{ + // these use cdecl for Asm code references. + extern VIFregisters *_vifRegs; + extern u32* _vifMaskRegs; + extern u32* _vifRow; + extern u32* _vifCol; +} + +#define vif0Regs ((VIFregisters*)&PS2MEM_HW[0x3800]) +#define vif1Regs ((VIFregisters*)&PS2MEM_HW[0x3c00]) + +void dmaVIF0(); +void dmaVIF1(); +void mfifoVIF1transfer(int qwc); +int VIF0transfer(u32 *data, int size, int istag); +int VIF1transfer(u32 *data, int size, int istag); +void vifMFIFOInterrupt(); + +void SetNewMask(u32* vif1masks, u32* hasmask, u32 mask, u32 oldmask); + +#define XMM_R0 xmm0 +#define XMM_R1 xmm1 +#define XMM_R2 xmm2 +#define XMM_WRITEMASK xmm3 +#define XMM_ROWMASK xmm4 +#define XMM_ROWCOLMASK xmm5 +#define XMM_ROW xmm6 +#define XMM_COL xmm7 + +#define XMM_R3 XMM_COL + + +#endif /* __VIF_H__ */ diff --git a/pcsx2/VifDma.cpp b/pcsx2/VifDma.cpp new file mode 100644 index 0000000000..487538ffe2 --- /dev/null +++ b/pcsx2/VifDma.cpp @@ -0,0 +1,2425 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "Vif.h" +#include "VUmicro.h" +#include "GS.h" + +#include "VifDma.h" + +#ifdef _MSC_VER +#include +#include +#endif + +using namespace std; // for min / max + +//#define VIFUNPACKDEBUG //enable unpack debugging output + +#define gif ((DMACh*)&PS2MEM_HW[0xA000]) + +// Extern variables +extern "C" +{ + // Need cdecl on these for ASM references. + extern VIFregisters *_vifRegs; + extern u32* _vifMaskRegs; + extern u32* _vifRow; + extern u32* _vifCol; +} + +extern PCSX2_ALIGNED16_DECL(u32 g_vifRow0[4]); +extern PCSX2_ALIGNED16_DECL(u32 g_vifCol0[4]); +extern PCSX2_ALIGNED16_DECL(u32 g_vifRow1[4]); +extern PCSX2_ALIGNED16_DECL(u32 g_vifCol1[4]); + +extern vifStruct *_vif; + +vifStruct vif0, vif1; + +PCSX2_ALIGNED16(u32 g_vif1Masks[64]); +PCSX2_ALIGNED16(u32 g_vif0Masks[64]); +u32 g_vif1HasMask3[4] = {0}, g_vif0HasMask3[4] = {0}; + +// Generic constants +static const unsigned int VIF0intc = 4; +static const unsigned int VIF1intc = 5; +static const unsigned int VIF0dmanum = 0; +static const unsigned int VIF1dmanum = 1; + +int g_vifCycles = 0; +int path3hack = 0; + +typedef void (*UNPACKFUNCTYPE)( u32 *dest, u32 *data, int size ); +typedef int (*UNPACKPARTFUNCTYPESSE)( u32 *dest, u32 *data, int size ); +extern void (*Vif1CMDTLB[82])(); +extern void (*Vif0CMDTLB[75])(); +extern int (*Vif1TransTLB[128])(u32 *data); +extern int (*Vif0TransTLB[128])(u32 *data); + +struct VIFUnpackFuncTable { + UNPACKFUNCTYPE funcU; + UNPACKFUNCTYPE funcS; + + int bsize; // currently unused + int dsize; // byte size of one channel + int gsize; // size of data in bytes used for each write cycle + int qsize; // used for unpack parts, num of vectors that + // will be decompressed from data for 1 cycle +}; + +/* block size; data size; group size; qword size; */ +#define _UNPACK_TABLE32(name, bsize, dsize, gsize, qsize) \ + { UNPACK_##name, UNPACK_##name, \ + bsize, dsize, gsize, qsize }, + +#define _UNPACK_TABLE(name, bsize, dsize, gsize, qsize) \ + { UNPACK_##name##u, UNPACK_##name##s, \ + bsize, dsize, gsize, qsize }, + +// Main table for function unpacking +static const VIFUnpackFuncTable VIFfuncTable[16] = { + _UNPACK_TABLE32(S_32, 1, 4, 4, 4) // 0x0 - S-32 + _UNPACK_TABLE(S_16, 2, 2, 2, 4) // 0x1 - S-16 + _UNPACK_TABLE(S_8, 4, 1, 1, 4) // 0x2 - S-8 + { NULL, NULL, 0, 0, 0, 0 }, // 0x3 + + _UNPACK_TABLE32(V2_32, 24, 4, 8, 2) // 0x4 - V2-32 + _UNPACK_TABLE(V2_16, 12, 2, 4, 2) // 0x5 - V2-16 + _UNPACK_TABLE(V2_8, 6, 1, 2, 2) // 0x6 - V2-8 + { NULL, NULL, 0, 0, 0, 0 }, // 0x7 + + _UNPACK_TABLE32(V3_32, 36, 4, 12, 3) // 0x8 - V3-32 + _UNPACK_TABLE(V3_16, 18, 2, 6, 3) // 0x9 - V3-16 + _UNPACK_TABLE(V3_8, 9, 1, 3, 3) // 0xA - V3-8 + { NULL, NULL, 0, 0, 0, 0 }, // 0xB + + _UNPACK_TABLE32(V4_32, 48, 4, 16, 4) // 0xC - V4-32 + _UNPACK_TABLE(V4_16, 24, 2, 8, 4) // 0xD - V4-16 + _UNPACK_TABLE(V4_8, 12, 1, 4, 4) // 0xE - V4-8 + _UNPACK_TABLE32(V4_5, 6, 2, 2, 4) // 0xF - V4-5 +}; + +struct VIFSSEUnpackTable { + // regular 0, 1, 2; mask 0, 1, 2 + UNPACKPARTFUNCTYPESSE funcU[9], funcS[9]; +}; + +#define DECL_UNPACK_TABLE_SSE(name, sign) \ +extern "C" { \ + extern int UNPACK_SkippingWrite_##name##_##sign##_Regular_0(u32* dest, u32* data, int dmasize); \ + extern int UNPACK_SkippingWrite_##name##_##sign##_Regular_1(u32* dest, u32* data, int dmasize); \ + extern int UNPACK_SkippingWrite_##name##_##sign##_Regular_2(u32* dest, u32* data, int dmasize); \ + extern int UNPACK_SkippingWrite_##name##_##sign##_Mask_0(u32* dest, u32* data, int dmasize); \ + extern int UNPACK_SkippingWrite_##name##_##sign##_Mask_1(u32* dest, u32* data, int dmasize); \ + extern int UNPACK_SkippingWrite_##name##_##sign##_Mask_2(u32* dest, u32* data, int dmasize); \ + extern int UNPACK_SkippingWrite_##name##_##sign##_WriteMask_0(u32* dest, u32* data, int dmasize); \ + extern int UNPACK_SkippingWrite_##name##_##sign##_WriteMask_1(u32* dest, u32* data, int dmasize); \ + extern int UNPACK_SkippingWrite_##name##_##sign##_WriteMask_2(u32* dest, u32* data, int dmasize); \ +} + +#define _UNPACK_TABLE_SSE(name, sign) \ + UNPACK_SkippingWrite_##name##_##sign##_Regular_0, \ + UNPACK_SkippingWrite_##name##_##sign##_Regular_1, \ + UNPACK_SkippingWrite_##name##_##sign##_Regular_2, \ + UNPACK_SkippingWrite_##name##_##sign##_Mask_0, \ + UNPACK_SkippingWrite_##name##_##sign##_Mask_1, \ + UNPACK_SkippingWrite_##name##_##sign##_Mask_2, \ + UNPACK_SkippingWrite_##name##_##sign##_WriteMask_0, \ + UNPACK_SkippingWrite_##name##_##sign##_WriteMask_1, \ + UNPACK_SkippingWrite_##name##_##sign##_WriteMask_2 \ + +#define _UNPACK_TABLE_SSE_NULL \ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + +// Main table for function unpacking +DECL_UNPACK_TABLE_SSE(S_32, u); +DECL_UNPACK_TABLE_SSE(S_16, u); +DECL_UNPACK_TABLE_SSE(S_8, u); +DECL_UNPACK_TABLE_SSE(S_16, s); +DECL_UNPACK_TABLE_SSE(S_8, s); + +DECL_UNPACK_TABLE_SSE(V2_32, u); +DECL_UNPACK_TABLE_SSE(V2_16, u); +DECL_UNPACK_TABLE_SSE(V2_8, u); +DECL_UNPACK_TABLE_SSE(V2_16, s); +DECL_UNPACK_TABLE_SSE(V2_8, s); + +DECL_UNPACK_TABLE_SSE(V3_32, u); +DECL_UNPACK_TABLE_SSE(V3_16, u); +DECL_UNPACK_TABLE_SSE(V3_8, u); +DECL_UNPACK_TABLE_SSE(V3_16, s); +DECL_UNPACK_TABLE_SSE(V3_8, s); + +DECL_UNPACK_TABLE_SSE(V4_32, u); +DECL_UNPACK_TABLE_SSE(V4_16, u); +DECL_UNPACK_TABLE_SSE(V4_8, u); +DECL_UNPACK_TABLE_SSE(V4_16, s); +DECL_UNPACK_TABLE_SSE(V4_8, s); +DECL_UNPACK_TABLE_SSE(V4_5, u); + +static const VIFSSEUnpackTable VIFfuncTableSSE[16] = { + { _UNPACK_TABLE_SSE(S_32, u), _UNPACK_TABLE_SSE(S_32, u) }, + { _UNPACK_TABLE_SSE(S_16, u), _UNPACK_TABLE_SSE(S_16, s) }, + { _UNPACK_TABLE_SSE(S_8, u), _UNPACK_TABLE_SSE(S_8, s) }, + { _UNPACK_TABLE_SSE_NULL, _UNPACK_TABLE_SSE_NULL }, + + { _UNPACK_TABLE_SSE(V2_32, u), _UNPACK_TABLE_SSE(V2_32, u) }, + { _UNPACK_TABLE_SSE(V2_16, u), _UNPACK_TABLE_SSE(V2_16, s) }, + { _UNPACK_TABLE_SSE(V2_8, u), _UNPACK_TABLE_SSE(V2_8, s) }, + { _UNPACK_TABLE_SSE_NULL, _UNPACK_TABLE_SSE_NULL }, + + { _UNPACK_TABLE_SSE(V3_32, u), _UNPACK_TABLE_SSE(V3_32, u) }, + { _UNPACK_TABLE_SSE(V3_16, u), _UNPACK_TABLE_SSE(V3_16, s) }, + { _UNPACK_TABLE_SSE(V3_8, u), _UNPACK_TABLE_SSE(V3_8, s) }, + { _UNPACK_TABLE_SSE_NULL, _UNPACK_TABLE_SSE_NULL }, + + { _UNPACK_TABLE_SSE(V4_32, u), _UNPACK_TABLE_SSE(V4_32, u) }, + { _UNPACK_TABLE_SSE(V4_16, u), _UNPACK_TABLE_SSE(V4_16, s) }, + { _UNPACK_TABLE_SSE(V4_8, u), _UNPACK_TABLE_SSE(V4_8, s) }, + { _UNPACK_TABLE_SSE(V4_5, u), _UNPACK_TABLE_SSE(V4_5, u) }, +}; + + +__forceinline void vif0FLUSH() { + int _cycles; + _cycles = VU0.cycle; + + // fixme: this code should call _vu0WaitMicro instead? I'm not sure if + // it's purposefully ignoring ee cycles or not (see below for more) + + vu0Finish(); + g_vifCycles+= (VU0.cycle - _cycles)*BIAS; +} + +__forceinline void vif1FLUSH() { + int _cycles; + _cycles = VU1.cycle; + + // fixme: Same as above, is this a "stalling" offense? I think the cycles should + // be added to cpuRegs.cycle instead of g_vifCycles, but not sure (air) + + if( VU0.VI[REG_VPU_STAT].UL & 0x100 ) { + do { + CpuVU1.ExecuteBlock(); + } while(VU0.VI[REG_VPU_STAT].UL & 0x100); + + g_vifCycles+= (VU1.cycle - _cycles)*BIAS; + } +} + +void vifDmaInit() { +} + +__forceinline static int _limit( int a, int max ) { + return ( a > max ? max : a ); +} + +static void ProcessMemSkip(int size, unsigned int unpackType, const unsigned int VIFdmanum){ + const VIFUnpackFuncTable *unpack; + vifStruct *vif; + VIFregisters *vifRegs; + unpack = &VIFfuncTable[ unpackType ]; +// varLog |= 0x00000400; + + if (VIFdmanum == 0) + { + vif = &vif0; + vifRegs = vif0Regs; + } + else + { + vif = &vif1; + vifRegs = vif1Regs; + } + + switch(unpackType){ + case 0x0: + vif->tag.addr += size*4; + VIFUNPACK_LOG("Processing S-32 skip, size = %d\n", size); + break; + case 0x1: + vif->tag.addr += size*8; + VIFUNPACK_LOG("Processing S-16 skip, size = %d\n", size); + break; + case 0x2: + vif->tag.addr += size*16; + VIFUNPACK_LOG("Processing S-8 skip, size = %d\n", size); + break; + case 0x4: + vif->tag.addr += size + ((size / unpack->gsize) * 8); + VIFUNPACK_LOG("Processing V2-32 skip, size = %d\n", size); + break; + case 0x5: + vif->tag.addr += (size * 2) + ((size / unpack->gsize) * 8); + VIFUNPACK_LOG("Processing V2-16 skip, size = %d\n", size); + break; + case 0x6: + vif->tag.addr += (size * 4) + ((size / unpack->gsize) * 8); + VIFUNPACK_LOG("Processing V2-8 skip, size = %d\n", size); + break; + case 0x8: + vif->tag.addr += size + ((size / unpack->gsize) * 4); + VIFUNPACK_LOG("Processing V3-32 skip, size = %d\n", size); + break; + case 0x9: + vif->tag.addr += (size * 2) + ((size / unpack->gsize) * 4); + VIFUNPACK_LOG("Processing V3-16 skip, size = %d\n", size); + break; + case 0xA: + vif->tag.addr += (size * 4) + ((size / unpack->gsize) * 4); + VIFUNPACK_LOG("Processing V3-8 skip, size = %d\n", size); + break; + case 0xC: + vif->tag.addr += size; + VIFUNPACK_LOG("Processing V4-32 skip, size = %d, CL = %d, WL = %d\n", size, vif1Regs->cycle.cl, vif1Regs->cycle.wl); + break; + case 0xD: + vif->tag.addr += size * 2; + VIFUNPACK_LOG("Processing V4-16 skip, size = %d\n", size); + break; + case 0xE: + vif->tag.addr += size * 4; + VIFUNPACK_LOG("Processing V4-8 skip, size = %d\n", size); + break; + case 0xF: + vif->tag.addr += size * 8; + VIFUNPACK_LOG("Processing V4-5 skip, size = %d\n", size); + break; + default: + SysPrintf("Invalid unpack type %x\n", unpackType); + break; + } + //if(vifRegs->offset == 0) { + //vif->tag.addr += (size / unpack->gsize) * ((vifRegs->cycle.cl - vifRegs->cycle.wl)*16); + //if(vifRegs->cycle.cl != vifRegs->cycle.wl)SysPrintf("Adjusting\n"); + //} + if((vif->tag.addr & 0xf) == unpack->gsize) { + //SysPrintf("Making up for lost bit Addr %x Gsize %x new addr %x\n", vif->tag.addr, unpack->gsize, vif->tag.addr + (16 - unpack->gsize)); + vif->tag.addr += 16 - unpack->gsize; + } +} + +//u32 unpacktotal = 0; + +static void VIFunpack(u32 *data, vifCode *v, int size, const unsigned int VIFdmanum) { + u32 *dest; + unsigned int unpackType; + UNPACKFUNCTYPE func; + const VIFUnpackFuncTable *ft; + vifStruct *vif; + VIFregisters *vifRegs; + VURegs * VU; + u8 *cdata = (u8*)data; + //u64 basetick = GetCPUTick(); +#ifdef _DEBUG + u32 memsize = VIFdmanum ? 0x4000 : 0x1000; +#endif + +#ifdef _MSC_VER + _mm_prefetch((char*)data, _MM_HINT_NTA); +#endif + + if (VIFdmanum == 0) + { + VU = &VU0; + vif = &vif0; + vifRegs = vif0Regs; + assert( v->addr < memsize ); + //v->addr &= 0xfff; + } + else + { + + VU = &VU1; + vif = &vif1; + vifRegs = vif1Regs; + assert( v->addr < memsize ); + //v->addr &= 0x3fff; + + if( vu1MicroIsSkipping() ) { + // don't process since the frame is dummy + vif->tag.addr += (size / (VIFfuncTable[ vif->cmd & 0xf ].gsize* vifRegs->cycle.wl)) * ((vifRegs->cycle.cl - vifRegs->cycle.wl)*16); + // unpacktotal += GetCPUTick()-basetick; + return; + } + } + + dest = (u32*)(VU->Mem + v->addr); + + VIF_LOG("VIF%d UNPACK: Mode=%x, v->size=%d, size=%d, v->addr=%x\n", + VIFdmanum, v->cmd & 0xf, v->size, size, v->addr ); + +/* if (vifRegs->cycle.cl > vifRegs->cycle.wl) { + SysPrintf( "VIF%d UNPACK: Mode=%x, v->size=%d, size=%d, v->addr=%x\n", + VIFdmanum, v->cmd & 0xf, v->size, size, v->addr ); + }*/ +#ifdef _DEBUG + if (v->size != size) { + VIF_LOG("*PCSX2*: warning v->size != size\n"); + } + if ((v->addr+size*4) > memsize) { + SysPrintf("*PCSX2*: fixme unpack overflow\n"); + SysPrintf( "VIF%d UNPACK: Mode=%x, v->size=%d, size=%d, v->addr=%x\n", + VIFdmanum, v->cmd & 0xf, v->size, size, v->addr ); + } +#endif + // The unpack type + unpackType = v->cmd & 0xf; + /*if (v->size != size) { + SysPrintf("*PCSX2*: v->size = %d, size = %d mode = %x\n", v->size, size, unpackType); + }*/ + if (size == 0) { + VIFUNPACK_LOG("*PCSX2*: Unpack %x with size 0!! v->size = %d cl = %d, wl = %d, mode %d mask %x\n", v->cmd, v->size, vifRegs->cycle.cl, vifRegs->cycle.wl, vifRegs->mode, vifRegs->mask); + //return; + } + +#ifdef _MSC_VER + _mm_prefetch((char*)data+128, _MM_HINT_NTA); +#endif + _vifRegs = (VIFregisters*)vifRegs; + _vifMaskRegs = VIFdmanum ? g_vif1Masks : g_vif0Masks; + _vif = vif; + _vifRow = VIFdmanum ? g_vifRow1 : g_vifRow0; + ft = &VIFfuncTable[ unpackType ]; + func = _vif->usn ? ft->funcU : ft->funcS; + // Unpacking + //vif->wl = 0; vif->cl = 0; + + size<<= 2; +#ifdef _DEBUG + memsize = size; +#endif + if( _vifRegs->offset > 0) { + int destinc, unpacksize; + + VIFUNPACK_LOG("aligning packet size = %d offset %d addr %x\n", size, vifRegs->offset, vif->tag.addr); + + // SSE doesn't handle such small data + if (v->size != (size>>2))ProcessMemSkip(size, unpackType, VIFdmanum); + + if(vifRegs->offset < (u32)ft->qsize){ + if(((u32)size/(u32)ft->dsize) < ((u32)ft->qsize - vifRegs->offset)){ + SysPrintf("wasnt enough left size/dsize = %x left to write %x\n", (size/ft->dsize), (ft->qsize - vifRegs->offset)); + } + unpacksize = min(((u32)size/(u32)ft->dsize), ((u32)ft->qsize - vifRegs->offset)); + } + else { + unpacksize = 0; + SysPrintf("Unpack align offset = 0\n"); + } + destinc = (4 - ft->qsize) + unpacksize; + + func(dest, (u32*)cdata, unpacksize); + size -= unpacksize*ft->dsize; + cdata += unpacksize*ft->dsize; + + vifRegs->num--; + ++vif->cl; + if (vif->cl == vifRegs->cycle.wl) { + if(vifRegs->cycle.cl != vifRegs->cycle.wl){ + dest += ((vifRegs->cycle.cl - vifRegs->cycle.wl)<<2) + destinc; + //vif->tag.addr += (destinc<<2) + ((vifRegs->cycle.cl - vifRegs->cycle.wl)*16); + } else { + dest += destinc; + //vif->tag.addr += destinc << 2; + } + vif->cl = 0; + } + else { + dest += destinc; + //vif->tag.addr += destinc << 2; + } + VIFUNPACK_LOG("aligning packet done size = %d offset %d addr %x\n", size, vifRegs->offset, vif->tag.addr); + //} + //skipmeminc += (((vifRegs->cycle.cl - vifRegs->cycle.wl)<<2)*4) * skipped; + } else if (v->size != (size>>2))ProcessMemSkip(size, unpackType, VIFdmanum); + + if (vifRegs->cycle.cl >= vifRegs->cycle.wl) { // skipping write + +#ifdef _DEBUG + static int s_count=0; +#endif + //u32* olddest = dest; + + int incdest; + //ft = &VIFfuncTable[ unpackType ]; + if( vif->cl != 0 ) { + // continuation from last stream + + // func = vif->usn ? ft->funcU : ft->funcS; + incdest = ((vifRegs->cycle.cl - vifRegs->cycle.wl)<<2) + 4; + while (size >= ft->gsize && vifRegs->num > 0) { + func( dest, (u32*)cdata, ft->qsize); + cdata += ft->gsize; + size -= ft->gsize; + + vifRegs->num--; + ++vif->cl; + if (vif->cl == vifRegs->cycle.wl) { + dest += incdest; + vif->cl = 0; + break; + } + + dest += 4; + } + + // have to update + _vifRow[0] = _vifRegs->r0; + _vifRow[1] = _vifRegs->r1; + _vifRow[2] = _vifRegs->r2; + _vifRow[3] = _vifRegs->r3; + + } + + if( size >= ft->gsize && !(v->addr&0xf)) { + const UNPACKPARTFUNCTYPESSE* pfn; + int writemask; + //static LARGE_INTEGER lbase, lfinal; + //QueryPerformanceCounter(&lbase); + u32 oldcycle = -1; + //FreezeXMMRegs(1); + +// u16 tempdata[4] = { 0x8000, 0x7fff, 0x1010, 0xd0d0 }; +// vifRegs->cycle.cl = 4; +// vifRegs->cycle.wl = 1; +// SetNewMask(g_vif1Masks, g_vif1HasMask3, 0x3f, ~0x3f); +// memset(dest, 0xcd, 64*4); +// VIFfuncTableSSE[1].funcS[6](dest, (u32*)tempdata, 8); + +#ifdef _MSC_VER + if( VIFdmanum ) { + __asm movaps XMM_ROW, xmmword ptr [g_vifRow1] + __asm movaps XMM_COL, xmmword ptr [g_vifCol1] + } + else { + __asm movaps XMM_ROW, xmmword ptr [g_vifRow0] + __asm movaps XMM_COL, xmmword ptr [g_vifCol0] + } +#else + if( VIFdmanum ) { + __asm__(".intel_syntax\n" + "movaps %%xmm6, xmmword ptr [%0]\n" + "movaps %%xmm7, xmmword ptr [%1]\n" + ".att_syntax\n" : :"r"(g_vifRow1), "r"(g_vifCol1) ); + } + else { + __asm__(".intel_syntax\n" + "movaps %%xmm6, xmmword ptr [%0]\n" + "movaps %%xmm7, xmmword ptr [%1]\n" + ".att_syntax\n" : : "r"(g_vifRow0), "r"(g_vifCol0) ); + } +#endif + + if( vifRegs->cycle.cl == 0 || vifRegs->cycle.wl == 0 || (vifRegs->cycle.cl == vifRegs->cycle.wl && !(vifRegs->code&0x10000000)) ) { + oldcycle = *(u32*)&vifRegs->cycle; + vifRegs->cycle.cl = vifRegs->cycle.wl = 1; + } + size = min(size, (int)vifRegs->num*ft->gsize); //size will always be the same or smaller + + pfn = vif->usn ? VIFfuncTableSSE[unpackType].funcU: VIFfuncTableSSE[unpackType].funcS; + writemask = VIFdmanum ? g_vif1HasMask3[min(vifRegs->cycle.wl,(u8)3)] : g_vif0HasMask3[min(vifRegs->cycle.wl,(u8)3)]; + writemask = pfn[(((vifRegs->code & 0x10000000)>>28)<mode](dest, (u32*)cdata, size); + + if( oldcycle != -1 ) *(u32*)&vifRegs->cycle = oldcycle; + + // if size is left over, update the src,dst pointers + if( writemask > 0 ) { + int left = (size-writemask)/ft->gsize; + cdata += left * ft->gsize; + dest = (u32*)((u8*)dest + ((left/vifRegs->cycle.wl)*vifRegs->cycle.cl + left%vifRegs->cycle.wl)*16); + vifRegs->num -= left; + // Add split transfer skipping + //vif->tag.addr += (size / (ft->gsize* vifRegs->cycle.wl)) * ((vifRegs->cycle.cl - vifRegs->cycle.wl)*16); + // check for left over write cycles (so can spill to next transfer) + _vif->cl = (size % (ft->gsize*vifRegs->cycle.wl)) / ft->gsize; + } + else { + vifRegs->num -= size/ft->gsize; + if(vifRegs->num > 0) _vif->cl = (size % (ft->gsize*vifRegs->cycle.wl)) / ft->gsize; + } + + size = writemask; + + _vifRegs->r0 = _vifRow[0]; + _vifRegs->r1 = _vifRow[1]; + _vifRegs->r2 = _vifRow[2]; + _vifRegs->r3 = _vifRow[3]; + //QueryPerformanceCounter(&lfinal); + //((LARGE_INTEGER*)g_nCounters)->QuadPart += lfinal.QuadPart - lbase.QuadPart; + } + else + { + + if(unpackType == 0xC && vifRegs->cycle.cl == vifRegs->cycle.wl) { //No use when SSE is available + // v4-32 + if(vifRegs->mode == 0 && !(vifRegs->code & 0x10000000) && vif->usn == 0){ + vifRegs->num -= size>>4; + memcpy_fast((u8*)dest, cdata, size); + size = 0; + //unpacktotal += GetCPUTick()-basetick; + return; + } + } + // Assigning the normal upack function, the part type is assigned later + //func = vif->usn ? ft->funcU : ft->funcS; + + incdest = ((vifRegs->cycle.cl - vifRegs->cycle.wl)<<2) + 4; + + //SysPrintf("slow vif\n"); + //if(skipped > 0) skipped = 0; + // Add split transfer skipping + //vif->tag.addr += (size / (ft->gsize*vifRegs->cycle.wl)) * ((vifRegs->cycle.cl - vifRegs->cycle.wl)*16); + + while (size >= ft->gsize && vifRegs->num > 0) { + func( dest, (u32*)cdata, ft->qsize); + cdata += ft->gsize; + size -= ft->gsize; + + vifRegs->num--; + //SysPrintf("%d transferred, remaining %d, vifnum %d\n", ft->gsize, size, vifRegs->num); + ++vif->cl; + if (vif->cl == vifRegs->cycle.wl) { + dest += incdest; + vif->cl = 0; + } + else + { + dest += 4; + } + } + + // have to update + _vifRow[0] = _vifRegs->r0; + _vifRow[1] = _vifRegs->r1; + _vifRow[2] = _vifRegs->r2; + _vifRow[3] = _vifRegs->r3; + } + + // used for debugging vif +// { +// int i, j, k; +// u32* curdest = olddest; +// FILE* ftemp = fopen("temp.txt", s_count?"a+":"w"); +// fprintf(ftemp, "%x %x %x\n", s_count, size, vif->tag.addr); +// fprintf(ftemp, "%x %x %x\n", vifRegs->code>>24, vifRegs->mode, *(u32*)&vifRegs->cycle); +// fprintf(ftemp, "row: %x %x %x %x\n", _vifRow[0], _vifRow[1], _vifRow[2], _vifRow[3]); +// //fprintf(ftemp, "row2: %x %x %x %x\n", _vifRegs->r0, _vifRegs->r1, _vifRegs->r2, _vifRegs->r3); +// +// for(i = 0; i < memsize; ) { +// for(k = 0; k < vifRegs->cycle.wl; ++k) { +// for(j = 0; j <= ((vifRegs->code>>26)&3); ++j) { +// fprintf(ftemp, "%x ", curdest[4*k+j]); +// } +// } +// +// fprintf(ftemp, "\n"); +// curdest += 4*vifRegs->cycle.cl; +// i += (((vifRegs->code>>26)&3)+1)*ft->dsize*vifRegs->cycle.wl; +// } +// fclose(ftemp); +// } +// s_count++; + + if( size >= ft->dsize && vifRegs->num > 0) { + VIF_LOG("warning, end with size = %d\n", size); + + // SSE doesn't handle such small data + //ft = &VIFfuncTable[ unpackType ]; + //func = vif->usn ? ft->funcU : ft->funcS; + VIFUNPACK_LOG("end with size %x dsize = %x unpacktype %x\n", size, ft->dsize, unpackType); + //while (size >= ft->dsize) { + /* unpack one qword */ + func(dest, (u32*)cdata, size / ft->dsize); + //cdata += ft->dsize; + //dest += 1; + size = 0; + //} + VIFUNPACK_LOG("leftover done, size %d, vifnum %d, addr %x\n", size, vifRegs->num, vif->tag.addr); + } + + } + else { /* filling write */ + VIF_LOG("*PCSX2*: filling write\n"); + + //ft = &VIFfuncTable[ unpackType ]; + //func = vif->usn ? ft->funcU : ft->funcS; + VIFUNPACK_LOG("filling write %d cl %d, wl %d mask %x mode %x unpacktype %x\n", vifRegs->num, vifRegs->cycle.cl, vifRegs->cycle.wl, vifRegs->mask, vifRegs->mode, unpackType); + while (size >= ft->gsize || vifRegs->num > 0) { + if (vif->cl == vifRegs->cycle.wl) { + vif->cl = 0; + } + // + if (vif->cl < vifRegs->cycle.cl) { /* unpack one qword */ + func( dest, (u32*)cdata, ft->qsize); + cdata += ft->gsize; + size -= ft->gsize; + vif->cl++; + vifRegs->num--; + if (vif->cl == vifRegs->cycle.wl) { + vif->cl = 0; + } + } + else + { + func( dest, (u32*)cdata, ft->qsize); + //cdata += ft->gsize; + //size -= ft->gsize; + vif->tag.addr += 16; + vifRegs->num--; + ++vif->cl; + + } + dest += 4; + //++vif->wl; + if(vifRegs->num == 0) break; + } + } + //unpacktotal += GetCPUTick()-basetick; + //if(vifRegs->num == 0 && size > 3) SysPrintf("Size = %x, Vifnum = 0!\n", size); +} + +static void vuExecMicro( u32 addr, const u32 VIFdmanum ) +{ + int _cycles; + VURegs * VU; + //void (*_vuExecMicro)(); + +// MessageBox(NULL, "3d doesn't work\n", "Query", MB_OK); +// return; + + if (VIFdmanum == 0) { + //_vuExecMicro = Cpu->ExecuteVU0Block; + VU = &VU0; + vif0FLUSH(); + } else { + //_vuExecMicro = Cpu->ExecuteVU1Block; + VU = &VU1; + vif1FLUSH(); + } + if(VU->vifRegs->itops > (VIFdmanum ? 0x3ffu : 0xffu)) + SysPrintf("VIF%d ITOP overrun! %x\n", VIFdmanum, VU->vifRegs->itops); + + VU->vifRegs->itop = VU->vifRegs->itops; + + if (VIFdmanum == 1) { + /* in case we're handling a VIF1 execMicro + set the top with the tops value */ + VU->vifRegs->top = VU->vifRegs->tops & 0x3ff; + + /* is DBF flag set in VIF_STAT? */ + if (VU->vifRegs->stat & 0x80) { + /* it is, so set tops with base + ofst + and clear stat DBF flag */ + VU->vifRegs->tops = VU->vifRegs->base; + VU->vifRegs->stat &= ~0x80; + } else { + /* it is not, so set tops with base + and set the stat DBF flag */ + VU->vifRegs->tops = VU->vifRegs->base + VU->vifRegs->ofst; + VU->vifRegs->stat |= 0x80; + } + } + //FreezeXMMRegs(1); + if (VIFdmanum == 0) { + _cycles = VU0.cycle; + vu0ExecMicro(addr); + // too much delay + //g_vifCycles+= (VU0.cycle - _cycles)*BIAS; + } else { + _cycles = VU1.cycle; + vu1ExecMicro(addr); + // too much delay + //g_vifCycles+= (VU1.cycle - _cycles)*BIAS; + } + //FreezeXMMRegs(0); +} + +u8 s_maskwrite[256]; +void vif0Init() +{ + + u32 i; + + for(i = 0; i < 256; ++i ) { + s_maskwrite[i] = ((i&3)==3)||((i&0xc)==0xc)||((i&0x30)==0x30)||((i&0xc0)==0xc0); + } + + SetNewMask(g_vif0Masks, g_vif0HasMask3, 0, 0xffffffff); +} + +static __forceinline void vif0UNPACK(u32 *data) { + int vifNum; + int vl, vn; + int len; + + if(vif0Regs->cycle.wl == 0 && vif0Regs->cycle.wl < vif0Regs->cycle.cl){ + SysPrintf("Vif0 CL %d, WL %d\n", vif0Regs->cycle.cl, vif0Regs->cycle.wl); + vif0.cmd &= ~0x7f; + return; + } + + vif0FLUSH(); + + vl = (vif0.cmd ) & 0x3; + vn = (vif0.cmd >> 2) & 0x3; + vif0.tag.addr = (vif0Regs->code & 0x3ff) << 4; + vif0.usn = (vif0Regs->code >> 14) & 0x1; + vifNum = (vif0Regs->code >> 16) & 0xff; + if ( vifNum == 0 ) vifNum = 256; + vif0Regs->num = vifNum; + + if ( vif0Regs->cycle.wl <= vif0Regs->cycle.cl ) { + len = ((( 32 >> vl ) * ( vn + 1 )) * vifNum + 31) >> 5; + } else { + int n = vif0Regs->cycle.cl * (vifNum / vif0Regs->cycle.wl) + + _limit( vifNum % vif0Regs->cycle.wl, vif0Regs->cycle.cl ); + + len = ( ((( 32 >> vl ) * ( vn + 1 )) * n) + 31 ) >> 5; + } + //if((vif0.tag.addr + (vifNum * 16)) > 0x1000) SysPrintf("VIF0 Oops, Addr %x, NUM %x overlaps to %x\n", vif0.tag.addr, vifNum, (vif0.tag.addr + (vifNum * 16))); + vif0.wl = 0; vif0.cl = 0; + vif0.tag.cmd = vif0.cmd; + vif0.tag.addr &= 0xfff; + vif0.tag.size = len; + vif0Regs->offset = 0; +} + +static __forceinline void _vif0mpgTransfer(u32 addr, u32 *data, int size) { +/* SysPrintf("_vif0mpgTransfer addr=%x; size=%x\n", addr, size); + { + FILE *f = fopen("vu1.raw", "wb"); + fwrite(data, 1, size*4, f); + fclose(f); + }*/ + if (memcmp(VU0.Micro + addr, data, size << 2)) { + CpuVU0.Clear(addr, size << 2); // Clear before writing! :/ (cottonvibes) + memcpy_fast(VU0.Micro + addr, data, size << 2); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Vif1 Data Transfer Commands +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int Vif0TransNull(u32 *data){ // Shouldnt go here + SysPrintf("VIF0 Shouldnt go here CMD = %x\n", vif0Regs->code); + vif0.cmd = 0; + return 0; +} +static int Vif0TransSTMask(u32 *data){ // STMASK + SetNewMask(g_vif0Masks, g_vif0HasMask3, data[0], vif0Regs->mask); + vif0Regs->mask = data[0]; + VIF_LOG("STMASK == %x\n", vif0Regs->mask); + + vif0.tag.size = 0; + vif0.cmd = 0; + return 1; +} + +static int Vif0TransSTRow(u32 *data){ // STROW + int ret; + + u32* pmem = &vif0Regs->r0+(vif0.tag.addr<<2); + u32* pmem2 = g_vifRow0+vif0.tag.addr; + assert( vif0.tag.addr < 4 ); + ret = min(4-vif0.tag.addr, vif0.vifpacketsize); + assert( ret > 0 ); + switch(ret) { + case 4: pmem[12] = data[3]; pmem2[3] = data[3]; + case 3: pmem[8] = data[2]; pmem2[2] = data[2]; + case 2: pmem[4] = data[1]; pmem2[1] = data[1]; + case 1: pmem[0] = data[0]; pmem2[0] = data[0]; break; + + jNO_DEFAULT + } + vif0.tag.addr += ret; + vif0.tag.size -= ret; + if(vif0.tag.size == 0) vif0.cmd = 0; + + return ret; +} + +static int Vif0TransSTCol(u32 *data){ // STCOL + int ret; + + u32* pmem = &vif0Regs->c0+(vif0.tag.addr<<2); + u32* pmem2 = g_vifCol0+vif0.tag.addr; + ret = min(4-vif0.tag.addr, vif0.vifpacketsize); + switch(ret) { + case 4: pmem[12] = data[3]; pmem2[3] = data[3]; + case 3: pmem[8] = data[2]; pmem2[2] = data[2]; + case 2: pmem[4] = data[1]; pmem2[1] = data[1]; + case 1: pmem[0] = data[0]; pmem2[0] = data[0]; break; + + jNO_DEFAULT + } + vif0.tag.addr += ret; + vif0.tag.size -= ret; + if(vif0.tag.size == 0) vif0.cmd = 0; + return ret; +} + +static int Vif0TransMPG(u32 *data){ // MPG + if (vif0.vifpacketsize < vif0.tag.size) { + _vif0mpgTransfer(vif0.tag.addr, data, vif0.vifpacketsize); + vif0.tag.addr += vif0.vifpacketsize << 2; + vif0.tag.size -= vif0.vifpacketsize; + return vif0.vifpacketsize; + } else { + int ret; + _vif0mpgTransfer(vif0.tag.addr, data, vif0.tag.size); + ret = vif0.tag.size; + vif0.tag.size = 0; + vif0.cmd = 0; + return ret; + } +} + +static int Vif0TransUnpack(u32 *data){ // UNPACK + FreezeXMMRegs(1); + if (vif0.vifpacketsize < vif0.tag.size) { + /* size is less that the total size, transfer is + 'in pieces' */ + VIFunpack(data, &vif0.tag, vif0.vifpacketsize, VIF0dmanum); + // g_vifCycles+= size >> 1; + //vif0.tag.addr += size << 2; + vif0.tag.size -= vif0.vifpacketsize; + FreezeXMMRegs(0); + return vif0.vifpacketsize; + } else { + int ret; + /* we got all the data, transfer it fully */ + VIFunpack(data, &vif0.tag, vif0.tag.size, VIF0dmanum); + //g_vifCycles+= vif0.tag.size >> 1; + ret = vif0.tag.size; + vif0.tag.size = 0; + vif0.cmd = 0; + FreezeXMMRegs(0); + return ret; + } + +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Vif0 CMD Base Commands +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void Vif0CMDNop(){ // NOP + vif0.cmd &= ~0x7f; +} + +static void Vif0CMDSTCycl(){ // STCYCL + vif0Regs->cycle.cl = (u8)vif0Regs->code; + vif0Regs->cycle.wl = (u8)(vif0Regs->code >> 8); + vif0.cmd &= ~0x7f; +} + +static void Vif0CMDITop(){ // ITOP + vif0Regs->itops = vif0Regs->code & 0x3ff; + vif0.cmd &= ~0x7f; +} + +static void Vif0CMDSTMod(){ // STMOD + vif0Regs->mode = vif0Regs->code & 0x3; + vif0.cmd &= ~0x7f; +} + +static void Vif0CMDMark(){ // MARK + vif0Regs->mark = (u16)vif0Regs->code; + vif0Regs->stat |= VIF0_STAT_MRK; + vif0.cmd &= ~0x7f; +} + +static void Vif0CMDFlushE(){ // FLUSHE + vif0FLUSH(); + vif0.cmd &= ~0x7f; +} + +static void Vif0CMDMSCALF(){ //MSCAL/F + vuExecMicro( (u16)(vif0Regs->code) << 3, VIF0dmanum ); + vif0.cmd &= ~0x7f; +} + +static void Vif0CMDMSCNT(){ // MSCNT + vuExecMicro( -1, VIF0dmanum ); + vif0.cmd &= ~0x7f; +} + +static void Vif0CMDSTMask(){ // STMASK + vif0.tag.size = 1; +} + +static void Vif0CMDSTRowCol(){// STROW / STCOL + vif0.tag.addr = 0; + vif0.tag.size = 4; +} + +static void Vif0CMDMPGTransfer(){ // MPG + int vifNum; + vif0FLUSH(); + vifNum = (u8)(vif0Regs->code >> 16); + if (vifNum == 0) vifNum = 256; + vif0.tag.addr = (u16)(vif0Regs->code) << 3; + vif0.tag.size = vifNum * 2; +} + +static void Vif0CMDNull(){ // invalid opcode + // if ME1, then force the vif to interrupt + if ((vif0Regs->err & 0x4) == 0) { //Ignore vifcode and tag mismatch error + SysPrintf( "UNKNOWN VifCmd: %x\n", vif0.cmd ); + vif0Regs->stat |= 1 << 13; + vif0.irq++; + } + vif0.cmd &= ~0x7f; +} + +int VIF0transfer(u32 *data, int size, int istag) { + int ret; + int transferred=vif0.vifstalled ? vif0.irqoffset : 0; // irqoffset necessary to add up the right qws, or else will spin (spiderman) + //vif0.irqoffset = 0; + VIF_LOG( "VIF0transfer: size %x (vif0.cmd %x)\n", size, vif0.cmd ); + + vif0.stallontag = 0; + vif0.vifstalled = 0; + vif0.vifpacketsize = size; + + while (vif0.vifpacketsize > 0) { + + if (vif0.cmd) { + //vif0Regs->stat |= VIF0_STAT_VPS_T; + ret = Vif0TransTLB[(vif0.cmd & 0x7f)](data); + data+= ret; vif0.vifpacketsize-= ret; + //vif0Regs->stat &= ~VIF0_STAT_VPS_T; + continue; + } + + vif0Regs->stat &= ~VIF0_STAT_VPS_W; + + if(vif0.tag.size != 0) SysPrintf("no vif0 cmd but tag size is left last cmd read %x\n", vif0Regs->code); + // if interrupt and new cmd is NOT MARK + if(vif0.irq) { + break; + } + + vif0.cmd = (data[0] >> 24); + vif0Regs->code = data[0]; + + + //vif0Regs->stat |= VIF0_STAT_VPS_D; + if ((vif0.cmd & 0x60) == 0x60) { + vif0UNPACK(data); + } else { + VIF_LOG( "VIFtransfer: cmd %x, num %x, imm %x, size %x\n", vif0.cmd, (data[0] >> 16) & 0xff, data[0] & 0xffff, size ); + + if((vif0.cmd & 0x7f) > 0x4A){ + if ((vif0Regs->err & 0x4) == 0) { //Ignore vifcode and tag mismatch error + SysPrintf( "UNKNOWN VifCmd: %x\n", vif0.cmd ); + vif0Regs->stat |= 1 << 13; + vif0.irq++; + } + vif0.cmd = 0; + } else Vif0CMDTLB[(vif0.cmd & 0x7f)](); + } + //vif0Regs->stat &= ~VIF0_STAT_VPS_D; + if(vif0.tag.size > 0) vif0Regs->stat |= VIF0_STAT_VPS_W; + ++data; + --vif0.vifpacketsize; + + if ((vif0.cmd & 0x80)) { //i bit on vifcode and not masked by VIF0_ERR + if(!(vif0Regs->err & 0x1)){ + VIF_LOG( "Interrupt on VIFcmd: %x (INTC_MASK = %x)\n", vif0.cmd, psHu32(INTC_MASK) ); + + ++vif0.irq; + if(istag && vif0.tag.size <= vif0.vifpacketsize) vif0.stallontag = 1; + } + vif0.cmd &= 0x7f; + } + } + transferred += size - vif0.vifpacketsize; + g_vifCycles+= (transferred >> 2)*BIAS; /* guessing */ + // use tag.size because some game doesn't like .cmd + //if( !vif0.cmd ) + if( !vif0.tag.size ) + vif0Regs->stat &= ~VIF0_STAT_VPS_W; + + if (vif0.irq && vif0.tag.size == 0) { + vif0.vifstalled = 1; + + if(((vif0Regs->code >> 24) & 0x7f) != 0x7)vif0Regs->stat|= VIF0_STAT_VIS; + //else SysPrintf("VIF0 IRQ on MARK\n"); + // spiderman doesn't break on qw boundaries + vif0.irqoffset = transferred%4; // cannot lose the offset + + if( istag ) { + return -2; + } + + transferred = transferred >> 2; + vif0ch->madr+= (transferred << 4); + vif0ch->qwc-= transferred; + //SysPrintf("Stall on vif0, FromSPR = %x, Vif0MADR = %x Sif0MADR = %x STADR = %x\n", psHu32(0x1000d010), vif0ch->madr, psHu32(0x1000c010), psHu32(DMAC_STADR)); + return -2; + } + + if( !istag ) { + transferred = transferred >> 2; + vif0ch->madr+= (transferred << 4); + vif0ch->qwc-= transferred; + } + + return 0; +} + +int _VIF0chain() { + u32 *pMem; + //u32 qwc = vif0ch->qwc; + u32 ret; + + if (vif0ch->qwc == 0 && vif0.vifstalled == 0) return 0; + + pMem = (u32*)dmaGetAddr(vif0ch->madr); + if (pMem == NULL) + return -1; + + if( vif0.vifstalled ) { + ret = VIF0transfer(pMem+vif0.irqoffset, vif0ch->qwc*4-vif0.irqoffset, 0); + } + else { + ret = VIF0transfer(pMem, vif0ch->qwc*4, 0); + } + /*vif0ch->madr+= (vif0ch->qwc << 4); + vif0ch->qwc-= qwc;*/ + + return ret; +} + + +int _chainVIF0() { + int id; + u32 *ptag; + //int done=0; + int ret; + + ptag = (u32*)dmaGetAddr(vif0ch->tadr); //Set memory pointer to TADR + if (ptag == NULL) { //Is ptag empty? + SysPrintf("Vif0 Tag BUSERR\n"); + vif0ch->chcr = ( vif0ch->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + psHu32(DMAC_STAT)|= 1<<15; //If yes, set BEIS (BUSERR) in DMAC_STAT register + return -1; //Return -1 as an error has occurred + } + + id = (ptag[0] >> 28) & 0x7; //ID for DmaChain copied from bit 28 of the tag + vif0ch->qwc = (u16)ptag[0]; //QWC set to lower 16bits of the tag + vif0ch->madr = ptag[1]; //MADR = ADDR field + g_vifCycles+=1; // Add 1 g_vifCycles from the QW read for the tag + VIF_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx\n", + ptag[1], ptag[0], vif0ch->qwc, id, vif0ch->madr, vif0ch->tadr); + + vif0ch->chcr = ( vif0ch->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + // Transfer dma tag if tte is set + + if (vif0ch->chcr & 0x40) { + if(vif0.vifstalled == 1) ret = VIF0transfer(ptag+(2+vif0.irqoffset), 2-vif0.irqoffset, 1); //Transfer Tag on stall + else ret = VIF0transfer(ptag+2, 2, 1); //Transfer Tag + if (ret == -1) return -1; //There has been an error + if (ret == -2) { + //SysPrintf("VIF0 Stall on tag %x\n", vif0.irqoffset); + //vif0.vifstalled = 1; + return vif0.done; //IRQ set by VIFTransfer + } + } + + vif0.done |= hwDmacSrcChainWithStack(vif0ch, id); + + VIF_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx\n", + ptag[1], ptag[0], vif0ch->qwc, id, vif0ch->madr, vif0ch->tadr); + + //done |= hwDmacSrcChainWithStack(vif0ch, id); + ret = _VIF0chain(); //Transfers the data set by the switch + if (ret == -1) { return -1; } //There's been an error + if (ret == -2) { //IRQ has been set by VifTransfer + //vif0.vifstalled = 1; + return vif0.done; + } + + //if(id == 7)vif0ch->tadr = vif0ch->madr; + + vif0.vifstalled = 0; + + if ((vif0ch->chcr & 0x80) && (ptag[0] >> 31)) { //Check TIE bit of CHCR and IRQ bit of tag + VIF_LOG( "dmaIrq Set\n" ); + + //SysPrintf("VIF0 TIE\n"); + //SysPrintf( "VIF0dmaIrq Set\n" ); + //vif0ch->qwc = 0; + //vif0Regs->stat|= VIF0_STAT_VIS; //Set the Tag Interrupt flag of VIF0_STAT + vif0.done = 1; + return vif0.done; //End Transfer + } + return vif0.done; //Return Done +} + +void vif0Interrupt() { + int ret; + g_vifCycles = 0; //Reset the cycle count, Wouldnt reset on stall if put lower down. + VIF_LOG("vif0Interrupt: %8.8x\n", cpuRegs.cycle); + + //if(vif0.vifstalled == 1) { + if(vif0.irq && vif0.tag.size == 0) { + vif0Regs->stat|= VIF0_STAT_INT; + hwIntcIrq(VIF0intc); + --vif0.irq; + + if (vif0Regs->stat & (VIF0_STAT_VSS|VIF0_STAT_VIS|VIF0_STAT_VFS)) + { + vif0Regs->stat&= ~0xF000000; // FQC=0 + vif0ch->chcr &= ~0x100; + return; + } + if(vif0ch->qwc > 0 || vif0.irqoffset > 0){ + if(vif0.stallontag == 1) { + _chainVIF0(); + } + else _VIF0chain(); + CPU_INT(0, g_vifCycles); + return; + } + } + + //} + if((vif0ch->chcr & 0x100) == 0) { + SysPrintf("Vif0 running when CHCR = %x\n", vif0ch->chcr); + /*prevviftag = NULL; + prevvifcycles = 0; + vif1ch->chcr &= ~0x100; + hwDmacIrq(DMAC_VIF1); + hwIntcIrq(VIF1intc); + vif1Regs->stat&= ~0x1F000000; // FQC=0 + return 1;*/ + } + if (vif0ch->chcr & 0x4 && vif0.done == 0 && vif0.vifstalled == 0) { + + if( !(psHu32(DMAC_CTRL) & 0x1) ) { + SysPrintf("vif0 dma masked\n"); + return; + } + + if(vif0ch->qwc > 0) _VIF0chain(); + ret = _chainVIF0(); + CPU_INT(0, g_vifCycles); + return; + //if(ret!=2) + /*else*/ //return 1; + } + + + if(vif0ch->qwc > 0) SysPrintf("VIF0 Ending with QWC left\n"); + if(vif0.cmd != 0) SysPrintf("vif0.cmd still set %x\n", vif0.cmd); + vif0ch->chcr &= ~0x100; + hwDmacIrq(DMAC_VIF0); + vif0Regs->stat&= ~0xF000000; // FQC=0 + +} + +// Vif1 Data Transfer Table +int (*Vif0TransTLB[128])(u32 *data) = +{ + Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x7*/ + Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0xF*/ + Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x17*/ + Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x1F*/ + Vif0TransSTMask , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x27*/ + Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x2F*/ + Vif0TransSTRow , Vif0TransSTCol , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x37*/ + Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x3F*/ + Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x47*/ + Vif0TransNull , Vif0TransNull , Vif0TransMPG , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x4F*/ + Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x57*/ + Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , Vif0TransNull , /*0x5F*/ + Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransNull , /*0x67*/ + Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , /*0x6F*/ + Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransNull , /*0x77*/ + Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransNull , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack , Vif0TransUnpack /*0x7F*/ +}; + +// Vif1 CMD Table +void (*Vif0CMDTLB[75])() = +{ + Vif0CMDNop , Vif0CMDSTCycl , Vif0CMDNull , Vif0CMDNull , Vif0CMDITop , Vif0CMDSTMod , Vif0CMDNull, Vif0CMDMark , /*0x7*/ + Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , /*0xF*/ + Vif0CMDFlushE , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull, Vif0CMDMSCALF, Vif0CMDMSCALF, Vif0CMDNull , Vif0CMDMSCNT, /*0x17*/ + Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , /*0x1F*/ + Vif0CMDSTMask , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , /*0x27*/ + Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , /*0x2F*/ + Vif0CMDSTRowCol, Vif0CMDSTRowCol, Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , /*0x37*/ + Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , /*0x3F*/ + Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , Vif0CMDNull , /*0x47*/ + Vif0CMDNull , Vif0CMDNull , Vif0CMDMPGTransfer +}; + +void dmaVIF0() { + VIF_LOG("dmaVIF0 chcr = %lx, madr = %lx, qwc = %lx\n" + " tadr = %lx, asr0 = %lx, asr1 = %lx\n", + vif0ch->chcr, vif0ch->madr, vif0ch->qwc, + vif0ch->tadr, vif0ch->asr0, vif0ch->asr1 ); + + /* Check if there is a pending irq */ + /*if (vif0.irq > 0) { + vif0.irq--; + hwIntcIrq(VIF0intc); + return; + }*/ +// if(vif0ch->qwc > 0) { +// _VIF0chain(); +// CPU_INT(0, g_vifCycles); +// } + g_vifCycles = 0; + + + vif0Regs->stat|= 0x8000000; // FQC=8 + + if (!(vif0ch->chcr & 0x4) || vif0ch->qwc > 0) { // Normal Mode + if(_VIF0chain() == -2) { + SysPrintf("Stall on normal %x\n", vif0Regs->stat); + //vif0.vifstalled = 1; + return; + } + vif0.done = 1; + CPU_INT(0, g_vifCycles); + return; + } + +/* if (_VIF0chain() != 0) { + CPU_INT(0, g_vifCycles); + return; + }*/ + // Chain Mode + vif0.done = 0; + CPU_INT(0, g_vifCycles); +} + + +void vif0Write32(u32 mem, u32 value) { + if (mem == 0x10003830) { // MARK + VIF_LOG("VIF0_MARK write32 0x%8.8x\n", value); + + /* Clear mark flag in VIF0_STAT and set mark with 'value' */ + vif0Regs->stat&= ~VIF0_STAT_MRK; + vif0Regs->mark = value; + } else + if (mem == 0x10003810) { // FBRST + VIF_LOG("VIF0_FBRST write32 0x%8.8x\n", value); + + if (value & 0x1) { + /* Reset VIF */ + //SysPrintf("Vif0 Reset %x\n", vif0Regs->stat); + memzero_obj(vif0); + vif0ch->qwc = 0; //? + psHu64(0x10004000) = 0; + psHu64(0x10004008) = 0; + vif0.done = 1; + vif0Regs->err = 0; + vif0Regs->stat&= ~(0xF000000|VIF0_STAT_INT|VIF0_STAT_VSS|VIF0_STAT_VIS|VIF0_STAT_VFS); // FQC=0 + } + if (value & 0x2) { + /* Force Break the VIF */ + /* I guess we should stop the VIF dma here + but not 100% sure (linuz) */ + vif0Regs->stat |= VIF0_STAT_VFS; + SysPrintf("vif0 force break\n"); + } + if (value & 0x4) { + /* Stop VIF */ + /* Not completly sure about this, can't remember what game + used this, but 'draining' the VIF helped it, instead of + just stoppin the VIF (linuz) */ + vif0Regs->stat |= VIF0_STAT_VSS; + //SysPrintf("Vif0 Stop\n"); + //dmaVIF0(); // Drain the VIF --- VIF Stops as not to outstrip dma source (refraction) + //FreezeXMMRegs(0); + } + if (value & 0x8) { + int cancel = 0; + + /* Cancel stall, first check if there is a stall to cancel, + and then clear VIF0_STAT VSS|VFS|VIS|INT|ER0|ER1 bits */ + if (vif0Regs->stat & (VIF0_STAT_VSS|VIF0_STAT_VIS|VIF0_STAT_VFS)) { + cancel = 1; + } + + vif0Regs->stat &= ~(VIF0_STAT_VSS | VIF0_STAT_VFS | VIF0_STAT_VIS | + VIF0_STAT_INT | VIF0_STAT_ER0 | VIF0_STAT_ER1); + if (cancel) { + //SysPrintf("VIF0 Stall Resume\n"); + if( vif0.vifstalled ) { + // loop necessary for spiderman + if(vif0.stallontag == 1){ + //SysPrintf("Sorting VIF0 Stall on tag\n"); + _chainVIF0(); + } else _VIF0chain(); + + vif0ch->chcr |= 0x100; + CPU_INT(0, g_vifCycles); // Gets the timing right - Flatout + } + } + } + } else + if (mem == 0x10003820) { // ERR + VIF_LOG("VIF0_ERR write32 0x%8.8x\n", value); + + /* Set VIF0_ERR with 'value' */ + vif0Regs->err = value; + } else{ + SysPrintf("Unknown Vif0 write to %x\n", mem); + if( mem >= 0x10003900 && mem < 0x10003980 ) { + + assert( (mem&0xf) == 0 ); + if( mem < 0x10003940 ) g_vifRow0[(mem>>4)&3] = value; + else g_vifCol0[(mem>>4)&3] = value; + } else psHu32(mem) = value; + } + + /* Other registers are read-only so do nothing for them */ +} + +void vif0Reset() { + /* Reset the whole VIF, meaning the internal pcsx2 vars + and all the registers */ + memzero_obj(vif0); + memzero_obj(*vif0Regs); + SetNewMask(g_vif0Masks, g_vif0HasMask3, 0, 0xffffffff); + psHu64(0x10004000) = 0; + psHu64(0x10004008) = 0; + vif0.done = 1; + vif0Regs->stat&= ~0xF000000; // FQC=0 + //FreezeXMMRegs(0); + //FreezeMMXRegs(0); +} + +void SaveState::vif0Freeze() { + Freeze(vif0); + if( IsLoading() ) + SetNewMask(g_vif0Masks, g_vif0HasMask3, vif0Regs->mask, ~vif0Regs->mask); +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +void vif1Init() { + SetNewMask(g_vif1Masks, g_vif1HasMask3, 0, 0xffffffff); +} + +static __forceinline void vif1UNPACK(u32 *data) { + int vifNum; + int vl, vn; + //int len; + if(vif1Regs->cycle.wl == 0){ + if(vif1Regs->cycle.wl < vif1Regs->cycle.cl){ + SysPrintf("Vif1 CL %d, WL %d\n", vif1Regs->cycle.cl, vif1Regs->cycle.wl); + vif1.cmd &= ~0x7f; + return; +} + } + vif1FLUSH(); + + vl = (vif1.cmd ) & 0x3; + vn = (vif1.cmd >> 2) & 0x3; + + vif1.usn = (vif1Regs->code >> 14) & 0x1; + vifNum = (vif1Regs->code >> 16) & 0xff; + if ( vifNum == 0 ) vifNum = 256; + vif1Regs->num = vifNum; + + if ( vif1Regs->cycle.wl <= vif1Regs->cycle.cl ) { + vif1.tag.size = ((( 32 >> vl ) * ( vn + 1 )) * vifNum + 31) >> 5; + } else { + int n = vif1Regs->cycle.cl * (vifNum / vif1Regs->cycle.wl) + + _limit( vifNum % vif1Regs->cycle.wl, vif1Regs->cycle.cl ); + vif1.tag.size = ( ((( 32 >> vl ) * ( vn + 1 )) * n) + 31 ) >> 5; + } + if ( ( vif1Regs->code >> 15) & 0x1 ) { + vif1.tag.addr = (vif1Regs->code + vif1Regs->tops) & 0x3ff; + } else vif1.tag.addr = vif1Regs->code & 0x3ff; + + //vif1.wl = 0; + vif1.cl = 0; + vif1.tag.addr <<= 4; + //if((vif1.tag.addr + (vifNum * 16)) > 0x4000) SysPrintf("Oops, Addr %x, NUM %x overlaps to %x\n", vif1.tag.addr, vifNum, (vif1.tag.addr + (vifNum * 16))); + + vif1.tag.cmd = vif1.cmd; + // vif1Regs->offset = 0; +} + +static __forceinline void _vif1mpgTransfer(u32 addr, u32 *data, int size) { +/* SysPrintf("_vif1mpgTransfer addr=%x; size=%x\n", addr, size); + { + FILE *f = fopen("vu1.raw", "wb"); + fwrite(data, 1, size*4, f); + fclose(f); + }*/ + assert( VU1.Micro > 0 ); + if (memcmp(VU1.Micro + addr, data, size << 2)) { + CpuVU1.Clear(addr, size << 2); // Clear before writing! :/ + memcpy_fast(VU1.Micro + addr, data, size << 2); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Vif1 Data Transfer Commands +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int Vif1TransNull(u32 *data){ // Shouldnt go here + SysPrintf("Shouldnt go here CMD = %x\n", vif1Regs->code); + vif1.cmd = 0; + return 0; +} +static int Vif1TransSTMask(u32 *data){ // STMASK + SetNewMask(g_vif1Masks, g_vif1HasMask3, data[0], vif1Regs->mask); + vif1Regs->mask = data[0]; + VIF_LOG("STMASK == %x\n", vif1Regs->mask); + + vif1.tag.size = 0; + vif1.cmd = 0; + return 1; +} + +static int Vif1TransSTRow(u32 *data){ + int ret; + + u32* pmem = &vif1Regs->r0+(vif1.tag.addr<<2); + u32* pmem2 = g_vifRow1+vif1.tag.addr; + assert( vif1.tag.addr < 4 ); + ret = min(4-vif1.tag.addr, vif1.vifpacketsize); + assert( ret > 0 ); + switch(ret) { + case 4: pmem[12] = data[3]; pmem2[3] = data[3]; + case 3: pmem[8] = data[2]; pmem2[2] = data[2]; + case 2: pmem[4] = data[1]; pmem2[1] = data[1]; + case 1: pmem[0] = data[0]; pmem2[0] = data[0]; break; + jNO_DEFAULT; + } + vif1.tag.addr += ret; + vif1.tag.size -= ret; + if(vif1.tag.size == 0) vif1.cmd = 0; + + return ret; +} + +static int Vif1TransSTCol(u32 *data){ + int ret; + + u32* pmem = &vif1Regs->c0+(vif1.tag.addr<<2); + u32* pmem2 = g_vifCol1+vif1.tag.addr; + ret = min(4-vif1.tag.addr, vif1.vifpacketsize); + switch(ret) { + case 4: pmem[12] = data[3]; pmem2[3] = data[3]; + case 3: pmem[8] = data[2]; pmem2[2] = data[2]; + case 2: pmem[4] = data[1]; pmem2[1] = data[1]; + case 1: pmem[0] = data[0]; pmem2[0] = data[0]; break; + jNO_DEFAULT; + } + vif1.tag.addr += ret; + vif1.tag.size -= ret; + if(vif1.tag.size == 0) vif1.cmd = 0; + return ret; +} + +static int Vif1TransMPG(u32 *data){ + if (vif1.vifpacketsize < vif1.tag.size) { + _vif1mpgTransfer(vif1.tag.addr, data, vif1.vifpacketsize); + vif1.tag.addr += vif1.vifpacketsize << 2; + vif1.tag.size -= vif1.vifpacketsize; + return vif1.vifpacketsize; + } else { + int ret; + _vif1mpgTransfer(vif1.tag.addr, data, vif1.tag.size); + ret = vif1.tag.size; + vif1.tag.size = 0; + vif1.cmd = 0; + return ret; + } +} +u32 splittransfer[4]; +u32 splitptr = 0; + +static int Vif1TransDirectHL(u32 *data){ + int ret = 0; + + + if(splitptr > 0){ //Leftover data from the last packet, filling the rest and sending to the GS + if(splitptr < 4 && vif1.vifpacketsize >= (4-splitptr)){ + + while(splitptr < 4){ + splittransfer[splitptr++] = (u32)data++; + ret++; + vif1.tag.size--; + } + } + //if(splitptr < 4) SysPrintf("Whoopsie\n"); + if( mtgsThread != NULL ) + { + // copy 16 bytes the fast way: + const u64* src = (u64*)splittransfer[0]; + const uint count = mtgsThread->PrepDataPacket( GIF_PATH_2, src, 1); + jASSUME( count == 1 ); + u64* dst = (u64*)mtgsThread->GetDataPacketPtr(); + dst[0] = src[0]; + dst[1] = src[1]; + + mtgsThread->SendDataPacket(); + } + else + { + FreezeXMMRegs(1); + FreezeMMXRegs(1); + GSGIFTRANSFER2((u32*)splittransfer[0], 1); + FreezeMMXRegs(0); + FreezeXMMRegs(0); + } + + if(vif1.tag.size == 0) vif1.cmd = 0; + splitptr = 0; + return ret; + } + if (vif1.vifpacketsize < vif1.tag.size) { + if(vif1.vifpacketsize < 4 && splitptr != 4) { //Not a full QW left in the buffer, saving left over data + ret = vif1.vifpacketsize; + while(ret > 0){ + splittransfer[splitptr++] = (u32)data++; + vif1.tag.size--; + ret--; + } + //if(vif1.tag.size < 0) SysPrintf("Help\n"); + return vif1.vifpacketsize; + } //else if(vif1.vifpacketsize%4 != 0) SysPrintf("Size left = %x, non-qw aligned amount == %x\n", vif1.vifpacketsize, vif1.vifpacketsize%4); + + vif1.tag.size-= vif1.vifpacketsize; + ret = vif1.vifpacketsize; + } else { + ret = vif1.tag.size; + vif1.tag.size = 0; + vif1.cmd = 0; + } + + //TODO: ret is guaranteed to be qword aligned ? + + if( mtgsThread != NULL ) + { + //unaligned copy.VIF handling is -very- messy, so i'l use this code til i fix it :) + // Round ret up, just in case it's not 128bit aligned. + const uint count = mtgsThread->PrepDataPacket( GIF_PATH_2, data, (ret+3)>>2 ); + memcpy_fast( mtgsThread->GetDataPacketPtr(), data, count<<4 ); + mtgsThread->SendDataPacket(); + } + else { + + FreezeXMMRegs(1); + FreezeMMXRegs(1); + GSGIFTRANSFER2(data, (ret >> 2)); + FreezeMMXRegs(0); + FreezeXMMRegs(0); + } + + return ret; +} + + +static int Vif1TransUnpack(u32 *data){ + FreezeXMMRegs(1); + + if (vif1.vifpacketsize < vif1.tag.size) + { + /* size is less that the total size, transfer is + 'in pieces' */ + VIFunpack(data, &vif1.tag, vif1.vifpacketsize, VIF1dmanum); + // g_vifCycles+= size >> 1; + //vif1.tag.addr += size << 2; + vif1.tag.size -= vif1.vifpacketsize; + FreezeXMMRegs(0); + return vif1.vifpacketsize; + } + else + { + int ret; + /* we got all the data, transfer it fully */ + VIFunpack(data, &vif1.tag, vif1.tag.size, VIF1dmanum); + //g_vifCycles+= vif1.tag.size >> 1; + ret = vif1.tag.size; + vif1.tag.size = 0; + vif1.cmd = 0; + FreezeXMMRegs(0); + return ret; + } + +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Vif1 CMD Base Commands +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +int transferred = 0; +int Path3transfer=0; +static void Vif1CMDNop(){ // NOP + vif1.cmd &= ~0x7f; +} +static void Vif1CMDSTCycl(){ // STCYCL + vif1Regs->cycle.cl = (u8)vif1Regs->code; + vif1Regs->cycle.wl = (u8)(vif1Regs->code >> 8); + vif1.cmd &= ~0x7f; +} +static void Vif1CMDOffset(){ // OFFSET + vif1Regs->ofst = vif1Regs->code & 0x3ff; + vif1Regs->stat &= ~0x80; + vif1Regs->tops = vif1Regs->base; + vif1.cmd &= ~0x7f; +} +static void Vif1CMDBase(){ // BASE + vif1Regs->base = vif1Regs->code & 0x3ff; + vif1.cmd &= ~0x7f; +} +static void Vif1CMDITop(){ // ITOP + vif1Regs->itops = vif1Regs->code & 0x3ff; + vif1.cmd &= ~0x7f; +} + +static void Vif1CMDSTMod(){ // STMOD + vif1Regs->mode = vif1Regs->code & 0x3; + vif1.cmd &= ~0x7f; +} + +static void Vif1CMDMskPath3(){ // MSKPATH3 + + vif1Regs->mskpath3 = (vif1Regs->code >> 15) & 0x1; + //SysPrintf("VIF MSKPATH3 %x\n", vif1Regs->mskpath3); +#ifdef GSPATH3FIX + + if ( (vif1Regs->code >> 15) & 0x1 ) { + while((gif->chcr & 0x100)){ //Can be done 2 different ways, depends on the game/company + if(path3hack == 0)if(Path3transfer == 0 && gif->qwc == 0) break; + + gsInterrupt(); + + if(path3hack == 1)if(gif->qwc == 0) break; //add games not working with it to elfheader.c to enable this instead + } + //while(gif->chcr & 0x100) gsInterrupt(); // Finish the transfer first + psHu32(GIF_STAT) |= 0x2; + } else { + if(gif->chcr & 0x100) CPU_INT(2, (transferred>>2) * BIAS); // Restart Path3 on its own, time it right! + psHu32(GIF_STAT) &= ~0x2; + } +#else + if ( vif1Regs->mskpath3 ) { + if(gif->qwc) _GIFchain(); // Finish the transfer first + psHu32(GIF_STAT) |= 0x2; + } else { + psHu32(GIF_STAT) &= ~0x2; + if(gif->qwc) _GIFchain(); // Finish the transfer first + } +#endif + vif1.cmd &= ~0x7f; +} + +static void Vif1CMDMark(){ // MARK + vif1Regs->mark = (u16)vif1Regs->code; + vif1Regs->stat |= VIF1_STAT_MRK; + vif1.cmd &= ~0x7f; +} +static void Vif1CMDFlush(){ // FLUSH/E/A + + vif1FLUSH(); + + if((vif1.cmd & 0x7f) == 0x13) { + //SysPrintf("FlushA\n"); + while((gif->chcr & 0x100)){ + if(Path3transfer == 0 && gif->qwc == 0) break; + gsInterrupt(); + } + } + + vif1.cmd &= ~0x7f; +} +static void Vif1CMDMSCALF(){ //MSCAL/F + vuExecMicro( (u16)(vif1Regs->code) << 3, VIF1dmanum ); + vif1.cmd &= ~0x7f; +} +static void Vif1CMDMSCNT(){ // MSCNT + vuExecMicro( -1, VIF1dmanum ); + vif1.cmd &= ~0x7f; +} +static void Vif1CMDSTMask(){ // STMASK + vif1.tag.size = 1; +} +static void Vif1CMDSTRowCol(){// STROW / STCOL + vif1.tag.addr = 0; + vif1.tag.size = 4; +} + +static void Vif1CMDMPGTransfer(){ // MPG + int vifNum; + vif1FLUSH(); + vifNum = (u8)(vif1Regs->code >> 16); + if (vifNum == 0) vifNum = 256; + vif1.tag.addr = (u16)(vif1Regs->code) << 3; + vif1.tag.size = vifNum * 2; +} +static void Vif1CMDDirectHL(){ // DIRECT/HL + int vifImm; + vifImm = (u16)vif1Regs->code; + if (vifImm == 0) { + vif1.tag.size = 65536 << 2; + } else { + vif1.tag.size = vifImm << 2; + } + while((gif->chcr & 0x100) && (vif1.cmd & 0x7f) == 0x51){ + gsInterrupt(); //DirectHL flushes the lot + //if((psHu32(GIF_STAT) & 0xE00) == 0) break; + } +} +static void Vif1CMDNull(){ // invalid opcode + // if ME1, then force the vif to interrupt + + if ((vif1Regs->err & 0x4) == 0) { //Ignore vifcode and tag mismatch error + SysPrintf( "UNKNOWN VifCmd: %x\n", vif1.cmd ); + vif1Regs->stat |= 1 << 13; + vif1.irq++; + } + vif1.cmd = 0; +} + +// Vif1 Data Transfer Table + +int (*Vif1TransTLB[128])(u32 *data) = +{ + Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x7*/ + Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0xF*/ + Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x17*/ + Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x1F*/ + Vif1TransSTMask , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x27*/ + Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x2F*/ + Vif1TransSTRow , Vif1TransSTCol , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x37*/ + Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x3F*/ + Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x47*/ + Vif1TransNull , Vif1TransNull , Vif1TransMPG , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x4F*/ + Vif1TransDirectHL, Vif1TransDirectHL, Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x57*/ + Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , Vif1TransNull , /*0x5F*/ + Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransNull , /*0x67*/ + Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , /*0x6F*/ + Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransNull , /*0x77*/ + Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransNull , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack , Vif1TransUnpack /*0x7F*/ +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Vif1 CMD Table +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void (*Vif1CMDTLB[82])() = +{ + Vif1CMDNop , Vif1CMDSTCycl , Vif1CMDOffset , Vif1CMDBase , Vif1CMDITop , Vif1CMDSTMod , Vif1CMDMskPath3, Vif1CMDMark , /*0x7*/ + Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , /*0xF*/ + Vif1CMDFlush , Vif1CMDFlush , Vif1CMDNull , Vif1CMDFlush, Vif1CMDMSCALF, Vif1CMDMSCALF, Vif1CMDNull , Vif1CMDMSCNT, /*0x17*/ + Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , /*0x1F*/ + Vif1CMDSTMask , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , /*0x27*/ + Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , /*0x2F*/ + Vif1CMDSTRowCol, Vif1CMDSTRowCol, Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , /*0x37*/ + Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , /*0x3F*/ + Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , /*0x47*/ + Vif1CMDNull , Vif1CMDNull , Vif1CMDMPGTransfer, Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , Vif1CMDNull , /*0x4F*/ + Vif1CMDDirectHL, Vif1CMDDirectHL +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int VIF1transfer(u32 *data, int size, int istag) { + int ret; + transferred=vif1.vifstalled ? vif1.irqoffset : 0; // irqoffset necessary to add up the right qws, or else will spin (spiderman) + + VIF_LOG( "VIF1transfer: size %x (vif1.cmd %x)\n", size, vif1.cmd ); + + vif1.irqoffset = 0; + vif1.vifstalled = 0; + vif1.stallontag = 0; + vif1.vifpacketsize = size; + + //vif1.irq = 0; + while (vif1.vifpacketsize > 0) { + + if (vif1.cmd) { + //vif1Regs->stat |= VIF1_STAT_VPS_T; + ret = Vif1TransTLB[vif1.cmd](data); + data+= ret; vif1.vifpacketsize-= ret; + //vif1Regs->stat &= ~VIF1_STAT_VPS_T; + continue; + } + + if(vif1.tag.size != 0) SysPrintf("no vif1 cmd but tag size is left last cmd read %x\n", vif1Regs->code); + //vif1Regs->stat &= ~VIF1_STAT_VPS_W; + + //if(vif1.tag.size > 0) SysPrintf("VIF1 Tag size %x when cmd == 0!\n", vif1.tag.size); + + + if(vif1.irq) break; + + vif1.cmd = (data[0] >> 24); + vif1Regs->code = data[0]; + + //vif1Regs->stat |= VIF1_STAT_VPS_D; + if ((vif1.cmd & 0x60) == 0x60) { + vif1UNPACK(data); + } else { + VIF_LOG( "VIFtransfer: cmd %x, num %x, imm %x, size %x\n", vif1.cmd, (data[0] >> 16) & 0xff, data[0] & 0xffff, vif1.vifpacketsize ); + //vif1CMD(data, size); + /*if((vif1.cmd & 0x7f) > 0x51){ + if ((vif1Regs->err & 0x4) == 0) { //Ignore vifcode and tag mismatch error + SysPrintf( "UNKNOWN VifCmd: %x\n", vif1.cmd ); + vif1Regs->stat |= 1 << 13; + vif1.irq++; + } + vif1.cmd = 0; + } else*/ Vif1CMDTLB[(vif1.cmd & 0x7f)](); + } + //vif1Regs->stat &= ~VIF1_STAT_VPS_D; + //if(vif1.tag.size > 0) vif1Regs->stat |= VIF1_STAT_VPS_W; + ++data; + --vif1.vifpacketsize; + + if ((vif1.cmd & 0x80)) { //i bit on vifcode and not masked by VIF1_ERR + VIF_LOG( "Interrupt on VIFcmd: %x (INTC_MASK = %x)\n", vif1.cmd, psHu32(INTC_MASK) ); + + /*if((psHu32(DMAC_CTRL) & 0xC) == 0x8){ + SysPrintf("VIF1 Stall on MFIFO, not implemented!\n"); + }*/ + + if(!(vif1Regs->err & 0x1)){ + ++vif1.irq; + if(istag && vif1.tag.size <= vif1.vifpacketsize) vif1.stallontag = 1; + } + vif1.cmd &= 0x7f; + } + } + //if(vif1.cmd != 0 && vif1.tag.size == 0) SysPrintf("cmd but no tag size is left %x\n", vif1.cmd); + //if(vif1.cmd == 0 && vif1.tag.size != 0) SysPrintf("no cmd but tag size is left last cmd read %x\n", vif1Regs->code); + transferred += size - vif1.vifpacketsize; + g_vifCycles+= (transferred>>2)*BIAS; /* guessing */ + // use tag.size because some game doesn't like .cmd + //if( !vif1.cmd ) + //if( !vif1.tag.size ) + // vif1Regs->stat &= ~VIF1_STAT_VPS_W; + + if (vif1.irq && vif1.tag.size == 0) { + vif1.vifstalled = 1; + if(((vif1Regs->code >> 24) & 0x7f) != 0x7)vif1Regs->stat|= VIF1_STAT_VIS; // Note: commenting this out fixes WALL-E + //else SysPrintf("Stall on Vif1 MARK\n"); + // spiderman doesn't break on qw boundaries + vif1.irqoffset = transferred%4; // cannot lose the offset + + if( istag ) { + return -2; + } + + transferred = transferred >> 2; + vif1ch->madr+= (transferred << 4); + vif1ch->qwc-= transferred; + //SysPrintf("Stall on vif1, FromSPR = %x, Vif1MADR = %x Sif0MADR = %x STADR = %x\n", psHu32(0x1000d010), vif1ch->madr, psHu32(0x1000c010), psHu32(DMAC_STADR)); + return -2; + } + + + if( !istag ) { + /*if(transferred & 0x3) vif1.irqoffset = transferred%4; + else vif1.irqoffset = 0;*/ + + transferred = transferred >> 2; + vif1ch->madr+= (transferred << 4); + vif1ch->qwc-= transferred; + + //if(vif1ch->qwc > 0 && size != 0) vif1.vifstalled = 1; + + } + + return 0; +} + +int _VIF1chain() { + u32 *pMem; + //u32 qwc = vif1ch->qwc; + u32 ret; + + + if (vif1ch->qwc == 0 && vif1.vifstalled == 0) return 0; + + pMem = (u32*)dmaGetAddr(vif1ch->madr); + if (pMem == NULL) + return -1; + + VIF_LOG("dmaChain size=%d, madr=%lx, tadr=%lx\n", + vif1ch->qwc, vif1ch->madr, vif1ch->tadr); + + if( vif1.vifstalled ) { + ret = VIF1transfer(pMem+vif1.irqoffset, vif1ch->qwc*4-vif1.irqoffset, 0); + } + else { + ret = VIF1transfer(pMem, vif1ch->qwc*4, 0); + } + /*vif1ch->madr+= (vif1ch->qwc << 4); + vif1ch->qwc-= qwc;*/ + + return ret; +} + +static int prevvifcycles = 0; +static u32* prevviftag = NULL; +u32 *vifptag; +int _chainVIF1() { + int id; + //int done=0; + int ret; + //g_vifCycles = prevvifcycles; + + vifptag = (u32*)dmaGetAddr(vif1ch->tadr); //Set memory pointer to TADR + if (vifptag == NULL) { //Is ptag empty? + SysPrintf("Vif1 Tag BUSERR\n"); + vif1ch->chcr = ( vif1ch->chcr & 0xFFFF ) | ( (*vifptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + psHu32(DMAC_STAT)|= 1<<15; //If yes, set BEIS (BUSERR) in DMAC_STAT register + return -1; //Return -1 as an error has occurred + } + + id = (vifptag[0] >> 28) & 0x7; //ID for DmaChain copied from bit 28 of the tag + vif1ch->qwc = (u16)vifptag[0]; //QWC set to lower 16bits of the tag + vif1ch->madr = vifptag[1]; //MADR = ADDR field + g_vifCycles+=1; // Add 1 g_vifCycles from the QW read for the tag + + vif1ch->chcr = ( vif1ch->chcr & 0xFFFF ) | ( (*vifptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 + // Transfer dma tag if tte is set + + VIF_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx\n", + vifptag[1], vifptag[0], vif1ch->qwc, id, vif1ch->madr, vif1ch->tadr); + + //} else + + + if (!vif1.done && (psHu32(DMAC_CTRL) & 0xC0) == 0x40 && id == 4) { // STD == VIF1 + //vif1.done |= hwDmacSrcChainWithStack(vif1ch, id); + // there are still bugs, need to also check if gif->madr +16*qwc >= stadr, if not, stall + if( (vif1ch->madr + vif1ch->qwc * 16) >= psHu32(DMAC_STADR) ) { + // stalled + + //SysPrintf("Vif1 Stalling %x, %x, DMA_CTRL = %x\n",vif1ch->madr, psHu32(DMAC_STADR), psHu32(DMAC_CTRL)); + /*prevvifcycles = g_vifCycles; + prevviftag = vifptag;*/ + hwDmacIrq(13); + //vif1ch->tadr -= 16; + return 0; + } + } + //prevvifcycles = 0; + + if (vif1ch->chcr & 0x40) { + if(vif1.vifstalled == 1) ret = VIF1transfer(vifptag+(2+vif1.irqoffset), 2-vif1.irqoffset, 1); //Transfer Tag on stall + else ret = VIF1transfer(vifptag+2, 2, 1); //Transfer Tag + if (ret == -1) return -1; //There has been an error + if (ret == -2) { + //if(vif1.tag.size > 0)SysPrintf("VIF1 Stall on tag %x code %x\n", vif1.irqoffset, vif1Regs->code); + return 0; //IRQ set by VIFTransfer + } + } + //if((psHu32(DMAC_CTRL) & 0xC0) != 0x40 || id != 4) + vif1.done |= hwDmacSrcChainWithStack(vif1ch, id); + + VIF_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx\n", + vifptag[1], vifptag[0], vif1ch->qwc, id, vif1ch->madr, vif1ch->tadr); + + //done |= hwDmacSrcChainWithStack(vif1ch, id); + ret = _VIF1chain(); //Transfers the data set by the switch + //if (ret == -1) { return -1; } //There's been an error + //if (ret == -2) { //IRQ has been set by VifTransfer + // return 0; + //} + + + if ((vif1ch->chcr & 0x80) && (vifptag[0] >> 31)) { //Check TIE bit of CHCR and IRQ bit of tag + VIF_LOG( "dmaIrq Set\n" ); + + //SysPrintf("VIF1 TIE\n"); + //SysPrintf( "VIF1dmaIrq Set\n" ); + //vif1ch->qwc = 0; + //vif1Regs->stat|= VIF1_STAT_VIS; //Set the Tag Interrupt flag of VIF1_STAT + vif1.done = 1; + return 0; //End Transfer + } + return vif1.done; //Return Done +} + +__forceinline void vif1Interrupt() { + VIF_LOG("vif1Interrupt: %8.8x\n", cpuRegs.cycle); + + g_vifCycles = 0; + + //if(vif1.vifstalled == 1) { + if(vif1.irq && vif1.tag.size == 0) { + vif1Regs->stat|= VIF1_STAT_INT; + hwIntcIrq(VIF1intc); + --vif1.irq; + if(vif1Regs->stat & (VIF1_STAT_VSS|VIF1_STAT_VIS|VIF1_STAT_VFS)) + { + vif1Regs->stat&= ~0x1F000000; // FQC=0 + // One game doesnt like vif stalling at end, cant remember what. Spiderman isnt keen on it tho + vif1ch->chcr &= ~0x100; + return; + } + //return 0; + if(vif1ch->qwc > 0 || vif1.irqoffset > 0){ + if(vif1.stallontag == 1) { + _chainVIF1(); + } + else _VIF1chain(); + CPU_INT(1, g_vifCycles); + return; + } + } + + + //} + if((vif1ch->chcr & 0x100) == 0) { + SysPrintf("Vif1 running when CHCR == %x\n", vif1ch->chcr); + /*prevviftag = NULL; + prevvifcycles = 0; + vif1ch->chcr &= ~0x100; + hwDmacIrq(DMAC_VIF1); + hwIntcIrq(VIF1intc); + vif1Regs->stat&= ~0x1F000000; // FQC=0 + return 1;*/ + } + /*if(vif1ch->qwc > 0){ + _VIF1chain(); + CPU_INT(1, g_vifCycles); + return 0; + }*/ + if ((vif1ch->chcr & 0x104) == 0x104 && vif1.done == 0) { + + if( !(psHu32(DMAC_CTRL) & 0x1) ) { + SysPrintf("vif1 dma masked\n"); + return; + } + + _chainVIF1(); + CPU_INT(1, g_vifCycles); + + return; + } +#ifdef PCSX2_DEVBUILD + if(vif1ch->qwc > 0) SysPrintf("VIF1 Ending with QWC left\n"); + if(vif1.cmd != 0) SysPrintf("vif1.cmd still set %x\n", vif1.cmd); +#endif + //SysPrintf("VIF Interrupt\n"); + //if((gif->chcr & 0x100) && vif1Regs->mskpath3) gsInterrupt(); + prevviftag = NULL; + prevvifcycles = 0; + + vif1ch->chcr &= ~0x100; + hwDmacIrq(DMAC_VIF1); + if(vif1Regs->mskpath3 == 0 || (vif1ch->chcr & 0x1) == 0x1)vif1Regs->stat&= ~0x1F000000; // FQC=0 +} + +#define spr0 ((DMACh*)&PS2MEM_HW[0xD000]) +void dmaVIF1() +{ + VIF_LOG("dmaVIF1 chcr = %lx, madr = %lx, qwc = %lx\n" + " tadr = %lx, asr0 = %lx, asr1 = %lx\n", + vif1ch->chcr, vif1ch->madr, vif1ch->qwc, + vif1ch->tadr, vif1ch->asr0, vif1ch->asr1 ); + + /*if ((psHu32(DMAC_CTRL) & 0xC0)) { + SysPrintf("DMA Stall Control %x\n",(psHu32(DMAC_CTRL) & 0xC0)); + }*/ + /* Check if there is a pending irq */ + /*if (vif1.irq > 0) { + vif1.irq--; + hwIntcIrq(VIF1intc); + return; + }*/ +// if(vif1ch->qwc > 0) { +// _VIF1chain(); +// CPU_INT(1, g_vifCycles); +// } + vif1.done = 0; + g_vifCycles = 0; + + /*if( prevvifcycles != 0 ) { + int stallret = 0; + assert( prevviftag != NULL ); + + vifptag = prevviftag; + // transfer interrupted, so continue + //_VIF1chain(); + _chainVIF1(); + + if (vif1ch->chcr & 0x80 && vifptag[0] >> 31) { //Check TIE bit of CHCR and IRQ bit of tag +#ifdef VIF_LOG + VIF_LOG("dmaIrq Set\n"); +#endif + vif1.done = 1; + CPU_INT(1, g_vifCycles); + return; + } + //vif1.done = 1; + CPU_INT(1, g_vifCycles); + return; + }*/ + + if (((psHu32(DMAC_CTRL) & 0xC) == 0x8)) { // VIF MFIFO + //SysPrintf("VIFMFIFO\n"); + if(!(vif1ch->chcr & 0x4)) SysPrintf("MFIFO mode != Chain! %x\n", vif1ch->chcr); + if(vif1ch->madr != spr0->madr)vifMFIFOInterrupt(); + return; + } + +#ifdef PCSX2_DEVBUILD + if ((psHu32(DMAC_CTRL) & 0xC0) == 0x40) { // STD == VIF1 + //SysPrintf("VIF Stall Control Source = %x, Drain = %x\n", (psHu32(0xe000) >> 4) & 0x3, (psHu32(0xe000) >> 6) & 0x3); + //return; + } +#endif + + + vif1Regs->stat|= 0x10000000; // FQC=16 + + if (!(vif1ch->chcr & 0x4) || vif1ch->qwc > 0) { // Normal Mode + if ((psHu32(DMAC_CTRL) & 0xC0) == 0x40) { + SysPrintf("DMA Stall Control on VIF1 normal\n"); + } + if ((vif1ch->chcr & 0x1)) { // to Memory + if(_VIF1chain() == -2) { + SysPrintf("Stall on normal\n"); + vif1.vifstalled = 1; + return; + } + CPU_INT(1, g_vifCycles); + } else { + + int size; + u64* pMem = (u64*)dmaGetAddr(vif1ch->madr); + + // VIF from gsMemory + + if (pMem == NULL) { //Is ptag empty? + SysPrintf("Vif1 Tag BUSERR\n"); + psHu32(DMAC_STAT)|= 1<<15; //If yes, set BEIS (BUSERR) in DMAC_STAT register + vif1.done = 1; + vif1Regs->stat&= ~0x1f000000; + vif1ch->qwc = 0; + CPU_INT(1, g_vifCycles); + + return; //Return -1 as an error has occurred + } + + // MTGS concerns: The MTGS is inherently disagreeable with the idea of downloading + // stuff from the GS. The *only* way to handle this case safely is to flush the GS + // completely and execute the transfer there-after. + + FreezeXMMRegs(1); + if( GSreadFIFO2 == NULL ) { + for (size=vif1ch->qwc; size>0; --size) { + if (size > 1 ) { + mtgsWaitGS(); + GSreadFIFO((u64*)&PS2MEM_HW[0x5000]); + } + pMem[0] = psHu64(0x5000); + pMem[1] = psHu64(0x5008); pMem+= 2; + } + } + else { + mtgsWaitGS(); + GSreadFIFO2(pMem, vif1ch->qwc); + + // set incase read + psHu64(0x5000) = pMem[2*vif1ch->qwc-2]; + psHu64(0x5008) = pMem[2*vif1ch->qwc-1]; + } + FreezeXMMRegs(0); + + if(vif1Regs->mskpath3 == 0)vif1Regs->stat&= ~0x1f000000; + g_vifCycles += vif1ch->qwc * 2; + vif1ch->madr += vif1ch->qwc * 16; // mgs3 scene changes + vif1ch->qwc = 0; + CPU_INT(1, g_vifCycles); + } + + vif1.done = 1; + return; + } + +/* if (_VIF1chain() != 0) { + CPU_INT(1, g_vifCycles); + return; + }*/ + + // Chain Mode + vif1.done = 0; + CPU_INT(1, g_vifCycles); +} + + +void vif1Write32(u32 mem, u32 value) { + if (mem == 0x10003c30) { // MARK + VIF_LOG("VIF1_MARK write32 0x%8.8x\n", value); + + /* Clear mark flag in VIF1_STAT and set mark with 'value' */ + vif1Regs->stat&= ~VIF1_STAT_MRK; + vif1Regs->mark = value; + } else + if (mem == 0x10003c10) { // FBRST + VIF_LOG("VIF1_FBRST write32 0x%8.8x\n", value); + + if (value & 0x1) { + /* Reset VIF */ + //SysPrintf("Vif1 Reset %x\n", vif1Regs->stat); + memzero_obj(vif1); + vif1ch->qwc = 0; //? + psHu64(0x10005000) = 0; + psHu64(0x10005008) = 0; + vif1.done = 1; + vif1Regs->err = 0; + vif1Regs->stat&= ~(0x1F800000|VIF1_STAT_INT|VIF1_STAT_VSS|VIF1_STAT_VIS|VIF1_STAT_VFS); // FQC=0 + } + if (value & 0x2) { + /* Force Break the VIF */ + /* I guess we should stop the VIF dma here + but not 100% sure (linuz) */ + vif1Regs->stat |= VIF1_STAT_VFS; + cpuRegs.interrupt &= ~0x402; + vif1.vifstalled = 1; + SysPrintf("vif1 force break\n"); + } + if (value & 0x4) { + /* Stop VIF */ + /* Not completly sure about this, can't remember what game + used this, but 'draining' the VIF helped it, instead of + just stoppin the VIF (linuz) */ + vif1Regs->stat |= VIF1_STAT_VSS; + vif1.vifstalled = 1; + //SysPrintf("Vif1 Stop\n"); + //dmaVIF1(); // Drain the VIF --- VIF Stops as not to outstrip dma source (refraction) + //FreezeXMMRegs(0); + } + if (value & 0x8) { + int cancel = 0; + + /* Cancel stall, first check if there is a stall to cancel, + and then clear VIF1_STAT VSS|VFS|VIS|INT|ER0|ER1 bits */ + if (vif1Regs->stat & (VIF1_STAT_VSS|VIF1_STAT_VIS|VIF1_STAT_VFS)) { + cancel = 1; + } + + vif1Regs->stat &= ~(VIF1_STAT_VSS | VIF1_STAT_VFS | VIF1_STAT_VIS | + VIF1_STAT_INT | VIF1_STAT_ER0 | VIF1_STAT_ER1); + if (cancel) { + //SysPrintf("VIF1 Stall Resume\n"); + if( vif1.vifstalled ) { + // loop necessary for spiderman + if((psHu32(DMAC_CTRL) & 0xC) == 0x8){ + //vif1.vifstalled = 0; + //SysPrintf("MFIFO Stall\n"); + CPU_INT(10, 0); + }else { + if(vif1.stallontag == 1){ + //SysPrintf("Sorting VIF Stall on tag\n"); + _chainVIF1(); + } else _VIF1chain(); + //vif1.vifstalled = 0' + CPU_INT(1, g_vifCycles); // Gets the timing right - Flatout + } + vif1ch->chcr |= 0x100; + } + } + } + } else + if (mem == 0x10003c20) { // ERR + VIF_LOG("VIF1_ERR write32 0x%8.8x\n", value); + + /* Set VIF1_ERR with 'value' */ + vif1Regs->err = value; + } else + if (mem == 0x10003c00) { // STAT + VIF_LOG("VIF1_STAT write32 0x%8.8x\n", value); + +#ifdef PCSX2_DEVBUILD + /* Only FDR bit is writable, so mask the rest */ + if( (vif1Regs->stat & VIF1_STAT_FDR) ^ (value & VIF1_STAT_FDR) ) { + // different so can't be stalled + if (vif1Regs->stat & (VIF1_STAT_INT|VIF1_STAT_VSS|VIF1_STAT_VIS|VIF1_STAT_VFS)) { + SysPrintf("changing dir when vif1 fifo stalled\n"); + } + } +#endif + + vif1Regs->stat = (vif1Regs->stat & ~VIF1_STAT_FDR) | (value & VIF1_STAT_FDR); + if (vif1Regs->stat & VIF1_STAT_FDR) { + vif1Regs->stat|= 0x01000000; + } else { + vif1ch->qwc = 0; + vif1.vifstalled = 0; + vif1.done = 1; + vif1Regs->stat&= ~0x1F000000; // FQC=0 + } + } + else + if (mem == 0x10003c50) { // MODE + vif1Regs->mode = value; + } + else { + SysPrintf("Unknown Vif1 write to %x\n", mem); + if( mem >= 0x10003d00 && mem < 0x10003d80 ) { + assert( (mem&0xf) == 0 ); + if( mem < 0x10003d40) g_vifRow1[(mem>>4)&3] = value; + else g_vifCol1[(mem>>4)&3] = value; + } else psHu32(mem) = value; + } + + /* Other registers are read-only so do nothing for them */ +} + +void vif1Reset() { + /* Reset the whole VIF, meaning the internal pcsx2 vars + and all the registers */ + memzero_obj(vif1); + memzero_obj(*vif1Regs); + SetNewMask(g_vif1Masks, g_vif1HasMask3, 0, 0xffffffff); + psHu64(0x10005000) = 0; + psHu64(0x10005008) = 0; + vif1.done = 1; + vif1Regs->stat&= ~0x1F000000; // FQC=0 + /*FreezeXMMRegs(0); + FreezeMMXRegs(0);*/ +} + +void SaveState::vif1Freeze() { + Freeze(vif1); + if( IsLoading() ) + SetNewMask(g_vif1Masks, g_vif1HasMask3, vif1Regs->mask, ~vif1Regs->mask); +} diff --git a/pcsx2/VifDma.h b/pcsx2/VifDma.h new file mode 100644 index 0000000000..6e24133de8 --- /dev/null +++ b/pcsx2/VifDma.h @@ -0,0 +1,97 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +#ifndef __VIFDMA_H__ +#define __VIFDMA_H__ + +struct vifCode { + u32 addr; + u32 size; + u32 cmd; + u16 wl; + u16 cl; +}; + +// NOTE, if debugging vif stalls, use sega classics, spyro, gt4, and taito +struct vifStruct { + vifCode tag; + int cmd; + int irq; + int cl; + int wl; + u8 usn; + u8 done; + u8 vifstalled; + u8 stallontag; + u8 irqoffset; // 32bit offset where next vif code is + u32 savedtag; // need this for backwards compat with save states + u32 vifpacketsize; +}; + +extern vifStruct vif0, vif1; +extern int Path3transfer; + +#define vif0ch ((DMACh*)&PS2MEM_HW[0x8000]) +#define vif1ch ((DMACh*)&PS2MEM_HW[0x9000]) + +void UNPACK_S_32( u32 *dest, u32 *data, int size ); + +void UNPACK_S_16u( u32 *dest, u32 *data, int size ); +void UNPACK_S_16s( u32 *dest, u32 *data, int size ); + +void UNPACK_S_8u( u32 *dest, u32 *data, int size ); +void UNPACK_S_8s( u32 *dest, u32 *data, int size ); + +void UNPACK_V2_32( u32 *dest, u32 *data, int size ); + +void UNPACK_V2_16u( u32 *dest, u32 *data, int size ); +void UNPACK_V2_16s( u32 *dest, u32 *data, int size ); + +void UNPACK_V2_8u( u32 *dest, u32 *data, int size ); +void UNPACK_V2_8s( u32 *dest, u32 *data, int size ); + +void UNPACK_V3_32( u32 *dest, u32 *data, int size ); + +void UNPACK_V3_16u( u32 *dest, u32 *data, int size ); +void UNPACK_V3_16s( u32 *dest, u32 *data, int size ); + +void UNPACK_V3_8u( u32 *dest, u32 *data, int size ); +void UNPACK_V3_8s( u32 *dest, u32 *data, int size ); + +void UNPACK_V4_32( u32 *dest, u32 *data, int size ); + +void UNPACK_V4_16u( u32 *dest, u32 *data, int size ); +void UNPACK_V4_16s( u32 *dest, u32 *data, int size ); + +void UNPACK_V4_8u( u32 *dest, u32 *data, int size ); +void UNPACK_V4_8s( u32 *dest, u32 *data, int size ); + +void UNPACK_V4_5( u32 *dest, u32 *data, int size ); + +void vifDmaInit(); +void vif0Init(); +void vif1Init(); +extern void vif0Interrupt(); +extern void vif1Interrupt(); + +void vif0Write32(u32 mem, u32 value); +void vif1Write32(u32 mem, u32 value); + +void vif0Reset(); +void vif1Reset(); + +#endif diff --git a/pcsx2/bin b/pcsx2/bin new file mode 100644 index 0000000000..c8c1c5ac3e --- /dev/null +++ b/pcsx2/bin @@ -0,0 +1 @@ +link ../bin \ No newline at end of file diff --git a/pcsx2/build.sh b/pcsx2/build.sh new file mode 100644 index 0000000000..bbe7df2b04 --- /dev/null +++ b/pcsx2/build.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# Pcsx2 - Pc Ps2 Emulator +# Copyright (C) 2002-2008 Pcsx2 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 +# + +#Normal +#export PCSX2OPTIONS="--enable-debug --enable-devbuild --enable-sse3 --enable-sse4 --prefix `pwd`" + +echo --------------- +echo Building Pcsx2 +echo --------------- + +if [ $# -gt 0 ] && [ $1 = "all" ] +then + +aclocal +automake +autoconf +chmod +x configure +./configure ${PCSX2OPTIONS} +make clean +make install + +else +make $@ +fi + +if [ $? -ne 0 ] +then +exit 1 +fi diff --git a/pcsx2/cheatscpp.h b/pcsx2/cheatscpp.h new file mode 100644 index 0000000000..3a8ac28b98 --- /dev/null +++ b/pcsx2/cheatscpp.h @@ -0,0 +1,48 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +#ifndef CHEATSCPP_H_INCLUDED +#define CHEATSCPP_H_INCLUDED + +class Group +{ +public: + string title; + bool enabled; + int parentIndex; + + Group(int nParent,bool nEnabled, string &nTitle); + +}; + +class Patch +{ +public: + string title; + int group; + bool enabled; + int patchIndex; + + Patch(int patch, int grp, bool en, string &ttl); + + Patch operator =(const Patch&p); +}; + +extern vector groups; +extern vector patches; + +#endif//CHEATSCPP_H_INCLUDED diff --git a/pcsx2/common/PS2Edefs.h b/pcsx2/common/PS2Edefs.h new file mode 100644 index 0000000000..1f65fc02b9 --- /dev/null +++ b/pcsx2/common/PS2Edefs.h @@ -0,0 +1,885 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +#ifndef __PS2EDEFS_H__ +#define __PS2EDEFS_H__ + +/* + * PS2E Definitions v0.6.2 (beta) + * + * Author: linuzappz@hotmail.com + * shadowpcsx2@yahoo.gr + * florinsasu@hotmail.com + */ + +/* + Notes: + * Since this is still beta things may change. + + * OSflags: + __LINUX__ (linux OS) + _WIN32 (win32 OS) + + * common return values (for ie. GSinit): + 0 - success + -1 - error + + * reserved keys: + F1 to F10 are reserved for the emulator + + * plugins should NOT change the current + working directory. + (on win32, add flag OFN_NOCHANGEDIR for + GetOpenFileName) + +*/ + +#include "PS2Etypes.h" + + +/* common defines */ +#ifndef C_ASSERT +#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +#endif + +#if defined(GSdefs) || defined(PADdefs) || defined(SIOdefs) || \ + defined(SPU2defs) || defined(CDVDdefs) || defined(DEV9defs) || \ + defined(USBdefs) || defined(FWdefs) +#define COMMONdefs +#endif + +// PS2EgetLibType returns (may be OR'd) +#define PS2E_LT_GS 0x01 +#define PS2E_LT_PAD 0x02 // -=[ OBSOLETE ]=- +#define PS2E_LT_SPU2 0x04 +#define PS2E_LT_CDVD 0x08 +#define PS2E_LT_DEV9 0x10 +#define PS2E_LT_USB 0x20 +#define PS2E_LT_FW 0x40 +#define PS2E_LT_SIO 0x80 + +// PS2EgetLibVersion2 (high 16 bits) +#define PS2E_GS_VERSION 0x0006 +#define PS2E_PAD_VERSION 0x0002 // -=[ OBSOLETE ]=- +#define PS2E_SPU2_VERSION 0x0005 +#define PS2E_CDVD_VERSION 0x0005 +#define PS2E_DEV9_VERSION 0x0003 +#define PS2E_USB_VERSION 0x0003 +#define PS2E_FW_VERSION 0x0002 +#define PS2E_SIO_VERSION 0x0001 +#ifdef COMMONdefs + +u32 CALLBACK PS2EgetLibType(void); +u32 CALLBACK PS2EgetLibVersion2(u32 type); +char* CALLBACK PS2EgetLibName(void); + +#endif + +// key values: +/* key values must be OS dependant: + win32: the VK_XXX will be used (WinUser) + linux: the XK_XXX will be used (XFree86) +*/ + +// event values: +#define KEYPRESS 1 +#define KEYRELEASE 2 + +typedef struct _keyEvent { + u32 key; + u32 evt; +} keyEvent; + +// for 64bit compilers +typedef char __keyEvent_Size__[(sizeof(keyEvent) == 8)?1:-1]; + +// plugin types +#define SIO_TYPE_PAD 0x00000001 +#define SIO_TYPE_MTAP 0x00000004 +#define SIO_TYPE_RM 0x00000040 +#define SIO_TYPE_MC 0x00000100 + +typedef int (CALLBACK * SIOchangeSlotCB)(int slot); + +typedef struct _cdvdSubQ { + u8 ctrl:4; // control and mode bits + u8 mode:4; // control and mode bits + u8 trackNum; // current track number (1 to 99) + u8 trackIndex; // current index within track (0 to 99) + u8 trackM; // current minute location on the disc (BCD encoded) + u8 trackS; // current sector location on the disc (BCD encoded) + u8 trackF; // current frame location on the disc (BCD encoded) + u8 pad; // unused + u8 discM; // current minute offset from first track (BCD encoded) + u8 discS; // current sector offset from first track (BCD encoded) + u8 discF; // current frame offset from first track (BCD encoded) +} cdvdSubQ; + +typedef struct _cdvdTD { // NOT bcd coded + u32 lsn; + u8 type; +} cdvdTD; + +typedef struct _cdvdTN { + u8 strack; //number of the first track (usually 1) + u8 etrack; //number of the last track +} cdvdTN; + +// CDVDreadTrack mode values: +#define CDVD_MODE_2352 0 // full 2352 bytes +#define CDVD_MODE_2340 1 // skip sync (12) bytes +#define CDVD_MODE_2328 2 // skip sync+head+sub (24) bytes +#define CDVD_MODE_2048 3 // skip sync+head+sub (24) bytes +#define CDVD_MODE_2368 4 // full 2352 bytes + 16 subq + +// CDVDgetDiskType returns: +#define CDVD_TYPE_ILLEGAL 0xff // Illegal Disc +#define CDVD_TYPE_DVDV 0xfe // DVD Video +#define CDVD_TYPE_CDDA 0xfd // Audio CD +#define CDVD_TYPE_PS2DVD 0x14 // PS2 DVD +#define CDVD_TYPE_PS2CDDA 0x13 // PS2 CD (with audio) +#define CDVD_TYPE_PS2CD 0x12 // PS2 CD +#define CDVD_TYPE_PSCDDA 0x11 // PS CD (with audio) +#define CDVD_TYPE_PSCD 0x10 // PS CD +#define CDVD_TYPE_UNKNOWN 0x05 // Unknown +#define CDVD_TYPE_DETCTDVDD 0x04 // Detecting Dvd Dual Sided +#define CDVD_TYPE_DETCTDVDS 0x03 // Detecting Dvd Single Sided +#define CDVD_TYPE_DETCTCD 0x02 // Detecting Cd +#define CDVD_TYPE_DETCT 0x01 // Detecting +#define CDVD_TYPE_NODISC 0x00 // No Disc + +// CDVDgetTrayStatus returns: +#define CDVD_TRAY_CLOSE 0x00 +#define CDVD_TRAY_OPEN 0x01 + +// cdvdTD.type (track types for cds) +#define CDVD_AUDIO_TRACK 0x01 +#define CDVD_MODE1_TRACK 0x41 +#define CDVD_MODE2_TRACK 0x61 + +#define CDVD_AUDIO_MASK 0x00 +#define CDVD_DATA_MASK 0x40 +// CDROM_DATA_TRACK 0x04 //do not enable this! (from linux kernel) + +typedef void (*DEV9callback)(int cycles); +typedef int (*DEV9handler)(void); + +typedef void (*USBcallback)(int cycles); +typedef int (*USBhandler)(void); + +// freeze modes: +#define FREEZE_LOAD 0 +#define FREEZE_SAVE 1 +#define FREEZE_SIZE 2 + +typedef struct _GSdriverInfo { + char name[8]; + void *common; +} GSdriverInfo; + +#ifdef _WINDOWS_ +typedef struct _winInfo { // unsupported values must be set to zero + HWND hWnd; + HMENU hMenu; + HWND hStatusWnd; +} winInfo; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* GS plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef GSdefs + +// basic funcs + +s32 CALLBACK GSinit(); +s32 CALLBACK GSopen(void *pDsp, char *Title, int multithread); +void CALLBACK GSclose(); +void CALLBACK GSshutdown(); +void CALLBACK GSvsync(int field); +void CALLBACK GSgifTransfer1(u32 *pMem, u32 addr); +void CALLBACK GSgifTransfer2(u32 *pMem, u32 size); +void CALLBACK GSgifTransfer3(u32 *pMem, u32 size); +void CALLBACK GSgetLastTag(u64* ptag); // returns the last tag processed (64 bits) +void CALLBACK GSgifSoftReset(u32 mask); +void CALLBACK GSreadFIFO(u64 *mem); +void CALLBACK GSreadFIFO2(u64 *mem, int qwc); + +// extended funcs + +// GSkeyEvent gets called when there is a keyEvent from the PAD plugin +void CALLBACK GSkeyEvent(keyEvent *ev); +void CALLBACK GSchangeSaveState(int, const char* filename); +void CALLBACK GSmakeSnapshot(char *path); +void CALLBACK GSmakeSnapshot2(char *pathname, int* snapdone, int savejpg); +void CALLBACK GSirqCallback(void (*callback)()); +void CALLBACK GSprintf(int timeout, char *fmt, ...); +void CALLBACK GSsetBaseMem(void*); +void CALLBACK GSsetGameCRC(int crc, int gameoptions); + +// controls frame skipping in the GS, if this routine isn't present, frame skipping won't be done +void CALLBACK GSsetFrameSkip(int frameskip); + +// if start is 1, starts recording spu2 data, else stops +// returns a non zero value if successful +// for now, pData is not used +int CALLBACK GSsetupRecording(int start, void* pData); + +void CALLBACK GSreset(); +void CALLBACK GSwriteCSR(u32 value); +void CALLBACK GSgetDriverInfo(GSdriverInfo *info); +#ifdef _WIN32 +s32 CALLBACK GSsetWindowInfo(winInfo *info); +#endif +s32 CALLBACK GSfreeze(int mode, freezeData *data); +void CALLBACK GSconfigure(); +void CALLBACK GSabout(); +s32 CALLBACK GStest(); + +#endif + +/* PAD plugin API -=[ OBSOLETE ]=- */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef PADdefs + +// basic funcs + +s32 CALLBACK PADinit(u32 flags); +s32 CALLBACK PADopen(void *pDsp); +void CALLBACK PADclose(); +void CALLBACK PADshutdown(); +// PADkeyEvent is called every vsync (return NULL if no event) +keyEvent* CALLBACK PADkeyEvent(); +u8 CALLBACK PADstartPoll(int pad); +u8 CALLBACK PADpoll(u8 value); +// returns: 1 if supported pad1 +// 2 if supported pad2 +// 3 if both are supported +u32 CALLBACK PADquery(); + +// call to give a hint to the PAD plugin to query for the keyboard state. A +// good plugin will query the OS for keyboard state ONLY in this function. +// This function is necessary when multithreading because otherwise +// the PAD plugin can get into deadlocks with the thread that really owns +// the window (and input). Note that PADupdate can be called from a different +// thread than the other functions, so mutex or other multithreading primitives +// have to be added to maintain data integrity. +void CALLBACK PADupdate(int pad); + +// extended funcs + +void CALLBACK PADgsDriverInfo(GSdriverInfo *info); +void CALLBACK PADconfigure(); +void CALLBACK PADabout(); +s32 CALLBACK PADtest(); + +#endif + +/* SIO plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef SIOdefs + +// basic funcs + +s32 CALLBACK SIOinit(u32 port, u32 slot, SIOchangeSlotCB f); +s32 CALLBACK SIOopen(void *pDsp); +void CALLBACK SIOclose(); +void CALLBACK SIOshutdown(); +u8 CALLBACK SIOstartPoll(u8 value); +u8 CALLBACK SIOpoll(u8 value); +// returns: SIO_TYPE_{PAD,MTAP,RM,MC} +u32 CALLBACK SIOquery(); + +// extended funcs + +void CALLBACK SIOconfigure(); +void CALLBACK SIOabout(); +s32 CALLBACK SIOtest(); + +#endif + +/* SPU2 plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef SPU2defs + +// basic funcs + +s32 CALLBACK SPU2init(); +s32 CALLBACK SPU2open(void *pDsp); +void CALLBACK SPU2close(); +void CALLBACK SPU2shutdown(); +void CALLBACK SPU2write(u32 mem, u16 value); +u16 CALLBACK SPU2read(u32 mem); +void CALLBACK SPU2readDMA4Mem(u16 *pMem, int size); +void CALLBACK SPU2writeDMA4Mem(u16 *pMem, int size); +void CALLBACK SPU2interruptDMA4(); +void CALLBACK SPU2readDMA7Mem(u16* pMem, int size); +void CALLBACK SPU2writeDMA7Mem(u16 *pMem, int size); + +// all addresses passed by dma will be pointers to the array starting at baseaddr +// This function is necessary to successfully save and reload the spu2 state +void CALLBACK SPU2setDMABaseAddr(uptr baseaddr); + +void CALLBACK SPU2interruptDMA7(); +u32 CALLBACK SPU2ReadMemAddr(int core); +void CALLBACK SPU2WriteMemAddr(int core,u32 value); +void CALLBACK SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)()); + +// extended funcs +// if start is 1, starts recording spu2 data, else stops +// returns a non zero value if successful +// for now, pData is not used +int CALLBACK SPU2setupRecording(int start, void* pData); + +void CALLBACK SPU2setClockPtr(u32* ptr); +void CALLBACK SPU2setTimeStretcher(short int enable); + +void CALLBACK SPU2async(u32 cycles); +s32 CALLBACK SPU2freeze(int mode, freezeData *data); +void CALLBACK SPU2configure(); +void CALLBACK SPU2about(); +s32 CALLBACK SPU2test(); + +#endif + +/* CDVD plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef CDVDdefs + +// basic funcs + +s32 CALLBACK CDVDinit(); +s32 CALLBACK CDVDopen(const char* pTitleFilename); +void CALLBACK CDVDclose(); +void CALLBACK CDVDshutdown(); +s32 CALLBACK CDVDreadTrack(u32 lsn, int mode); + +// return can be NULL (for async modes) +u8* CALLBACK CDVDgetBuffer(); + +s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ* subq);//read subq from disc (only cds have subq data) +s32 CALLBACK CDVDgetTN(cdvdTN *Buffer); //disk information +s32 CALLBACK CDVDgetTD(u8 Track, cdvdTD *Buffer); //track info: min,sec,frame,type +s32 CALLBACK CDVDgetTOC(void* toc); //gets ps2 style toc from disc +s32 CALLBACK CDVDgetDiskType(); //CDVD_TYPE_xxxx +s32 CALLBACK CDVDgetTrayStatus(); //CDVD_TRAY_xxxx +s32 CALLBACK CDVDctrlTrayOpen(); //open disc tray +s32 CALLBACK CDVDctrlTrayClose(); //close disc tray + +// extended funcs + +void CALLBACK CDVDconfigure(); +void CALLBACK CDVDabout(); +s32 CALLBACK CDVDtest(); +void CALLBACK CDVDnewDiskCB(void (*callback)()); + +#endif + +/* DEV9 plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef DEV9defs + +// basic funcs + +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +s32 CALLBACK DEV9init(); +s32 CALLBACK DEV9open(void *pDsp); +void CALLBACK DEV9close(); +void CALLBACK DEV9shutdown(); +u8 CALLBACK DEV9read8(u32 addr); +u16 CALLBACK DEV9read16(u32 addr); +u32 CALLBACK DEV9read32(u32 addr); +void CALLBACK DEV9write8(u32 addr, u8 value); +void CALLBACK DEV9write16(u32 addr, u16 value); +void CALLBACK DEV9write32(u32 addr, u32 value); +void CALLBACK DEV9readDMA8Mem(u32 *pMem, int size); +void CALLBACK DEV9writeDMA8Mem(u32 *pMem, int size); +// cycles = IOP cycles before calling callback, +// if callback returns 1 the irq is triggered, else not +void CALLBACK DEV9irqCallback(DEV9callback callback); +DEV9handler CALLBACK DEV9irqHandler(void); + +// extended funcs + +s32 CALLBACK DEV9freeze(int mode, freezeData *data); +void CALLBACK DEV9configure(); +void CALLBACK DEV9about(); +s32 CALLBACK DEV9test(); + +#endif + +/* USB plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef USBdefs + +// basic funcs + +s32 CALLBACK USBinit(); +s32 CALLBACK USBopen(void *pDsp); +void CALLBACK USBclose(); +void CALLBACK USBshutdown(); +u8 CALLBACK USBread8(u32 addr); +u16 CALLBACK USBread16(u32 addr); +u32 CALLBACK USBread32(u32 addr); +void CALLBACK USBwrite8(u32 addr, u8 value); +void CALLBACK USBwrite16(u32 addr, u16 value); +void CALLBACK USBwrite32(u32 addr, u32 value); +void CALLBACK USBasync(u32 cycles); + +// cycles = IOP cycles before calling callback, +// if callback returns 1 the irq is triggered, else not +void CALLBACK USBirqCallback(USBcallback callback); +USBhandler CALLBACK USBirqHandler(void); +void CALLBACK USBsetRAM(void *mem); + +// extended funcs + +s32 CALLBACK USBfreeze(int mode, freezeData *data); +void CALLBACK USBconfigure(); +void CALLBACK USBabout(); +s32 CALLBACK USBtest(); + +#endif + +/* FW plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef FWdefs +// basic funcs + +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +s32 CALLBACK FWinit(); +s32 CALLBACK FWopen(void *pDsp); +void CALLBACK FWclose(); +void CALLBACK FWshutdown(); +u32 CALLBACK FWread32(u32 addr); +void CALLBACK FWwrite32(u32 addr, u32 value); +void CALLBACK FWirqCallback(void (*callback)()); + +// extended funcs + +s32 CALLBACK FWfreeze(int mode, freezeData *data); +void CALLBACK FWconfigure(); +void CALLBACK FWabout(); +s32 CALLBACK FWtest(); +#endif + +// might be useful for emulators +#ifdef PLUGINtypedefs + +typedef u32 (CALLBACK* _PS2EgetLibType)(void); +typedef u32 (CALLBACK* _PS2EgetLibVersion2)(u32 type); +typedef char*(CALLBACK* _PS2EgetLibName)(void); + +// GS +// NOTE: GSreadFIFOX/GSwriteCSR functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _GSinit)(); +typedef s32 (CALLBACK* _GSopen)(void *pDsp, char *Title, int multithread); +typedef void (CALLBACK* _GSclose)(); +typedef void (CALLBACK* _GSshutdown)(); +typedef void (CALLBACK* _GSvsync)(int field); +typedef void (CALLBACK* _GSgifTransfer1)(u32 *pMem, u32 addr); +typedef void (CALLBACK* _GSgifTransfer2)(u32 *pMem, u32 size); +typedef void (CALLBACK* _GSgifTransfer3)(u32 *pMem, u32 size); +typedef void (CALLBACK* _GSgetLastTag)(u64* ptag); // returns the last tag processed (64 bits) +typedef void (CALLBACK* _GSgifSoftReset)(u32 mask); +typedef void (CALLBACK* _GSreadFIFO)(u64 *pMem); +typedef void (CALLBACK* _GSreadFIFO2)(u64 *pMem, int qwc); + +typedef void (CALLBACK* _GSkeyEvent)(keyEvent* ev); +typedef void (CALLBACK* _GSchangeSaveState)(int, const char* filename); +typedef void (CALLBACK* _GSirqCallback)(void (*callback)()); +typedef void (CALLBACK* _GSprintf)(int timeout, char *fmt, ...); +typedef void (CALLBACK* _GSsetBaseMem)(void*); +typedef void (CALLBACK* _GSsetGameCRC)(int, int); +typedef void (CALLBACK* _GSsetFrameSkip)(int frameskip); +typedef int (CALLBACK* _GSsetupRecording)(int, void*); +typedef void (CALLBACK* _GSreset)(); +typedef void (CALLBACK* _GSwriteCSR)(u32 value); +typedef void (CALLBACK* _GSgetDriverInfo)(GSdriverInfo *info); +#ifdef _WINDOWS_ +typedef s32 (CALLBACK* _GSsetWindowInfo)(winInfo *info); +#endif +typedef void (CALLBACK* _GSmakeSnapshot)(const char *path); +typedef void (CALLBACK* _GSmakeSnapshot2)(const char *path, int*, int); +typedef s32 (CALLBACK* _GSfreeze)(int mode, freezeData *data); +typedef void (CALLBACK* _GSconfigure)(); +typedef s32 (CALLBACK* _GStest)(); +typedef void (CALLBACK* _GSabout)(); + +// PAD +typedef s32 (CALLBACK* _PADinit)(u32 flags); +typedef s32 (CALLBACK* _PADopen)(void *pDsp); +typedef void (CALLBACK* _PADclose)(); +typedef void (CALLBACK* _PADshutdown)(); +typedef keyEvent* (CALLBACK* _PADkeyEvent)(); +typedef u8 (CALLBACK* _PADstartPoll)(int pad); +typedef u8 (CALLBACK* _PADpoll)(u8 value); +typedef u32 (CALLBACK* _PADquery)(); +typedef void (CALLBACK* _PADupdate)(int pad); + +typedef void (CALLBACK* _PADgsDriverInfo)(GSdriverInfo *info); +typedef void (CALLBACK* _PADconfigure)(); +typedef s32 (CALLBACK* _PADtest)(); +typedef void (CALLBACK* _PADabout)(); + +// SIO +typedef s32 (CALLBACK* _SIOinit)(u32 port, u32 slot, SIOchangeSlotCB f); +typedef s32 (CALLBACK* _SIOopen)(void *pDsp); +typedef void (CALLBACK* _SIOclose)(); +typedef void (CALLBACK* _SIOshutdown)(); +typedef u8 (CALLBACK* _SIOstartPoll)(u8 value); +typedef u8 (CALLBACK* _SIOpoll)(u8 value); +typedef u32 (CALLBACK* _SIOquery)(); + +typedef void (CALLBACK* _SIOconfigure)(); +typedef s32 (CALLBACK* _SIOtest)(); +typedef void (CALLBACK* _SIOabout)(); + +// SPU2 +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _SPU2init)(); +typedef s32 (CALLBACK* _SPU2open)(void *pDsp); +typedef void (CALLBACK* _SPU2close)(); +typedef void (CALLBACK* _SPU2shutdown)(); +typedef void (CALLBACK* _SPU2write)(u32 mem, u16 value); +typedef u16 (CALLBACK* _SPU2read)(u32 mem); +typedef void (CALLBACK* _SPU2readDMA4Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2writeDMA4Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2interruptDMA4)(); +typedef void (CALLBACK* _SPU2readDMA7Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2writeDMA7Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2setDMABaseAddr)(uptr baseaddr); +typedef void (CALLBACK* _SPU2interruptDMA7)(); +typedef void (CALLBACK* _SPU2irqCallback)(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)()); +typedef int (CALLBACK* _SPU2setupRecording)(int, void*); + +typedef void (CALLBACK* _SPU2setClockPtr)(u32*ptr); +typedef void (CALLBACK* _SPU2setTimeStretcher)(short int enable); + +typedef u32 (CALLBACK* _SPU2ReadMemAddr)(int core); +typedef void (CALLBACK* _SPU2WriteMemAddr)(int core,u32 value); +typedef void (CALLBACK* _SPU2async)(u32 cycles); +typedef s32 (CALLBACK* _SPU2freeze)(int mode, freezeData *data); +typedef void (CALLBACK* _SPU2configure)(); +typedef s32 (CALLBACK* _SPU2test)(); +typedef void (CALLBACK* _SPU2about)(); + + +// CDVD +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _CDVDinit)(); +typedef s32 (CALLBACK* _CDVDopen)(const char* pTitleFilename); +typedef void (CALLBACK* _CDVDclose)(); +typedef void (CALLBACK* _CDVDshutdown)(); +typedef s32 (CALLBACK* _CDVDreadTrack)(u32 lsn, int mode); +typedef u8* (CALLBACK* _CDVDgetBuffer)(); +typedef s32 (CALLBACK* _CDVDreadSubQ)(u32 lsn, cdvdSubQ* subq); +typedef s32 (CALLBACK* _CDVDgetTN)(cdvdTN *Buffer); +typedef s32 (CALLBACK* _CDVDgetTD)(u8 Track, cdvdTD *Buffer); +typedef s32 (CALLBACK* _CDVDgetTOC)(void* toc); +typedef s32 (CALLBACK* _CDVDgetDiskType)(); +typedef s32 (CALLBACK* _CDVDgetTrayStatus)(); +typedef s32 (CALLBACK* _CDVDctrlTrayOpen)(); +typedef s32 (CALLBACK* _CDVDctrlTrayClose)(); + +typedef void (CALLBACK* _CDVDconfigure)(); +typedef s32 (CALLBACK* _CDVDtest)(); +typedef void (CALLBACK* _CDVDabout)(); +typedef void (CALLBACK* _CDVDnewDiskCB)(void (*callback)()); + +// DEV9 +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _DEV9init)(); +typedef s32 (CALLBACK* _DEV9open)(void *pDsp); +typedef void (CALLBACK* _DEV9close)(); +typedef void (CALLBACK* _DEV9shutdown)(); +typedef u8 (CALLBACK* _DEV9read8)(u32 mem); +typedef u16 (CALLBACK* _DEV9read16)(u32 mem); +typedef u32 (CALLBACK* _DEV9read32)(u32 mem); +typedef void (CALLBACK* _DEV9write8)(u32 mem, u8 value); +typedef void (CALLBACK* _DEV9write16)(u32 mem, u16 value); +typedef void (CALLBACK* _DEV9write32)(u32 mem, u32 value); +typedef void (CALLBACK* _DEV9readDMA8Mem)(u32 *pMem, int size); +typedef void (CALLBACK* _DEV9writeDMA8Mem)(u32 *pMem, int size); +typedef void (CALLBACK* _DEV9irqCallback)(DEV9callback callback); +typedef DEV9handler (CALLBACK* _DEV9irqHandler)(void); + +typedef s32 (CALLBACK* _DEV9freeze)(int mode, freezeData *data); +typedef void (CALLBACK* _DEV9configure)(); +typedef s32 (CALLBACK* _DEV9test)(); +typedef void (CALLBACK* _DEV9about)(); + +// USB +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _USBinit)(); +typedef s32 (CALLBACK* _USBopen)(void *pDsp); +typedef void (CALLBACK* _USBclose)(); +typedef void (CALLBACK* _USBshutdown)(); +typedef u8 (CALLBACK* _USBread8)(u32 mem); +typedef u16 (CALLBACK* _USBread16)(u32 mem); +typedef u32 (CALLBACK* _USBread32)(u32 mem); +typedef void (CALLBACK* _USBwrite8)(u32 mem, u8 value); +typedef void (CALLBACK* _USBwrite16)(u32 mem, u16 value); +typedef void (CALLBACK* _USBwrite32)(u32 mem, u32 value); +typedef void (CALLBACK* _USBasync)(u32 cycles); + + +typedef void (CALLBACK* _USBirqCallback)(USBcallback callback); +typedef USBhandler (CALLBACK* _USBirqHandler)(void); +typedef void (CALLBACK* _USBsetRAM)(void *mem); + +typedef s32 (CALLBACK* _USBfreeze)(int mode, freezeData *data); +typedef void (CALLBACK* _USBconfigure)(); +typedef s32 (CALLBACK* _USBtest)(); +typedef void (CALLBACK* _USBabout)(); + +//FW +typedef s32 (CALLBACK* _FWinit)(); +typedef s32 (CALLBACK* _FWopen)(void *pDsp); +typedef void (CALLBACK* _FWclose)(); +typedef void (CALLBACK* _FWshutdown)(); +typedef u32 (CALLBACK* _FWread32)(u32 mem); +typedef void (CALLBACK* _FWwrite32)(u32 mem, u32 value); +typedef void (CALLBACK* _FWirqCallback)(void (*callback)()); + +typedef s32 (CALLBACK* _FWfreeze)(int mode, freezeData *data); +typedef void (CALLBACK* _FWconfigure)(); +typedef s32 (CALLBACK* _FWtest)(); +typedef void (CALLBACK* _FWabout)(); + +#endif + +#ifdef PLUGINfuncs + +// GS +extern _GSinit GSinit; +extern _GSopen GSopen; +extern _GSclose GSclose; +extern _GSshutdown GSshutdown; +extern _GSvsync GSvsync; +extern _GSgifTransfer1 GSgifTransfer1; +extern _GSgifTransfer2 GSgifTransfer2; +extern _GSgifTransfer3 GSgifTransfer3; +extern _GSgetLastTag GSgetLastTag; +extern _GSgifSoftReset GSgifSoftReset; +extern _GSreadFIFO GSreadFIFO; +extern _GSreadFIFO2 GSreadFIFO2; + +extern _GSkeyEvent GSkeyEvent; +extern _GSchangeSaveState GSchangeSaveState; +extern _GSmakeSnapshot GSmakeSnapshot; +extern _GSmakeSnapshot2 GSmakeSnapshot2; +extern _GSirqCallback GSirqCallback; +extern _GSprintf GSprintf; +extern _GSsetBaseMem GSsetBaseMem; +extern _GSsetGameCRC GSsetGameCRC; +extern _GSsetFrameSkip GSsetFrameSkip; +extern _GSsetupRecording GSsetupRecording; +extern _GSreset GSreset; +extern _GSwriteCSR GSwriteCSR; +extern _GSgetDriverInfo GSgetDriverInfo; +#ifdef _WINDOWS_ +extern _GSsetWindowInfo GSsetWindowInfo; +#endif +extern _GSfreeze GSfreeze; +extern _GSconfigure GSconfigure; +extern _GStest GStest; +extern _GSabout GSabout; + +// PAD1 +extern _PADinit PAD1init; +extern _PADopen PAD1open; +extern _PADclose PAD1close; +extern _PADshutdown PAD1shutdown; +extern _PADkeyEvent PAD1keyEvent; +extern _PADstartPoll PAD1startPoll; +extern _PADpoll PAD1poll; +extern _PADquery PAD1query; +extern _PADupdate PAD1update; + +extern _PADgsDriverInfo PAD1gsDriverInfo; +extern _PADconfigure PAD1configure; +extern _PADtest PAD1test; +extern _PADabout PAD1about; + +// PAD2 +extern _PADinit PAD2init; +extern _PADopen PAD2open; +extern _PADclose PAD2close; +extern _PADshutdown PAD2shutdown; +extern _PADkeyEvent PAD2keyEvent; +extern _PADstartPoll PAD2startPoll; +extern _PADpoll PAD2poll; +extern _PADquery PAD2query; +extern _PADupdate PAD2update; + +extern _PADgsDriverInfo PAD2gsDriverInfo; +extern _PADconfigure PAD2configure; +extern _PADtest PAD2test; +extern _PADabout PAD2about; + +// SIO[2] +extern _SIOinit SIOinit[2][9]; +extern _SIOopen SIOopen[2][9]; +extern _SIOclose SIOclose[2][9]; +extern _SIOshutdown SIOshutdown[2][9]; +extern _SIOstartPoll SIOstartPoll[2][9]; +extern _SIOpoll SIOpoll[2][9]; +extern _SIOquery SIOquery[2][9]; + +extern _SIOconfigure SIOconfigure[2][9]; +extern _SIOtest SIOtest[2][9]; +extern _SIOabout SIOabout[2][9]; + +// SPU2 +extern _SPU2init SPU2init; +extern _SPU2open SPU2open; +extern _SPU2close SPU2close; +extern _SPU2shutdown SPU2shutdown; +extern _SPU2write SPU2write; +extern _SPU2read SPU2read; +extern _SPU2readDMA4Mem SPU2readDMA4Mem; +extern _SPU2writeDMA4Mem SPU2writeDMA4Mem; +extern _SPU2interruptDMA4 SPU2interruptDMA4; +extern _SPU2readDMA7Mem SPU2readDMA7Mem; +extern _SPU2writeDMA7Mem SPU2writeDMA7Mem; +extern _SPU2setDMABaseAddr SPU2setDMABaseAddr; +extern _SPU2interruptDMA7 SPU2interruptDMA7; +extern _SPU2ReadMemAddr SPU2ReadMemAddr; +extern _SPU2setupRecording SPU2setupRecording; +extern _SPU2WriteMemAddr SPU2WriteMemAddr; +extern _SPU2irqCallback SPU2irqCallback; + +extern _SPU2setClockPtr SPU2setClockPtr; +extern _SPU2setTimeStretcher SPU2setTimeStretcher; + +extern _SPU2async SPU2async; +extern _SPU2freeze SPU2freeze; +extern _SPU2configure SPU2configure; +extern _SPU2test SPU2test; +extern _SPU2about SPU2about; + +// CDVD +extern _CDVDinit CDVDinit; +extern _CDVDopen CDVDopen; +extern _CDVDclose CDVDclose; +extern _CDVDshutdown CDVDshutdown; +extern _CDVDreadTrack CDVDreadTrack; +extern _CDVDgetBuffer CDVDgetBuffer; +extern _CDVDreadSubQ CDVDreadSubQ; +extern _CDVDgetTN CDVDgetTN; +extern _CDVDgetTD CDVDgetTD; +extern _CDVDgetTOC CDVDgetTOC; +extern _CDVDgetDiskType CDVDgetDiskType; +extern _CDVDgetTrayStatus CDVDgetTrayStatus; +extern _CDVDctrlTrayOpen CDVDctrlTrayOpen; +extern _CDVDctrlTrayClose CDVDctrlTrayClose; + +extern _CDVDconfigure CDVDconfigure; +extern _CDVDtest CDVDtest; +extern _CDVDabout CDVDabout; +extern _CDVDnewDiskCB CDVDnewDiskCB; + +// DEV9 +extern _DEV9init DEV9init; +extern _DEV9open DEV9open; +extern _DEV9close DEV9close; +extern _DEV9shutdown DEV9shutdown; +extern _DEV9read8 DEV9read8; +extern _DEV9read16 DEV9read16; +extern _DEV9read32 DEV9read32; +extern _DEV9write8 DEV9write8; +extern _DEV9write16 DEV9write16; +extern _DEV9write32 DEV9write32; +extern _DEV9readDMA8Mem DEV9readDMA8Mem; +extern _DEV9writeDMA8Mem DEV9writeDMA8Mem; +extern _DEV9irqCallback DEV9irqCallback; +extern _DEV9irqHandler DEV9irqHandler; + +extern _DEV9configure DEV9configure; +extern _DEV9freeze DEV9freeze; +extern _DEV9test DEV9test; +extern _DEV9about DEV9about; + +// USB +extern _USBinit USBinit; +extern _USBopen USBopen; +extern _USBclose USBclose; +extern _USBshutdown USBshutdown; +extern _USBread8 USBread8; +extern _USBread16 USBread16; +extern _USBread32 USBread32; +extern _USBwrite8 USBwrite8; +extern _USBwrite16 USBwrite16; +extern _USBwrite32 USBwrite32; +extern _USBasync USBasync; + +extern _USBirqCallback USBirqCallback; +extern _USBirqHandler USBirqHandler; +extern _USBsetRAM USBsetRAM; + +extern _USBconfigure USBconfigure; +extern _USBfreeze USBfreeze; +extern _USBtest USBtest; +extern _USBabout USBabout; + +// FW +extern _FWinit FWinit; +extern _FWopen FWopen; +extern _FWclose FWclose; +extern _FWshutdown FWshutdown; +extern _FWread32 FWread32; +extern _FWwrite32 FWwrite32; +extern _FWirqCallback FWirqCallback; + +extern _FWconfigure FWconfigure; +extern _FWfreeze FWfreeze; +extern _FWtest FWtest; +extern _FWabout FWabout; +#endif + +#ifdef __cplusplus +} // End extern "C" +#endif + +#endif /* __PS2EDEFS_H__ */ diff --git a/pcsx2/common/PS2Etypes.h b/pcsx2/common/PS2Etypes.h new file mode 100644 index 0000000000..59be1c3de4 --- /dev/null +++ b/pcsx2/common/PS2Etypes.h @@ -0,0 +1,219 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +#ifndef __PS2ETYPES_H__ +#define __PS2ETYPES_H__ + +#if defined (__linux__) && !defined(__LINUX__) // some distributions are lower case +#define __LINUX__ +#endif + +#ifdef __CYGWIN__ +#define __LINUX__ +#endif + +#ifndef ARRAYSIZE +#define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0])) +#endif + +#ifdef __LINUX__ +#define CALLBACK +#else +#define CALLBACK __stdcall +#endif + + +// jASSUME - give hints to the optimizer +// This is primarily useful for the default case switch optimizer, which enables VC to +// generate more compact switches. + +#ifdef NDEBUG +# define jBREAKPOINT() ((void) 0) +# ifdef _MSC_VER +# define jASSUME(exp) (__assume(exp)) +# else +# define jASSUME(exp) ((void) sizeof(exp)) +# endif +#else +# if defined(_MSC_VER) +# define jBREAKPOINT() do { __asm int 3 } while(0) +# else +# define jBREAKPOINT() ((void) *(volatile char *) 0) +# endif +# define jASSUME(exp) if(exp) ; else jBREAKPOINT() +#endif + +// disable the default case in a switch +#define jNO_DEFAULT \ +{ \ + break; \ + \ +default: \ + jASSUME(0); \ + break; \ +} + + +// Basic types +#if defined(_MSC_VER) + +typedef __int8 s8; +typedef __int16 s16; +typedef __int32 s32; +typedef __int64 s64; + +typedef unsigned __int8 u8; +typedef unsigned __int16 u16; +typedef unsigned __int32 u32; +typedef unsigned __int64 u64; + +typedef unsigned int uint; + +#define PCSX2_ALIGNED(alig,x) __declspec(align(alig)) x +#define PCSX2_ALIGNED16(x) __declspec(align(16)) x +#define PCSX2_ALIGNED16_DECL(x) __declspec(align(16)) x + +#define __naked __declspec(naked) + +#else // _MSC_VER + +#ifdef __LINUX__ + +#ifdef HAVE_STDINT_H +#include "stdint.h" + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef uintptr_t uptr; +typedef intptr_t sptr; + +#else // HAVE_STDINT_H + +typedef char s8; +typedef short s16; +typedef int s32; +typedef long long s64; + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +#endif // HAVE_STDINT_H + +typedef unsigned int uint; + +#define LONG long +typedef union _LARGE_INTEGER +{ + long long QuadPart; +} LARGE_INTEGER; + +#define __fastcall __attribute__((fastcall)) +#define __unused __attribute__((unused)) +#define _inline __inline__ __attribute__((unused)) +#define __forceinline __attribute__((always_inline,unused)) +#define __naked // GCC lacks the naked specifier + +#endif // __LINUX__ + +#define PCSX2_ALIGNED(alig,x) x __attribute((aligned(alig))) +#define PCSX2_ALIGNED16(x) x __attribute((aligned(16))) + +#define PCSX2_ALIGNED16_DECL(x) x + +#endif // _MSC_VER + +#if !defined(__LINUX__) || !defined(HAVE_STDINT_H) +#if defined(__x86_64__) +typedef u64 uptr; +typedef s64 sptr; +#else +typedef u32 uptr; +typedef s32 sptr; +#endif +#endif + +// A rough-and-ready cross platform 128-bit datatype, Non-SSE style. +#ifdef __cplusplus +struct u128 +{ + u64 lo; + u64 hi; + + // Implicit conversion from u64 + u128( u64 src ) : + lo( src ) + , hi( 0 ) {} + + // Implicit conversion from u32 + u128( u32 src ) : + lo( src ) + , hi( 0 ) {} +}; + +struct s128 +{ + s64 lo; + s64 hi; + + // Implicit conversion from u64 + s128( s64 src ) : + lo( src ) + , hi( 0 ) {} + + // Implicit conversion from u32 + s128( s32 src ) : + lo( src ) + , hi( 0 ) {} +}; + +#else + +typedef union _u128_t +{ + u64 lo; + u64 hi; +} u128; + +typedef union _s128_t +{ + s64 lo; + s64 hi; +} s128; + +#endif + +typedef struct { + int size; + s8 *data; +} freezeData; + +/* common defines */ +#ifndef C_ASSERT +#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +#endif + +#endif /* __PS2ETYPES_H__ */ diff --git a/pcsx2/configure.ac b/pcsx2/configure.ac new file mode 100644 index 0000000000..3e75ef8045 --- /dev/null +++ b/pcsx2/configure.ac @@ -0,0 +1,138 @@ +AC_INIT(pcsx2,0.9.5,zerofrog@gmail.com) +AM_INIT_AUTOMAKE(pcsx2,0.9.5) + +AC_PROG_CC([gcc g++ cl KCC CC cxx cc++ xlC aCC c++]) +AC_PROG_CXX([gcc g++ cl KCC CC cxx cc++ xlC aCC c++]) +AC_PROG_CPP([gcc g++ cl KCC CC cxx cc++ xlC aCC c++]) + +AC_PROG_RANLIB + +dnl necessary for compiling assembly +AM_PROG_AS + +svnrev="`svn info | grep Revision:`" + +if test -n "${svnrev}" ; then + AC_REVISION("${svnrev}") + AC_DEFINE_UNQUOTED(SVN_REV,"${svnrev}", + [Define to be the subversion revision number]) +else + AC_REVISION([$Revision: 0 $]) + AC_DEFINE(SVN_REV,"$Revision: 0 $", + [Define to be the subversion revision number]) +fi + +AC_ARG_ENABLE(customcflags, AC_HELP_STRING([--enable-customcflags], [Use custom CFLAGS]), +customcflags=$enableval,customcflags=no) + +if test "x$customcflags" == xno +then +CFLAGS= +CPPFLAGS= +CXXFLAGS= +CCASFLAGS= +else +DEBUG_FLAGS=" -O0 -g " +fi + +WARNING_FLAGS="-Wall -Wno-format -Wno-unused-value" +#Pcsx2 now crashes if --fomit-frame-pointer is enabled and MTGS is off +NORMAL_FLAGS=" -pipe -O3 -fno-omit-frame-pointer -msse " +DEBUG_FLAGS+=" -g -msse ${WARNING_FLAGS} " + +dnl Check for debug build +AC_MSG_CHECKING(debug build) +AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [debug build]), + debug=$enableval,debug=no) +if test "x$debug" == xyes +then + AC_DEFINE(_DEBUG,1,[_DEBUG]) + CFLAGS+=" ${DEBUG_FLAGS} -fpermissive -Xlinker -zmuldefs " + CXXFLAGS+=" ${DEBUG_FLAGS} -fpermissive -Xlinker -zmuldefs " + CCASFLAGS+=" -D_DEBUG ${DEBUG_FLAGS} " + MYOBJDIR="Debug" +else + AC_DEFINE(NDEBUG,1,[NDEBUG]) + CFLAGS+=" ${NORMAL_FLAGS}" + CXXFLAGS+=" ${NORMAL_FLAGS} -fpermissive -Xlinker -zmuldefs " + MYOBJDIR="Release" +fi +AC_MSG_RESULT($debug) + +AC_CHECK_FUNCS([ _aligned_malloc _aligned_free ], AC_DEFINE(HAVE_ALIGNED_MALLOC)) + +dnl Check for dev build +AC_MSG_CHECKING(for development build) +AC_ARG_ENABLE(devbuild, AC_HELP_STRING([--enable-devbuild], [Special Build for developers that simplifies testing and adds extra checks]), + devbuild=$enableval,devbuild=no) +if test "x$devbuild" == xyes +then + AC_DEFINE(PCSX2_DEVBUILD,1,[PCSX2_DEVBUILD]) + MYOBJDIR=$MYOBJDIR"Dev" +fi +AC_MSG_RESULT($devbuild) + +AC_MSG_CHECKING(force sse3 instructions) +AC_ARG_ENABLE(sse3, AC_HELP_STRING([--enable-sse3], [Forces sse3 detection on CPUs]), + sse3=$enableval,sse3=no) +if test "x$sse3" == xyes +then + AC_DEFINE(PCSX2_FORCESSE3,1,[PCSX2_DEVBUILD]) +fi +AC_MSG_RESULT(sse3) + +AC_MSG_CHECKING(force sse4 instructions) +AC_ARG_ENABLE(sse4, AC_HELP_STRING([--enable-sse4], [Forces sse4 detection on CPUs]), + sse4=$enableval,sse4=no) +if test "x$sse4" == xyes +then + AC_DEFINE(PCSX2_FORCESSE4,1,[PCSX2_DEVBUILD]) +fi +AC_MSG_RESULT(sse4) + +dnl gtk +AC_MSG_CHECKING(gtk+) +AC_CHECK_PROG(GTK_CONFIG, pkg-config, pkg-config) +LIBS+=$(pkg-config --libs gtk+-2.0 gthread-2.0) + +dnl AC_CHECK_LIB(pthread,main,[LIBS="$LIBS -lpthread"]) +AC_CHECK_LIB(stdc++,main,[LIBS="$LIBS -lstdc++"]) +AC_CHECK_LIB(z,main,[LIBS="$LIBS -lz"]) + +AC_ARG_ENABLE(nls, AC_HELP_STRING([--disable-nls], [Disable nls support]), +nls=$enableval,nls=yes) +if test "x$nls" == xyes +then +AC_CHECK_HEADER([libintl.h], [AC_DEFINE(ENABLE_NLS,1,[__x86_64__])]) +fi + +AC_ARG_ENABLE(local-inis, AC_HELP_STRING([--enable-local-inis], [Enable local plugin inis]), +localinis=$enableval, localinis=no) +if test "x$localinis" == xyes +then +AC_DEFINE(LOCAL_PLUGIN_INIS) +fi + +AC_OUTPUT([ + Makefile + DebugTools/Makefile + Linux/Makefile + IPU/Makefile + IPU/mpeg2lib/Makefile + RDebug/Makefile + tinyxml/Makefile + x86/Makefile + x86/ix86/Makefile + 3rdparty/zlib/Makefile + ]) + +dnl bindir = pcsx2exe + +echo "Configuration:" +echo " Target system type: $target" +echo " Debug build? $debug" +echo " Dev build? $devbuild" +echo " Force sse3? $sse3" +echo " nls support? $nls" +echo " local plugin inis? $localinis" +echo " custom cflags? $customcflags" diff --git a/pcsx2/depcomp b/pcsx2/depcomp new file mode 100644 index 0000000000..88b3a13969 --- /dev/null +++ b/pcsx2/depcomp @@ -0,0 +1,529 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2005-05-14.22 + +# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + +# 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, 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. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by `PROGRAMS ARGS'. + object Object file output by `PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputing dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts `$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + stat=$? + + if test -f "$tmpdepfile"; then : + else + stripped=`echo "$stripped" | sed 's,^.*/,,'` + tmpdepfile="$stripped.u" + fi + + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + outname="$stripped.o" + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +icc) + # Intel's C compiler understands `-MD -MF file'. However on + # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want: + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using \ : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | + sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + # With Tru64 cc, shared objects can also be used to make a + # static library. This mecanism is used in libtool 1.4 series to + # handle both shared and static libraries in a single compilation. + # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. + # + # With libtool 1.5 this exception was removed, and libtool now + # generates 2 separate objects for the 2 libraries. These two + # compilations output dependencies in in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 + tmpdepfile2=$dir$base.o.d # libtool 1.5 + tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 + tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.o.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + tmpdepfile4=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for `:' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + "$@" $dashmflag | + sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no + for arg in "$@"; do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + "$@" || exit $? + IFS=" " + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/pcsx2/install-sh b/pcsx2/install-sh new file mode 100644 index 0000000000..4d4a9519ea --- /dev/null +++ b/pcsx2/install-sh @@ -0,0 +1,323 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2005-05-14.22 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit $?;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit $?;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + shift + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit 1 + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit 1; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit 0 +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/pcsx2/missing b/pcsx2/missing new file mode 100644 index 0000000000..894e786e16 --- /dev/null +++ b/pcsx2/missing @@ -0,0 +1,360 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2005-06-08.21 + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# 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, 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. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + +esac + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). +case "$1" in + lex|yacc) + # Not GNU programs, they don't have --version. + ;; + + tar) + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + exit 1 + fi + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi + # If the file does not exist, the user really needs makeinfo; + # let's fail without touching anything. + test -f $file || exit 1 + touch $file + ;; + + tar) + shift + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/pcsx2/mkinstalldirs b/pcsx2/mkinstalldirs new file mode 100644 index 0000000000..259dbfcd35 --- /dev/null +++ b/pcsx2/mkinstalldirs @@ -0,0 +1,158 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy + +scriptversion=2005-06-29.22 + +# Original author: Noah Friedman +# Created: 1993-05-16 +# Public domain. +# +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +errstatus=0 +dirmode= + +usage="\ +Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... + +Create each directory DIR (with mode MODE, if specified), including all +leading file name components. + +Report bugs to ." + +# process command line arguments +while test $# -gt 0 ; do + case $1 in + -h | --help | --h*) # -h for help + echo "$usage" + exit $? + ;; + -m) # -m PERM arg + shift + test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } + dirmode=$1 + shift + ;; + --version) + echo "$0 $scriptversion" + exit $? + ;; + --) # stop option processing + shift + break + ;; + -*) # unknown option + echo "$usage" 1>&2 + exit 1 + ;; + *) # first non-opt arg + break + ;; + esac +done + +for file +do + if test -d "$file"; then + shift + else + break + fi +done + +case $# in + 0) exit 0 ;; +esac + +# Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and +# mkdir -p a/c at the same time, both will detect that a is missing, +# one will create a, then the other will try to create a and die with +# a "File exists" error. This is a problem when calling mkinstalldirs +# from a parallel make. We use --version in the probe to restrict +# ourselves to GNU mkdir, which is thread-safe. +case $dirmode in + '') + if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + echo "mkdir -p -- $*" + exec mkdir -p -- "$@" + else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + test -d ./-p && rmdir ./-p + test -d ./--version && rmdir ./--version + fi + ;; + *) + if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 && + test ! -d ./--version; then + echo "mkdir -m $dirmode -p -- $*" + exec mkdir -m "$dirmode" -p -- "$@" + else + # Clean up after NextStep and OpenStep mkdir. + for d in ./-m ./-p ./--version "./$dirmode"; + do + test -d $d && rmdir $d + done + fi + ;; +esac + +for file +do + case $file in + /*) pathcomp=/ ;; + *) pathcomp= ;; + esac + oIFS=$IFS + IFS=/ + set fnord $file + shift + IFS=$oIFS + + for d + do + test "x$d" = x && continue + + pathcomp=$pathcomp$d + case $pathcomp in + -*) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + else + if test ! -z "$dirmode"; then + echo "chmod $dirmode $pathcomp" + lasterr= + chmod "$dirmode" "$pathcomp" || lasterr=$? + + if test ! -z "$lasterr"; then + errstatus=$lasterr + fi + fi + fi + fi + + pathcomp=$pathcomp/ + done +done + +exit $errstatus + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/pcsx2/pcsxAbout.bmp b/pcsx2/pcsxAbout.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a5ceb9aa37d5d16e08a7097151f46ae5fb168e1d GIT binary patch literal 251190 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 z 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 + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, '\0' }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/pcsx2/tinyxml/tinystr.h b/pcsx2/tinyxml/tinystr.h new file mode 100644 index 0000000000..74b6b7e815 --- /dev/null +++ b/pcsx2/tinyxml/tinystr.h @@ -0,0 +1,319 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/pcsx2/tinyxml/tinyxml.cpp b/pcsx2/tinyxml/tinyxml.cpp new file mode 100644 index 0000000000..64067b4622 --- /dev/null +++ b/pcsx2/tinyxml/tinyxml.cpp @@ -0,0 +1,1866 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml.h" + + +bool TiXmlBase::condenseWhiteSpace = true; + +void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::DOCUMENT ) + { + delete node; + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return &node->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const char* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s ); + } + else { + *i = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const std::string* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s->c_str() ); + } + else { + *i = 0; + } + } + return s; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const char* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s ); + } + else { + *d = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const std::string* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s->c_str() ); + } + else { + *d = 0; + } + } + return s; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + std::ostringstream oss; + oss << val; + SetAttribute( name, oss.str() ); +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING _name( cname ); + TIXML_STRING _value( cvalue ); + #else + const char* _name = cname; + const char* _value = cvalue; + #endif + + TiXmlAttribute* node = attributeSet.Find( _name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + //StringToBuffer buf( value ); + + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. +// StringToBuffer buf( value ); +// +// if ( buf.buffer && SaveFile( buf.buffer ) ) +// return true; +// +// return false; + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = fopen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length == 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + delete [] buf; + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = fopen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorDesc = errorDesc.c_str (); + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + PutString( name, &n ); + PutString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( sscanf( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( sscanf( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + #else + sprintf (buf, "%lf", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + PutString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} +*/ +#endif + + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} +*/ + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + buffer += text.Value(); + } + else + { + DoIndent(); + buffer += text.Value(); + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/pcsx2/tinyxml/tinyxml.h b/pcsx2/tinyxml/tinyxml.h new file mode 100644 index 0000000000..d5df66864b --- /dev/null +++ b/pcsx2/tinyxml/tinyxml.h @@ -0,0 +1,1776 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SNSCANF _snscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 5; +const int TIXML_PATCH_VERSION = 2; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simple called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& doc ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& doc ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& element ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& declaration ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& text ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& comment ) { return true; } + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& unknown ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Puts a string to a stream, expanding entities as it goes. + // Note this should not contian the '<', '>', etc, or they will be transformed into entities! + static void PutString( const TIXML_STRING& str, TIXML_STRING* out ); + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* Find( const char* _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + #ifdef TIXML_USE_STL + const TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* Find( const std::string& _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + + #endif + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + #ifdef TIXML_USE_STL + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && LoadFile( f.buffer, encoding )); + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && SaveFile( f.buffer )); + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p ) + p += strlen( endTag ); + return p; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + p = ReadText( p, &value, false, endTag, false, encoding ); + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + +// int tabsize = 4; +// if ( document ) +// tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i + +#ifdef KERNEL +#define NOFLOAT +#endif + +#define ZEROPAD 1 // Pad with zero (not to be confused with Zero's PAD plugin) +#define SIGN 2 // Unsigned/signed long +#define PLUS 4 // Show plus +#define SPACE 8 // Space if plus +#define LEFT 16 // Left justified +#define SPECIAL 32 // 0x +#define LARGE 64 // Use 'ABCDEF' instead of 'abcdef' + +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +#ifdef __LINUX__ +#define _CVTBUFSIZE (309+40) +#endif + +static const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz"; +static const char *upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +#ifdef NEED_STRLEN +static size_t strnlen(const char *s, size_t count) +{ + const char *sc; + for (sc = s; *sc != '\0' && count--; ++sc); + return sc - s; +} +#endif + +static void cvt(char (&buf)[_CVTBUFSIZE], double arg, int preci, int& decpt, int& sign, int eflag) +{ + int r2; + double fi, fj; + char *p, *p1; + + r2 = 0; + sign = 0; + p = &buf[0]; + if (arg < 0) + { + sign = 1; + arg = -arg; + } + arg = modf(arg, &fi); + p1 = &buf[_CVTBUFSIZE]; + + if (fi != 0) + { + while (fi != 0) + { + fj = modf(fi / 10, &fi); + *--p1 = (int)((fj + .03) * 10) + '0'; + r2++; + } + while (p1 < &buf[_CVTBUFSIZE]) *p++ = *p1++; + } + else if (arg > 0) + { + while ((fj = arg * 10) < 1) + { + arg = fj; + r2--; + } + } + + p1 = &buf[preci]; + + if (eflag == 0) p1 += r2; + decpt = r2; + if (p1 < &buf[0]) + { + buf[0] = '\0'; + return; + } + while (p <= p1 && p < &buf[_CVTBUFSIZE]) + { + arg *= 10; + arg = modf(arg, &fj); + *p++ = (int) fj + '0'; + } + if (p1 >= &buf[_CVTBUFSIZE]) + { + buf[_CVTBUFSIZE - 1] = '\0'; + return; + } + p = p1; + *p1 += 5; + while (*p1 > '9') + { + *p1 = '0'; + if (p1 > buf) + ++*--p1; + else + { + *p1 = '1'; + decpt++; + if (eflag == 0) + { + if (p > buf) *p = '0'; + p++; + } + } + } + *p = '\0'; + return; +} + +static void ecvtbuf(char (&buf)[_CVTBUFSIZE], double arg, int preci, int& decpt, int& sign) +{ + cvt(buf, arg, preci, decpt, sign, 1); +} + +static void fcvtbuf(char (&buf)[_CVTBUFSIZE], double arg, int preci, int& decpt, int& sign) +{ + cvt(buf, arg, preci, decpt, sign, 0); +} + +static int skip_atoi(const char **s) +{ + int i = 0; + while (is_digit(**s)) i = i*10 + *((*s)++) - '0'; + return i; +} + +template +static void number(std::string& dest, T num, int base, int size, int precision, int type) +{ + char c, sign, tmp[88]; + const char *dig = digits; + int i; + + if (type & LARGE) dig = upper_digits; + if (type & LEFT) type &= ~ZEROPAD; + if (base < 2 || base > 36) return; + + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) + { + if (num < 0) + { + sign = '-'; + num = -num; + size--; + } + else if (type & PLUS) + { + sign = '+'; + size--; + } + else if (type & SPACE) + { + sign = ' '; + size--; + } + } + + if (type & SPECIAL) + { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + + i = 0; + + if (num == 0) + tmp[i++] = '0'; + else + { + while (num != 0) + { + tmp[i++] = dig[num % (uint) base]; + num = num / (uint) base; + } + } + + if (i > precision) precision = i; + size -= precision; + if (!(type & (ZEROPAD | LEFT))) while (size-- > 0) dest += ' '; + if (sign) dest += sign; + + if (type & SPECIAL) + { + if (base == 8) + dest += '0'; + else if (base == 16) + { + dest += '0'; + dest += digits[33]; + } + } + + if (!(type & LEFT)) while (size-- > 0) dest += c; + while (i < precision--) dest += '0'; + while (i-- > 0) dest += tmp[i]; + while (size-- > 0) dest += ' '; +} + +static void eaddr( std::string& dest, unsigned char *addr, int size, int precision, int type) +{ + char tmp[24]; + const char *dig = digits; + int i, len; + + if (type & LARGE) dig = upper_digits; + len = 0; + for (i = 0; i < 6; i++) + { + if (i != 0) tmp[len++] = ':'; + tmp[len++] = dig[addr[i] >> 4]; + tmp[len++] = dig[addr[i] & 0x0F]; + } + + if (!(type & LEFT)) while (len < size--) dest += ' '; + for (i = 0; i < len; ++i) dest += tmp[i]; + while (len < size--) dest += ' '; +} + +static void iaddr( std::string& dest, unsigned char *addr, int size, int precision, int type) +{ + char tmp[24]; + int i, n, len; + + len = 0; + for (i = 0; i < 4; i++) + { + if (i != 0) tmp[len++] = '.'; + n = addr[i]; + + if (n == 0) + tmp[len++] = digits[0]; + else + { + if (n >= 100) + { + tmp[len++] = digits[n / 100]; + n = n % 100; + tmp[len++] = digits[n / 10]; + n = n % 10; + } + else if (n >= 10) + { + tmp[len++] = digits[n / 10]; + n = n % 10; + } + + tmp[len++] = digits[n]; + } + } + + if (!(type & LEFT)) while (len < size--) dest += ' '; + for (i = 0; i < len; ++i) dest += tmp[i]; + while (len < size--) dest += ' '; +} + +#ifndef NOFLOAT + +static void cfltcvt(double value, char *buffer, char fmt, int precision) +{ + int decpt, sign, exp, pos; + char cvtbuf[_CVTBUFSIZE]; + int capexp = 0; + int magnitude; + + if (fmt == 'G' || fmt == 'E') + { + capexp = 1; + fmt += 'a' - 'A'; + } + + if (fmt == 'g') + { + ecvtbuf(cvtbuf, value, precision, decpt, sign); + + magnitude = decpt - 1; + if (magnitude < -4 || magnitude > precision - 1) + { + fmt = 'e'; + precision -= 1; + } + else + { + fmt = 'f'; + precision -= decpt; + } + } + + if (fmt == 'e') + { + ecvtbuf(cvtbuf, value, precision+1, decpt, sign); + + const char* digits = cvtbuf; + + if (sign) *buffer++ = '-'; + *buffer++ = *digits; + if (precision > 0) *buffer++ = '.'; + memcpy(buffer, digits + 1, precision); + buffer += precision; + *buffer++ = capexp ? 'E' : 'e'; + + if (decpt == 0) + { + if (value == 0.0) + exp = 0; + else + exp = -1; + } + else + exp = decpt - 1; + + if (exp < 0) + { + *buffer++ = '-'; + exp = -exp; + } + else + *buffer++ = '+'; + + buffer[2] = (exp % 10) + '0'; + exp = exp / 10; + buffer[1] = (exp % 10) + '0'; + exp = exp / 10; + buffer[0] = (exp % 10) + '0'; + buffer += 3; + } + else if (fmt == 'f') + { + fcvtbuf(cvtbuf, value, precision, decpt, sign); + + const char* digits = cvtbuf; + + if (sign) *buffer++ = '-'; + if (*digits) + { + if (decpt <= 0) + { + *buffer++ = '0'; + *buffer++ = '.'; + for (pos = 0; pos < -decpt; pos++) *buffer++ = '0'; + while (*digits) *buffer++ = *digits++; + } + else + { + pos = 0; + while (*digits) + { + if (pos++ == decpt) *buffer++ = '.'; + *buffer++ = *digits++; + } + } + } + else + { + *buffer++ = '0'; + if (precision > 0) + { + *buffer++ = '.'; + for (pos = 0; pos < precision; pos++) *buffer++ = '0'; + } + } + } + + *buffer = '\0'; +} + +static void forcdecpt(char *buffer) +{ + while (*buffer) + { + if (*buffer == '.') return; + if (*buffer == 'e' || *buffer == 'E') break; + buffer++; + } + + if (*buffer) + { + int n = strlen(buffer); + while (n > 0) + { + buffer[n + 1] = buffer[n]; + n--; + } + + *buffer = '.'; + } + else + { + *buffer++ = '.'; + *buffer = '\0'; + } +} + +static void cropzeros(char *buffer) +{ + char *stop; + + while (*buffer && *buffer != '.') buffer++; + if (*buffer++) + { + while (*buffer && *buffer != 'e' && *buffer != 'E') buffer++; + stop = buffer--; + while (*buffer == '0') buffer--; + if (*buffer == '.') buffer--; + while (*++buffer = *stop++); + } +} + +static void flt( std::string& dest, double num, int size, int precision, char fmt, int flags) +{ + char tmp[80]; + char c, sign; + int n, i; + + // Left align means no zero padding + if (flags & LEFT) flags &= ~ZEROPAD; + + // Determine padding and sign char + c = (flags & ZEROPAD) ? '0' : ' '; + sign = 0; + if (flags & SIGN) + { + if (num < 0.0) + { + sign = '-'; + num = -num; + size--; + } + else if (flags & PLUS) + { + sign = '+'; + size--; + } + else if (flags & SPACE) + { + sign = ' '; + size--; + } + } + + // Compute the precision value + if (precision < 0) + precision = 6; // Default precision: 6 + else if (precision == 0 && fmt == 'g') + precision = 1; // ANSI specified + + // Convert floating point number to text + cfltcvt(num, tmp, fmt, precision); + + // '#' and precision == 0 means force a decimal point + if ((flags & SPECIAL) && precision == 0) forcdecpt(tmp); + + // 'g' format means crop zero unless '#' given + if (fmt == 'g' && !(flags & SPECIAL)) cropzeros(tmp); + + n = strlen(tmp); + + // Output number with alignment and padding + size -= n; + if (!(flags & (ZEROPAD | LEFT))) while (size-- > 0) dest += ' '; + if (sign) dest += sign; + if (!(flags & LEFT)) while (size-- > 0) dest += c; + for (i = 0; i < n; i++) dest += tmp[i]; + while (size-- > 0) dest += ' '; +} + +#endif + +/////////////////////////////////////////////////////////////////////////// +// This is a "mostly" direct replacement for vsprintf, that is more secure and easier +// to use than vsnprintf or vsprintf_s. See the docs for ssprintf for usage notes. +void vssappendf(std::string& dest, const char* format, va_list args) +{ + int base; + + int flags; // Flags to number() + + int field_width; // Width of output field + int precision; // Min. # of digits for integers; max number of chars for from string + int qualifier; // 'h', 'l', or 'L' for integer fields + + // Optimization: Memory is cheap. Allocating it on the fly is not. Allocate more room + // than we'll likely need right upfront! + dest.reserve( strlen( format ) * 2 ); + + for( const char* fmt = format; *fmt; fmt++ ) + { + if (*fmt != '%') + { + dest += *fmt; + continue; + } + + // Process flags + flags = 0; +repeat: + fmt++; // This also skips first '%' + switch (*fmt) + { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + // Get field width + field_width = -1; + if (is_digit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') + { + fmt++; + field_width = va_arg(args, int); + if (field_width < 0) + { + field_width = -field_width; + flags |= LEFT; + } + } + + // Get the precision + precision = -1; + if (*fmt == '.') + { + ++fmt; + if (is_digit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') + { + ++fmt; + precision = va_arg(args, int); + } + if (precision < 0) precision = 0; + } + + // Get the conversion qualifier + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ) + { + qualifier = *fmt; + fmt++; + } + + // Default base + base = 10; + + switch (*fmt) + { + case 'c': + if (!(flags & LEFT)) while (--field_width > 0) dest += ' '; + dest += (unsigned char) va_arg(args, int); + while (--field_width > 0) dest += ' '; + continue; + + case 's': + { + // let's add support for std::string as a formatted parameter! (air) + if( qualifier == 'h' ) + { + static const string nullstring( "" ); + + const std::string* ss = va_arg(args, std::string*); + if( ss == NULL ) ss = &nullstring; + int len = ss->length(); + if( precision < 0 ) + { + // no precision override so just copy the whole string. + if (!(flags & LEFT)) while (len < field_width--) dest += ' '; + dest += *ss; + } + else + { + if( len > precision ) len = precision; + if (!(flags & LEFT)) while (len < field_width--) dest += ' '; + dest.append( ss->begin(), ss->begin()+len ); + } + while (len < field_width--) dest += ' '; + } + else + { + const char* s = va_arg(args, char *); + if (!s) s = ""; + + int len = strlen(s); + if( precision < 0 ) + { + if (!(flags & LEFT)) while (len < field_width--) dest += ' '; + dest += s; + } + else + { + if( len > precision ) len = precision; + if (!(flags & LEFT)) while (len < field_width--) dest += ' '; + dest.append( s, s+len ); + } + while (len < field_width--) dest += ' '; + } + } + continue; + + case 'p': + { + if (field_width == -1) + { + field_width = 2 * sizeof(void *); + flags |= ZEROPAD; + } + // use sptr as it avoids warnings during template code gen. + number( dest, (sptr) va_arg(args, void *), 16, field_width, precision, flags ); + } + continue; + + case 'n': + if (qualifier == 'l') + { + long *ip = va_arg(args, long *); + *ip = dest.length(); + } + else + { + int *ip = va_arg(args, int *); + *ip = dest.length(); + } + continue; + + // What the hell is %a? (air) + case 'A': + flags |= LARGE; + + case 'a': + if (qualifier == 'l') + eaddr(dest, va_arg(args, unsigned char *), field_width, precision, flags); + else + iaddr(dest, va_arg(args, unsigned char *), field_width, precision, flags); + continue; + + // Integer number formats - set up the flags and "break" + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + + case 'u': + break; + + #ifndef NOFLOAT + + case 'E': + case 'G': + case 'e': + case 'f': + case 'g': + flt(dest, va_arg(args, double), field_width, precision, *fmt, flags | SIGN); + continue; + + #endif + + default: + if (*fmt != '%') dest += '%'; + if (*fmt) + dest += *fmt; + else + --fmt; + continue; + } + + if (qualifier == 'L') + { + // 64-bit integer support! (air) + number(dest, va_arg(args, s64), base, field_width, precision, flags); + } + else + { + s32 num; + if (qualifier == 'h') + num = va_arg(args, s16); + else // 'l' qualifier or no qualifier means 32 bits on all our std target platforms. + num = va_arg(args, s32); + + number(dest, num, base, field_width, precision, flags); + } + } +} + +void vssprintf( std::string& dest, const char* format, va_list args ) +{ + dest.clear(); + vssappendf( dest, format, args ); +} + +void ssappendf( std::string& dest, const char* format, ...) +{ + va_list args; + va_start(args, format); + vssappendf( dest, format, args ); + va_end(args); +} + +// This is a "mostly" direct replacement for sprintf, based on std::string. +// The most notable difference in use is the requirement of a "params" keyword delimiting +// the format string from the parameters used to fill the string's tokens. It looks +// like this in practice: +// +// ssprintf( dest, "Yo Joe, %d. In the Hizzou %s.", params intval, strval ); +// +// In addition to all standard printf formatting tokens, ssprintf also supports a new token +// for std::string parameters as %hs (passed by reference/pointer). I opted for %hs (using 'h' +// as a qualifier) over %S because under MSVC %S acts as a char/widechar conversion. Note +// that these are passed by pointer so you *must* use the & operator most of the time. +// Example: +// +// ssprintf( dest, "Yo Joe, %hs.", params &strval ); +// +// This can be a cavet of sorts since forgetting to use the & will always compile but +// will cause undefined behavior and odd crashes (much like how the same thing happens +// when exchanging an intvalu for a c-string normally -- it's just more tricky with +// strings since we're not used to prefixing sprintf parameters with &s). +// +// === 64-bit -- s64 / u64 -- Support === +// +// ssprintf supports u64/s64 via the L qualifier, which can be prefixed to any one of the +// integer tokens (d, i, x). This isn't standard, but it's easy and doesn't conflict with +// anything, and none of the other 64-bit qualifiers aren't really standard anyway. +// Example: +// +// ssprintf( dest, "Yo Joe, %Ld, %Lx.", params int64, hex64 ); +// +void ssprintf(std::string& str, const char* fmt, ...) +{ + //varg_assert(); + + va_list args; + va_start(args, fmt); + vssprintf(str, fmt, args); + va_end(args); +} + +// See ssprintf for usage details and differences from sprintf formatting. +std::string fmt_string( const char* fmt, ... ) +{ + //varg_assert(); + + std::string retval; + va_list args; + va_start( args, fmt ); + vssprintf( retval, fmt, args ); + va_end( args ); + + return retval; +} + +std::string vfmt_string( const char* fmt, va_list args ) +{ + //varg_assert(); + + std::string retval; + vssprintf( retval, fmt, args ); + + return retval; +} diff --git a/pcsx2/vtlb.cpp b/pcsx2/vtlb.cpp new file mode 100644 index 0000000000..cb78e41222 --- /dev/null +++ b/pcsx2/vtlb.cpp @@ -0,0 +1,591 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +/* + EE physical map : + [0000 0000,1000 0000) -> Ram (mirrored ?) + [1000 0000,1400 0000) -> Registers + [1400 0000,1fc0 0000) -> Reserved (ingored writes, 'random' reads) + [1fc0 0000,2000 0000) -> Boot ROM + + [2000 0000,4000 0000) -> Unmapped (BUS ERROR) + [4000 0000,8000 0000) -> "Extended memory", probably unmapped (BUS ERROR) on retail ps2's :) + [8000 0000,FFFF FFFF] -> Unmapped (BUS ERROR) + + vtlb/phy only supports the [0000 0000,2000 0000) region, with 4k pages. + vtlb/vmap supports mapping to either of these locations, or some other (externaly) specified address. +*/ + +#include "PrecompiledHeader.h" + +#include "Common.h" +#include "vtlb.h" +#include "COP0.h" + +using namespace R5900; +using namespace vtlb_private; + +#ifdef PCSX2_DEVBUILD +#define verify(x) {if (!(x)) { (*(u8*)0)=3; }} +#else +#define verify jASSUME +#endif + +namespace vtlb_private +{ + s32 pmap[VTLB_PMAP_ITEMS]; //512KB + s32 vmap[VTLB_VMAP_ITEMS]; //4MB + + // first indexer -- 8/16/32/64/128 bit tables [values 0-4] + // second indexer -- read/write [0 or 1] + // third indexer -- 128 pages of memory! + void* RWFT[5][2][128]; +} + +vtlbHandler vtlbHandlerCount=0; + +vtlbHandler DefaultPhyHandler; +vtlbHandler UnmappedVirtHandler0; +vtlbHandler UnmappedVirtHandler1; +vtlbHandler UnmappedPhyHandler0; +vtlbHandler UnmappedPhyHandler1; + + + /* + __asm + { + mov eax,ecx; + shr ecx,12; + mov ecx,[ecx*4+vmap]; //translate + add ecx,eax; //transform + + js callfunction; //if <0 its invalid ptr :) + + mov eax,[ecx]; + mov [edx],eax; + xor eax,eax; + ret; + +callfunction: + xchg eax,ecx; + shr eax,12; //get the 'ppn' + + //ecx = original addr + //eax = function entry + 0x800000 + //edx = data ptr + jmp [readfunctions8-0x800000+eax]; + }*/ + +///////////////////////////////////////////////////////////////////////// +// Interpreter Implementations of VTLB Memory Operations. +// See recVTLB.cpp for the dynarec versions. + +// Interpreterd VTLB lookup for 8, 16, and 32 bit accesses +template +__forceinline DataType __fastcall MemOp_r0(u32 addr) +{ + u32 vmv=vmap[addr>>VTLB_PAGE_BITS]; + s32 ppf=addr+vmv; + + if (!(ppf<0)) + return *reinterpret_cast(ppf); + + //has to: translate, find function, call function + u32 hand=(u8)vmv; + u32 paddr=ppf-hand+0x80000000; + //SysPrintf("Translated 0x%08X to 0x%08X\n",addr,paddr); + //return reinterpret_cast::HandlerType*>(RWFT[TemplateHelper::sidx][0][hand])(paddr,data); + + switch( DataSize ) + { + case 8: return ((vltbMemR8FP*)RWFT[0][0][hand])(paddr); + case 16: return ((vltbMemR16FP*)RWFT[1][0][hand])(paddr); + case 32: return ((vltbMemR32FP*)RWFT[2][0][hand])(paddr); + + jNO_DEFAULT; + } +} + +// Interpreterd VTLB lookup for 64 and 128 bit accesses. +template +__forceinline void __fastcall MemOp_r1(u32 addr, DataType* data) +{ + u32 vmv=vmap[addr>>VTLB_PAGE_BITS]; + s32 ppf=addr+vmv; + + if (!(ppf<0)) + { + data[0]=*reinterpret_cast(ppf); + if (DataSize==128) + data[1]=*reinterpret_cast(ppf+8); + } + else + { + //has to: translate, find function, call function + u32 hand=(u8)vmv; + u32 paddr=ppf-hand+0x80000000; + //SysPrintf("Translated 0x%08X to 0x%08X\n",addr,paddr); + //return reinterpret_cast::HandlerType*>(RWFT[TemplateHelper::sidx][0][hand])(paddr,data); + + switch( DataSize ) + { + case 64: ((vltbMemR64FP*)RWFT[3][0][hand])(paddr, data); break; + case 128: ((vltbMemR128FP*)RWFT[4][0][hand])(paddr, data); break; + + jNO_DEFAULT; + } + } +} + +template +__forceinline void __fastcall MemOp_w0(u32 addr, DataType data) +{ + u32 vmv=vmap[addr>>VTLB_PAGE_BITS]; + s32 ppf=addr+vmv; + if (!(ppf<0)) + { + *reinterpret_cast(ppf)=data; + } + else + { + //has to: translate, find function, call function + u32 hand=(u8)vmv; + u32 paddr=ppf-hand+0x80000000; + //SysPrintf("Translted 0x%08X to 0x%08X\n",addr,paddr); + + switch( DataSize ) + { + case 8: return ((vltbMemW8FP*)RWFT[0][1][hand])(paddr, (u8)data); + case 16: return ((vltbMemW16FP*)RWFT[1][1][hand])(paddr, (u16)data); + case 32: return ((vltbMemW32FP*)RWFT[2][1][hand])(paddr, (u32)data); + + jNO_DEFAULT; + } + } +} +template +__forceinline void __fastcall MemOp_w1(u32 addr,const DataType* data) +{ + verify(DataSize==128 || DataSize==64); + u32 vmv=vmap[addr>>VTLB_PAGE_BITS]; + s32 ppf=addr+vmv; + if (!(ppf<0)) + { + *reinterpret_cast(ppf)=*data; + if (DataSize==128) + *reinterpret_cast(ppf+8)=data[1]; + } + else + { + //has to: translate, find function, call function + u32 hand=(u8)vmv; + u32 paddr=ppf-hand+0x80000000; + //SysPrintf("Translted 0x%08X to 0x%08X\n",addr,paddr); + switch( DataSize ) + { + case 64: return ((vltbMemW64FP*)RWFT[3][1][hand])(paddr, data); + case 128: return ((vltbMemW128FP*)RWFT[4][1][hand])(paddr, data); + + jNO_DEFAULT; + } + } +} + +mem8_t __fastcall vtlb_memRead8(u32 mem) +{ + return MemOp_r0<8,mem8_t>(mem); +} +mem16_t __fastcall vtlb_memRead16(u32 mem) +{ + return MemOp_r0<16,mem16_t>(mem); +} +mem32_t __fastcall vtlb_memRead32(u32 mem) +{ + return MemOp_r0<32,mem32_t>(mem); +} +void __fastcall vtlb_memRead64(u32 mem, u64 *out) +{ + return MemOp_r1<64,mem64_t>(mem,out); +} +void __fastcall vtlb_memRead128(u32 mem, u64 *out) +{ + return MemOp_r1<128,mem128_t>(mem,out); +} +void __fastcall vtlb_memWrite8 (u32 mem, mem8_t value) +{ + MemOp_w0<8,mem8_t>(mem,value); +} +void __fastcall vtlb_memWrite16(u32 mem, mem16_t value) +{ + MemOp_w0<16,mem16_t>(mem,value); +} +void __fastcall vtlb_memWrite32(u32 mem, mem32_t value) +{ + MemOp_w0<32,mem32_t>(mem,value); +} +void __fastcall vtlb_memWrite64(u32 mem, const mem64_t* value) +{ + MemOp_w1<64,mem64_t>(mem,value); +} +void __fastcall vtlb_memWrite128(u32 mem, const mem128_t *value) +{ + MemOp_w1<128,mem128_t>(mem,value); +} + +// Some functions used by interpreters and stuff... +// These maintain a "consistent" API with 64/128 reads. +void __fastcall memRead8(u32 mem, u8 *out) { *out = vtlb_memRead8( mem ); } +void __fastcall memRead16(u32 mem, u16 *out) { *out = vtlb_memRead16( mem ); } +void __fastcall memRead32(u32 mem, u32 *out) { *out = vtlb_memRead32( mem ); } + +///////////////////////////////////////////////////////////////////////// +// Error / TLB Miss Handlers +// + +// Generates a VtlbMiss Exception +static __forceinline void vtlb_Miss(u32 addr,u32 mode) +{ + Console::Error("vtlb miss : addr 0x%X, mode %d", params addr,mode); + verify(false); + + if (mode==0) + cpuTlbMissR(addr, cpuRegs.branch); + else + cpuTlbMissW(addr, cpuRegs.branch); +} + +// Just dies a horrible death for now. +// +static __forceinline void vtlb_BusError(u32 addr,u32 mode) +{ + Console::Error("vtlb bus error : addr 0x%X, mode %d\n",params addr,mode); + verify(false); +} + +///// Virtual Mapping Errors (TLB Miss) +template +mem8_t __fastcall vtlbUnmappedVRead8(u32 addr) { vtlb_Miss(addr|saddr,0); return 0; } +template +mem16_t __fastcall vtlbUnmappedVRead16(u32 addr) { vtlb_Miss(addr|saddr,0); return 0; } +template +mem32_t __fastcall vtlbUnmappedVRead32(u32 addr) { vtlb_Miss(addr|saddr,0); return 0; } +template +void __fastcall vtlbUnmappedVRead64(u32 addr,mem64_t* data) { vtlb_Miss(addr|saddr,0); } +template +void __fastcall vtlbUnmappedVRead128(u32 addr,mem128_t* data) { vtlb_Miss(addr|saddr,0); } +template +void __fastcall vtlbUnmappedVWrite8(u32 addr,mem8_t data) { vtlb_Miss(addr|saddr,1); } +template +void __fastcall vtlbUnmappedVWrite16(u32 addr,mem16_t data) { vtlb_Miss(addr|saddr,1); } +template +void __fastcall vtlbUnmappedVWrite32(u32 addr,mem32_t data) { vtlb_Miss(addr|saddr,1); } +template +void __fastcall vtlbUnmappedVWrite64(u32 addr,const mem64_t* data) { vtlb_Miss(addr|saddr,1); } +template +void __fastcall vtlbUnmappedVWrite128(u32 addr,const mem128_t* data) { vtlb_Miss(addr|saddr,1); } + +///// Physical Mapping Errors (Bus Error) +template +mem8_t __fastcall vtlbUnmappedPRead8(u32 addr) { vtlb_BusError(addr|saddr,0); return 0; } +template +mem16_t __fastcall vtlbUnmappedPRead16(u32 addr) { vtlb_BusError(addr|saddr,0); return 0; } +template +mem32_t __fastcall vtlbUnmappedPRead32(u32 addr) { vtlb_BusError(addr|saddr,0); return 0; } +template +void __fastcall vtlbUnmappedPRead64(u32 addr,mem64_t* data) { vtlb_BusError(addr|saddr,0); } +template +void __fastcall vtlbUnmappedPRead128(u32 addr,mem128_t* data) { vtlb_BusError(addr|saddr,0); } +template +void __fastcall vtlbUnmappedPWrite8(u32 addr,mem8_t data) { vtlb_BusError(addr|saddr,1); } +template +void __fastcall vtlbUnmappedPWrite16(u32 addr,mem16_t data) { vtlb_BusError(addr|saddr,1); } +template +void __fastcall vtlbUnmappedPWrite32(u32 addr,mem32_t data) { vtlb_BusError(addr|saddr,1); } +template +void __fastcall vtlbUnmappedPWrite64(u32 addr,const mem64_t* data) { vtlb_BusError(addr|saddr,1); } +template +void __fastcall vtlbUnmappedPWrite128(u32 addr,const mem128_t* data) { vtlb_BusError(addr|saddr,1); } + +///// VTLB mapping errors (unmapped address spaces) +mem8_t __fastcall vtlbDefaultPhyRead8(u32 addr) { Console::Error("vtlbDefaultPhyRead8: 0x%X",params addr); verify(false); return -1; } +mem16_t __fastcall vtlbDefaultPhyRead16(u32 addr) { Console::Error("vtlbDefaultPhyRead16: 0x%X",params addr); verify(false); return -1; } +mem32_t __fastcall vtlbDefaultPhyRead32(u32 addr) { Console::Error("vtlbDefaultPhyRead32: 0x%X",params addr); verify(false); return -1; } +void __fastcall vtlbDefaultPhyRead64(u32 addr,mem64_t* data) { Console::Error("vtlbDefaultPhyRead64: 0x%X",params addr); verify(false); } +void __fastcall vtlbDefaultPhyRead128(u32 addr,mem128_t* data) { Console::Error("vtlbDefaultPhyRead128: 0x%X",params addr); verify(false); } + +void __fastcall vtlbDefaultPhyWrite8(u32 addr,mem8_t data) { Console::Error("vtlbDefaultPhyWrite8: 0x%X",params addr); verify(false); } +void __fastcall vtlbDefaultPhyWrite16(u32 addr,mem16_t data) { Console::Error("vtlbDefaultPhyWrite16: 0x%X",params addr); verify(false); } +void __fastcall vtlbDefaultPhyWrite32(u32 addr,mem32_t data) { Console::Error("vtlbDefaultPhyWrite32: 0x%X",params addr); verify(false); } +void __fastcall vtlbDefaultPhyWrite64(u32 addr,const mem64_t* data) { Console::Error("vtlbDefaultPhyWrite64: 0x%X",params addr); verify(false); } +void __fastcall vtlbDefaultPhyWrite128(u32 addr,const mem128_t* data) { Console::Error("vtlbDefaultPhyWrite128: 0x%X",params addr); verify(false); } + + +///////////////////////////////////////////////////////////////////////// +// VTLB Public API -- Init/Term/RegisterHandler stuff +// + + +// Registers a handler into the VTLB's internal handler array. The handler defines specific behavior +// for how memory pages bound to the handler are read from / written to. If any of the handler pointers +// are NULL, the memory operations will be mapped to the BusError handler (thus generating BusError +// exceptions if the emulated app attempts to access them). +// +// Note: All handlers persist across calls to vtlb_Reset(), but are wiped/invalidated by calls to vtlb_Init() +// +// Returns a handle for the newly created handler See .vtlb_MapHandler for use of the return value. +vtlbHandler vtlb_RegisterHandler( vltbMemR8FP* r8,vltbMemR16FP* r16,vltbMemR32FP* r32,vltbMemR64FP* r64,vltbMemR128FP* r128, + vltbMemW8FP* w8,vltbMemW16FP* w16,vltbMemW32FP* w32,vltbMemW64FP* w64,vltbMemW128FP* w128) +{ + //write the code :p + vtlbHandler rv=vtlbHandlerCount++; + + RWFT[0][0][rv] = (r8!=0) ? r8:vtlbDefaultPhyRead8; + RWFT[1][0][rv] = (r16!=0) ? r16:vtlbDefaultPhyRead16; + RWFT[2][0][rv] = (r32!=0) ? r32:vtlbDefaultPhyRead32; + RWFT[3][0][rv] = (r64!=0) ? r64:vtlbDefaultPhyRead64; + RWFT[4][0][rv] = (r128!=0) ? r128:vtlbDefaultPhyRead128; + + RWFT[0][1][rv] = (w8!=0) ? w8:vtlbDefaultPhyWrite8; + RWFT[1][1][rv] = (w16!=0) ? w16:vtlbDefaultPhyWrite16; + RWFT[2][1][rv] = (w32!=0) ? w32:vtlbDefaultPhyWrite32; + RWFT[3][1][rv] = (w64!=0) ? w64:vtlbDefaultPhyWrite64; + RWFT[4][1][rv] = (w128!=0) ? w128:vtlbDefaultPhyWrite128; + + return rv; +} + +// Maps the given hander (created with vtlb_RegisterHandler) to the specified memory region. +// New mappings always assume priority over previous mappings, so place "generic" mappings for +// large areas of memory first, and then specialize specific small regions of memory afterward. +// A single handler can be mapped to many different regions by using multiple calls to this +// function. +// +// The memory region start and size parameters must be pagesize aligned. +void vtlb_MapHandler(vtlbHandler handler,u32 start,u32 size) +{ + verify(0==(start&VTLB_PAGE_MASK)); + verify(0==(size&VTLB_PAGE_MASK) && size>0); + s32 value=handler|0x80000000; + + while(size>0) + { + pmap[start>>VTLB_PAGE_BITS]=value; + + start+=VTLB_PAGE_SIZE; + size-=VTLB_PAGE_SIZE; + } +} + +void vtlb_MapBlock(void* base,u32 start,u32 size,u32 blocksize) +{ + s32 baseint=(s32)base; + + verify(0==(start&VTLB_PAGE_MASK)); + verify(0==(size&VTLB_PAGE_MASK) && size>0); + if (blocksize==0) + blocksize=size; + verify(0==(blocksize&VTLB_PAGE_MASK) && blocksize>0); + verify(0==(size%blocksize)); + + while(size>0) + { + u32 blocksz=blocksize; + s32 ptr=baseint; + + while(blocksz>0) + { + pmap[start>>VTLB_PAGE_BITS]=ptr; + + start+=VTLB_PAGE_SIZE; + ptr+=VTLB_PAGE_SIZE; + blocksz-=VTLB_PAGE_SIZE; + size-=VTLB_PAGE_SIZE; + } + } +} + +void vtlb_Mirror(u32 new_region,u32 start,u32 size) +{ + verify(0==(new_region&VTLB_PAGE_MASK)); + verify(0==(start&VTLB_PAGE_MASK)); + verify(0==(size&VTLB_PAGE_MASK) && size>0); + + while(size>0) + { + pmap[start>>VTLB_PAGE_BITS]=pmap[new_region>>VTLB_PAGE_BITS]; + + start+=VTLB_PAGE_SIZE; + new_region+=VTLB_PAGE_SIZE; + size-=VTLB_PAGE_SIZE; + } +} + +__forceinline void* vtlb_GetPhyPtr(u32 paddr) +{ + if (paddr>=VTLB_PMAP_SZ || pmap[paddr>>VTLB_PAGE_BITS]<0) + return 0; + else + return reinterpret_cast(pmap[paddr>>VTLB_PAGE_BITS]+(paddr&VTLB_PAGE_MASK)); + +} + +//virtual mappings +//TODO: Add invalid paddr checks +void vtlb_VMap(u32 vaddr,u32 paddr,u32 sz) +{ + verify(0==(vaddr&VTLB_PAGE_MASK)); + verify(0==(paddr&VTLB_PAGE_MASK)); + verify(0==(sz&VTLB_PAGE_MASK) && sz>0); + + while(sz>0) + { + s32 pme; + if (paddr>=VTLB_PMAP_SZ) + { + pme=UnmappedPhyHandler0; + if (paddr&0x80000000) + pme=UnmappedPhyHandler1; + pme|=0x80000000; + pme|=paddr;// top bit is set anyway ... + } + else + { + pme=pmap[paddr>>VTLB_PAGE_BITS]; + if (pme<0) + pme|=paddr;// top bit is set anyway ... + } + vmap[vaddr>>VTLB_PAGE_BITS]=pme-vaddr; + vaddr+=VTLB_PAGE_SIZE; + paddr+=VTLB_PAGE_SIZE; + sz-=VTLB_PAGE_SIZE; + } +} + +void vtlb_VMapBuffer(u32 vaddr,void* buffer,u32 sz) +{ + verify(0==(vaddr&VTLB_PAGE_MASK)); + verify(0==(sz&VTLB_PAGE_MASK) && sz>0); + u32 bu8=(u32)buffer; + while(sz>0) + { + vmap[vaddr>>VTLB_PAGE_BITS]=bu8-vaddr; + vaddr+=VTLB_PAGE_SIZE; + bu8+=VTLB_PAGE_SIZE; + sz-=VTLB_PAGE_SIZE; + } +} +void vtlb_VMapUnmap(u32 vaddr,u32 sz) +{ + verify(0==(vaddr&VTLB_PAGE_MASK)); + verify(0==(sz&VTLB_PAGE_MASK) && sz>0); + + while(sz>0) + { + u32 handl=UnmappedVirtHandler0; + if (vaddr&0x80000000) + { + handl=UnmappedVirtHandler1; + } + handl|=vaddr; // top bit is set anyway ... + handl|=0x80000000; + vmap[vaddr>>VTLB_PAGE_BITS]=handl-vaddr; + vaddr+=VTLB_PAGE_SIZE; + sz-=VTLB_PAGE_SIZE; + } +} + +// Clears vtlb handlers and memory mappings. +void vtlb_Init() +{ + vtlbHandlerCount=0; + memzero_obj(RWFT); + + //Register default handlers + //Unmapped Virt handlers _MUST_ be registered first. + //On address translation the top bit cannot be preserved.This is not normaly a problem since + //the physical address space can be 'compressed' to just 29 bits.However, to properly handle exceptions + //there must be a way to get the full address back.Thats why i use these 2 functions and encode the hi bit directly into em :) + + UnmappedVirtHandler0=vtlb_RegisterHandler(vtlbUnmappedVRead8<0>,vtlbUnmappedVRead16<0>,vtlbUnmappedVRead32<0>,vtlbUnmappedVRead64<0>,vtlbUnmappedVRead128<0>, + vtlbUnmappedVWrite8<0>,vtlbUnmappedVWrite16<0>,vtlbUnmappedVWrite32<0>,vtlbUnmappedVWrite64<0>,vtlbUnmappedVWrite128<0>); + + UnmappedVirtHandler1=vtlb_RegisterHandler(vtlbUnmappedVRead8<0x80000000>,vtlbUnmappedVRead16<0x80000000>,vtlbUnmappedVRead32<0x80000000>, + vtlbUnmappedVRead64<0x80000000>,vtlbUnmappedVRead128<0x80000000>, + vtlbUnmappedVWrite8<0x80000000>,vtlbUnmappedVWrite16<0x80000000>,vtlbUnmappedVWrite32<0x80000000>, + vtlbUnmappedVWrite64<0x80000000>,vtlbUnmappedVWrite128<0x80000000>); + + UnmappedPhyHandler0=vtlb_RegisterHandler(vtlbUnmappedPRead8<0>,vtlbUnmappedPRead16<0>,vtlbUnmappedPRead32<0>,vtlbUnmappedPRead64<0>,vtlbUnmappedPRead128<0>, + vtlbUnmappedPWrite8<0>,vtlbUnmappedPWrite16<0>,vtlbUnmappedPWrite32<0>,vtlbUnmappedPWrite64<0>,vtlbUnmappedPWrite128<0>); + + UnmappedPhyHandler1=vtlb_RegisterHandler(vtlbUnmappedPRead8<0x80000000>,vtlbUnmappedPRead16<0x80000000>,vtlbUnmappedPRead32<0x80000000>, + vtlbUnmappedPRead64<0x80000000>,vtlbUnmappedPRead128<0x80000000>, + vtlbUnmappedPWrite8<0x80000000>,vtlbUnmappedPWrite16<0x80000000>,vtlbUnmappedPWrite32<0x80000000>, + vtlbUnmappedPWrite64<0x80000000>,vtlbUnmappedPWrite128<0x80000000>); + DefaultPhyHandler=vtlb_RegisterHandler(0,0,0,0,0,0,0,0,0,0); + + //done ! + + //Setup the initial mappings + vtlb_MapHandler(DefaultPhyHandler,0,VTLB_PMAP_SZ); + + //Set the V space as unmapped + vtlb_VMapUnmap(0,(VTLB_VMAP_ITEMS-1)*VTLB_PAGE_SIZE); + //yeah i know, its stupid .. but this code has to be here for now ;p + vtlb_VMapUnmap((VTLB_VMAP_ITEMS-1)*VTLB_PAGE_SIZE,VTLB_PAGE_SIZE); +} + +// Performs a COP0-level reset of the PS2's TLB. +// This function should probably be part of the COP0 rather than here in VTLB. +void vtlb_Reset() +{ + for(int i=0; i<48; i++) UnmapTLB(i); +} + +void vtlb_Term() +{ + //nothing to do for now +} + +// This function allocates memory block with are compatible with the Vtlb's requirements +// for memory locations. The Vtlb requires the topmost bit (Sign bit) of the memory +// pointer to be cleared. Some operating systems and/or implementations of malloc do that, +// but others do not. So use this instead to allocate the memory correctly for your +// platform. +u8* vtlb_malloc( uint size, uint align, uptr tryBaseAddress ) +{ +#ifdef __LINUX__ + return SysMmapEx( tryBaseAddress, size, 0x80000000, "Vtlb" ); +#else + // Win32 just needs this, since malloc always maps below 2GB. + return (u8*)_aligned_malloc(size, align); +#endif +} + +void vtlb_free( void* pmem, uint size ) +{ + if( pmem == NULL ) return; + +#ifdef __LINUX__ + SafeSysMunmap( pmem, size ); +#else + // Make sure and unprotect memory first, since CrtDebug will try to write to it. + DWORD old; + VirtualProtect( pmem, size, PAGE_READWRITE, &old ); + safe_aligned_free( pmem ); +#endif +} diff --git a/pcsx2/vtlb.h b/pcsx2/vtlb.h new file mode 100644 index 0000000000..5b7e17eea9 --- /dev/null +++ b/pcsx2/vtlb.h @@ -0,0 +1,79 @@ +#ifndef _VTLB_H_ +#define _VTLB_H_ + +typedef u8 mem8_t; +typedef u16 mem16_t; +typedef u32 mem32_t; +typedef u64 mem64_t; +typedef u64 mem128_t; + +// Specialized function pointers for each read type +typedef mem8_t __fastcall vltbMemR8FP(u32 addr); +typedef mem16_t __fastcall vltbMemR16FP(u32 addr); +typedef mem32_t __fastcall vltbMemR32FP(u32 addr); +typedef void __fastcall vltbMemR64FP(u32 addr,mem64_t* data); +typedef void __fastcall vltbMemR128FP(u32 addr,mem128_t* data); + +// Specialized function pointers for each write type +typedef void __fastcall vltbMemW8FP(u32 addr,mem8_t data); +typedef void __fastcall vltbMemW16FP(u32 addr,mem16_t data); +typedef void __fastcall vltbMemW32FP(u32 addr,mem32_t data); +typedef void __fastcall vltbMemW64FP(u32 addr,const mem64_t* data); +typedef void __fastcall vltbMemW128FP(u32 addr,const mem128_t* data); + +typedef u32 vtlbHandler; + +extern void vtlb_Init(); +extern void vtlb_Reset(); +extern void vtlb_Term(); +extern u8* vtlb_malloc( uint size, uint align, uptr tryBaseAddress ); +extern void vtlb_free( void* pmem, uint size ); + + +//physical stuff +vtlbHandler vtlb_RegisterHandler( vltbMemR8FP* r8,vltbMemR16FP* r16,vltbMemR32FP* r32,vltbMemR64FP* r64,vltbMemR128FP* r128, + vltbMemW8FP* w8,vltbMemW16FP* w16,vltbMemW32FP* w32,vltbMemW64FP* w64,vltbMemW128FP* w128); + +extern void vtlb_MapHandler(vtlbHandler handler,u32 start,u32 size); +extern void vtlb_MapBlock(void* base,u32 start,u32 size,u32 blocksize=0); +extern void* vtlb_GetPhyPtr(u32 paddr); +//extern void vtlb_Mirror(u32 new_region,u32 start,u32 size); // -> not working yet :( + +//virtual mappings +extern void vtlb_VMap(u32 vaddr,u32 paddr,u32 sz); +extern void vtlb_VMapBuffer(u32 vaddr,void* buffer,u32 sz); +extern void vtlb_VMapUnmap(u32 vaddr,u32 sz); + +//Memory functions + +extern u8 __fastcall vtlb_memRead8(u32 mem); +extern u16 __fastcall vtlb_memRead16(u32 mem); +extern u32 __fastcall vtlb_memRead32(u32 mem); +extern void __fastcall vtlb_memRead64(u32 mem, u64 *out); +extern void __fastcall vtlb_memRead128(u32 mem, u64 *out); +extern void __fastcall vtlb_memWrite8 (u32 mem, u8 value); +extern void __fastcall vtlb_memWrite16(u32 mem, u16 value); +extern void __fastcall vtlb_memWrite32(u32 mem, u32 value); +extern void __fastcall vtlb_memWrite64(u32 mem, const u64* value); +extern void __fastcall vtlb_memWrite128(u32 mem, const u64* value); + +extern void vtlb_DynGenWrite(u32 sz); +extern void vtlb_DynGenRead32(u32 bits, bool sign); +extern void vtlb_DynGenRead64(u32 sz); + +namespace vtlb_private +{ + static const uint VTLB_PAGE_BITS = 12; + static const uint VTLB_PAGE_MASK = 4095; + static const uint VTLB_PAGE_SIZE = 4096; + + static const uint VTLB_PMAP_ITEMS = 0x20000000 / VTLB_PAGE_SIZE; + static const uint VTLB_PMAP_SZ = 0x20000000; + static const uint VTLB_VMAP_ITEMS = 0x100000000ULL / VTLB_PAGE_SIZE; + + extern void* RWFT[5][2][128]; + extern s32 pmap[VTLB_PMAP_ITEMS]; //512KB + extern s32 vmap[VTLB_VMAP_ITEMS]; //4MB +} + +#endif diff --git a/pcsx2/windows/AboutDlg.cpp b/pcsx2/windows/AboutDlg.cpp new file mode 100644 index 0000000000..77da30ae49 --- /dev/null +++ b/pcsx2/windows/AboutDlg.cpp @@ -0,0 +1,57 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "Win32.h" + +#include "AboutDlg.h" +#include "Common.h" + +#define IDC_STATIC (-1) + +HWND hW; +HBITMAP hBMP, hSilverBMP; + +LRESULT WINAPI AboutDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + + switch(uMsg) { + case WM_INITDIALOG: + hBMP = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(SPLASH_LOGO)); + hSilverBMP = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_PS2SILVER)); + + hW = CreateWindow("STATIC", "", WS_VISIBLE | WS_CHILD | SS_BITMAP, + 230, 10, 211, 110, hDlg, (HMENU)IDC_STATIC, GetModuleHandle(NULL), NULL); + SendMessage(hW, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBMP); + + SetWindowText(hDlg, _("About PCSX2 - Playground")); + + Button_SetText(GetDlgItem(hDlg, IDOK), _("OK")); + Static_SetText(GetDlgItem(hDlg, IDC_PCSX_ABOUT_AUTHORS), _(LabelAuthors)); + Static_SetText(GetDlgItem(hDlg, IDC_PCSX_ABOUT_GREETS), _(LabelGreets)); + return TRUE; + + case WM_COMMAND: + switch(wParam) { + case IDOK: + EndDialog(hDlg, TRUE ); + return TRUE; + } + break; + } + return FALSE; +} diff --git a/pcsx2/windows/AboutDlg.h b/pcsx2/windows/AboutDlg.h new file mode 100644 index 0000000000..5807d5bcc1 --- /dev/null +++ b/pcsx2/windows/AboutDlg.h @@ -0,0 +1,24 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef _PCSX2_ABOUTDLG_H_ +#define _PCSX2_ABOUTDLG_H_ + +LRESULT WINAPI AboutDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +#endif diff --git a/pcsx2/windows/AdvancedDlg.cpp b/pcsx2/windows/AdvancedDlg.cpp new file mode 100644 index 0000000000..993aaa7e08 --- /dev/null +++ b/pcsx2/windows/AdvancedDlg.cpp @@ -0,0 +1,183 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Win32.h" + +#include "Common.h" +#include "ix86/ix86.h" + +// the EE and VU roundmodes are passed in so that we can "retain" the config values during +// default button action. The checkbox statuses are checked against the Config settings when +// the user hits OK, and the game only resets if they differ. + +static void InitRoundClampModes( HWND hDlg, u32 new_eeopt, u32 new_vuopt ) +{ + CheckRadioButton(hDlg, IDC_EE_ROUNDMODE0, IDC_EE_ROUNDMODE3, IDC_EE_ROUNDMODE0 + ((Config.sseMXCSR & 0x6000) >> 13)); + CheckRadioButton(hDlg, IDC_VU_ROUNDMODE0, IDC_VU_ROUNDMODE3, IDC_VU_ROUNDMODE0 + ((Config.sseVUMXCSR & 0x6000) >> 13)); + CheckRadioButton(hDlg, IDC_EE_CLAMPMODE0, IDC_EE_CLAMPMODE2, IDC_EE_CLAMPMODE0 + ((new_eeopt & 0x2) ? 2 : (new_eeopt & 0x1))); + + if (new_vuopt & 0x4) CheckRadioButton(hDlg, IDC_VU_CLAMPMODE0, IDC_VU_CLAMPMODE3, IDC_VU_CLAMPMODE0 + 3); + else if (new_vuopt & 0x2) CheckRadioButton(hDlg, IDC_VU_CLAMPMODE0, IDC_VU_CLAMPMODE3, IDC_VU_CLAMPMODE0 + 2); + else if (new_vuopt & 0x1) CheckRadioButton(hDlg, IDC_VU_CLAMPMODE0, IDC_VU_CLAMPMODE3, IDC_VU_CLAMPMODE0 + 1); + else CheckRadioButton(hDlg, IDC_VU_CLAMPMODE0, IDC_VU_CLAMPMODE3, IDC_VU_CLAMPMODE0 + 0); + + if (Config.sseMXCSR & 0x8000) CheckDlgButton(hDlg, IDC_EE_CHECK1, TRUE); + if (Config.sseVUMXCSR & 0x8000) CheckDlgButton(hDlg, IDC_VU_CHECK1, TRUE); +} + +BOOL APIENTRY AdvancedOptionsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + + InitRoundClampModes( hDlg, Config.eeOptions, Config.vuOptions ); + + if( !cpucaps.hasStreamingSIMD2Extensions ) { + // SSE1 cpus do not support Denormals Are Zero flag. + Config.sseMXCSR &= ~0x0040; + Config.sseVUMXCSR &= ~0x0040; + EnableWindow( GetDlgItem( hDlg, IDC_EE_CHECK2 ), FALSE ); + EnableWindow( GetDlgItem( hDlg, IDC_VU_CHECK2 ), FALSE ); + CheckDlgButton( hDlg, IDC_EE_CHECK2, FALSE ); + CheckDlgButton( hDlg, IDC_VU_CHECK2, FALSE ); + } + else { + if (Config.sseMXCSR & 0x0040) CheckDlgButton(hDlg, IDC_EE_CHECK2, TRUE); + if (Config.sseVUMXCSR & 0x0040) CheckDlgButton(hDlg, IDC_VU_CHECK2, TRUE); + } + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + { + u32 new_eeopt = 0; + u32 new_vuopt = 0; + + Config.sseMXCSR &= 0x1fbf; + Config.sseVUMXCSR &= 0x1fbf; + + Config.sseMXCSR |= IsDlgButtonChecked(hDlg, IDC_EE_ROUNDMODE0) ? 0x0000 : 0; // Round Nearest + Config.sseMXCSR |= IsDlgButtonChecked(hDlg, IDC_EE_ROUNDMODE1) ? 0x2000 : 0; // Round Negative + Config.sseMXCSR |= IsDlgButtonChecked(hDlg, IDC_EE_ROUNDMODE2) ? 0x4000 : 0; // Round Postive + Config.sseMXCSR |= IsDlgButtonChecked(hDlg, IDC_EE_ROUNDMODE3) ? 0x6000 : 0; // Round Zero / Chop + + Config.sseVUMXCSR |= IsDlgButtonChecked(hDlg, IDC_VU_ROUNDMODE0) ? 0x0000 : 0; // Round Nearest + Config.sseVUMXCSR |= IsDlgButtonChecked(hDlg, IDC_VU_ROUNDMODE1) ? 0x2000 : 0; // Round Negative + Config.sseVUMXCSR |= IsDlgButtonChecked(hDlg, IDC_VU_ROUNDMODE2) ? 0x4000 : 0; // Round Postive + Config.sseVUMXCSR |= IsDlgButtonChecked(hDlg, IDC_VU_ROUNDMODE3) ? 0x6000 : 0; // Round Zero / Chop + + new_eeopt |= IsDlgButtonChecked(hDlg, IDC_EE_CLAMPMODE0) ? 0x0 : 0; + new_eeopt |= IsDlgButtonChecked(hDlg, IDC_EE_CLAMPMODE1) ? 0x1 : 0; + new_eeopt |= IsDlgButtonChecked(hDlg, IDC_EE_CLAMPMODE2) ? 0x3 : 0; + + new_vuopt |= IsDlgButtonChecked(hDlg, IDC_VU_CLAMPMODE0) ? 0x0 : 0; + new_vuopt |= IsDlgButtonChecked(hDlg, IDC_VU_CLAMPMODE1) ? 0x1 : 0; + new_vuopt |= IsDlgButtonChecked(hDlg, IDC_VU_CLAMPMODE2) ? 0x3 : 0; + new_vuopt |= IsDlgButtonChecked(hDlg, IDC_VU_CLAMPMODE3) ? 0x7 : 0; + + Config.sseMXCSR |= IsDlgButtonChecked(hDlg, IDC_EE_CHECK1) ? 0x8000 : 0; // FtZ + Config.sseVUMXCSR |= IsDlgButtonChecked(hDlg, IDC_VU_CHECK1) ? 0x8000 : 0; // FtZ + + Config.sseMXCSR |= IsDlgButtonChecked(hDlg, IDC_EE_CHECK2) ? 0x0040 : 0; // DaZ + Config.sseVUMXCSR |= IsDlgButtonChecked(hDlg, IDC_VU_CHECK2) ? 0x0040 : 0; // DaZ + + EndDialog(hDlg, TRUE); + + // Roundmode options do not require CPU resets to take effect. + SetCPUState(Config.sseMXCSR, Config.sseVUMXCSR); + + if( new_eeopt != Config.eeOptions || new_vuopt != Config.vuOptions ) + { + // these do, however... + Config.eeOptions = new_eeopt; + Config.vuOptions = new_vuopt; + SysRestorableReset(); + } + + SaveConfig(); + break; + } + + case IDCANCEL: + + EndDialog(hDlg, FALSE); + break; + + case IDDEFAULT: + + Config.sseMXCSR = DEFAULT_sseMXCSR; + Config.sseVUMXCSR = DEFAULT_sseVUMXCSR; + + // SSE1 cpus do not support Denormals Are Zero flag. + if( !cpucaps.hasStreamingSIMD2Extensions ) { + Config.sseMXCSR &= ~0x0040; + Config.sseVUMXCSR &= ~0x0040; + } + + InitRoundClampModes( hDlg, DEFAULT_eeOptions, DEFAULT_vuOptions ); + + CheckDlgButton(hDlg, IDC_EE_CHECK1, (Config.sseMXCSR & 0x8000) ? TRUE : FALSE); + CheckDlgButton(hDlg, IDC_VU_CHECK1, (Config.sseVUMXCSR & 0x8000) ? TRUE : FALSE); + + CheckDlgButton(hDlg, IDC_EE_CHECK2, (Config.sseMXCSR & 0x0040) ? TRUE : FALSE); + CheckDlgButton(hDlg, IDC_VU_CHECK2, (Config.sseVUMXCSR & 0x0040) ? TRUE : FALSE); + break; + + case IDC_EE_ROUNDMODE0: + case IDC_EE_ROUNDMODE1: + case IDC_EE_ROUNDMODE2: + case IDC_EE_ROUNDMODE3: + + CheckRadioButton(hDlg, IDC_EE_ROUNDMODE0, IDC_EE_ROUNDMODE3, IDC_EE_ROUNDMODE0 + ( LOWORD(wParam) % IDC_EE_ROUNDMODE0 ) ); + break; + + case IDC_VU_ROUNDMODE0: + case IDC_VU_ROUNDMODE1: + case IDC_VU_ROUNDMODE2: + case IDC_VU_ROUNDMODE3: + + CheckRadioButton(hDlg, IDC_VU_ROUNDMODE0, IDC_VU_ROUNDMODE3, IDC_VU_ROUNDMODE0 + ( LOWORD(wParam) % IDC_VU_ROUNDMODE0 ) ); + break; + + case IDC_EE_CLAMPMODE0: + case IDC_EE_CLAMPMODE1: + case IDC_EE_CLAMPMODE2: + + CheckRadioButton(hDlg, IDC_EE_CLAMPMODE0, IDC_EE_CLAMPMODE2, IDC_EE_CLAMPMODE0 + ( LOWORD(wParam) % IDC_EE_CLAMPMODE0 ) ); + break; + + case IDC_VU_CLAMPMODE0: + case IDC_VU_CLAMPMODE1: + case IDC_VU_CLAMPMODE2: + case IDC_VU_CLAMPMODE3: + + CheckRadioButton(hDlg, IDC_VU_CLAMPMODE0, IDC_VU_CLAMPMODE3, IDC_VU_CLAMPMODE0 + ( LOWORD(wParam) % IDC_VU_CLAMPMODE0 ) ); + break; + } + + return TRUE; + } + + return FALSE; +} diff --git a/pcsx2/windows/Cdrom02.ico b/pcsx2/windows/Cdrom02.ico new file mode 100644 index 0000000000000000000000000000000000000000..0fa4d2be80454316c894473ddb00499949a2f8cc GIT binary patch literal 12862 zcmeHO+mcn)747^)DsOx3H=pwbep*oEB7}>82pUa{5)kAnAP4~z1VqSp(5L&H)A!T7 zm~okF?X`hjCDqkcR3$K3HP)D8jycw*d1P+x5BUA=yK^}IH22P*=jQ%6H#heV{(zeM z3;vKF`t|%jE$dl&{tPE@0p{bJr^bTn#FY8y@;XnMc#wtnZ0+!@rKk0l6+HoC(8{fP zQOF?h_8j0-bH? zK4#Yfj#&0M#mM7S2E$N;*-%}ZLy1?!cq=Qt0_F%%oC8={jbpafIP)XsiFl9N(W5kv zLN;J61;BMaU+b0z=|8f7)9caR--?s3JTa<17VM#)UB+dDqnfzyzk~j~q3LICr zOj@6rBB)1bMZ|`87D+B;tOI950<>7dxuWj?{zP{|u=3HTt z=G+42Zo7yR&UcB)7#Bc+_EMY{sgYqP)6`v)#U%HTjB#>?2C{+?l&1^f=c9wFxTS|2SqQ^Q?e zh-;r3FG4AS#AV7R8n=nvZuh=JjBvci*Zwuuu96BMucJ9lU^xCEp&03hb`gLl ztM;ieS{hMQ>+xg)`0(v=3oyRb%SmU7dDfmo7&5wHBjRP}*v=T*ax*R7NMoEgTL9zn z%|U;(pd8=8i78@U$?%F(R~o8%KHfsvuz&p)uDAg)troR0cF{qBDUjy-H2%I_#0kA{ z?NeiRJrZZ=Jp~gNt%P4c%dU2n?F1r=e*i1(ma;9yi{5ozt?TU=;CegZ$*T9(7_CW& zskgX0>1jS%FU?AcR&=$_*IGEm`8~f8DI++SwNCWzDrSP0cWPpTw^+L zgs;wGn8+tuX*d!Jm1B<=SzZbOTlS}{v(iXj+uI!A}SQKY&fjQq!&!?sH04gN8Fr5E<8%oH^2!XGKgbDHQ$vLE< z1HX(*xZ4PG)wgNtI+vEdZGo3`9rA<*f~9kqqIim_b7XK8U%Ip zY+p91Jwx9ug7}+$ooUSs=0B5`vnFQGWCs9taSfABpcbzMW~4>zFJE6<*|ipLr)*BxfMN2_)9T-JR$%4xw=O?IB9+swl)zvV|TVwu}6>y+t z=x5KK%o$cP#Pvj4J<+V5FaTzege;TNXs{wJLvk8Qxptxn<*Qdb$If~hM>rLG39`vh zevOB}Rtsb5lTr=vy06EHUth=58oD?`P^K!9f}X>=ubNu#&OAa-tv){d{Xeld+a7On z)U^I_T8nCE{xKMegExSlK*1memQrGmR3>sOwj6XqIgrP5Vxwl&rPXl$Sce#SFqmV_ zIxDRmYu1h>0AXBvIA5u(^z}9Ll|wj7Z*!iq@EIN{y=jhemHF$ZZ-}l0iU?Kc+Eu( z_@dVp3~QY{TR3$!>Dge|I%K_-zy%cjV19>;kwe|Cjf1j1luuj_rj0{`K11JUyLrC7 zt7aWf;8kf4B);NcGc(Y+-Fe>Be};qzc0eb-u!~HZ{gt)BYIO4YrvBTlXSM^4bC$F- zko%k2{-gpX5YudTo;UR`A!qxUlOY{`Uz+V}X8Y0x3&8dEegDYGs49bIb8nh`*v$4e zvk%iIfNSqtmml$vO$ilPSEk*QHW41Lz_~|Pop1X0yWRT8xd%BDb278i?oOL1CnrcV zGR=19c~gIcob6^{K4f>Jz&e55B4}p2n$2Aep5RviQ!EX;eykZO`F31)=_3FWNWUx1 zgt1g6nP=A{g~yck(w|)qmQ;}>1}T<92H4^xAEZrGxe}=LfflhPz_VbDKwu5{aInDF zGL-?v5R?*MNV;KAo`H46-XOC%8MSr_?95X;n-?#Sr%iMr3yqao4>9?g^fkTcCE=DA z-qqtFbCT@fEuqXIdyzUl4WnevoQ~SO*l9-bq$y&w;yHLMWkuPEZHtlI4O{TVC$BbR zpJG)fDIpC)!x-U7HfTenAm}mv;G3!f>X|ROR+_}3eI|McZ_`I zmX~G|grq>F)rzg;Zuu2#`9+m;kPKW^o-Kng4{QgC`F#vLs~9i)6tWuZX*`=9cBG;I M7w~`nf8z`M7kSCVjsO4v literal 0 HcmV?d00001 diff --git a/pcsx2/windows/ConfigDlg.cpp b/pcsx2/windows/ConfigDlg.cpp new file mode 100644 index 0000000000..4e0359878e --- /dev/null +++ b/pcsx2/windows/ConfigDlg.cpp @@ -0,0 +1,578 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "Win32.h" + +#include +#include "common.h" +#include "plugins.h" +#include "resource.h" + +struct ComboInitializer +{ + HWND hwnd; + + HANDLE Find; + HANDLE Lib; + + WIN32_FIND_DATA FindData; + + _PS2EgetLibType PS2E_GetLibType; + _PS2EgetLibName PS2E_GetLibName; + _PS2EgetLibVersion2 PS2E_GetLibVersion2; + + long version; + u32 type; + + ComboInitializer( HWND hwndDlg ) : + hwnd( hwndDlg ) + , Find( INVALID_HANDLE_VALUE ) + , Lib( NULL ) + , PS2E_GetLibType( NULL ) + , PS2E_GetLibName( NULL ) + , PS2E_GetLibVersion2( NULL ) + { + string tmpStr; + Path::Combine( tmpStr, Config.PluginsDir, "*.dll" ); + Find = FindFirstFile(tmpStr.c_str(), &FindData); + } + + ~ComboInitializer() + { + if (Find!=INVALID_HANDLE_VALUE) + FindClose(Find); + } + + bool FindNext() + { + return !!FindNextFile( Find, &FindData ); + } + + bool LoadNextLibrary() + { + string tmpStr; + Path::Combine( tmpStr, Config.PluginsDir, FindData.cFileName ); + Lib = LoadLibrary(tmpStr.c_str()); + if (Lib == NULL) + { + Console::Error( "Plugin load failure: %hs\n\tSysLibError Message: %s", params &tmpStr, SysLibError() ); + return false; + } + + PS2E_GetLibType = (_PS2EgetLibType) GetProcAddress((HMODULE)Lib,"PS2EgetLibType"); + PS2E_GetLibName = (_PS2EgetLibName) GetProcAddress((HMODULE)Lib,"PS2EgetLibName"); + PS2E_GetLibVersion2 = (_PS2EgetLibVersion2) GetProcAddress((HMODULE)Lib,"PS2EgetLibVersion2"); + + if( PS2E_GetLibType == NULL || PS2E_GetLibName == NULL || PS2E_GetLibVersion2 == NULL ) + return false; + + type = PS2E_GetLibType(); + return true; + } + + void AddPlugin(HWND hwndCombo, const char* str) + { + string tmpStr; + int i; + + ssprintf(tmpStr, "%s %d.%d.%d", PS2E_GetLibName(), (version>>8)&0xff, version&0xff, (version>>24)&0xff); + char* lp = (char *)malloc(strlen(FindData.cFileName)+8); + sprintf(lp, "%s", FindData.cFileName); + i = ComboBox_AddString(hwndCombo, tmpStr.c_str()); + ComboBox_SetItemData(hwndCombo, i, lp); + if (_stricmp(str, lp)==0) + ComboBox_SetCurSel(hwndCombo, i); + } + + bool CheckVersion( const char* typeStr, u32 pluginType, long checkver ) + { + if( type & pluginType ) + { + version = PS2E_GetLibVersion2( pluginType ); + if ( ((version >> 16)&0xff) == checkver ) + return true; + + Console::Notice("%s Plugin %s: Version %x != %x", params typeStr, FindData.cFileName, 0xff&(version >> 16), checkver); + } + return false; + } +}; + +BOOL OnConfigureDialog(HWND hW) { + HWND const hWC_GS=GetDlgItem(hW,IDC_LISTGS); + HWND const hWC_PAD1=GetDlgItem(hW,IDC_LISTPAD1); + HWND const hWC_PAD2=GetDlgItem(hW,IDC_LISTPAD2); + HWND const hWC_SPU2=GetDlgItem(hW,IDC_LISTSPU2); + HWND const hWC_CDVD=GetDlgItem(hW,IDC_LISTCDVD); + HWND const hWC_DEV9=GetDlgItem(hW,IDC_LISTDEV9); + HWND const hWC_USB=GetDlgItem(hW,IDC_LISTUSB); + HWND const hWC_FW=GetDlgItem(hW,IDC_LISTFW); + HWND const hWC_BIOS=GetDlgItem(hW,IDC_LISTBIOS); + + ComboInitializer tool(hW); + if( tool.Find == INVALID_HANDLE_VALUE ) // epic fail? + return FALSE; + + do + { + if( !tool.LoadNextLibrary() ) continue; + + if( tool.CheckVersion( "GS", PS2E_LT_GS, PS2E_GS_VERSION ) ) + tool.AddPlugin(hWC_GS, winConfig.GS); + + if (tool.type & PS2E_LT_PAD) + { + _PADquery query; + + query = (_PADquery)GetProcAddress((HMODULE)tool.Lib, "PADquery"); + if( query != NULL ) + { + if( tool.CheckVersion( "PAD", PS2E_LT_PAD, PS2E_PAD_VERSION ) ) + { + if (query() & 0x1) + tool.AddPlugin(hWC_PAD1, winConfig.PAD1); + if (query() & 0x2) + tool.AddPlugin(hWC_PAD2, winConfig.PAD2); + } + } + } + + if( tool.CheckVersion( "SPU2", PS2E_LT_SPU2, PS2E_SPU2_VERSION ) ) + tool.AddPlugin(hWC_SPU2, winConfig.SPU2); + + if( tool.CheckVersion( "CDVD", PS2E_LT_CDVD, PS2E_CDVD_VERSION ) ) + tool.AddPlugin(hWC_CDVD, winConfig.CDVD); + + if( tool.CheckVersion( "DEV9", PS2E_LT_DEV9, PS2E_DEV9_VERSION ) ) + tool.AddPlugin(hWC_DEV9, winConfig.DEV9); + + if( tool.CheckVersion( "USB", PS2E_LT_USB, PS2E_USB_VERSION ) ) + tool.AddPlugin(hWC_USB, winConfig.USB); + + if( tool.CheckVersion( "FW", PS2E_LT_FW, PS2E_FW_VERSION ) ) + tool.AddPlugin(hWC_FW, winConfig.FW); + + } while( tool.FindNext() ); + +// BIOS + + /*lp=(char *)malloc(strlen("HLE") + 1); + sprintf(lp, "HLE"); + i=ComboBox_AddString(hWC_BIOS, _("Internal HLE Bios")); + ComboBox_SetItemData(hWC_BIOS, i, lp); + if (_stricmp(Config.Bios, lp)==0) + ComboBox_SetCurSel(hWC_BIOS, i);*/ + + HANDLE Find; + + WIN32_FIND_DATA FindData; + string tmpStr; + + Path::Combine( tmpStr, Config.BiosDir, "*" ); + Find=FindFirstFile(tmpStr.c_str(), &FindData); + + do + { + char* lp; + int i; + + char description[50]; //2002-09-22 (Florin) + if (Find==INVALID_HANDLE_VALUE) break; + if (!strcmp(FindData.cFileName, ".")) continue; + if (!strcmp(FindData.cFileName, "..")) continue; + if (FindData.nFileSizeLow > 1024 * 4096) continue; //2002-09-22 (Florin) + if (!IsBIOS(FindData.cFileName, description)) continue;//2002-09-22 (Florin) + lp = (char *)malloc(strlen(FindData.cFileName)+8); + sprintf(lp, "%s", (char *)FindData.cFileName); + i = ComboBox_AddString(hWC_BIOS, description); //2002-09-22 (Florin) modified + ComboBox_SetItemData(hWC_BIOS, i, lp); + if (_stricmp(Config.Bios, FindData.cFileName)==0) + ComboBox_SetCurSel(hWC_BIOS, i); + } while (FindNextFile(Find,&FindData)); + + if (Find!=INVALID_HANDLE_VALUE) FindClose(Find); + + if (ComboBox_GetCurSel(hWC_GS) == -1) + ComboBox_SetCurSel(hWC_GS, 0); + if (ComboBox_GetCurSel(hWC_PAD1) == -1) + ComboBox_SetCurSel(hWC_PAD1, 0); + if (ComboBox_GetCurSel(hWC_PAD2) == -1) + ComboBox_SetCurSel(hWC_PAD2, 0); + if (ComboBox_GetCurSel(hWC_SPU2) == -1) + ComboBox_SetCurSel(hWC_SPU2, 0); + if (ComboBox_GetCurSel(hWC_CDVD) == -1) + ComboBox_SetCurSel(hWC_CDVD, 0); + if (ComboBox_GetCurSel(hWC_DEV9) == -1) + ComboBox_SetCurSel(hWC_DEV9, 0); + if (ComboBox_GetCurSel(hWC_USB) == -1) + ComboBox_SetCurSel(hWC_USB, 0); + if (ComboBox_GetCurSel(hWC_FW) == -1) + ComboBox_SetCurSel(hWC_FW, 0); + if (ComboBox_GetCurSel(hWC_BIOS) == -1) + ComboBox_SetCurSel(hWC_BIOS, 0); + + return TRUE; +} + +#define CleanCombo(item) \ + hWC = GetDlgItem(hW, item); \ + iCnt = ComboBox_GetCount(hWC); \ + for (i=0; i>10)); + + sprintf(cfps,"%d",Config.CustomFps); + SetDlgItemText(hW, IDC_CUSTOMFPS, cfps); + + sprintf(cFrameskip,"%d",Config.CustomFrameSkip); + SetDlgItemText(hW, IDC_CUSTOM_FRAMESKIP, cFrameskip); + + sprintf(cConsecutiveFrames,"%d",Config.CustomConsecutiveFrames); + SetDlgItemText(hW, IDC_CUSTOM_CONSECUTIVE_FRAMES, cConsecutiveFrames); + + sprintf(cConsecutiveSkip,"%d",Config.CustomConsecutiveSkip); + SetDlgItemText(hW, IDC_CUSTOM_CONSECUTIVE_SKIP, cConsecutiveSkip); + + //EnableWindow( GetDlgItem( hW, IDC_CPU_GSMULTI ), !g_GameInProgress ); + + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDCANCEL: + EndDialog(hW, FALSE); + return FALSE; + + case IDOK: + newopts = 0; + + if( SendDlgItemMessage(hW,IDC_CPU_EEREC,BM_GETCHECK,0,0) ) newopts |= PCSX2_EEREC; + + if( SendDlgItemMessage(hW,IDC_CPU_VU0REC,BM_GETCHECK,0,0) ) newopts |= PCSX2_VU0REC; + if( SendDlgItemMessage(hW,IDC_CPU_VU1REC,BM_GETCHECK,0,0) ) newopts |= PCSX2_VU1REC; + + if( SendDlgItemMessage(hW,IDC_CPU_GSMULTI,BM_GETCHECK,0,0) ) newopts |= PCSX2_GSMULTITHREAD; + + if( SendDlgItemMessage(hW,IDC_CPU_FL_NORMAL,BM_GETCHECK,0,0) ) newopts |= PCSX2_FRAMELIMIT_NORMAL; + else if( SendDlgItemMessage(hW,IDC_CPU_FL_LIMIT,BM_GETCHECK,0,0) ) newopts |= PCSX2_FRAMELIMIT_LIMIT; + else if( SendDlgItemMessage(hW,IDC_CPU_FL_SKIP,BM_GETCHECK,0,0) ) newopts |= PCSX2_FRAMELIMIT_SKIP; + else if( SendDlgItemMessage(hW,IDC_CPU_FL_SKIPVU,BM_GETCHECK,0,0) ) newopts |= PCSX2_FRAMELIMIT_VUSKIP; + + GetDlgItemText(hW, IDC_CUSTOMFPS, cfps, 20); + Config.CustomFps = atoi(cfps); + + GetDlgItemText(hW, IDC_CUSTOM_FRAMESKIP, cFrameskip, 20); + Config.CustomFrameSkip = atoi(cFrameskip); + + GetDlgItemText(hW, IDC_CUSTOM_CONSECUTIVE_FRAMES, cConsecutiveFrames, 20); + Config.CustomConsecutiveFrames = atoi(cConsecutiveFrames); + + GetDlgItemText(hW, IDC_CUSTOM_CONSECUTIVE_SKIP, cConsecutiveSkip, 20); + Config.CustomConsecutiveSkip = atoi(cConsecutiveSkip); + + EndDialog(hW, TRUE); + + if( Config.Options != newopts ) + { + SysRestorableReset(); + + if( (Config.Options&PCSX2_GSMULTITHREAD) ^ (newopts&PCSX2_GSMULTITHREAD) ) + { + // Need the MTGS setting to take effect, so close out the plugins: + PluginsResetGS(); + if( CHECK_MULTIGS ) + Console::Notice( "MTGS mode disabled.\n\tEnjoy the fruits of single-threaded simpicity." ); + else + Console::Notice( "MTGS mode enabled.\n\tWelcome to multi-threaded awesomeness." ); + } + Config.Options = newopts; + } + else if( Cpu != NULL ) + UpdateVSyncRate(); + + SaveConfig(); + + return FALSE; + } + return TRUE; + } + return FALSE; +} diff --git a/pcsx2/windows/DebugMemory.cpp b/pcsx2/windows/DebugMemory.cpp new file mode 100644 index 0000000000..4d674701c5 --- /dev/null +++ b/pcsx2/windows/DebugMemory.cpp @@ -0,0 +1,240 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Win32.h" +#include "Common.h" +#include "resource.h" + +unsigned long memory_addr; +BOOL mem_inupdate = FALSE; +HWND memoryhWnd,hWnd_memscroll,hWnd_memorydump; +unsigned long memory_patch; +unsigned long data_patch; + +///MEMORY DUMP +unsigned char Debug_Read8(unsigned long addr)//just for anycase.. +{ +#ifdef _WIN32 + __try + { +#endif + u8 val8; + memRead8(addr, &val8); + return val8; +#ifdef _WIN32 + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return 0; + } +#endif +} + + +void RefreshMemory(void) +{ + int x, y; + unsigned long addr; + unsigned char b; + + char buf[128], text[32], temp[8]; + + + + addr = memory_addr; + + if (!mem_inupdate) + { + sprintf(buf, "%08X", addr); + SetDlgItemText(memoryhWnd, IDC_MEMORY_ADDR, buf); + } + + SendMessage(hWnd_memorydump, LB_RESETCONTENT, 0, 0); + + for (y = 0; y < 21; y++) + { + memzero_obj(text); + sprintf(buf, "%08X: ", addr); + + for (x = 0; x < 16; x++) + { + b = Debug_Read8(addr++); + + sprintf(temp, "%02X ", b); + strcat(buf, temp); + + if (b < 32 || b > 127) b = 32; + sprintf(temp, "%c", b); + strcat(text, temp); + + + if (x == 7) strcat(buf, " "); + } + + strcat(buf, " "); + strcat(buf, text); + + SendMessage(hWnd_memorydump, LB_ADDSTRING, 0, (LPARAM)buf); + } +} + + +BOOL APIENTRY DumpMemProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + char start[16], end[16], fname[128], buf[128]; + u32 start_pc, end_pc, addr; + u8 data; + + FILE *fp; + + switch (message) + { + case WM_INITDIALOG: + sprintf(buf, "%08X", cpuRegs.pc); + SetDlgItemText(hDlg, IDC_DUMPMEM_START, buf); + SetDlgItemText(hDlg, IDC_DUMPMEM_END, buf); + SetDlgItemText(hDlg, IDC_DUMPMEM_FNAME, "dump.raw"); + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK) + { + GetDlgItemText(hDlg, IDC_DUMPMEM_START, start, 9); + start[8] = 0; + sscanf(start, "%x", &start_pc); + start_pc &= 0xFFFFFFFC; + + GetDlgItemText(hDlg, IDC_DUMPMEM_END, end, 9); + end[8] = 0; + sscanf(end, "%x", &end_pc); + end_pc &= 0xFFFFFFFC; + + GetDlgItemText(hDlg, IDC_DUMPMEM_FNAME, fname, 128); + fp = fopen(fname, "wb"); + if (fp == NULL) + { + Msgbox::Alert("Can't open file '%s' for writing!", params fname); + } + else + { + for (addr = start_pc; addr < end_pc; addr ++) { + memRead8( addr, &data ); + fwrite(&data, 1, 1, fp); + } + + fclose(fp); + } + + EndDialog(hDlg, TRUE); + } else if (LOWORD(wParam) == IDCANCEL) { + EndDialog(hDlg, TRUE); + } + return TRUE; + } + + return FALSE; +} + + + +BOOL APIENTRY MemoryProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + + char buf[16]; + switch (message) + { + case WM_INITDIALOG: + memory_addr = cpuRegs.pc; + sprintf(buf, "%08X", memory_addr); + SetDlgItemText(hDlg, IDC_MEMORY_ADDR, buf); + memory_patch= 0; + sprintf(buf, "%08X", memory_patch); + SetDlgItemText(hDlg, IDC_ADDRESS_PATCH, buf); + data_patch=0; + sprintf(buf, "%08X", data_patch); + SetDlgItemText(hDlg, IDC_DATA_PATCH, buf); + hWnd_memorydump = GetDlgItem(hDlg, IDC_MEMORY_DUMP); + hWnd_memscroll = GetDlgItem(hDlg, IDC_MEM_SCROLL); + + + + SendMessage(hWnd_memorydump, LB_INITSTORAGE, 11, 1280); + SendMessage(hWnd_memscroll, SBM_SETRANGE, 0, MAXLONG); + SendMessage(hWnd_memscroll, SBM_SETPOS, MAXLONG / 2, TRUE); + + RefreshMemory(); + return TRUE; + case WM_VSCROLL: + switch ((int) LOWORD(wParam)) + { + case SB_LINEDOWN: memory_addr += 0x00000010; RefreshMemory(); break; + case SB_LINEUP: memory_addr -= 0x00000010; RefreshMemory(); break; + case SB_PAGEDOWN: memory_addr += 0x00000150; RefreshMemory(); break; + case SB_PAGEUP: memory_addr -= 0x00000150; RefreshMemory(); break; + } + + return TRUE; + + case WM_CLOSE: + EndDialog(hDlg, TRUE ); + return TRUE; + + + case WM_COMMAND: + if (HIWORD(wParam) == EN_UPDATE) + { + mem_inupdate = TRUE; + GetDlgItemText(hDlg, IDC_MEMORY_ADDR, buf, 9); + buf[8] = 0; + + sscanf(buf, "%x", &memory_addr); + RefreshMemory(); + mem_inupdate = FALSE; + return TRUE; + } + + switch (LOWORD(wParam)) { + case IDC_PATCH: + GetDlgItemText(hDlg, IDC_ADDRESS_PATCH, buf, 9);//32bit address + buf[8] = 0; + sscanf(buf, "%x", &memory_patch); + GetDlgItemText(hDlg, IDC_DATA_PATCH, buf, 9);//32 bit data only for far + buf[8] = 0; + sscanf(buf, "%x", &data_patch); + memWrite32( memory_patch, data_patch ); + sprintf(buf, "%08X", memory_patch); + SetDlgItemText(hDlg, IDC_MEMORY_ADDR, buf); + RefreshMemory(); + return TRUE; + + case IDC_DUMPRAW: + DialogBox(gApp.hInstance, MAKEINTRESOURCE(IDD_DUMPMEM), hDlg, (DLGPROC)DumpMemProc); + + return TRUE; + + case IDC_MEMORY_CLOSE: + EndDialog(hDlg, TRUE ); + return TRUE; + } + break; + } + + return FALSE; +} diff --git a/pcsx2/windows/Debugger.cpp b/pcsx2/windows/Debugger.cpp new file mode 100644 index 0000000000..ed46beb81c --- /dev/null +++ b/pcsx2/windows/Debugger.cpp @@ -0,0 +1,679 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "win32.h" + +#include "resource.h" +#include "R5900OpcodeTables.h" +#include "Debugger.h" +#include "Common.h" +#include "IopMem.h" +#include "R3000A.h" + +#ifdef _MSC_VER +#pragma warning(disable:4996) //ignore the stricmp deprecated warning +#endif + +void RefreshIOPDebugger(void); +extern int ISR3000A;//for disasm +HWND hWnd_debugdisasm, hWnd_debugscroll,hWnd_IOP_debugdisasm, hWnd_IOP_debugscroll; +unsigned long DebuggerPC = 0; +HWND hRegDlg;//for debug registers.. +HWND debughWnd; +unsigned long DebuggerIOPPC=0; +HWND hIOPDlg;//IOP debugger + +breakpoints bkpt_regv[NUM_BREAKPOINTS]; + + +void RefreshDebugAll()//refresh disasm and register window +{ + RefreshDebugger(); + RefreshIOPDebugger(); + UpdateRegs(); +} + +void MakeDebugOpcode(void) +{ + memRead32( opcode_addr, &cpuRegs.code ); +} + +void MakeIOPDebugOpcode(void) +{ + psxRegs.code = PSXMu32( opcode_addr); +} + +BOOL HasBreakpoint() +{ + int t; + + for (t = 0; t < NUM_BREAKPOINTS; t++) + { + switch (bkpt_regv[t].type) { + case 1: // exec + if (cpuRegs.pc == bkpt_regv[t].value) return TRUE; + break; + + case 2: // count + if ((cpuRegs.cycle - 10) <= bkpt_regv[t].value && + (cpuRegs.cycle + 10) >= bkpt_regv[t].value) return TRUE; + break; + } + } + return FALSE; + +} +BOOL APIENTRY JumpProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + char buf[16]; + unsigned long temp; + + switch (message) + { + case WM_INITDIALOG: + sprintf(buf, "%08X", cpuRegs.pc); + SetDlgItemText(hDlg, IDC_JUMP_PC, buf); + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK) + { + GetDlgItemText(hDlg, IDC_JUMP_PC, buf, 9); + + buf[8] = 0; + sscanf(buf, "%x", &temp); + + temp &= 0xFFFFFFFC; + DebuggerPC = temp - 0x00000038; + + EndDialog(hDlg, TRUE); + } else if (LOWORD(wParam) == IDCANCEL) { + EndDialog(hDlg, TRUE); + } + return TRUE; + } + + return FALSE; +} + +extern void EEDumpRegs(FILE * fp); +extern void IOPDumpRegs(FILE * fp); +BOOL APIENTRY DumpProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + char start[16], end[16], fname[128], tmp[128]; + unsigned long start_pc, end_pc, temp; + + FILE *fp; + + switch (message) + { + case WM_INITDIALOG: + sprintf(tmp, "%08X", cpuRegs.pc); + SetDlgItemText(hDlg, IDC_DUMP_START, tmp); + SetDlgItemText(hDlg, IDC_DUMP_END, tmp); + SetDlgItemText(hDlg, IDC_DUMP_FNAME, "EEdisasm.txt"); + + sprintf(tmp, "%08X", psxRegs.pc); + SetDlgItemText(hDlg, IDC_DUMP_STARTIOP, tmp); + SetDlgItemText(hDlg, IDC_DUMP_ENDIOP, tmp); + SetDlgItemText(hDlg, IDC_DUMP_FNAMEIOP, "IOPdisasm.txt"); + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK) + { + GetDlgItemText(hDlg, IDC_DUMP_START, start, 9); + start[8] = 0; + sscanf(start, "%x", &start_pc); + start_pc &= 0xFFFFFFFC; + + GetDlgItemText(hDlg, IDC_DUMP_END, end, 9); + end[8] = 0; + sscanf(end, "%x", &end_pc); + end_pc &= 0xFFFFFFFC; + + GetDlgItemText(hDlg, IDC_DUMP_FNAME, fname, 128); + fp = fopen(fname, "wt"); + if (fp == NULL) + { + //MessageBox(MainhWnd, "Can't open file for writing!", NULL, MB_OK); + } + else + { + std::string output; + + fprintf(fp,"----------------------------------\n"); + fprintf(fp,"EE DISASM TEXT DOCUMENT BY PCSX2 \n"); + fprintf(fp,"----------------------------------\n"); + for (temp = start_pc; temp <= end_pc; temp += 4) + { + opcode_addr=temp; + MakeDebugOpcode(); + + output.assign( HasBreakpoint() ? "*" : "" ); + R5900::GetCurrentInstruction().disasm( output ); + + fprintf(fp, "%08X %08X: %s\n", temp, cpuRegs.code, output.c_str()); + } + + + fprintf(fp,"\n\n\n----------------------------------\n"); + fprintf(fp,"EE REGISTER DISASM TEXT DOCUMENT BY PCSX2 \n"); + fprintf(fp,"----------------------------------\n"); + EEDumpRegs(fp); + fclose(fp); + } + + + + GetDlgItemText(hDlg, IDC_DUMP_STARTIOP, start, 9); + start[8] = 0; + sscanf(start, "%x", &start_pc); + start_pc &= 0xFFFFFFFC; + + GetDlgItemText(hDlg, IDC_DUMP_ENDIOP, end, 9); + end[8] = 0; + sscanf(end, "%x", &end_pc); + end_pc &= 0xFFFFFFFC; + + GetDlgItemText(hDlg, IDC_DUMP_FNAMEIOP, fname, 128); + fp = fopen(fname, "wt"); + if (fp == NULL) + { + //MessageBox(MainhWnd, "Can't open file for writing!", NULL, MB_OK); + } + else + { + fprintf(fp,"----------------------------------\n"); + fprintf(fp,"IOP DISASM TEXT DOCUMENT BY PCSX2 \n"); + fprintf(fp,"----------------------------------\n"); + for (temp = start_pc; temp <= end_pc; temp += 4) + { + opcode_addr=temp; + MakeIOPDebugOpcode(); + R3000A::IOP_DEBUG_BSC[(psxRegs.code) >> 26](tmp); + fprintf(fp, "%08X %08X: %s\n", temp, psxRegs.code, tmp); + } + + fprintf(fp,"\n\n\n----------------------------------\n"); + fprintf(fp,"IOP REGISTER DISASM TEXT DOCUMENT BY PCSX2 \n"); + fprintf(fp,"----------------------------------\n"); + IOPDumpRegs(fp); + fclose(fp); + } + EndDialog(hDlg, TRUE); + } else if (LOWORD(wParam) == IDCANCEL) { + EndDialog(hDlg, TRUE); + } + return TRUE; + } + + return FALSE; +} + +BOOL APIENTRY BpexecProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + char buf[16]; + + switch (message) + { + case WM_INITDIALOG: + sprintf(buf, "%08X", bkpt_regv[0].value); + SetDlgItemText(hDlg, IDC_EXECBP, buf); + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK) + { + GetDlgItemText(hDlg, IDC_EXECBP, buf, 9); + + buf[8] = 0; + sscanf(buf, "%x", &bkpt_regv[0].value); + bkpt_regv[0].type = 1; + + EndDialog(hDlg, TRUE); + } else if (LOWORD(wParam) == IDCANCEL) { + EndDialog(hDlg, TRUE); + } + return TRUE; + } + + return FALSE; +} + +BOOL APIENTRY BpcntProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + char buf[16]; + + switch (message) + { + case WM_INITDIALOG: + sprintf(buf, "%08X", bkpt_regv[1].value); + SetDlgItemText(hDlg, IDC_CNTBP, buf); + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK) + { + GetDlgItemText(hDlg, IDC_CNTBP, buf, 9); + + buf[8] = 0; + sscanf(buf, "%x", &bkpt_regv[1].value); + bkpt_regv[1].type = 2; + + EndDialog(hDlg, TRUE); + } else if (LOWORD(wParam) == IDCANCEL) { + EndDialog(hDlg, TRUE); + } + return TRUE; + } + + return FALSE; +} +HINSTANCE m2_hInst; +HWND m2_hWnd; +HWND hIopDlg; + +LRESULT CALLBACK IOP_DISASM(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + hWnd_IOP_debugdisasm = GetDlgItem(hDlg, IDC_DEBUG_DISASM_IOP); + hWnd_IOP_debugscroll = GetDlgItem(hDlg, IDC_DEBUG_SCROLL_IOP); + + SendMessage(hWnd_IOP_debugdisasm, LB_INITSTORAGE, 29, 1131); + SendMessage(hWnd_IOP_debugscroll, SBM_SETRANGE, 0, MAXLONG); + SendMessage(hWnd_IOP_debugscroll, SBM_SETPOS, MAXLONG / 2, TRUE); + RefreshIOPDebugger(); + return (TRUE); + case WM_VSCROLL: + switch ((int) LOWORD(wParam)) + { + case SB_LINEDOWN: DebuggerIOPPC += 0x00000004; RefreshIOPDebugger(); break; + case SB_LINEUP: DebuggerIOPPC -= 0x00000004; RefreshIOPDebugger(); break; + case SB_PAGEDOWN: DebuggerIOPPC += 0x00000029; RefreshIOPDebugger(); break; + case SB_PAGEUP: DebuggerIOPPC -= 0x00000029; RefreshIOPDebugger(); break; + } + return TRUE; + break; + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + case (IDOK || IDCANCEL): + EndDialog(hDlg,TRUE); + return(TRUE); + break; + } + break; + } + + return(FALSE); +} +int CreatePropertySheet2(HWND hwndOwner) +{ + PROPSHEETPAGE psp[1]; + PROPSHEETHEADER psh; + + psp[0].dwSize = sizeof(PROPSHEETPAGE); + psp[0].dwFlags = PSP_USETITLE; + psp[0].hInstance = m2_hInst; + psp[0].pszTemplate = MAKEINTRESOURCE( IDD_IOP_DEBUG); + psp[0].pszIcon = NULL; + psp[0].pfnDlgProc =(DLGPROC)IOP_DISASM; + psp[0].pszTitle = "Iop Disasm"; + psp[0].lParam = 0; + + psh.dwSize = sizeof(PROPSHEETHEADER); + psh.dwFlags = PSH_PROPSHEETPAGE | PSH_MODELESS; + psh.hwndParent =hwndOwner; + psh.hInstance = m2_hInst; + psh.pszIcon = NULL; + psh.pszCaption = (LPSTR) "IOP Debugger"; + psh.nStartPage = 0; + psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE); + psh.ppsp = (LPCPROPSHEETPAGE) &psp; + + return (PropertySheet(&psh)); +} + +/** non-zero if the dialog is currently executing instructions. */ +static int isRunning = 0; + +/** non-zero if the user has requested a break in the execution of instructions. */ +static int breakRequested = 0; + +static +void EnterRunningState(HWND hDlg) +{ + isRunning = 1; + breakRequested = 0; + EnableWindow(GetDlgItem(hDlg, IDC_DEBUG_STEP_OVER), FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_DEBUG_STEP_EE), FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_DEBUG_STEP), FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_DEBUG_SKIP), FALSE); + OpenPlugins(NULL); +} + +static +void EnterHaltedState(HWND hDlg) +{ + isRunning = 0; + breakRequested = 0; + EnableWindow(GetDlgItem(hDlg, IDC_DEBUG_STEP_OVER), TRUE); + EnableWindow(GetDlgItem(hDlg, IDC_DEBUG_STEP_EE), TRUE); + EnableWindow(GetDlgItem(hDlg, IDC_DEBUG_STEP), TRUE); + EnableWindow(GetDlgItem(hDlg, IDC_DEBUG_SKIP), TRUE); +} + +BOOL APIENTRY DebuggerProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + FARPROC jmpproc, dumpproc; + FARPROC bpexecproc, bpcntproc; + u32 oldpc = 0; + + switch (message) + { + case WM_INITDIALOG: + ShowCursor(TRUE); + isRunning = 0; + breakRequested = 0; + + SetWindowText(hDlg, "R5900 Debugger"); + debughWnd=hDlg; + DebuggerPC = 0; + // Clear all breakpoints. + memzero_obj(bkpt_regv); + + hWnd_debugdisasm = GetDlgItem(hDlg, IDC_DEBUG_DISASM); + hWnd_debugscroll = GetDlgItem(hDlg, IDC_DEBUG_SCROLL); + + SendMessage(hWnd_debugdisasm, LB_INITSTORAGE, 29, 1131); + SendMessage(hWnd_debugscroll, SBM_SETRANGE, 0, MAXLONG); + SendMessage(hWnd_debugscroll, SBM_SETPOS, MAXLONG / 2, TRUE); + + hRegDlg = (HWND)CreatePropertySheet(hDlg); + hIopDlg = (HWND)CreatePropertySheet2(hDlg); + UpdateRegs(); + SetWindowPos(hRegDlg, NULL, 525, 0, 600, 515,0 ); + SetWindowPos(hIopDlg, NULL, 0 ,515,600,230,0); + RefreshDebugger(); + RefreshIOPDebugger(); + return TRUE; + + case WM_VSCROLL: + + switch ((int) LOWORD(wParam)) + { + case SB_LINEDOWN: DebuggerPC += 0x00000004; RefreshDebugAll(); break; + case SB_LINEUP: DebuggerPC -= 0x00000004; RefreshDebugAll(); break; + case SB_PAGEDOWN: DebuggerPC += 0x00000074; RefreshDebugAll(); break; + case SB_PAGEUP: DebuggerPC -= 0x00000074; RefreshDebugAll(); break; + } + return TRUE; + + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_DEBUG_STEP: + oldpc = psxRegs.pc; + EnterRunningState(hDlg); + Cpu->Step(); + while(oldpc == psxRegs.pc) Cpu->Step(); + DebuggerPC = 0; + DebuggerIOPPC=0; + EnterHaltedState(hDlg); + RefreshDebugAll(); + return TRUE; + + case IDC_DEBUG_STEP_EE: + EnterRunningState(hDlg); + Cpu->Step(); + EnterHaltedState(hDlg); + DebuggerPC = 0; + DebuggerIOPPC=0; + RefreshDebugAll(); + return TRUE; + + case IDC_DEBUG_STEP_OVER: + /* Step over a subroutine call. */ + /* Note that this may take some time to execute and + * because Cpu->Step() pumps the message loop, we need + * to guard against re-entry. We do that by disabling the step buttons. + */ + EnterRunningState(hDlg); + + memRead32(cpuRegs.pc, &cpuRegs.code); + + { + u32 target_pc = 0; + if (3 == (cpuRegs.code >> 26)){ + /* it's a JAL instruction. */ + target_pc = cpuRegs.pc + 8; + } else if (0x0c == (cpuRegs.code & 0xFF)){ + /* it's a syscall. */ + target_pc = cpuRegs.pc + 4; + } + if (0 != target_pc){ + while(target_pc != cpuRegs.pc && !breakRequested) { + Cpu->Step(); + } + } else { + Cpu->Step(); + } + } + + DebuggerPC = 0; + DebuggerIOPPC=0; + EnterHaltedState(hDlg); + RefreshDebugAll(); + + return TRUE; + + case IDC_DEBUG_SKIP: + cpuRegs.pc+= 4; + DebuggerPC = 0; + RefreshDebugAll(); + return TRUE; + + case IDC_DEBUG_BREAK: + breakRequested = 1; + return TRUE; + + case IDC_DEBUG_GO: + EnterRunningState(hDlg); + for (;;) { + if (breakRequested || HasBreakpoint()) { + Cpu->Step(); + break; + } + Cpu->Step(); + } + DebuggerPC = 0; + DebuggerIOPPC=0; + EnterHaltedState(hDlg); + RefreshDebugAll(); + return TRUE; + + case IDC_DEBUG_RUN_TO_CURSOR: + { + /* Run to the cursor without checking for breakpoints. */ + int sel = SendMessage(hWnd_debugdisasm, LB_GETCURSEL,0,0); + if (sel != LB_ERR){ + const u32 target_pc = DebuggerPC + sel*4; + EnterRunningState(hDlg); + while(target_pc != cpuRegs.pc && !breakRequested) { + Cpu->Step(); + } + DebuggerPC = 0; + DebuggerIOPPC=0; + EnterHaltedState(hDlg); + RefreshDebugAll(); + } + return TRUE; + } + + case IDC_DEBUG_STEP_TO_CURSOR: + { + int sel = SendMessage(hWnd_debugdisasm, LB_GETCURSEL,0,0); + if (sel != LB_ERR){ + const u32 target_pc = DebuggerPC + sel*4; + EnterRunningState(hDlg); + while(target_pc != cpuRegs.pc && !breakRequested) { + if (HasBreakpoint()) { + Cpu->Step(); + break; + } + Cpu->Step(); + } + DebuggerPC = 0; + DebuggerIOPPC=0; + EnterHaltedState(hDlg); + RefreshDebugAll(); + } + return TRUE; + } + + #ifdef PCSX2_DEVBUILD + case IDC_DEBUG_LOG: + if( varLog ) + varLog &= ~0x80000000; + else + varLog |= 0x80000000; + return TRUE; + #endif + + case IDC_DEBUG_RESETTOPC: + DebuggerPC = 0; + DebuggerIOPPC=0; + RefreshDebugAll(); + return TRUE; + + case IDC_DEBUG_JUMP: + jmpproc = MakeProcInstance((FARPROC)JumpProc, MainhInst); + DialogBox(gApp.hInstance, MAKEINTRESOURCE(IDD_JUMP), debughWnd, (DLGPROC)jmpproc); + FreeProcInstance(jmpproc); + + RefreshDebugAll(); + return TRUE; + + case IDC_CPUOP: + // This updated a global opcode counter. + //UpdateR5900op(); + return TRUE; + + case IDC_DEBUG_BP_EXEC: + bpexecproc = MakeProcInstance((FARPROC)BpexecProc, MainhInst); + DialogBox(gApp.hInstance, MAKEINTRESOURCE(IDD_BPEXEC), debughWnd, (DLGPROC)bpexecproc); + FreeProcInstance(bpexecproc); + return TRUE; + + case IDC_DEBUG_BP_COUNT: + bpcntproc = MakeProcInstance((FARPROC)BpcntProc, MainhInst); + DialogBox(gApp.hInstance, MAKEINTRESOURCE(IDD_BPCNT), debughWnd, (DLGPROC)bpcntproc); + FreeProcInstance(bpcntproc); + return TRUE; + + case IDC_DEBUG_BP_CLEAR: + memzero_obj(bkpt_regv); + return TRUE; + + case IDC_DEBUG_DUMP: + dumpproc = MakeProcInstance((FARPROC)DumpProc, MainhInst); + DialogBox(gApp.hInstance, MAKEINTRESOURCE(IDD_DUMP), debughWnd, (DLGPROC)dumpproc); + FreeProcInstance(dumpproc); + return TRUE; + + case IDC_DEBUG_MEMORY: + DialogBox(gApp.hInstance, MAKEINTRESOURCE(IDD_MEMORY), debughWnd, (DLGPROC)MemoryProc); + return TRUE; + + case IDC_DEBUG_CLOSE: + + EndDialog(hRegDlg ,TRUE); + EndDialog(hDlg,TRUE); + EndDialog(hIopDlg,TRUE); + + ClosePlugins(); + isRunning = 0; + return TRUE; + + } + break; + } + + return FALSE; +} + +void RefreshDebugger(void) +{ + unsigned long t; + int cnt; + + if (DebuggerPC == 0) + DebuggerPC = cpuRegs.pc; //- 0x00000038; + + SendMessage(hWnd_debugdisasm, LB_RESETCONTENT, 0, 0); + + for (t = DebuggerPC, cnt = 0; t < (DebuggerPC + 0x00000074); t += 0x00000004, cnt++) + { + char syscall_str[256]; + // Make the opcode. + u32 *mem = (u32*)PSM(t); + if (mem == NULL) { + sprintf(syscall_str, "%8.8lx 00000000: NULL MEMORY", t); + } else { + /* special procesing for syscall. This should probably be moved into the disR5900Fasm() call in the future. */ + if (0x0c == *mem && 0x24030000 == (*(mem-1) & 0xFFFFFF00)){ + /* it's a syscall preceeded by a li v1,$data instruction. */ + u8 bios_call = *(mem-1) & 0xFF; + sprintf(syscall_str, "%08X:\tsyscall\t%s", t, R5900::bios[bios_call]); + } else { + std::string str; + R5900::disR5900Fasm(str, *mem, t); + str.copy( syscall_str, 256 ); + syscall_str[str.length()] = 0; + } + } + SendMessage(hWnd_debugdisasm, LB_ADDSTRING, 0, (LPARAM)syscall_str ); + } +} + +void RefreshIOPDebugger(void) +{ + unsigned long t; + int cnt; + + if (DebuggerIOPPC == 0){ + DebuggerIOPPC = psxRegs.pc; //- 0x00000038; + } + SendMessage(hWnd_IOP_debugdisasm, LB_RESETCONTENT, 0, 0); + + for (t = DebuggerIOPPC, cnt = 0; t < (DebuggerIOPPC + 0x00000029); t += 0x00000004, cnt++) + { + // Make the opcode. + u32 mem = PSXMu32(t); + char *str = R3000A::disR3000Fasm(mem, t); + SendMessage(hWnd_IOP_debugdisasm, LB_ADDSTRING, 0, (LPARAM)str); + } + +} diff --git a/pcsx2/windows/Debugger.h b/pcsx2/windows/Debugger.h new file mode 100644 index 0000000000..0035ea942e --- /dev/null +++ b/pcsx2/windows/Debugger.h @@ -0,0 +1,47 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 +#include +#include + +#define NUM_BREAKPOINTS 8 + +extern BOOL APIENTRY DebuggerProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +extern BOOL APIENTRY MemoryProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +extern void RefreshDebugger(void); + +extern unsigned long opcode_addr; + +struct breakpoints +{ + int type; + unsigned long value; +}; + + +LRESULT CALLBACK R5900reg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK COP0reg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK COP1reg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK COP2Freg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK COP2Creg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK VU1Freg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK VU1Creg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +void UpdateRegs(void); +int CreatePropertySheet(HWND hwndOwner); diff --git a/pcsx2/windows/Debugreg.cpp b/pcsx2/windows/Debugreg.cpp new file mode 100644 index 0000000000..bf12ffa979 --- /dev/null +++ b/pcsx2/windows/Debugreg.cpp @@ -0,0 +1,1486 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "Win32.h" + +#include + +#include "resource.h" +#include "Debugger.h" +#include "Debug.h" +#include "R5900.h" +#include "R3000a.h" +#include "VUmicro.h" + +HINSTANCE m_hInst; +HWND m_hWnd; +char text1[256]; + +// Wow! This module is a lot of copy-paste! +// Between this and DisAsm modules, *someone* needs a new Ctrl-V combo on their keyboard. (air) + +/*R3000a registers handle */ +static HWND IOPGPR0Handle=NULL; +static HWND IOPGPR1Handle=NULL; +static HWND IOPGPR2Handle=NULL; +static HWND IOPGPR3Handle=NULL; +static HWND IOPGPR4Handle=NULL; +static HWND IOPGPR5Handle=NULL; +static HWND IOPGPR6Handle=NULL; +static HWND IOPGPR7Handle=NULL; +static HWND IOPGPR8Handle=NULL; +static HWND IOPGPR9Handle=NULL; +static HWND IOPGPR10Handle=NULL; +static HWND IOPGPR11Handle=NULL; +static HWND IOPGPR12Handle=NULL; +static HWND IOPGPR13Handle=NULL; +static HWND IOPGPR14Handle=NULL; +static HWND IOPGPR15Handle=NULL; +static HWND IOPGPR16Handle=NULL; +static HWND IOPGPR17Handle=NULL; +static HWND IOPGPR18Handle=NULL; +static HWND IOPGPR19Handle=NULL; +static HWND IOPGPR20Handle=NULL; +static HWND IOPGPR21Handle=NULL; +static HWND IOPGPR22Handle=NULL; +static HWND IOPGPR23Handle=NULL; +static HWND IOPGPR24Handle=NULL; +static HWND IOPGPR25Handle=NULL; +static HWND IOPGPR26Handle=NULL; +static HWND IOPGPR27Handle=NULL; +static HWND IOPGPR28Handle=NULL; +static HWND IOPGPR29Handle=NULL; +static HWND IOPGPR30Handle=NULL; +static HWND IOPGPR31Handle=NULL; +static HWND IOPGPRPCHandle=NULL; +static HWND IOPGPRHIHandle=NULL; +static HWND IOPGPRLOHandle=NULL; + +/*R5900 registers handle */ +static HWND GPR0Handle=NULL; +static HWND GPR1Handle=NULL; +static HWND GPR2Handle=NULL; +static HWND GPR3Handle=NULL; +static HWND GPR4Handle=NULL; +static HWND GPR5Handle=NULL; +static HWND GPR6Handle=NULL; +static HWND GPR7Handle=NULL; +static HWND GPR8Handle=NULL; +static HWND GPR9Handle=NULL; +static HWND GPR10Handle=NULL; +static HWND GPR11Handle=NULL; +static HWND GPR12Handle=NULL; +static HWND GPR13Handle=NULL; +static HWND GPR14Handle=NULL; +static HWND GPR15Handle=NULL; +static HWND GPR16Handle=NULL; +static HWND GPR17Handle=NULL; +static HWND GPR18Handle=NULL; +static HWND GPR19Handle=NULL; +static HWND GPR20Handle=NULL; +static HWND GPR21Handle=NULL; +static HWND GPR22Handle=NULL; +static HWND GPR23Handle=NULL; +static HWND GPR24Handle=NULL; +static HWND GPR25Handle=NULL; +static HWND GPR26Handle=NULL; +static HWND GPR27Handle=NULL; +static HWND GPR28Handle=NULL; +static HWND GPR29Handle=NULL; +static HWND GPR30Handle=NULL; +static HWND GPR31Handle=NULL; +static HWND GPRPCHandle=NULL; +static HWND GPRHIHandle=NULL; +static HWND GPRLOHandle=NULL; +/*end of r3000a registers handle */ +/*cop0 registers here */ +static HWND COP00Handle=NULL; +static HWND COP01Handle=NULL; +static HWND COP02Handle=NULL; +static HWND COP03Handle=NULL; +static HWND COP04Handle=NULL; +static HWND COP05Handle=NULL; +static HWND COP06Handle=NULL; +static HWND COP07Handle=NULL; +static HWND COP08Handle=NULL; +static HWND COP09Handle=NULL; +static HWND COP010Handle=NULL; +static HWND COP011Handle=NULL; +static HWND COP012Handle=NULL; +static HWND COP013Handle=NULL; +static HWND COP014Handle=NULL; +static HWND COP015Handle=NULL; +static HWND COP016Handle=NULL; +static HWND COP017Handle=NULL; +static HWND COP018Handle=NULL; +static HWND COP019Handle=NULL; +static HWND COP020Handle=NULL; +static HWND COP021Handle=NULL; +static HWND COP022Handle=NULL; +static HWND COP023Handle=NULL; +static HWND COP024Handle=NULL; +static HWND COP025Handle=NULL; +static HWND COP026Handle=NULL; +static HWND COP027Handle=NULL; +static HWND COP028Handle=NULL; +static HWND COP029Handle=NULL; +static HWND COP030Handle=NULL; +static HWND COP031Handle=NULL; +static HWND COP0PCHandle=NULL; +static HWND COP0HIHandle=NULL; +static HWND COP0LOHandle=NULL; +/*end of cop0 registers */ +/*cop1 registers here */ +static HWND COP10Handle=NULL; +static HWND COP11Handle=NULL; +static HWND COP12Handle=NULL; +static HWND COP13Handle=NULL; +static HWND COP14Handle=NULL; +static HWND COP15Handle=NULL; +static HWND COP16Handle=NULL; +static HWND COP17Handle=NULL; +static HWND COP18Handle=NULL; +static HWND COP19Handle=NULL; +static HWND COP110Handle=NULL; +static HWND COP111Handle=NULL; +static HWND COP112Handle=NULL; +static HWND COP113Handle=NULL; +static HWND COP114Handle=NULL; +static HWND COP115Handle=NULL; +static HWND COP116Handle=NULL; +static HWND COP117Handle=NULL; +static HWND COP118Handle=NULL; +static HWND COP119Handle=NULL; +static HWND COP120Handle=NULL; +static HWND COP121Handle=NULL; +static HWND COP122Handle=NULL; +static HWND COP123Handle=NULL; +static HWND COP124Handle=NULL; +static HWND COP125Handle=NULL; +static HWND COP126Handle=NULL; +static HWND COP127Handle=NULL; +static HWND COP128Handle=NULL; +static HWND COP129Handle=NULL; +static HWND COP130Handle=NULL; +static HWND COP131Handle=NULL; +static HWND COP1C0Handle=NULL; +static HWND COP1C1Handle=NULL; +static HWND COP1ACCHandle=NULL; +/*end of cop1 registers */ +/*cop2 floating registers*/ +static HWND VU0F00Handle=NULL; +static HWND VU0F01Handle=NULL; +static HWND VU0F02Handle=NULL; +static HWND VU0F03Handle=NULL; +static HWND VU0F04Handle=NULL; +static HWND VU0F05Handle=NULL; +static HWND VU0F06Handle=NULL; +static HWND VU0F07Handle=NULL; +static HWND VU0F08Handle=NULL; +static HWND VU0F09Handle=NULL; +static HWND VU0F10Handle=NULL; +static HWND VU0F11Handle=NULL; +static HWND VU0F12Handle=NULL; +static HWND VU0F13Handle=NULL; +static HWND VU0F14Handle=NULL; +static HWND VU0F15Handle=NULL; +static HWND VU0F16Handle=NULL; +static HWND VU0F17Handle=NULL; +static HWND VU0F18Handle=NULL; +static HWND VU0F19Handle=NULL; +static HWND VU0F20Handle=NULL; +static HWND VU0F21Handle=NULL; +static HWND VU0F22Handle=NULL; +static HWND VU0F23Handle=NULL; +static HWND VU0F24Handle=NULL; +static HWND VU0F25Handle=NULL; +static HWND VU0F26Handle=NULL; +static HWND VU0F27Handle=NULL; +static HWND VU0F28Handle=NULL; +static HWND VU0F29Handle=NULL; +static HWND VU0F30Handle=NULL; +static HWND VU0F31Handle=NULL; +/*end of cop2 floating registers*/ +/*cop2 control registers */ +static HWND VU0C00Handle=NULL; +static HWND VU0C01Handle=NULL; +static HWND VU0C02Handle=NULL; +static HWND VU0C03Handle=NULL; +static HWND VU0C04Handle=NULL; +static HWND VU0C05Handle=NULL; +static HWND VU0C06Handle=NULL; +static HWND VU0C07Handle=NULL; +static HWND VU0C08Handle=NULL; +static HWND VU0C09Handle=NULL; +static HWND VU0C10Handle=NULL; +static HWND VU0C11Handle=NULL; +static HWND VU0C12Handle=NULL; +static HWND VU0C13Handle=NULL; +static HWND VU0C14Handle=NULL; +static HWND VU0C15Handle=NULL; +static HWND VU0C16Handle=NULL; +static HWND VU0C17Handle=NULL; +static HWND VU0C18Handle=NULL; +static HWND VU0C19Handle=NULL; +static HWND VU0C20Handle=NULL; +static HWND VU0C21Handle=NULL; +static HWND VU0C22Handle=NULL; +static HWND VU0C23Handle=NULL; +static HWND VU0C24Handle=NULL; +static HWND VU0C25Handle=NULL; +static HWND VU0C26Handle=NULL; +static HWND VU0C27Handle=NULL; +static HWND VU0C28Handle=NULL; +static HWND VU0C29Handle=NULL; +static HWND VU0C30Handle=NULL; +static HWND VU0C31Handle=NULL; +static HWND VU0ACCHandle=NULL; +/*end of cop2 control registers */ +/*vu1 floating registers*/ +static HWND VU1F00Handle=NULL; +static HWND VU1F01Handle=NULL; +static HWND VU1F02Handle=NULL; +static HWND VU1F03Handle=NULL; +static HWND VU1F04Handle=NULL; +static HWND VU1F05Handle=NULL; +static HWND VU1F06Handle=NULL; +static HWND VU1F07Handle=NULL; +static HWND VU1F08Handle=NULL; +static HWND VU1F09Handle=NULL; +static HWND VU1F10Handle=NULL; +static HWND VU1F11Handle=NULL; +static HWND VU1F12Handle=NULL; +static HWND VU1F13Handle=NULL; +static HWND VU1F14Handle=NULL; +static HWND VU1F15Handle=NULL; +static HWND VU1F16Handle=NULL; +static HWND VU1F17Handle=NULL; +static HWND VU1F18Handle=NULL; +static HWND VU1F19Handle=NULL; +static HWND VU1F20Handle=NULL; +static HWND VU1F21Handle=NULL; +static HWND VU1F22Handle=NULL; +static HWND VU1F23Handle=NULL; +static HWND VU1F24Handle=NULL; +static HWND VU1F25Handle=NULL; +static HWND VU1F26Handle=NULL; +static HWND VU1F27Handle=NULL; +static HWND VU1F28Handle=NULL; +static HWND VU1F29Handle=NULL; +static HWND VU1F30Handle=NULL; +static HWND VU1F31Handle=NULL; +/*end of vu1 floating registers*/ +/*vu1 control registers */ +static HWND VU1C00Handle=NULL; +static HWND VU1C01Handle=NULL; +static HWND VU1C02Handle=NULL; +static HWND VU1C03Handle=NULL; +static HWND VU1C04Handle=NULL; +static HWND VU1C05Handle=NULL; +static HWND VU1C06Handle=NULL; +static HWND VU1C07Handle=NULL; +static HWND VU1C08Handle=NULL; +static HWND VU1C09Handle=NULL; +static HWND VU1C10Handle=NULL; +static HWND VU1C11Handle=NULL; +static HWND VU1C12Handle=NULL; +static HWND VU1C13Handle=NULL; +static HWND VU1C14Handle=NULL; +static HWND VU1C15Handle=NULL; +static HWND VU1C16Handle=NULL; +static HWND VU1C17Handle=NULL; +static HWND VU1C18Handle=NULL; +static HWND VU1C19Handle=NULL; +static HWND VU1C20Handle=NULL; +static HWND VU1C21Handle=NULL; +static HWND VU1C22Handle=NULL; +static HWND VU1C23Handle=NULL; +static HWND VU1C24Handle=NULL; +static HWND VU1C25Handle=NULL; +static HWND VU1C26Handle=NULL; +static HWND VU1C27Handle=NULL; +static HWND VU1C28Handle=NULL; +static HWND VU1C29Handle=NULL; +static HWND VU1C30Handle=NULL; +static HWND VU1C31Handle=NULL; +static HWND VU1ACCHandle=NULL; +/*end of vu1 control registers */ + +LRESULT CALLBACK R3000reg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +//comctl32 lib must add to project.. +int CreatePropertySheet(HWND hwndOwner) +{ + + PROPSHEETPAGE psp[7]; + PROPSHEETHEADER psh; + + psp[0].dwSize = sizeof(PROPSHEETPAGE); + psp[0].dwFlags = PSP_USETITLE; + psp[0].hInstance = m_hInst; + psp[0].pszTemplate = MAKEINTRESOURCE( IDD_GPREGS); + psp[0].pszIcon = NULL; + psp[0].pfnDlgProc =(DLGPROC)R5900reg; + psp[0].pszTitle = "R5900"; + psp[0].lParam = 0; + + psp[1].dwSize = sizeof(PROPSHEETPAGE); + psp[1].dwFlags = PSP_USETITLE; + psp[1].hInstance = m_hInst; + psp[1].pszTemplate = MAKEINTRESOURCE( IDD_CP0REGS ); + psp[1].pszIcon = NULL; + psp[1].pfnDlgProc =(DLGPROC)COP0reg; + psp[1].pszTitle = "COP0"; + psp[1].lParam = 0; + + psp[2].dwSize = sizeof(PROPSHEETPAGE); + psp[2].dwFlags = PSP_USETITLE; + psp[2].hInstance = m_hInst; + psp[2].pszTemplate = MAKEINTRESOURCE( IDD_CP1REGS ); + psp[2].pszIcon = NULL; + psp[2].pfnDlgProc =(DLGPROC)COP1reg; + psp[2].pszTitle = "COP1"; + psp[2].lParam = 0; + + psp[3].dwSize = sizeof(PROPSHEETPAGE); + psp[3].dwFlags = PSP_USETITLE; + psp[3].hInstance = m_hInst; + psp[3].pszTemplate = MAKEINTRESOURCE( IDD_VU0REGS ); + psp[3].pszIcon = NULL; + psp[3].pfnDlgProc =(DLGPROC)COP2Freg; + psp[3].pszTitle = "COP2F"; + psp[3].lParam = 0; + + psp[4].dwSize = sizeof(PROPSHEETPAGE); + psp[4].dwFlags = PSP_USETITLE; + psp[4].hInstance = m_hInst; + psp[4].pszTemplate = MAKEINTRESOURCE( IDD_VU0INTEGER ); + psp[4].pszIcon = NULL; + psp[4].pfnDlgProc =(DLGPROC)COP2Creg; + psp[4].pszTitle = "COP2C"; + psp[4].lParam = 0; + + psp[5].dwSize = sizeof(PROPSHEETPAGE); + psp[5].dwFlags = PSP_USETITLE; + psp[5].hInstance = m_hInst; + psp[5].pszTemplate = MAKEINTRESOURCE( IDD_VU1REGS ); + psp[5].pszIcon = NULL; + psp[5].pfnDlgProc =(DLGPROC)VU1Freg; + psp[5].pszTitle = "VU1F"; + psp[5].lParam = 0; + + psp[6].dwSize = sizeof(PROPSHEETPAGE); + psp[6].dwFlags = PSP_USETITLE; + psp[6].hInstance = m_hInst; + psp[6].pszTemplate = MAKEINTRESOURCE( IDD_VU1INTEGER ); + psp[6].pszIcon = NULL; + psp[6].pfnDlgProc =(DLGPROC)VU1Creg; + psp[6].pszTitle = "VU1C"; + psp[6].lParam = 0; + + psp[6].dwSize = sizeof(PROPSHEETPAGE); + psp[6].dwFlags = PSP_USETITLE; + psp[6].hInstance = m_hInst; + psp[6].pszTemplate = MAKEINTRESOURCE( IDD_IOPREGS ); + psp[6].pszIcon = NULL; + psp[6].pfnDlgProc =(DLGPROC)R3000reg; + psp[6].pszTitle = "R3000"; + psp[6].lParam = 0; + + psh.dwSize = sizeof(PROPSHEETHEADER); + psh.dwFlags = PSH_PROPSHEETPAGE | PSH_MODELESS; + psh.hwndParent =hwndOwner; + psh.hInstance = m_hInst; + psh.pszIcon = NULL; + psh.pszCaption = (LPSTR) "Debugger"; + psh.nStartPage = 0; + psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE); + psh.ppsp = (LPCPROPSHEETPAGE) &psp; + + return (PropertySheet(&psh)); + +} +LRESULT CALLBACK R3000reg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + IOPGPR0Handle=GetDlgItem(hDlg,IDC_IOPGPR0); + IOPGPR1Handle=GetDlgItem(hDlg,IDC_IOPGPR1); + IOPGPR2Handle=GetDlgItem(hDlg,IDC_IOPGPR2); + IOPGPR3Handle=GetDlgItem(hDlg,IDC_IOPGPR3); + IOPGPR4Handle=GetDlgItem(hDlg,IDC_IOPGPR4); + IOPGPR5Handle=GetDlgItem(hDlg,IDC_IOPGPR5); + IOPGPR6Handle=GetDlgItem(hDlg,IDC_IOPGPR6); + IOPGPR7Handle=GetDlgItem(hDlg,IDC_IOPGPR7); + IOPGPR8Handle=GetDlgItem(hDlg,IDC_IOPGPR8); + IOPGPR9Handle=GetDlgItem(hDlg,IDC_IOPGPR9); + IOPGPR10Handle=GetDlgItem(hDlg,IDC_IOPGPR10); + IOPGPR11Handle=GetDlgItem(hDlg,IDC_IOPGPR11); + IOPGPR12Handle=GetDlgItem(hDlg,IDC_IOPGPR12); + IOPGPR13Handle=GetDlgItem(hDlg,IDC_IOPGPR13); + IOPGPR14Handle=GetDlgItem(hDlg,IDC_IOPGPR14); + IOPGPR15Handle=GetDlgItem(hDlg,IDC_IOPGPR15); + IOPGPR16Handle=GetDlgItem(hDlg,IDC_IOPGPR16); + IOPGPR17Handle=GetDlgItem(hDlg,IDC_IOPGPR17); + IOPGPR18Handle=GetDlgItem(hDlg,IDC_IOPGPR18); + IOPGPR19Handle=GetDlgItem(hDlg,IDC_IOPGPR19); + IOPGPR20Handle=GetDlgItem(hDlg,IDC_IOPGPR20); + IOPGPR21Handle=GetDlgItem(hDlg,IDC_IOPGPR21); + IOPGPR22Handle=GetDlgItem(hDlg,IDC_IOPGPR22); + IOPGPR23Handle=GetDlgItem(hDlg,IDC_IOPGPR23); + IOPGPR24Handle=GetDlgItem(hDlg,IDC_IOPGPR24); + IOPGPR25Handle=GetDlgItem(hDlg,IDC_IOPGPR25); + IOPGPR26Handle=GetDlgItem(hDlg,IDC_IOPGPR26); + IOPGPR27Handle=GetDlgItem(hDlg,IDC_IOPGPR27); + IOPGPR28Handle=GetDlgItem(hDlg,IDC_IOPGPR28); + IOPGPR29Handle=GetDlgItem(hDlg,IDC_IOPGPR29); + IOPGPR30Handle=GetDlgItem(hDlg,IDC_IOPGPR30); + IOPGPR31Handle=GetDlgItem(hDlg,IDC_IOPGPR31); + IOPGPRPCHandle=GetDlgItem(hDlg,IDC_IOPGPR_PC); + IOPGPRHIHandle=GetDlgItem(hDlg,IDC_IOPGPR_HI); + IOPGPRLOHandle=GetDlgItem(hDlg,IDC_IOPGPR_LO); + UpdateRegs(); + return (TRUE); + break; + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + case (IDOK || IDCANCEL): + EndDialog(hDlg,TRUE); + return(TRUE); + break; + + } + break; + } + + return(FALSE); +} +LRESULT CALLBACK R5900reg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + GPR0Handle=GetDlgItem(hDlg,IDC_GPR0); + GPR1Handle=GetDlgItem(hDlg,IDC_GPR1); + GPR2Handle=GetDlgItem(hDlg,IDC_GPR2); + GPR3Handle=GetDlgItem(hDlg,IDC_GPR3); + GPR4Handle=GetDlgItem(hDlg,IDC_GPR4); + GPR5Handle=GetDlgItem(hDlg,IDC_GPR5); + GPR6Handle=GetDlgItem(hDlg,IDC_GPR6); + GPR7Handle=GetDlgItem(hDlg,IDC_GPR7); + GPR8Handle=GetDlgItem(hDlg,IDC_GPR8); + GPR9Handle=GetDlgItem(hDlg,IDC_GPR9); + GPR10Handle=GetDlgItem(hDlg,IDC_GPR10); + GPR11Handle=GetDlgItem(hDlg,IDC_GPR11); + GPR12Handle=GetDlgItem(hDlg,IDC_GPR12); + GPR13Handle=GetDlgItem(hDlg,IDC_GPR13); + GPR14Handle=GetDlgItem(hDlg,IDC_GPR14); + GPR15Handle=GetDlgItem(hDlg,IDC_GPR15); + GPR16Handle=GetDlgItem(hDlg,IDC_GPR16); + GPR17Handle=GetDlgItem(hDlg,IDC_GPR17); + GPR18Handle=GetDlgItem(hDlg,IDC_GPR18); + GPR19Handle=GetDlgItem(hDlg,IDC_GPR19); + GPR20Handle=GetDlgItem(hDlg,IDC_GPR20); + GPR21Handle=GetDlgItem(hDlg,IDC_GPR21); + GPR22Handle=GetDlgItem(hDlg,IDC_GPR22); + GPR23Handle=GetDlgItem(hDlg,IDC_GPR23); + GPR24Handle=GetDlgItem(hDlg,IDC_GPR24); + GPR25Handle=GetDlgItem(hDlg,IDC_GPR25); + GPR26Handle=GetDlgItem(hDlg,IDC_GPR26); + GPR27Handle=GetDlgItem(hDlg,IDC_GPR27); + GPR28Handle=GetDlgItem(hDlg,IDC_GPR28); + GPR29Handle=GetDlgItem(hDlg,IDC_GPR29); + GPR30Handle=GetDlgItem(hDlg,IDC_GPR30); + GPR31Handle=GetDlgItem(hDlg,IDC_GPR31); + GPRPCHandle=GetDlgItem(hDlg,IDC_GPR_PC); + GPRHIHandle=GetDlgItem(hDlg,IDC_GPR_HI); + GPRLOHandle=GetDlgItem(hDlg,IDC_GPR_LO); + UpdateRegs(); + return (TRUE); + break; + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + case (IDOK || IDCANCEL): + EndDialog(hDlg,TRUE); + return(TRUE); + break; + + } + break; + } + + return(FALSE); +} +LRESULT CALLBACK COP0reg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + COP00Handle=GetDlgItem(hDlg,IDC_CP00); + COP01Handle=GetDlgItem(hDlg,IDC_CP01); + COP02Handle=GetDlgItem(hDlg,IDC_CP02); + COP03Handle=GetDlgItem(hDlg,IDC_CP03); + COP04Handle=GetDlgItem(hDlg,IDC_CP04); + COP05Handle=GetDlgItem(hDlg,IDC_CP05); + COP06Handle=GetDlgItem(hDlg,IDC_CP06); + COP07Handle=GetDlgItem(hDlg,IDC_CP07); + COP08Handle=GetDlgItem(hDlg,IDC_CP08); + COP09Handle=GetDlgItem(hDlg,IDC_CP09); + COP010Handle=GetDlgItem(hDlg,IDC_CP010); + COP011Handle=GetDlgItem(hDlg,IDC_CP011); + COP012Handle=GetDlgItem(hDlg,IDC_CP012); + COP013Handle=GetDlgItem(hDlg,IDC_CP013); + COP014Handle=GetDlgItem(hDlg,IDC_CP014); + COP015Handle=GetDlgItem(hDlg,IDC_CP015); + COP016Handle=GetDlgItem(hDlg,IDC_CP016); + COP017Handle=GetDlgItem(hDlg,IDC_CP017); + COP018Handle=GetDlgItem(hDlg,IDC_CP018); + COP019Handle=GetDlgItem(hDlg,IDC_CP019); + COP020Handle=GetDlgItem(hDlg,IDC_CP020); + COP021Handle=GetDlgItem(hDlg,IDC_CP021); + COP022Handle=GetDlgItem(hDlg,IDC_CP022); + COP023Handle=GetDlgItem(hDlg,IDC_CP023); + COP024Handle=GetDlgItem(hDlg,IDC_CP024); + COP025Handle=GetDlgItem(hDlg,IDC_CP025); + COP026Handle=GetDlgItem(hDlg,IDC_CP026); + COP027Handle=GetDlgItem(hDlg,IDC_CP027); + COP028Handle=GetDlgItem(hDlg,IDC_CP028); + COP029Handle=GetDlgItem(hDlg,IDC_CP029); + COP030Handle=GetDlgItem(hDlg,IDC_CP030); + COP031Handle=GetDlgItem(hDlg,IDC_CP031); + UpdateRegs(); + return (TRUE); + break; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case (IDOK || IDCANCEL): + EndDialog(hDlg,TRUE); + return(TRUE); + break; + + } + break; + } + + return(FALSE); +} +LRESULT CALLBACK COP1reg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + COP10Handle=GetDlgItem(hDlg,IDC_FP0); + COP11Handle=GetDlgItem(hDlg,IDC_FP1); + COP12Handle=GetDlgItem(hDlg,IDC_FP2); + COP13Handle=GetDlgItem(hDlg,IDC_FP3); + COP14Handle=GetDlgItem(hDlg,IDC_FP4); + COP15Handle=GetDlgItem(hDlg,IDC_FP5); + COP16Handle=GetDlgItem(hDlg,IDC_FP6); + COP17Handle=GetDlgItem(hDlg,IDC_FP7); + COP18Handle=GetDlgItem(hDlg,IDC_FP8); + COP19Handle=GetDlgItem(hDlg,IDC_FP9); + COP110Handle=GetDlgItem(hDlg,IDC_FP10); + COP111Handle=GetDlgItem(hDlg,IDC_FP11); + COP112Handle=GetDlgItem(hDlg,IDC_FP12); + COP113Handle=GetDlgItem(hDlg,IDC_FP13); + COP114Handle=GetDlgItem(hDlg,IDC_FP14); + COP115Handle=GetDlgItem(hDlg,IDC_FP15); + COP116Handle=GetDlgItem(hDlg,IDC_FP16); + COP117Handle=GetDlgItem(hDlg,IDC_FP17); + COP118Handle=GetDlgItem(hDlg,IDC_FP18); + COP119Handle=GetDlgItem(hDlg,IDC_FP19); + COP120Handle=GetDlgItem(hDlg,IDC_FP20); + COP121Handle=GetDlgItem(hDlg,IDC_FP21); + COP122Handle=GetDlgItem(hDlg,IDC_FP22); + COP123Handle=GetDlgItem(hDlg,IDC_FP23); + COP124Handle=GetDlgItem(hDlg,IDC_FP24); + COP125Handle=GetDlgItem(hDlg,IDC_FP25); + COP126Handle=GetDlgItem(hDlg,IDC_FP26); + COP127Handle=GetDlgItem(hDlg,IDC_FP27); + COP128Handle=GetDlgItem(hDlg,IDC_FP28); + COP129Handle=GetDlgItem(hDlg,IDC_FP29); + COP130Handle=GetDlgItem(hDlg,IDC_FP30); + COP131Handle=GetDlgItem(hDlg,IDC_FP31); + COP1C0Handle=GetDlgItem(hDlg,IDC_FCR0); + COP1C1Handle=GetDlgItem(hDlg,IDC_FCR31); + COP1ACCHandle=GetDlgItem(hDlg,IDC_FPU_ACC); + UpdateRegs(); + return (TRUE); + break; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case (IDOK || IDCANCEL): + EndDialog(hDlg,TRUE); + return(TRUE); + break; + + } + break; + } + + return(FALSE); +} + +LRESULT CALLBACK COP2Freg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + VU0F00Handle=GetDlgItem(hDlg,IDC_VU0_VF00); + VU0F01Handle=GetDlgItem(hDlg,IDC_VU0_VF01); + VU0F02Handle=GetDlgItem(hDlg,IDC_VU0_VF02); + VU0F03Handle=GetDlgItem(hDlg,IDC_VU0_VF03); + VU0F04Handle=GetDlgItem(hDlg,IDC_VU0_VF04); + VU0F05Handle=GetDlgItem(hDlg,IDC_VU0_VF05); + VU0F06Handle=GetDlgItem(hDlg,IDC_VU0_VF06); + VU0F07Handle=GetDlgItem(hDlg,IDC_VU0_VF07); + VU0F08Handle=GetDlgItem(hDlg,IDC_VU0_VF08); + VU0F09Handle=GetDlgItem(hDlg,IDC_VU0_VF09); + VU0F10Handle=GetDlgItem(hDlg,IDC_VU0_VF10); + VU0F11Handle=GetDlgItem(hDlg,IDC_VU0_VF11); + VU0F12Handle=GetDlgItem(hDlg,IDC_VU0_VF12); + VU0F13Handle=GetDlgItem(hDlg,IDC_VU0_VF13); + VU0F14Handle=GetDlgItem(hDlg,IDC_VU0_VF14); + VU0F15Handle=GetDlgItem(hDlg,IDC_VU0_VF15); + VU0F16Handle=GetDlgItem(hDlg,IDC_VU0_VF16); + VU0F17Handle=GetDlgItem(hDlg,IDC_VU0_VF17); + VU0F18Handle=GetDlgItem(hDlg,IDC_VU0_VF18); + VU0F19Handle=GetDlgItem(hDlg,IDC_VU0_VF19); + VU0F20Handle=GetDlgItem(hDlg,IDC_VU0_VF20); + VU0F21Handle=GetDlgItem(hDlg,IDC_VU0_VF21); + VU0F22Handle=GetDlgItem(hDlg,IDC_VU0_VF22); + VU0F23Handle=GetDlgItem(hDlg,IDC_VU0_VF23); + VU0F24Handle=GetDlgItem(hDlg,IDC_VU0_VF24); + VU0F25Handle=GetDlgItem(hDlg,IDC_VU0_VF25); + VU0F26Handle=GetDlgItem(hDlg,IDC_VU0_VF26); + VU0F27Handle=GetDlgItem(hDlg,IDC_VU0_VF27); + VU0F28Handle=GetDlgItem(hDlg,IDC_VU0_VF28); + VU0F29Handle=GetDlgItem(hDlg,IDC_VU0_VF29); + VU0F30Handle=GetDlgItem(hDlg,IDC_VU0_VF30); + VU0F31Handle=GetDlgItem(hDlg,IDC_VU0_VF31); + UpdateRegs(); + return (TRUE); + break; + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + case (IDOK || IDCANCEL): + EndDialog(hDlg,TRUE); + return(TRUE); + break; + + } + break; + } + + return(FALSE); +} +LRESULT CALLBACK COP2Creg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + VU0C00Handle=GetDlgItem(hDlg,IDC_VU0_VI00); + VU0C01Handle=GetDlgItem(hDlg,IDC_VU0_VI01); + VU0C02Handle=GetDlgItem(hDlg,IDC_VU0_VI02); + VU0C03Handle=GetDlgItem(hDlg,IDC_VU0_VI03); + VU0C04Handle=GetDlgItem(hDlg,IDC_VU0_VI04); + VU0C05Handle=GetDlgItem(hDlg,IDC_VU0_VI05); + VU0C06Handle=GetDlgItem(hDlg,IDC_VU0_VI06); + VU0C07Handle=GetDlgItem(hDlg,IDC_VU0_VI07); + VU0C08Handle=GetDlgItem(hDlg,IDC_VU0_VI08); + VU0C09Handle=GetDlgItem(hDlg,IDC_VU0_VI09); + VU0C10Handle=GetDlgItem(hDlg,IDC_VU0_VI10); + VU0C11Handle=GetDlgItem(hDlg,IDC_VU0_VI11); + VU0C12Handle=GetDlgItem(hDlg,IDC_VU0_VI12); + VU0C13Handle=GetDlgItem(hDlg,IDC_VU0_VI13); + VU0C14Handle=GetDlgItem(hDlg,IDC_VU0_VI14); + VU0C15Handle=GetDlgItem(hDlg,IDC_VU0_VI15); + VU0C16Handle=GetDlgItem(hDlg,IDC_VU0_VI16); + VU0C17Handle=GetDlgItem(hDlg,IDC_VU0_VI17); + VU0C18Handle=GetDlgItem(hDlg,IDC_VU0_VI18); + VU0C19Handle=GetDlgItem(hDlg,IDC_VU0_VI19); + VU0C20Handle=GetDlgItem(hDlg,IDC_VU0_VI20); + VU0C21Handle=GetDlgItem(hDlg,IDC_VU0_VI21); + VU0C22Handle=GetDlgItem(hDlg,IDC_VU0_VI22); + VU0C23Handle=GetDlgItem(hDlg,IDC_VU0_VI23); + VU0C24Handle=GetDlgItem(hDlg,IDC_VU0_VI24); + VU0C25Handle=GetDlgItem(hDlg,IDC_VU0_VI25); + VU0C26Handle=GetDlgItem(hDlg,IDC_VU0_VI26); + VU0C27Handle=GetDlgItem(hDlg,IDC_VU0_VI27); + VU0C28Handle=GetDlgItem(hDlg,IDC_VU0_VI28); + VU0C29Handle=GetDlgItem(hDlg,IDC_VU0_VI29); + VU0C30Handle=GetDlgItem(hDlg,IDC_VU0_VI30); + VU0C31Handle=GetDlgItem(hDlg,IDC_VU0_VI31); + VU0ACCHandle=GetDlgItem(hDlg,IDC_VU0_ACC); + UpdateRegs(); + return (TRUE); + break; + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + case (IDOK || IDCANCEL): + EndDialog(hDlg,TRUE); + return(TRUE); + break; + + } + break; + } + + return(FALSE); +} + + +LRESULT CALLBACK VU1Freg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + VU1F00Handle=GetDlgItem(hDlg,IDC_VU1_VF00); + VU1F01Handle=GetDlgItem(hDlg,IDC_VU1_VF01); + VU1F02Handle=GetDlgItem(hDlg,IDC_VU1_VF02); + VU1F03Handle=GetDlgItem(hDlg,IDC_VU1_VF03); + VU1F04Handle=GetDlgItem(hDlg,IDC_VU1_VF04); + VU1F05Handle=GetDlgItem(hDlg,IDC_VU1_VF05); + VU1F06Handle=GetDlgItem(hDlg,IDC_VU1_VF06); + VU1F07Handle=GetDlgItem(hDlg,IDC_VU1_VF07); + VU1F08Handle=GetDlgItem(hDlg,IDC_VU1_VF08); + VU1F09Handle=GetDlgItem(hDlg,IDC_VU1_VF09); + VU1F10Handle=GetDlgItem(hDlg,IDC_VU1_VF10); + VU1F11Handle=GetDlgItem(hDlg,IDC_VU1_VF11); + VU1F12Handle=GetDlgItem(hDlg,IDC_VU1_VF12); + VU1F13Handle=GetDlgItem(hDlg,IDC_VU1_VF13); + VU1F14Handle=GetDlgItem(hDlg,IDC_VU1_VF14); + VU1F15Handle=GetDlgItem(hDlg,IDC_VU1_VF15); + VU1F16Handle=GetDlgItem(hDlg,IDC_VU1_VF16); + VU1F17Handle=GetDlgItem(hDlg,IDC_VU1_VF17); + VU1F18Handle=GetDlgItem(hDlg,IDC_VU1_VF18); + VU1F19Handle=GetDlgItem(hDlg,IDC_VU1_VF19); + VU1F20Handle=GetDlgItem(hDlg,IDC_VU1_VF20); + VU1F21Handle=GetDlgItem(hDlg,IDC_VU1_VF21); + VU1F22Handle=GetDlgItem(hDlg,IDC_VU1_VF22); + VU1F23Handle=GetDlgItem(hDlg,IDC_VU1_VF23); + VU1F24Handle=GetDlgItem(hDlg,IDC_VU1_VF24); + VU1F25Handle=GetDlgItem(hDlg,IDC_VU1_VF25); + VU1F26Handle=GetDlgItem(hDlg,IDC_VU1_VF26); + VU1F27Handle=GetDlgItem(hDlg,IDC_VU1_VF27); + VU1F28Handle=GetDlgItem(hDlg,IDC_VU1_VF28); + VU1F29Handle=GetDlgItem(hDlg,IDC_VU1_VF29); + VU1F30Handle=GetDlgItem(hDlg,IDC_VU1_VF30); + VU1F31Handle=GetDlgItem(hDlg,IDC_VU1_VF31); + UpdateRegs(); + return (TRUE); + break; + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + case (IDOK || IDCANCEL): + EndDialog(hDlg,TRUE); + return(TRUE); + break; + + } + break; + } + + return(FALSE); +} +LRESULT CALLBACK VU1Creg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + VU1C00Handle=GetDlgItem(hDlg,IDC_VU1_VI00); + VU1C01Handle=GetDlgItem(hDlg,IDC_VU1_VI01); + VU1C02Handle=GetDlgItem(hDlg,IDC_VU1_VI02); + VU1C03Handle=GetDlgItem(hDlg,IDC_VU1_VI03); + VU1C04Handle=GetDlgItem(hDlg,IDC_VU1_VI04); + VU1C05Handle=GetDlgItem(hDlg,IDC_VU1_VI05); + VU1C06Handle=GetDlgItem(hDlg,IDC_VU1_VI06); + VU1C07Handle=GetDlgItem(hDlg,IDC_VU1_VI07); + VU1C08Handle=GetDlgItem(hDlg,IDC_VU1_VI08); + VU1C09Handle=GetDlgItem(hDlg,IDC_VU1_VI09); + VU1C10Handle=GetDlgItem(hDlg,IDC_VU1_VI10); + VU1C11Handle=GetDlgItem(hDlg,IDC_VU1_VI11); + VU1C12Handle=GetDlgItem(hDlg,IDC_VU1_VI12); + VU1C13Handle=GetDlgItem(hDlg,IDC_VU1_VI13); + VU1C14Handle=GetDlgItem(hDlg,IDC_VU1_VI14); + VU1C15Handle=GetDlgItem(hDlg,IDC_VU1_VI15); + VU1C16Handle=GetDlgItem(hDlg,IDC_VU1_VI16); + VU1C17Handle=GetDlgItem(hDlg,IDC_VU1_VI17); + VU1C18Handle=GetDlgItem(hDlg,IDC_VU1_VI18); + VU1C19Handle=GetDlgItem(hDlg,IDC_VU1_VI19); + VU1C20Handle=GetDlgItem(hDlg,IDC_VU1_VI20); + VU1C21Handle=GetDlgItem(hDlg,IDC_VU1_VI21); + VU1C22Handle=GetDlgItem(hDlg,IDC_VU1_VI22); + VU1C23Handle=GetDlgItem(hDlg,IDC_VU1_VI23); + VU1C24Handle=GetDlgItem(hDlg,IDC_VU1_VI24); + VU1C25Handle=GetDlgItem(hDlg,IDC_VU1_VI25); + VU1C26Handle=GetDlgItem(hDlg,IDC_VU1_VI26); + VU1C27Handle=GetDlgItem(hDlg,IDC_VU1_VI27); + VU1C28Handle=GetDlgItem(hDlg,IDC_VU1_VI28); + VU1C29Handle=GetDlgItem(hDlg,IDC_VU1_VI29); + VU1C30Handle=GetDlgItem(hDlg,IDC_VU1_VI30); + VU1C31Handle=GetDlgItem(hDlg,IDC_VU1_VI31); + VU1ACCHandle=GetDlgItem(hDlg,IDC_VU1_ACC); + UpdateRegs(); + return (TRUE); + break; + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + case (IDOK || IDCANCEL): + EndDialog(hDlg,TRUE); + return(TRUE); + break; + + } + break; + } + + return(FALSE); +} + +void UpdateRegs(void) +{ + + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[0]); + SendMessage(IOPGPR0Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[1]); + SendMessage(IOPGPR1Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[2]); + SendMessage(IOPGPR2Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[3]); + SendMessage(IOPGPR3Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[4]); + SendMessage(IOPGPR4Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[5]); + SendMessage(IOPGPR5Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[6]); + SendMessage(IOPGPR6Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[7]); + SendMessage(IOPGPR7Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[8]); + SendMessage(IOPGPR8Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[9]); + SendMessage(IOPGPR9Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[10]); + SendMessage(IOPGPR10Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[11]); + SendMessage(IOPGPR11Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[12]); + SendMessage(IOPGPR12Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[13]); + SendMessage(IOPGPR13Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[14]); + SendMessage(IOPGPR14Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[15]); + SendMessage(IOPGPR15Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[16]); + SendMessage(IOPGPR16Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[17]); + SendMessage(IOPGPR17Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[18]); + SendMessage(IOPGPR18Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[19]); + SendMessage(IOPGPR19Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[20]); + SendMessage(IOPGPR20Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[21]); + SendMessage(IOPGPR21Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[22]); + SendMessage(IOPGPR22Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[23]); + SendMessage(IOPGPR23Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[24]); + SendMessage(IOPGPR24Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[25]); + SendMessage(IOPGPR25Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[26]); + SendMessage(IOPGPR26Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[27]); + SendMessage(IOPGPR27Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[28]); + SendMessage(IOPGPR28Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[29]); + SendMessage(IOPGPR29Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[30]); + SendMessage(IOPGPR30Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[31]); + SendMessage(IOPGPR31Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",psxRegs.pc ); + SendMessage(IOPGPRPCHandle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[32]); + SendMessage(IOPGPRHIHandle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X\0",psxRegs.GPR.r[33]); + SendMessage(IOPGPRLOHandle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[0].UL[3],cpuRegs.GPR.r[0].UL[2],cpuRegs.GPR.r[0].UL[1],cpuRegs.GPR.r[0].UL[0] ); + SendMessage(GPR0Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[1].UL[3], cpuRegs.GPR.r[1].UL[2],cpuRegs.GPR.r[1].UL[1],cpuRegs.GPR.r[1].UL[0] ); + SendMessage(GPR1Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[2].UL[3],cpuRegs.GPR.r[2].UL[2], cpuRegs.GPR.r[2].UL[1],cpuRegs.GPR.r[2].UL[0]); + SendMessage(GPR2Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[3].UL[3],cpuRegs.GPR.r[3].UL[2], cpuRegs.GPR.r[3].UL[1],cpuRegs.GPR.r[3].UL[0] ); + SendMessage(GPR3Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[4].UL[3],cpuRegs.GPR.r[4].UL[2], cpuRegs.GPR.r[4].UL[1],cpuRegs.GPR.r[4].UL[0] ); + SendMessage(GPR4Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[5].UL[3],cpuRegs.GPR.r[5].UL[2],cpuRegs.GPR.r[5].UL[1], cpuRegs.GPR.r[5].UL[0] ); + SendMessage(GPR5Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[6].UL[3],cpuRegs.GPR.r[6].UL[2], cpuRegs.GPR.r[6].UL[1], cpuRegs.GPR.r[6].UL[0]); + SendMessage(GPR6Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[7].UL[3], cpuRegs.GPR.r[7].UL[2],cpuRegs.GPR.r[7].UL[1],cpuRegs.GPR.r[7].UL[0] ); + SendMessage(GPR7Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[8].UL[3],cpuRegs.GPR.r[8].UL[2],cpuRegs.GPR.r[8].UL[1],cpuRegs.GPR.r[8].UL[0] ); + SendMessage(GPR8Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[9].UL[3],cpuRegs.GPR.r[9].UL[2],cpuRegs.GPR.r[9].UL[1], cpuRegs.GPR.r[9].UL[0] ); + SendMessage(GPR9Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[10].UL[3],cpuRegs.GPR.r[10].UL[2],cpuRegs.GPR.r[10].UL[1],cpuRegs.GPR.r[10].UL[0] ); + SendMessage(GPR10Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[11].UL[3],cpuRegs.GPR.r[11].UL[2],cpuRegs.GPR.r[11].UL[1],cpuRegs.GPR.r[11].UL[0] ); + SendMessage(GPR11Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[12].UL[3],cpuRegs.GPR.r[12].UL[2],cpuRegs.GPR.r[12].UL[1],cpuRegs.GPR.r[12].UL[0] ); + SendMessage(GPR12Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[13].UL[3],cpuRegs.GPR.r[13].UL[2],cpuRegs.GPR.r[13].UL[1],cpuRegs.GPR.r[13].UL[0] ); + SendMessage(GPR13Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[14].UL[3],cpuRegs.GPR.r[14].UL[2],cpuRegs.GPR.r[14].UL[1],cpuRegs.GPR.r[14].UL[0] ); + SendMessage(GPR14Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[15].UL[3],cpuRegs.GPR.r[15].UL[2],cpuRegs.GPR.r[15].UL[1],cpuRegs.GPR.r[15].UL[0] ); + SendMessage(GPR15Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[16].UL[3],cpuRegs.GPR.r[16].UL[2],cpuRegs.GPR.r[16].UL[1],cpuRegs.GPR.r[16].UL[0] ); + SendMessage(GPR16Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[17].UL[3],cpuRegs.GPR.r[17].UL[2],cpuRegs.GPR.r[17].UL[1],cpuRegs.GPR.r[17].UL[0] ); + SendMessage(GPR17Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[18].UL[3],cpuRegs.GPR.r[18].UL[2],cpuRegs.GPR.r[18].UL[1],cpuRegs.GPR.r[18].UL[0] ); + SendMessage(GPR18Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[19].UL[3],cpuRegs.GPR.r[19].UL[2],cpuRegs.GPR.r[19].UL[1],cpuRegs.GPR.r[19].UL[0] ); + SendMessage(GPR19Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[20].UL[3],cpuRegs.GPR.r[20].UL[2],cpuRegs.GPR.r[20].UL[1],cpuRegs.GPR.r[20].UL[0] ); + SendMessage(GPR20Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[21].UL[3],cpuRegs.GPR.r[21].UL[2],cpuRegs.GPR.r[21].UL[1],cpuRegs.GPR.r[21].UL[0] ); + SendMessage(GPR21Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[22].UL[3],cpuRegs.GPR.r[22].UL[2],cpuRegs.GPR.r[22].UL[1],cpuRegs.GPR.r[22].UL[0] ); + SendMessage(GPR22Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[23].UL[3],cpuRegs.GPR.r[23].UL[2],cpuRegs.GPR.r[23].UL[1],cpuRegs.GPR.r[23].UL[0] ); + SendMessage(GPR23Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[24].UL[3],cpuRegs.GPR.r[24].UL[2],cpuRegs.GPR.r[24].UL[1],cpuRegs.GPR.r[24].UL[0] ); + SendMessage(GPR24Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[25].UL[3],cpuRegs.GPR.r[25].UL[2],cpuRegs.GPR.r[25].UL[1],cpuRegs.GPR.r[25].UL[0] ); + SendMessage(GPR25Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[26].UL[3],cpuRegs.GPR.r[26].UL[2],cpuRegs.GPR.r[26].UL[1],cpuRegs.GPR.r[26].UL[0] ); + SendMessage(GPR26Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[27].UL[3],cpuRegs.GPR.r[27].UL[2],cpuRegs.GPR.r[27].UL[1],cpuRegs.GPR.r[27].UL[0] ); + SendMessage(GPR27Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[28].UL[3],cpuRegs.GPR.r[28].UL[2],cpuRegs.GPR.r[28].UL[1],cpuRegs.GPR.r[28].UL[0] ); + SendMessage(GPR28Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[29].UL[3],cpuRegs.GPR.r[29].UL[2],cpuRegs.GPR.r[29].UL[1],cpuRegs.GPR.r[29].UL[0] ); + SendMessage(GPR29Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[30].UL[3],cpuRegs.GPR.r[30].UL[2],cpuRegs.GPR.r[30].UL[1],cpuRegs.GPR.r[30].UL[0] ); + SendMessage(GPR30Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.GPR.r[31].UL[3],cpuRegs.GPR.r[31].UL[2],cpuRegs.GPR.r[31].UL[1],cpuRegs.GPR.r[31].UL[0] ); + SendMessage(GPR31Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.pc ); + SendMessage(GPRPCHandle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0",cpuRegs.HI.UL[3],cpuRegs.HI.UL[2] ,cpuRegs.HI.UL[1] ,cpuRegs.HI.UL[0] ); + SendMessage(GPRHIHandle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"0x%08X_%08X_%08X_%08X\0\0",cpuRegs.LO.UL[3],cpuRegs.LO.UL[2],cpuRegs.LO.UL[1],cpuRegs.LO.UL[0] ); + SendMessage(GPRLOHandle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[0] ); + SendMessage(COP00Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[1]); + SendMessage(COP01Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[2]); + SendMessage(COP02Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[3]); + SendMessage(COP03Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[4]); + SendMessage(COP04Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[5]); + SendMessage(COP05Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[6]); + SendMessage(COP06Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[7]); + SendMessage(COP07Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[8]); + SendMessage(COP08Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[9]); + SendMessage(COP09Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[10]); + SendMessage(COP010Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[11]); + SendMessage(COP011Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[12]); + SendMessage(COP012Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[13]); + SendMessage(COP013Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[14]); + SendMessage(COP014Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[15]); + SendMessage(COP015Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[16]); + SendMessage(COP016Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[17]); + SendMessage(COP017Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[18]); + SendMessage(COP018Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[19]); + SendMessage(COP019Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[20]); + SendMessage(COP020Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[21]); + SendMessage(COP021Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[22]); + SendMessage(COP022Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[23]); + SendMessage(COP023Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[24]); + SendMessage(COP024Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[25]); + SendMessage(COP025Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[26]); + SendMessage(COP026Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[27]); + SendMessage(COP027Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[28]); + SendMessage(COP028Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[29]); + SendMessage(COP029Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[30]); + SendMessage(COP030Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",cpuRegs.CP0.r[31]); + SendMessage(COP031Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + + sprintf(text1,"%f",fpuRegs.fpr[0].f ); + SendMessage(COP10Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[1].f); + SendMessage(COP11Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[2].f); + SendMessage(COP12Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[3].f); + SendMessage(COP13Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[4].f); + SendMessage(COP14Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[5].f); + SendMessage(COP15Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[6].f); + SendMessage(COP16Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[7].f); + SendMessage(COP17Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[8].f); + SendMessage(COP18Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[9].f); + SendMessage(COP19Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[10].f); + SendMessage(COP110Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[11].f); + SendMessage(COP111Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[12].f); + SendMessage(COP112Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[13].f); + SendMessage(COP113Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[14].f); + SendMessage(COP114Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[15].f); + SendMessage(COP115Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[16].f); + SendMessage(COP116Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[17].f); + SendMessage(COP117Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[18].f); + SendMessage(COP118Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[19].f); + SendMessage(COP119Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[20].f); + SendMessage(COP120Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[21].f); + SendMessage(COP121Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[22].f); + SendMessage(COP122Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[23].f); + SendMessage(COP123Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[24].f); + SendMessage(COP124Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[25].f); + SendMessage(COP125Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[26].f); + SendMessage(COP126Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[27].f); + SendMessage(COP127Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[28].f); + SendMessage(COP128Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[29].f); + SendMessage(COP129Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[30].f); + SendMessage(COP130Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.fpr[31].f); + SendMessage(COP131Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",fpuRegs.fprc[0]); + SendMessage(COP1C0Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",fpuRegs.fprc[31]); + SendMessage(COP1C1Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f",fpuRegs.ACC.f); + SendMessage(COP1ACCHandle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + + + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[0].f.w,VU0.VF[0].f.z,VU0.VF[0].f.y,VU0.VF[0].f.x ); + SendMessage(VU0F00Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[1].f.w,VU0.VF[1].f.z,VU0.VF[1].f.y,VU0.VF[1].f.x ); + SendMessage(VU0F01Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[2].f.w,VU0.VF[2].f.z,VU0.VF[2].f.y,VU0.VF[2].f.x ); + SendMessage(VU0F02Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[3].f.w,VU0.VF[3].f.z,VU0.VF[3].f.y,VU0.VF[3].f.x ); + SendMessage(VU0F03Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[4].f.w,VU0.VF[4].f.z,VU0.VF[4].f.y,VU0.VF[4].f.x ); + SendMessage(VU0F04Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[5].f.w,VU0.VF[5].f.z,VU0.VF[5].f.y,VU0.VF[5].f.x); + SendMessage(VU0F05Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[6].f.w,VU0.VF[6].f.z,VU0.VF[6].f.y,VU0.VF[6].f.x ); + SendMessage(VU0F06Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[7].f.w,VU0.VF[7].f.z,VU0.VF[7].f.y,VU0.VF[7].f.x ); + SendMessage(VU0F07Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[8].f.w,VU0.VF[8].f.z,VU0.VF[8].f.y,VU0.VF[8].f.x ); + SendMessage(VU0F08Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[9].f.w,VU0.VF[9].f.z,VU0.VF[9].f.y,VU0.VF[9].f.x ); + SendMessage(VU0F09Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[10].f.w,VU0.VF[10].f.z,VU0.VF[10].f.y,VU0.VF[10].f.x ); + SendMessage(VU0F10Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[11].f.w,VU0.VF[11].f.z,VU0.VF[11].f.y,VU0.VF[11].f.x ); + SendMessage(VU0F11Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[12].f.w,VU0.VF[12].f.z,VU0.VF[12].f.y,VU0.VF[12].f.x ); + SendMessage(VU0F12Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[13].f.w,VU0.VF[13].f.z,VU0.VF[13].f.y,VU0.VF[13].f.x ); + SendMessage(VU0F13Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[14].f.w,VU0.VF[14].f.z,VU0.VF[14].f.y,VU0.VF[14].f.x ); + SendMessage(VU0F14Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[15].f.w,VU0.VF[15].f.z,VU0.VF[15].f.y,VU0.VF[15].f.x ); + SendMessage(VU0F15Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[16].f.w,VU0.VF[16].f.z,VU0.VF[16].f.y,VU0.VF[16].f.x ); + SendMessage(VU0F16Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[17].f.w,VU0.VF[17].f.z,VU0.VF[17].f.y,VU0.VF[17].f.x ); + SendMessage(VU0F17Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[18].f.w,VU0.VF[18].f.z,VU0.VF[18].f.y,VU0.VF[18].f.x ); + SendMessage(VU0F18Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[19].f.w,VU0.VF[19].f.z,VU0.VF[19].f.y,VU0.VF[19].f.x ); + SendMessage(VU0F19Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[20].f.w,VU0.VF[20].f.z,VU0.VF[20].f.y,VU0.VF[20].f.x ); + SendMessage(VU0F20Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[21].f.w,VU0.VF[21].f.z,VU0.VF[21].f.y,VU0.VF[21].f.x ); + SendMessage(VU0F21Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[22].f.w,VU0.VF[22].f.z,VU0.VF[22].f.y,VU0.VF[22].f.x ); + SendMessage(VU0F22Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[23].f.w,VU0.VF[23].f.z,VU0.VF[23].f.y,VU0.VF[23].f.x ); + SendMessage(VU0F23Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[24].f.w,VU0.VF[24].f.z,VU0.VF[24].f.y,VU0.VF[24].f.x ); + SendMessage(VU0F24Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[25].f.w,VU0.VF[25].f.z,VU0.VF[25].f.y,VU0.VF[25].f.x ); + SendMessage(VU0F25Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[26].f.w,VU0.VF[26].f.z,VU0.VF[26].f.y,VU0.VF[26].f.x ); + SendMessage(VU0F26Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[27].f.w,VU0.VF[27].f.z,VU0.VF[27].f.y,VU0.VF[27].f.x ); + SendMessage(VU0F27Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[28].f.w,VU0.VF[28].f.z,VU0.VF[28].f.y,VU0.VF[28].f.x ); + SendMessage(VU0F28Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[29].f.w,VU0.VF[29].f.z,VU0.VF[29].f.y,VU0.VF[29].f.x ); + SendMessage(VU0F29Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[30].f.w,VU0.VF[30].f.z,VU0.VF[30].f.y,VU0.VF[30].f.x ); + SendMessage(VU0F30Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU0.VF[31].f.w,VU0.VF[31].f.z,VU0.VF[31].f.y,VU0.VF[31].f.x ); + SendMessage(VU0F31Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + + + wsprintf(text1,"%x",VU0.VI[0] ); + SendMessage(VU0C00Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[1]); + SendMessage(VU0C01Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[2]); + SendMessage(VU0C02Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[3]); + SendMessage(VU0C03Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[4]); + SendMessage(VU0C04Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[5]); + SendMessage(VU0C05Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[6]); + SendMessage(VU0C06Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[7]); + SendMessage(VU0C07Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[8]); + SendMessage(VU0C08Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[9]); + SendMessage(VU0C09Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[10]); + SendMessage(VU0C10Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[11]); + SendMessage(VU0C11Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[12]); + SendMessage(VU0C12Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[13]); + SendMessage(VU0C13Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[14]); + SendMessage(VU0C14Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[15]); + SendMessage(VU0C15Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[16]); + SendMessage(VU0C16Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[17]); + SendMessage(VU0C17Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[18]); + SendMessage(VU0C18Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[19]); + SendMessage(VU0C19Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[20]); + SendMessage(VU0C20Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[21]); + SendMessage(VU0C21Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[22]); + SendMessage(VU0C22Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[23]); + SendMessage(VU0C23Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[24]); + SendMessage(VU0C24Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[25]); + SendMessage(VU0C25Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[26]); + SendMessage(VU0C26Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[27]); + SendMessage(VU0C27Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[28]); + SendMessage(VU0C28Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[29]); + SendMessage(VU0C29Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[30]); + SendMessage(VU0C30Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU0.VI[31]); + SendMessage(VU0C31Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + + sprintf(text1,"%f_%f_%f_%f\0",VU0.ACC.f.w,VU0.ACC.f.z,VU0.ACC.f.y,VU0.ACC.f.x ); + SendMessage(VU0ACCHandle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + + + + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[0].f.w,VU1.VF[0].f.z,VU1.VF[0].f.y,VU1.VF[0].f.x ); + SendMessage(VU1F00Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[1].f.w,VU1.VF[1].f.z,VU1.VF[1].f.y,VU1.VF[1].f.x ); + SendMessage(VU1F01Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[2].f.w,VU1.VF[2].f.z,VU1.VF[2].f.y,VU1.VF[2].f.x ); + SendMessage(VU1F02Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[3].f.w,VU1.VF[3].f.z,VU1.VF[3].f.y,VU1.VF[3].f.x ); + SendMessage(VU1F03Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[4].f.w,VU1.VF[4].f.z,VU1.VF[4].f.y,VU1.VF[4].f.x ); + SendMessage(VU1F04Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[5].f.w,VU1.VF[5].f.z,VU1.VF[5].f.y,VU1.VF[5].f.x); + SendMessage(VU1F05Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[6].f.w,VU1.VF[6].f.z,VU1.VF[6].f.y,VU1.VF[6].f.x ); + SendMessage(VU1F06Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[7].f.w,VU1.VF[7].f.z,VU1.VF[7].f.y,VU1.VF[7].f.x ); + SendMessage(VU1F07Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[8].f.w,VU1.VF[8].f.z,VU1.VF[8].f.y,VU1.VF[8].f.x ); + SendMessage(VU1F08Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[9].f.w,VU1.VF[9].f.z,VU1.VF[9].f.y,VU1.VF[9].f.x ); + SendMessage(VU1F09Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[10].f.w,VU1.VF[10].f.z,VU1.VF[10].f.y,VU1.VF[10].f.x ); + SendMessage(VU1F10Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[11].f.w,VU1.VF[11].f.z,VU1.VF[11].f.y,VU1.VF[11].f.x ); + SendMessage(VU1F11Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[12].f.w,VU1.VF[12].f.z,VU1.VF[12].f.y,VU1.VF[12].f.x ); + SendMessage(VU1F12Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[13].f.w,VU1.VF[13].f.z,VU1.VF[13].f.y,VU1.VF[13].f.x ); + SendMessage(VU1F13Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[14].f.w,VU1.VF[14].f.z,VU1.VF[14].f.y,VU1.VF[14].f.x ); + SendMessage(VU1F14Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[15].f.w,VU1.VF[15].f.z,VU1.VF[15].f.y,VU1.VF[15].f.x ); + SendMessage(VU1F15Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[16].f.w,VU1.VF[16].f.z,VU1.VF[16].f.y,VU1.VF[16].f.x ); + SendMessage(VU1F16Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[17].f.w,VU1.VF[17].f.z,VU1.VF[17].f.y,VU1.VF[17].f.x ); + SendMessage(VU1F17Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[18].f.w,VU1.VF[18].f.z,VU1.VF[18].f.y,VU1.VF[18].f.x ); + SendMessage(VU1F18Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[19].f.w,VU1.VF[19].f.z,VU1.VF[19].f.y,VU1.VF[19].f.x ); + SendMessage(VU1F19Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[20].f.w,VU1.VF[20].f.z,VU1.VF[20].f.y,VU1.VF[20].f.x ); + SendMessage(VU1F20Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[21].f.w,VU1.VF[21].f.z,VU1.VF[21].f.y,VU1.VF[21].f.x ); + SendMessage(VU1F21Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[22].f.w,VU1.VF[22].f.z,VU1.VF[22].f.y,VU1.VF[22].f.x ); + SendMessage(VU1F22Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[23].f.w,VU1.VF[23].f.z,VU1.VF[23].f.y,VU1.VF[23].f.x ); + SendMessage(VU1F23Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[24].f.w,VU1.VF[24].f.z,VU1.VF[24].f.y,VU1.VF[24].f.x ); + SendMessage(VU1F24Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[25].f.w,VU1.VF[25].f.z,VU1.VF[25].f.y,VU1.VF[25].f.x ); + SendMessage(VU1F25Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[26].f.w,VU1.VF[26].f.z,VU1.VF[26].f.y,VU1.VF[26].f.x ); + SendMessage(VU1F26Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[27].f.w,VU1.VF[27].f.z,VU1.VF[27].f.y,VU1.VF[27].f.x ); + SendMessage(VU1F27Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[28].f.w,VU1.VF[28].f.z,VU1.VF[28].f.y,VU1.VF[28].f.x ); + SendMessage(VU1F28Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[29].f.w,VU1.VF[29].f.z,VU1.VF[29].f.y,VU1.VF[29].f.x ); + SendMessage(VU1F29Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[30].f.w,VU1.VF[30].f.z,VU1.VF[30].f.y,VU1.VF[30].f.x ); + SendMessage(VU1F30Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + sprintf(text1,"%f_%f_%f_%f\0",VU1.VF[31].f.w,VU1.VF[31].f.z,VU1.VF[31].f.y,VU1.VF[31].f.x ); + SendMessage(VU1F31Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + + + wsprintf(text1,"%x",VU1.VI[0] ); + SendMessage(VU1C00Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[1]); + SendMessage(VU1C01Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[2]); + SendMessage(VU1C02Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[3]); + SendMessage(VU1C03Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[4]); + SendMessage(VU1C04Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[5]); + SendMessage(VU1C05Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[6]); + SendMessage(VU1C06Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[7]); + SendMessage(VU1C07Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[8]); + SendMessage(VU1C08Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[9]); + SendMessage(VU1C09Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[10]); + SendMessage(VU1C10Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[11]); + SendMessage(VU1C11Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[12]); + SendMessage(VU1C12Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[13]); + SendMessage(VU1C13Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[14]); + SendMessage(VU1C14Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[15]); + SendMessage(VU1C15Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[16]); + SendMessage(VU1C16Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[17]); + SendMessage(VU1C17Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[18]); + SendMessage(VU1C18Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[19]); + SendMessage(VU1C19Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[20]); + SendMessage(VU1C20Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[21]); + SendMessage(VU1C21Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[22]); + SendMessage(VU1C22Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[23]); + SendMessage(VU1C23Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[24]); + SendMessage(VU1C24Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[25]); + SendMessage(VU1C25Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[26]); + SendMessage(VU1C26Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[27]); + SendMessage(VU1C27Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[28]); + SendMessage(VU1C28Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[29]); + SendMessage(VU1C29Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[30]); + SendMessage(VU1C30Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + wsprintf(text1,"%x",VU1.VI[31]); + SendMessage(VU1C31Handle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + + sprintf(text1,"%f_%f_%f_%f\0",VU1.ACC.f.w,VU1.ACC.f.z,VU1.ACC.f.y,VU1.ACC.f.x ); + SendMessage(VU1ACCHandle,WM_SETTEXT,0,(LPARAM)(LPCTSTR)text1); + +} + + +void EEDumpRegs(FILE * fp) +{ + char text2[256]; + int i; + for(i = 0; i < 32; i++) + { + sprintf(text1,"%x_%x_%x_%x",cpuRegs.GPR.r[i].UL[3],cpuRegs.GPR.r[i].UL[2],cpuRegs.GPR.r[i].UL[1],cpuRegs.GPR.r[i].UL[0]); + sprintf(text2,"GPR Register %d: ",i+1); + fprintf(fp,text2); + fprintf(fp,text1); + fprintf(fp,"\n"); + } + sprintf(text1,"0x%x",cpuRegs.pc); + fprintf(fp,"PC Register : "); + fprintf(fp,text1); + fprintf(fp,"\n"); + sprintf(text1,"%x_%x_%x_%x",cpuRegs.HI.UL[3],cpuRegs.HI.UL[2],cpuRegs.HI.UL[1],cpuRegs.HI.UL[0]); + fprintf(fp,"GPR Register HI: "); + fprintf(fp,text1); + fprintf(fp,"\n"); + sprintf(text1,"%x_%x_%x_%x",cpuRegs.LO.UL[3],cpuRegs.LO.UL[2],cpuRegs.LO.UL[1],cpuRegs.LO.UL[0]); + fprintf(fp,"GPR Register LO: "); + fprintf(fp,text1); + fprintf(fp,"\n"); + + + for(i = 0; i < 32; i++) + { + sprintf(text1,"0x%x",cpuRegs.CP0.r[i]); + sprintf(text2,"COP0 Register %d: ",i+1); + fprintf(fp,text2); + fprintf(fp,text1); + fprintf(fp,"\n"); + } +} + +void IOPDumpRegs(FILE * fp) +{ + char text2[256]; + int i; + for(i = 0; i < 32; i++) + { + sprintf(text1,"%x",psxRegs.GPR.r[i]); + sprintf(text2,"GPR Register %d: ",i+1); + fprintf(fp,text2); + fprintf(fp,text1); + fprintf(fp,"\n"); + } + sprintf(text1,"0x%x",psxRegs.pc); + fprintf(fp,"PC Register : "); + fprintf(fp,text1); + fprintf(fp,"\n"); + sprintf(text1,"%x",psxRegs.GPR.r[32]); + fprintf(fp,"GPR Register HI: "); + fprintf(fp,text1); + fprintf(fp,"\n"); + sprintf(text1,"%x",psxRegs.GPR.r[33]); + fprintf(fp,"GPR Register LO: "); + fprintf(fp,text1); + fprintf(fp,"\n"); +} diff --git a/pcsx2/windows/HacksDlg.cpp b/pcsx2/windows/HacksDlg.cpp new file mode 100644 index 0000000000..601f6d6a27 --- /dev/null +++ b/pcsx2/windows/HacksDlg.cpp @@ -0,0 +1,74 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "win32.h" + +BOOL APIENTRY HacksProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_INITDIALOG: + + CheckRadioButton( hDlg, IDC_EESYNC_DEFAULT, IDC_EESYNC3, IDC_EESYNC_DEFAULT + CHECK_EE_CYCLERATE ); + + if(CHECK_IOP_CYCLERATE) CheckDlgButton(hDlg, IDC_IOPSYNC, TRUE); + if(CHECK_WAITCYCLE_HACK) CheckDlgButton(hDlg, IDC_WAITCYCLES, TRUE); + if(CHECK_ESCAPE_HACK) CheckDlgButton(hDlg, IDC_ESCHACK, TRUE); + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + { + int newhacks = 0; + for( int i=1; i<4; i++ ) + { + if( IsDlgButtonChecked(hDlg, IDC_EESYNC_DEFAULT+i) ) + { + newhacks = i; + break; + } + } + + newhacks |= IsDlgButtonChecked(hDlg, IDC_IOPSYNC) << 3; + newhacks |= IsDlgButtonChecked(hDlg, IDC_WAITCYCLES) << 4; + newhacks |= IsDlgButtonChecked(hDlg, IDC_ESCHACK) << 10; + + EndDialog(hDlg, TRUE); + + if( newhacks != Config.Hacks ) + { + SysRestorableReset(); + Config.Hacks = newhacks; + SaveConfig(); + } + } + return FALSE; + + case IDCANCEL: + EndDialog(hDlg, FALSE); + return FALSE; + } + return TRUE; + } + + return FALSE; +} diff --git a/pcsx2/windows/McdsDlg.c b/pcsx2/windows/McdsDlg.c new file mode 100644 index 0000000000..fd47b71c0c --- /dev/null +++ b/pcsx2/windows/McdsDlg.c @@ -0,0 +1,126 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 +#include +#include +#include + +#include "Common.h" +#include "PsxCommon.h" +#include "plugins.h" +#include "resource.h" +#include "Win32.h" + +#include "Paths.h" + + +HWND mcdDlg; + + +void Open_Mcd_Proc(HWND hW, int mcd) { + OPENFILENAME ofn; + char szFileName[256]; + char szFileTitle[256]; + char szFilter[1024]; + char *str; + + memset(szFileName, 0, sizeof(szFileName)); + memset(szFileTitle, 0, sizeof(szFileTitle)); + memset(szFilter, 0, sizeof(szFilter)); + + + strcpy(szFilter, _("Ps2 Memory Card (*.ps2)")); + str = szFilter + strlen(szFilter) + 1; + strcpy(str, "*.ps2"); + + str+= strlen(str) + 1; + strcpy(str, _("All Files")); + str+= strlen(str) + 1; + strcpy(str, "*.*"); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hW; + ofn.lpstrFilter = szFilter; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = 256; + ofn.lpstrInitialDir = MEMCARDS_DIR; + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = 256; + ofn.lpstrTitle = NULL; + ofn.lpstrDefExt = "MC2"; + ofn.Flags = OFN_HIDEREADONLY | OFN_NOCHANGEDIR; + + if (GetOpenFileName ((LPOPENFILENAME)&ofn)) { + Edit_SetText(GetDlgItem(hW,mcd == 1 ? IDC_MCD1 : IDC_MCD2), szFileName); + } +} + +BOOL CALLBACK ConfigureMcdsDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + mcdDlg = hW; + + SetWindowText(hW, _("Memcard Manager")); + + Button_SetText(GetDlgItem(hW, IDOK), _("OK")); + Button_SetText(GetDlgItem(hW, IDCANCEL), _("Cancel")); + Button_SetText(GetDlgItem(hW, IDC_MCDSEL1), _("Select Mcd")); + Button_SetText(GetDlgItem(hW, IDC_MCDSEL2), _("Select Mcd")); + + Static_SetText(GetDlgItem(hW, IDC_FRAMEMCD1), _("Memory Card 1")); + Static_SetText(GetDlgItem(hW, IDC_FRAMEMCD2), _("Memory Card 2")); + + if (!strlen(Config.Mcd1)) strcpy(Config.Mcd1, "memcards\\Mcd001.ps2"); + if (!strlen(Config.Mcd2)) strcpy(Config.Mcd2, "memcards\\Mcd002.ps2"); + Edit_SetText(GetDlgItem(hW,IDC_MCD1), Config.Mcd1); + Edit_SetText(GetDlgItem(hW,IDC_MCD2), Config.Mcd2); + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_MCDSEL1: + Open_Mcd_Proc(hW, 1); + return TRUE; + case IDC_MCDSEL2: + Open_Mcd_Proc(hW, 2); + return TRUE; + case IDCANCEL: + EndDialog(hW,FALSE); + + return TRUE; + case IDOK: + Edit_GetText(GetDlgItem(hW,IDC_MCD1), Config.Mcd1, 256); + Edit_GetText(GetDlgItem(hW,IDC_MCD2), Config.Mcd2, 256); + + SaveConfig(); + + EndDialog(hW,TRUE); + + return TRUE; + } + case WM_DESTROY: + return TRUE; + } + return FALSE; +} + diff --git a/pcsx2/windows/McdsDlg.cpp b/pcsx2/windows/McdsDlg.cpp new file mode 100644 index 0000000000..3c92c34808 --- /dev/null +++ b/pcsx2/windows/McdsDlg.cpp @@ -0,0 +1,1743 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "Win32.h" + +#include +#include +#include "libintlmsc.h" + +#include "System.h" +#include "McdsDlg.h" + +#include +using namespace std; + + +u16 SJISTable[0xFFFF]; + + +void IniSJISTable() +{ + memzero_obj(SJISTable); + //Blow me sony for using this retarded sjis to store the savegame name + SJISTable[0x20] = 0x0020; + SJISTable[0x21] = 0x0021; + SJISTable[0x22] = 0x0022; + SJISTable[0x23] = 0x0023; + SJISTable[0x24] = 0x0024; + SJISTable[0x25] = 0x0025; + SJISTable[0x26] = 0x0026; + SJISTable[0x27] = 0x0027; + SJISTable[0x28] = 0x0028; + SJISTable[0x29] = 0x0029; + SJISTable[0x2A] = 0x002A; + SJISTable[0x2B] = 0x002B; + SJISTable[0x2C] = 0x002C; + SJISTable[0x2D] = 0x002D; + SJISTable[0x2E] = 0x002E; + SJISTable[0x2F] = 0x002F; + SJISTable[0x30] = 0x0030; + SJISTable[0x31] = 0x0031; + SJISTable[0x32] = 0x0032; + SJISTable[0x33] = 0x0033; + SJISTable[0x34] = 0x0034; + SJISTable[0x35] = 0x0035; + SJISTable[0x36] = 0x0036; + SJISTable[0x37] = 0x0037; + SJISTable[0x38] = 0x0038; + SJISTable[0x39] = 0x0039; + SJISTable[0x3A] = 0x003A; + SJISTable[0x3B] = 0x003B; + SJISTable[0x3C] = 0x003C; + SJISTable[0x3D] = 0x003D; + SJISTable[0x3E] = 0x003E; + SJISTable[0x3F] = 0x003F; + SJISTable[0x40] = 0x0040; + SJISTable[0x41] = 0x0041; + SJISTable[0x42] = 0x0042; + SJISTable[0x43] = 0x0043; + SJISTable[0x44] = 0x0044; + SJISTable[0x45] = 0x0045; + SJISTable[0x46] = 0x0046; + SJISTable[0x47] = 0x0047; + SJISTable[0x48] = 0x0048; + SJISTable[0x49] = 0x0049; + SJISTable[0x4A] = 0x004A; + SJISTable[0x4B] = 0x004B; + SJISTable[0x4C] = 0x004C; + SJISTable[0x4D] = 0x004D; + SJISTable[0x4E] = 0x004E; + SJISTable[0x4F] = 0x004F; + SJISTable[0x50] = 0x0050; + SJISTable[0x51] = 0x0051; + SJISTable[0x52] = 0x0052; + SJISTable[0x53] = 0x0053; + SJISTable[0x54] = 0x0054; + SJISTable[0x55] = 0x0055; + SJISTable[0x56] = 0x0056; + SJISTable[0x57] = 0x0057; + SJISTable[0x58] = 0x0058; + SJISTable[0x59] = 0x0059; + SJISTable[0x5A] = 0x005A; + SJISTable[0x5B] = 0x005B; + SJISTable[0x5C] = 0x00A5; + SJISTable[0x5D] = 0x005D; + SJISTable[0x5E] = 0x005E; + SJISTable[0x5F] = 0x005F; + SJISTable[0x60] = 0x0060; + SJISTable[0x61] = 0x0061; + SJISTable[0x62] = 0x0062; + SJISTable[0x63] = 0x0063; + SJISTable[0x64] = 0x0064; + SJISTable[0x65] = 0x0065; + SJISTable[0x66] = 0x0066; + SJISTable[0x67] = 0x0067; + SJISTable[0x68] = 0x0068; + SJISTable[0x69] = 0x0069; + SJISTable[0x6A] = 0x006A; + SJISTable[0x6B] = 0x006B; + SJISTable[0x6C] = 0x006C; + SJISTable[0x6D] = 0x006D; + SJISTable[0x6E] = 0x006E; + SJISTable[0x6F] = 0x006F; + SJISTable[0x70] = 0x0070; + SJISTable[0x71] = 0x0071; + SJISTable[0x72] = 0x0072; + SJISTable[0x73] = 0x0073; + SJISTable[0x74] = 0x0074; + SJISTable[0x75] = 0x0075; + SJISTable[0x76] = 0x0076; + SJISTable[0x77] = 0x0077; + SJISTable[0x78] = 0x0078; + SJISTable[0x79] = 0x0079; + SJISTable[0x7A] = 0x007A; + SJISTable[0x7B] = 0x007B; + SJISTable[0x7C] = 0x007C; + SJISTable[0x7D] = 0x007D; + SJISTable[0x7E] = 0x007E; + + SJISTable[0x8140] = 0x0020; + SJISTable[0x8149] = 0x0021; + SJISTable[0x22] = 0x0022; + SJISTable[0x8194] = 0x0023; + SJISTable[0x8190] = 0x0024; + SJISTable[0x8193] = 0x0025; + SJISTable[0x8195] = 0x0026; + SJISTable[0x27] = 0x0027; + SJISTable[0x8169] = 0x0028; + SJISTable[0x816A] = 0x0029; + SJISTable[0x8196] = 0x002A; + SJISTable[0x817B] = 0x002B; + SJISTable[0x8143] = 0x002C; + SJISTable[0x815D] = 0x002D; + SJISTable[0x8144] = 0x002E; + SJISTable[0x815E] = 0x002F; + SJISTable[0x824F] = 0x0030; + SJISTable[0x8250] = 0x0031; + SJISTable[0x8251] = 0x0032; + SJISTable[0x8252] = 0x0033; + SJISTable[0x8253] = 0x0034; + SJISTable[0x8254] = 0x0035; + SJISTable[0x8255] = 0x0036; + SJISTable[0x8256] = 0x0037; + SJISTable[0x8257] = 0x0038; + SJISTable[0x8258] = 0x0039; + SJISTable[0x8146] = 0x003A; + SJISTable[0x8147] = 0x003B; + SJISTable[0x8183] = 0x003C; + SJISTable[0x8181] = 0x003D; + SJISTable[0x8184] = 0x003E; + SJISTable[0x8148] = 0x003F; + SJISTable[0x8197] = 0x0040; + SJISTable[0x8260] = 0x0041; + SJISTable[0x8261] = 0x0042; + SJISTable[0x8262] = 0x0043; + SJISTable[0x8263] = 0x0044; + SJISTable[0x8264] = 0x0045; + SJISTable[0x8265] = 0x0046; + SJISTable[0x8266] = 0x0047; + SJISTable[0x8267] = 0x0048; + SJISTable[0x8268] = 0x0049; + SJISTable[0x8269] = 0x004A; + SJISTable[0x826A] = 0x004B; + SJISTable[0x826B] = 0x004C; + SJISTable[0x826C] = 0x004D; + SJISTable[0x826D] = 0x004E; + SJISTable[0x826E] = 0x004F; + SJISTable[0x826F] = 0x0050; + SJISTable[0x8270] = 0x0051; + SJISTable[0x8271] = 0x0052; + SJISTable[0x8272] = 0x0053; + SJISTable[0x8273] = 0x0054; + SJISTable[0x8274] = 0x0055; + SJISTable[0x8275] = 0x0056; + SJISTable[0x8276] = 0x0057; + SJISTable[0x8277] = 0x0058; + SJISTable[0x8278] = 0x0059; + SJISTable[0x8279] = 0x005A; + SJISTable[0x816D] = 0x005B; + SJISTable[0x818F] = 0x00A5; + SJISTable[0x816E] = 0x005D; + SJISTable[0x814F] = 0x005E; + SJISTable[0x8151] = 0x005F; + SJISTable[0x814D] = 0x0060; + SJISTable[0x8281] = 0x0061; + SJISTable[0x8282] = 0x0062; + SJISTable[0x8283] = 0x0063; + SJISTable[0x8284] = 0x0064; + SJISTable[0x8285] = 0x0065; + SJISTable[0x8286] = 0x0066; + SJISTable[0x8287] = 0x0067; + SJISTable[0x8288] = 0x0068; + SJISTable[0x8289] = 0x0069; + SJISTable[0x828A] = 0x006A; + SJISTable[0x828B] = 0x006B; + SJISTable[0x828C] = 0x006C; + SJISTable[0x828D] = 0x006D; + SJISTable[0x828E] = 0x006E; + SJISTable[0x828F] = 0x006F; + SJISTable[0x8290] = 0x0070; + SJISTable[0x8291] = 0x0071; + SJISTable[0x8292] = 0x0072; + SJISTable[0x8293] = 0x0073; + SJISTable[0x8294] = 0x0074; + SJISTable[0x8295] = 0x0075; + SJISTable[0x8296] = 0x0076; + SJISTable[0x8297] = 0x0077; + SJISTable[0x8298] = 0x0078; + SJISTable[0x8299] = 0x0079; + SJISTable[0x829A] = 0x007A; + SJISTable[0x816F] = 0x007B; + SJISTable[0x8162] = 0x007C; + SJISTable[0x8170] = 0x007D; + SJISTable[0x7E] = 0x007E; +} + + +HWND mcdDlg; +HTREEITEM root; +HWND treewindow; + +HTREEITEM AddTreeItem(HWND treeview, HTREEITEM parent, const char *name, LPARAM lp) +{ + TVINSERTSTRUCT node={ + parent, + 0, + { + TVIF_TEXT, + NULL, + TVIS_EXPANDED, + TVIS_EXPANDED, + (LPSTR)name, + 0, + 0, + 0, + 0, + lp, + 1 + } + }; + return TreeView_InsertItem(treeview,&node); +} + +void ReadDir(int cluster, int rec, HTREEITEM tree); + + +class Time +{ + public: + u8 sec; + u8 min; + u8 hour; + u8 day; + u8 month; + u16 year; +}; + + +class Dir +{ + protected: + + + public: + static const u16 DF_READ = 0x0001; + static const u16 DF_WRITE = 0x0002; + static const u16 DF_EXECUTE = 0x0004; + static const u16 DF_PROTECTED = 0x0008; + static const u16 DF_FILE = 0x0010; + static const u16 DF_DIRECTORY = 0x0020; + static const u16 DF_HIDDEN = 0x2000; + static const u16 DF_EXISTS = 0x8000; + + // Directory Attributes + u16 Mode; + s32 Lenght; + Time Created; + s32 Cluster; + u32 DirEntry; + Time Modified; + u32 Attribute; + s8 Name[256]; // just in case some names are longer + + // Parent dir and contents of dir + Dir *Parent; + vector

    Sons; + + // Used to store the contents of a file + u8 *File; + + Dir() + { + File = 0; + } + + bool IsHidden() + { + return (Mode & DF_HIDDEN) ? 1 : 0; + } + + bool IsFile() + { + return (Mode & DF_FILE) ? 1 : 0; + } + + bool IsDirectory() + { + return (Mode & DF_DIRECTORY) ? 1 : 0; + } + + bool Exists() + { + return (Mode & DF_EXISTS) ? 1 : 0; + } + + void Load(s8 *d) + { + File = 0; + // For the moment we dont need to load more data + Mode = *(u16 *)d; + Lenght = *(u32 *)(d + 0x04); + Cluster = *(u32 *)(d + 0x10); + strncpy(Name, d + 0x40, 255); + } + + Dir* AddSon(Dir &d) + { + d.Parent = this; + vector::iterator i = Sons.insert(Sons.end(), d); + return &i[0]; + } + + void Release() + { + for(unsigned int i=0;iFile + 12); + u32 NumVertex = *(u32 *)(File + 16); + + // Check if it's an ico file + if(FileID != 0x00010000) + { + MessageBox(mcdDlg, "It's not an ICO file.", "Error", 0); + return 0; + } + + // Is texture compressed? + if(TextureType != 0x07) + { + //MessageBox(mcdDlg, "Compressed texture, not yet supported.", "Error", 0); + //return; + } + + // Calculate the offset to the animation segment + u32 VertexSize = (AnimationShapes * 8) + 8 + 8; + u32 AnimationSegmentOffset = (VertexSize * NumVertex) + 20; + + // Check if we really are at the animation header + u32 AnimationID = *(u32 *)(File + AnimationSegmentOffset); + if(AnimationID != 0x00000001) + { + MessageBox(mcdDlg, "Animation header ID is incorrect.", "Error", 0); + return 0; + } + + // Get the size of all the animation segment + u32 FrameLenght = *(u32 *)(File + AnimationSegmentOffset + 4); + u32 NumberOfFrames = *(u32 *)(File + AnimationSegmentOffset + 16); + + u32 Offset = AnimationSegmentOffset + 20; + for(u32 i=0;i 0x07) + { + u32 CompressedDataSize = *(u32 *)(File + Offset); + //RLE compression + + Offset += 4; + u32 OffsetEnd = Offset + CompressedDataSize; + u32 WriteCount = 0; + + while(WriteCount < 0x8000 && Offset < OffsetEnd) + { + + u16 Data = *(u16 *)(File + Offset); + + if(Data < 0xFF00) //Replication + { + Offset += 2; + u16 Rep = *(u16 *)(File + Offset); + + for(int i=0;iLenght-0x8000*/Offset], 0x8000); + } + + // Convert from TIM to rgb + for(z=0;z<0x4000;z++) + { + dest[y] = 8 * (ico[z] & 0x1F); + dest[y+1] = 8 * ((ico[z] >>5) & 0x1F); + dest[y+2] = 8 * (ico[z] >>10); + y += 3; + } + + return 1; + } + +}; + + +class SaveGame +{ + public: + + Dir *D; + char Name1[256]; + char Name2[256]; + + u8 Icon[128*128*3]; + + + SaveGame(Dir *_Dir) + { + char IconName[256]; + + D = _Dir; + + // Find icon.sys + for(unsigned int i=0;iSons.size();i++) + { + if(!strcmp("icon.sys", D->Sons[i].Name)) + { + char temp[256]; + + // Get Name in SJIS format + u16 *p = (u16 *)(D->Sons[i].File + 192); + + while(*p != 0x0000) + { + // Switch bytes + u8 *pp = (u8 *)p; + u8 tb = *pp; + *pp = pp[1]; + pp[1] = tb; + + // Translate char + *p = SJISTable[*p]; + + p++; + } + + // Convert unicode string + wcstombs(temp, (wchar_t *)(D->Sons[i].File + 192), 255); + + // Get the second line separator + u16 SecondLine = *(u16 *)(D->Sons[i].File + 6); + SecondLine /= 2; + + // Copy the 2 parts of the string to different strings + strncpy(Name1, temp, SecondLine); + Name1[SecondLine] = 0; + + strcpy(Name2, &temp[SecondLine]); + if(strlen(Name2) + SecondLine > 68) + Name2[0] = 0; + + // Get the icon file name + strcpy(IconName, (char *)D->Sons[i].File + 260); + + } + } + + // Find the icon now + for(unsigned int i=0;iSons.size();i++) + { + if(!strcmp(IconName, D->Sons[i].Name)) + { + D->Sons[i].BuildICO((char*)Icon); + } + + } + } + + void AddIcoToImageList(HIMAGELIST il) + { + + HDC hDC = GetDC(NULL); + HDC memDC = CreateCompatibleDC (hDC); + HBITMAP memBM = CreateCompatibleBitmap ( hDC, 128, 128 ); + SelectObject ( memDC, memBM ); + + int px, py, pc=0; + for(px=0;px<128;px++){ + for(py=0;py<128;py++){ + SetPixel(memDC, py, px, RGB(Icon[pc], Icon[pc+1], Icon[pc+2])); + pc+=3; + } + } + + HDC memDC2 = CreateCompatibleDC (hDC); + HBITMAP memBM2 = CreateCompatibleBitmap ( hDC, 64, 64 ); + SelectObject ( memDC2, memBM2 ); + SetStretchBltMode(memDC2, HALFTONE); + StretchBlt(memDC2, 0, 0, 64, 64, memDC, 0, 0, 128, 128, SRCCOPY); + + DeleteDC(memDC2); + DeleteDC(memDC); + ImageList_Add(il, memBM2, NULL); + DeleteObject(memBM); + DeleteObject(memBM2); + ReleaseDC(NULL, hDC); + + + } + + void DrawICO(HWND hWnd) + { + // int px, py, pc=0; + HDC dc = GetDC(hWnd); + /* + for(px=0;px<128;px++){ + for(py=0;py<128;py++){ + SetPixel(dc, py, px, RGB(Icon[pc], Icon[pc+1], Icon[pc+2])); + pc+=3; + + } + } +*/ + /* + HDC hDC = GetDC(NULL); + HDC memDC = CreateCompatibleDC ( hDC ); + HBITMAP memBM = CreateCompatibleBitmap ( hDC, 256, 128 ); + SelectObject ( memDC, memBM ); + for(px=0;px<128;px++){ + for(py=0;py<128;py++){ + SetPixel(memDC, py, px, RGB(Icon[pc], Icon[pc+1], Icon[pc+2])); + pc+=3; + + } + } + RECT rect; + rect.left=132; + rect.right=255; + rect.top=4; + rect.bottom=255; + DrawText(memDC, Name1, strlen(Name1), &rect, 0); + rect.top=20; + DrawText(memDC, Name2, strlen(Name2), &rect, 0); + + + SetStretchBltMode(dc, HALFTONE); + StretchBlt(dc, 0, 0, 64, 64, memDC, 0, 0, 128, 128, SRCCOPY); + //BitBlt(dc, 0, 0, 256, 128, memDC, 0, 0, SRCCOPY); + */ + } + +}; + + +class MemoryCard +{ + public: + FILE *fp; + int FAT[256*256]; + int indirect_table[256]; + char FileName[1024]; + Dir Root; + vector SaveGameList; + HIMAGELIST ImageList; + + MemoryCard() + { + fp = 0; + ImageList = 0; + } + + int BuildFAT() + { + int IFC; + // indirect table containing the clusters with FAT data + int i, o; + + // First read IFC from the superblock (8MB cards only have 1) + fseek(fp, 0x50, SEEK_SET); + fread(&IFC, 4, 1, fp); + + if(IFC != 8) + { + MessageBox(mcdDlg, "IFC is not 8. Memory Card is probably corrupt.", "Error", 0); + return 0; + } + + DbgCon::WriteLn("IFC: %d", params IFC); + + // Read the cluster with the indirect data to the FAT. + fseek(fp, 0x420 * IFC, SEEK_SET); + fread(indirect_table, 4, 128, fp); + fseek(fp, (0x420 * IFC) + 0x210, SEEK_SET); + fread(&indirect_table[128], 4, 128, fp); + + // Build the FAT table from the indirect_table + o = 0; + i = 0; + + while(indirect_table[i] != 0xFFFFFFFF) + { + + fseek(fp, 0x420 * indirect_table[i], SEEK_SET); + fread(&FAT[o], 4, 128, fp); + o+=128; + + fseek(fp, (0x420 * indirect_table[i]) + 0x210, SEEK_SET); + fread(&FAT[o], 4, 128, fp); + o+=128; + + i++; + } + return 1; + } + + void ReadFile(Dir *d) + { + + int cluster = d->Cluster; + int size = d->Lenght; + int numclusters = (int)ceil((double)size / 1024); + int numpages = (int)ceil((double)size / 512); + int i=0, j=0, c=0, read=0; + d->File = (u8 *) malloc(size); + + + for(i=0;i 512) + read = 512; + + fseek(fp, 0xA920 + ((cluster) * 0x420), SEEK_SET); + fread(&d->File[c], 1, read, fp); + c+=read; + j++; + if(j == numpages) + break; + + read = size - c; + if(read > 512) + read = 512; + + fseek(fp, 0xA920 + (((cluster) * 0x420)+528), SEEK_SET); + fread(&d->File[c], 1, read, fp); + c+=read; + j++; + if(j == numpages) + break; + + cluster = FAT[cluster]; + cluster = cluster ^ 0x80000000; + if(cluster == 0x7FFFFFFF || cluster == 0xFFFFFFFF) + break; + } + + } + + void Read(u32 cluster, Dir *d) + { + int i; + s8 file1[512], file2[512]; + Dir D1, D2; + + for(i = 0; i < 0xffff; i++) + { + + // Read first page containing the first dir + fseek(fp, 0xA920 + ((cluster) * 0x420), SEEK_SET); + fread(file1, 1, 512, fp); + // Initialize the temporal dir + D1.Load(file1); + if(D1.Exists() && D1.Lenght >= 0 && D1.Cluster >= 0) + { + // If it exists add the dir to current dir + Dir *td = d->AddSon(D1); + + // If it's a directory and not . or .. recursive call + if(D1.IsDirectory() && D1.Name[0]!= '.' ) + { + Read(td->Cluster, td); + } + + // If it's a file load the file + if(D1.IsFile()) + { + ReadFile(td); + } + } + + + // Read second page with second dir or empty + fseek(fp, 0xA920 + (((cluster) * 0x420) + 528), SEEK_SET); + fread(file2, 1, 512, fp); + // Initialize the temporal dir and add it to current dir + D2.Load(file2); + if(D2.Exists() && D2.Lenght >= 0 && D2.Cluster >= 0) + { + Dir *td = d->AddSon(D2); + + if(D2.IsDirectory() && D2.Name[0]!= '.') + { + Read(td->Cluster, td); + } + + // If it's a file load the file + if(D2.IsFile() && D2.Name[0]!= '.') + { + ReadFile(td); + } + } + + // Get next cluster from the FAT table + cluster = FAT[cluster]; + cluster = cluster ^ 0x80000000; + if(cluster == 0x7FFFFFFF || cluster == 0xFFFFFFFF) + break; + } + + } + + void Load(char *filename) + { + strcpy(FileName, filename); + + // Clear previous data + if(fp != 0) + { + fclose(fp); + } + memzero_obj(FAT); + Root.Release(); + + SaveGameList.clear(); + ImageList_Destroy(ImageList); + + // Open memory card file + fp = fopen(FileName, "rb"); + + // Check if we opened the file + if(fp == NULL) + { + MessageBox(mcdDlg, "Unable to open memory card file.", "Error", 0); + return; + } + + // Build the FAT table for the card + if(BuildFAT() == 1) + { + // Read the root dir + Read(0, &Root); + } + + fclose(fp); + + ReadSaveGames(); + } + + void AddDirToTreeView(HWND hWnd, HTREEITEM tree, Dir *d) + { + for(unsigned int i=0;iSons.size();i++) + { + HTREEITEM temptree = AddTreeItem(hWnd, tree, d->Sons[i].Name, 0); + if(d->Sons[i].IsDirectory()) + { + TreeView_SetItemState(hWnd, temptree, TVIS_EXPANDED|TVIS_BOLD, 0x00F0); + AddDirToTreeView(hWnd, temptree, &d->Sons[i]); + } + } + } + + void AddToListView(HWND hWnd) + { + ListView_SetIconSpacing(hWnd, 128, 128); + ListView_DeleteAllItems(hWnd); + ListView_SetImageList(hWnd, ImageList, LVSIL_NORMAL); + + LVITEM item; + char temp[2560]; + item.mask = LVIF_IMAGE | LVIF_TEXT; + item.iItem = 0; + item.iSubItem = 0; + item.state = 0; + item.stateMask = 0; + item.pszText = temp; + item.cchTextMax = 255; + item.iImage = 1; + item.iIndent = 0; + item.lParam = 0; + + for(unsigned int i=0;iSons.size();i++) + { + if(!strcmp(file, d->Sons[i].Name)) + break; + } + + if(i == d->Sons.size()) + { + MessageBox(mcdDlg, "Unable to find the file.", "Error", 0); + return NULL; + } + + return &d->Sons[i]; + } + + void PaintICOFile(HWND hWnd, char *dir, char *name) + { + // First find the file + Dir *d = FindFile(dir, name); + if(d == NULL) + return; + + + + u32 FileID = *(u32 *)d->File; + u32 AnimationShapes = *(u32 *)(d->File + 4); + u32 TextureType = *(u32 *)(d->File + 8); + //u32 ConstantCheck = *(u32 *)(d->File + 12); + u32 NumVertex = *(u32 *)(d->File + 16); + + // Check if it's an ico file + if(FileID != 0x00010000) + { + MessageBox(mcdDlg, "It's not an ICO file.", "Error", 0); + return; + } + + // Is texture compressed? + if(TextureType != 0x07) + { + //MessageBox(mcdDlg, "Compressed texture, not yet supported.", "Error", 0); + //return; + } + + // Calculate the offset to the animation segment + u32 VertexSize = (AnimationShapes * 8) + 8 + 8; + u32 AnimationSegmentOffset = (VertexSize * NumVertex) + 20; + + // Check if we really are at the animation header + u32 AnimationID = *(u32 *)(d->File + AnimationSegmentOffset); + if(AnimationID != 0x00000001) + { + MessageBox(mcdDlg, "Animation header ID is incorrect.", "Error", 0); + return; + } + + // Get the size of all the animation segment + u32 FrameLenght = *(u32 *)(d->File + AnimationSegmentOffset + 4); + u32 NumberOfFrames = *(u32 *)(d->File + AnimationSegmentOffset + 16); + + u32 Offset = AnimationSegmentOffset + 20; + for(unsigned int i=0;iFile + Offset + 4); + Offset += 16 + (KeyNum * 8) - 8; // The -8 is there because the doc with the ico format spec is either wrong or I'm stupid + } + + int z, y=0; + u16 ico[0x4000]; + u8 cico[128*128*3]; + + if(TextureType > 0x07) + { + u32 CompressedDataSize = *(u32 *)(d->File + Offset); + //RLE compression + + Offset += 4; + u32 OffsetEnd = Offset + CompressedDataSize; + u32 WriteCount = 0; + + while(WriteCount < 0x8000 && Offset < OffsetEnd) + { + + u16 Data = *(u16 *)(d->File + Offset); + + if(Data < 0xFF00) //Replication + { + Offset += 2; + u16 Rep = *(u16 *)(d->File + Offset); + + for(int i=0;iFile + Offset); + WriteCount++; + Offset += 2; + } + } + + } + + } + else + { + // Get the texture from the file + memcpy(ico, &d->File[/*d->Lenght-0x8000*/Offset], 0x8000); + } + + // Convert from TIM to rgb + for(z=0;z<0x4000;z++) + { + cico[y] = 8 * (ico[z] & 0x1F); + cico[y+1] = 8 * ((ico[z] >>5) & 0x1F); + cico[y+2] = 8 * (ico[z] >>10); + y += 3; + } + + // Draw + int px, py, pc=0; + HDC dc = GetDC(hWnd); + for(px=0;px<128;px++){ + for(py=0;py<128;py++){ + SetPixel(dc, py, px, RGB(cico[pc], cico[pc+1], cico[pc+2])); + pc+=3; + + } + } + } + + + void ReadSaveGames() + { + // Initialize the conversion table for SJIS strings + IniSJISTable(); + + // Create the image list + ImageList = ImageList_Create(64, 64, ILC_COLOR32, 10, 256); + + for(unsigned int i=0;iSons.size(); // number of files inside the dir + *(u8 *)&dir[8] = 0; // creation time seconds + *(u8 *)&dir[9] = 0; // creation time minuts + *(u8 *)&dir[10] = 0; // creation time hours + *(u8 *)&dir[11] = 1; // creation time day + *(u8 *)&dir[12] = 1; // creation time month + *(u16 *)&dir[13] = 2008; // creation time year + *(u32 *)&dir[16] = dircluster; // cluster with the contents of the dir + *(u32 *)&dir[20] = 0; // dir entry for '.' + *(u8 *)&dir[24] = 0; // modification time seconds + *(u8 *)&dir[25] = 0; // modification time minuts + *(u8 *)&dir[26] = 0; // modification time hours + *(u8 *)&dir[27] = 1; // modification time day + *(u8 *)&dir[28] = 1; // modification time month + *(u16 *)&dir[29] = 2008; // modification time year + *(u32 *)&dir[32] = 0; // user attribute unused + strcpy(&dir[64], Di->Name); // DIRECTORY NAME + + + if(Lenght % 2) + { + // Find empty cluster... + int c = FindEmptyCluster(); + + // THIS MAY BE WRONG...TEST + FAT[oldcluster] = c; + FAT[c] = 0xFFFFFFFF; + + // Add dir to first page of new cluster + fseek(fp, 0xA920 + (((c) * 0x420) + 0), SEEK_SET); + fwrite(dir, 512, 1, fp); + } + else + { + // Add dir to second page of cluster + fseek(fp, 0xA920 + (((oldcluster) * 0x420) + 528), SEEK_SET); + fwrite(dir, 512, 1, fp); + } + + // CALCULATE CRCS OF CLUSTER + + + // INCREASE ROOT DIR SIZE BY 1 + fseek(fp, 0xA920 + (((0) * 0x420) + 4), SEEK_SET); + int nl = Lenght + 1; + fwrite(&nl, 4, 1, fp); + + + int numfiles = 5; + + // ADD FILE ENTRIES TO DIR ., .. + memzero_obj(dir); + *(u16 *)&dir[0] = Dir::DF_EXISTS | Dir::DF_DIRECTORY | Dir::DF_READ; // mode flag + *(u32 *)&dir[4] = numfiles; // number of files inside the dir + *(u8 *)&dir[8] = 0; // creation time seconds + *(u8 *)&dir[9] = 0; // creation time minuts + *(u8 *)&dir[10] = 0; // creation time hours + *(u8 *)&dir[11] = 1; // creation time day + *(u8 *)&dir[12] = 1; // creation time month + *(u16 *)&dir[13] = 2008; // creation time year + *(u32 *)&dir[16] = 0; // cluster with the contents of the dir + *(u32 *)&dir[20] = 0; // dir entry for '.' + *(u8 *)&dir[24] = 0; // modification time seconds + *(u8 *)&dir[25] = 0; // modification time minuts + *(u8 *)&dir[26] = 0; // modification time hours + *(u8 *)&dir[27] = 1; // modification time day + *(u8 *)&dir[28] = 1; // modification time month + *(u16 *)&dir[29] = 2008; // modification time year + *(u32 *)&dir[32] = 0; // user attribute unused + strcpy(&dir[64], "."); // DIRECTORY NAME + fseek(fp, 0xA920 + (((dircluster) * 0x420) + 0), SEEK_SET); + fwrite(dir, 512, 1, fp); + + memzero_obj(dir); + *(u16 *)&dir[0] = Dir::DF_EXISTS | Dir::DF_DIRECTORY | Dir::DF_READ; // mode flag + *(u32 *)&dir[4] = 2; // number of files inside the dir + *(u8 *)&dir[8] = 0; // creation time seconds + *(u8 *)&dir[9] = 0; // creation time minuts + *(u8 *)&dir[10] = 0; // creation time hours + *(u8 *)&dir[11] = 1; // creation time day + *(u8 *)&dir[12] = 1; // creation time month + *(u16 *)&dir[13] = 2008; // creation time year + *(u32 *)&dir[16] = 0; // cluster with the contents of the dir + *(u32 *)&dir[20] = 0; // dir entry for '.' + *(u8 *)&dir[24] = 0; // modification time seconds + *(u8 *)&dir[25] = 0; // modification time minuts + *(u8 *)&dir[26] = 0; // modification time hours + *(u8 *)&dir[27] = 1; // modification time day + *(u8 *)&dir[28] = 1; // modification time month + *(u16 *)&dir[29] = 2008; // modification time year + *(u32 *)&dir[32] = 0; // user attribute unused + strcpy(&dir[64], ".."); // DIRECTORY NAME + fseek(fp, 0xA920 + (((dircluster) * 0x420) + 528), SEEK_SET); + fwrite(dir, 512, 1, fp); + + FAT[dircluster] = 0xFFFFFFFF; + + + // ADD REST OF FILES + + for(int i=2;iFile, d->Lenght, 1, fp); + fclose(fp); + } + } +} + + + +FILE *fp; + +BOOL CALLBACK ConfigureMcdsDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + mcdDlg = hW; + + SetWindowText(hW, _("Memcard Manager")); + + Button_SetText(GetDlgItem(hW, IDOK), _("OK")); + Button_SetText(GetDlgItem(hW, IDCANCEL), _("Cancel")); + Button_SetText(GetDlgItem(hW, IDC_MCDSEL1), _("Select Mcd")); + Button_SetText(GetDlgItem(hW, IDC_MCDSEL2), _("Select Mcd")); + + Static_SetText(GetDlgItem(hW, IDC_FRAMEMCD1), _("Memory Card 1")); + Static_SetText(GetDlgItem(hW, IDC_FRAMEMCD2), _("Memory Card 2")); + + if (!strlen(Config.Mcd1)) strcpy(Config.Mcd1, MEMCARDS_DIR "\\" DEFAULT_MEMCARD1); + if (!strlen(Config.Mcd2)) strcpy(Config.Mcd2, MEMCARDS_DIR "\\" DEFAULT_MEMCARD2); + Edit_SetText(GetDlgItem(hW,IDC_MCD1), Config.Mcd1); + Edit_SetText(GetDlgItem(hW,IDC_MCD2), Config.Mcd2); + + //MC1.Load(Config.Mcd1); + //MC2.Load(Config.Mcd2); + + //MC1.AddCardToTreeView(GetDlgItem(hW, IDC_TREE1)); + //MC2.AddCardToTreeView(GetDlgItem(hW, IDC_TREE2)); + + //MC1.AddToListView(GetDlgItem(hW,IDC_LIST1)); + //MC2.AddToListView(GetDlgItem(hW,IDC_LIST2)); + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + + case IDC_MCDSEL1: + Open_Mcd_Proc(hW, 1); + return TRUE; + + case IDC_MCDSEL2: + Open_Mcd_Proc(hW, 2); + return TRUE; + + case IDCANCEL: + { + EndDialog(hW,FALSE); + return TRUE; + } + + case IDOK: + Edit_GetText(GetDlgItem(hW,IDC_MCD1), Config.Mcd1, 256); + Edit_GetText(GetDlgItem(hW,IDC_MCD2), Config.Mcd2, 256); +// SaveConfig(); + + EndDialog(hW,TRUE); + return TRUE; + + case IDC_RELOAD1: + { + char cardname[1024]; + Edit_GetText(GetDlgItem(hW, IDC_MCD1), cardname, 1024); + MC1.Load(cardname); + MC1.AddToListView(GetDlgItem(hW,IDC_LIST1)); + //MC1.AddCardToTreeView(GetDlgItem(hW,IDC_TREE1)); + break; + } + + case IDC_RELOAD2: + { + char cardname[1024]; + Edit_GetText(GetDlgItem(hW, IDC_MCD2), cardname, 1024); + MC2.Load(cardname); + MC2.AddToListView(GetDlgItem(hW,IDC_LIST2)); + //MC2.AddCardToTreeView(GetDlgItem(hW,IDC_TREE2)); + break; + } + + case IDC_LOADICO1: + { + char cardname[1024]; + MC1.SaveRootDir("test"); + Edit_GetText(GetDlgItem(hW, IDC_MCD1), cardname, 1024); + MC1.Load(cardname); + MC1.AddToListView(GetDlgItem(hW,IDC_LIST1)); + /* + char dir[256], name[256]; + if(GetTreeViewSelection(GetDlgItem(mcdDlg, IDC_TREE1), dir, name)) + MC1.PaintICOFile(GetDlgItem(mcdDlg, IDC_ICON1), dir, name); + else + MessageBox(mcdDlg, "Please select a file.", "Error", 0); + */ + break; + } + + case IDC_LOADICO2: + { + /* + char dir[256], name[256]; + if(GetTreeViewSelection(GetDlgItem(mcdDlg, IDC_TREE2), dir, name)) + MC1.PaintICOFile(GetDlgItem(mcdDlg, IDC_ICON2), dir, name); + else + MessageBox(mcdDlg, "Please select a file.", "Error", 0); + */ + break; + } + + case IDC_SAVE1: + { + /* + char dir[256], name[256]; + if(GetTreeViewSelection(GetDlgItem(mcdDlg, IDC_TREE1), dir, name)) + SaveFileDialog(mcdDlg, 1, dir, name); + else + MessageBox(mcdDlg, "Please select a file.", "Error", 0); + */ + break; + } + + case IDC_SAVE2: + { + /* + char dir[256], name[256]; + if(GetTreeViewSelection(GetDlgItem(mcdDlg, IDC_TREE2), dir, name)) + SaveFileDialog(mcdDlg, 2, dir, name); + else + MessageBox(mcdDlg, "Please select a file.", "Error", 0); + */ + break; + } + } + case WM_DESTROY: + return TRUE; + } + return FALSE; +} + + + + diff --git a/pcsx2/windows/McdsDlg.h b/pcsx2/windows/McdsDlg.h new file mode 100644 index 0000000000..756100a311 --- /dev/null +++ b/pcsx2/windows/McdsDlg.h @@ -0,0 +1,24 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __MCDSDLG_H__ +#define __MCDSDLG_H__ + +BOOL CALLBACK ConfigureMcdsDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam); + +#endif /* __MCDSDLG_H__ */ diff --git a/pcsx2/windows/PatchBrowser.cpp b/pcsx2/windows/PatchBrowser.cpp new file mode 100644 index 0000000000..8c4cbb7d3f --- /dev/null +++ b/pcsx2/windows/PatchBrowser.cpp @@ -0,0 +1,352 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +/************************** +* +* patchbrowser.c contains all the src of patchbrowser window +* no interaction with emulation code +***************************/ + +#include "Win32.h" +#include "Common.h" +#include "resource.h" + +/* + * TODO: + * - not topmost + * - resize stuff + * - ask to save in exit (check if changed) + */ +BOOL CALLBACK PatchBDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + + int tmpi,i; + char fileName[MAX_PATH], *tmpStr; + FILE *fp; + + switch(uMsg) { + + case WM_INITDIALOG: + SetWindowText(hW, _("Patches Browser")); + Button_SetText(GetDlgItem(hW,IDC_REFRESHPATCHLIST), _("Refresh List")); + Button_SetText(GetDlgItem(hW,IDC_NEWPATCH), _("New Patch")); + Button_SetText(GetDlgItem(hW,IDC_SAVEPATCH), _("Save Patch")); + Button_SetText(GetDlgItem(hW,IDC_EXITPB), _("Exit")); + Static_SetText(GetDlgItem(hW,IDC_GAMENAMESEARCH), _("Search game name patch:")); + //List Patches + ListPatches ((HWND) hW); + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + + case IDC_NEWPATCH: + + i = Save_Patch_Proc(fileName); + if ( i == FALSE || (fp = fopen(fileName,"a")) == NULL ) { + MessageBox(hW,(LPCTSTR)"Couldn't create the file.",NULL,(UINT)MB_ICONERROR); + return FALSE; + } + + fclose(fp); + i = MessageBox(hW,(LPCTSTR)"File created sucessfully.\nClear textbox?",NULL,(UINT)(MB_YESNO|MB_ICONQUESTION)); + if (i==IDYES) SetDlgItemText(hW, IDC_PATCHTEXT, (LPCTSTR)""); + + return TRUE; + + case IDC_SAVEPATCH: + + i = Save_Patch_Proc(fileName); + if ( i == FALSE || (fp = fopen(fileName,"w")) == NULL ) { + MessageBox(hW,(LPCTSTR)"Couldn't save the file.",NULL,(UINT)MB_ICONERROR); + return FALSE; + } + + tmpi = SendDlgItemMessage(hW, IDC_PATCHTEXT, EM_GETLINECOUNT, (WPARAM)NULL, (LPARAM)NULL); + i=0; + for (;tmpi>=0;tmpi--) + i += SendDlgItemMessage(hW, IDC_PATCHTEXT, EM_LINELENGTH, (WPARAM)tmpi, (LPARAM)NULL); + + tmpStr = (char *) malloc(i); + sprintf(tmpStr,""); + SendDlgItemMessage(hW, IDC_PATCHTEXT, WM_GETTEXT, (WPARAM)i, (LPARAM)tmpStr); + + //remove \r + for (i=0,tmpi=0; tmpStr[i]!='\0'; i++) + if (tmpStr[i] != '\r') + tmpStr[tmpi++] = tmpStr[i]; + tmpStr[tmpi] = '\0'; + + fputs(tmpStr,fp); + + fclose(fp); + free(tmpStr); + + MessageBox(hW,(LPCTSTR)"File saved sucessfully.",NULL,(UINT)MB_ICONINFORMATION); + + return TRUE; + + case IDC_REFRESHPATCHLIST: + + //List Patches + ListPatches ((HWND) hW); + + return TRUE; + + case IDC_EXITPB: + + //Close Dialog + EndDialog(hW, FALSE); + + return TRUE; + + case IDC_PATCHCRCLIST: + + //Get selected item + tmpi = SendDlgItemMessage(hW, IDC_PATCHCRCLIST, LB_GETCURSEL, 0, 0); + SendDlgItemMessage(hW, IDC_PATCHCRCLIST, LB_GETTEXT, (WPARAM)tmpi, (LPARAM)fileName); + + return ReadPatch ((HWND) hW, fileName); + + case IDC_PATCHNAMELIST: + + //Get selected item + tmpi = SendDlgItemMessage(hW, IDC_PATCHNAMELIST, LB_GETCURSEL, 0, 0); + SendDlgItemMessage(hW, IDC_PATCHNAMELIST, LB_GETTEXT, (WPARAM)tmpi, (LPARAM)fileName); + + //another small hack :p + //eg. SOCOM Demo PAL (7dd01dd9.pnach) + for (i=0;i<(int)strlen(fileName);i++) + if (fileName[i] == '(') tmpi = i; + + sprintf(fileName,"%c%c%c%c%c%c%c%c%c%c%c%c%c%c", + fileName[tmpi+1],fileName[tmpi+2],fileName[tmpi+3], + fileName[tmpi+4],fileName[tmpi+5],fileName[tmpi+6], + fileName[tmpi+7],fileName[tmpi+8],fileName[tmpi+9], + fileName[tmpi+10],fileName[tmpi+11],fileName[tmpi+12], + fileName[tmpi+13],fileName[tmpi+14]); + + //sanity check + if (fileName[tmpi+15] != ')') return FALSE; + + return ReadPatch ((HWND) hW, fileName); + + case IDC_SEARCHPATCHTEXT: + + //get text + SendDlgItemMessage(hW, IDC_SEARCHPATCHTEXT, EM_GETLINE, 0, (LPARAM)fileName); + //search + tmpi = SendDlgItemMessage(hW, IDC_PATCHNAMELIST, LB_FINDSTRING, (WPARAM)-1, (LPARAM)fileName); + //select match item + SendDlgItemMessage(hW, IDC_PATCHNAMELIST, LB_SETCURSEL, (WPARAM)tmpi, (LPARAM)NULL); + + return TRUE; + } + return TRUE; + + case WM_CLOSE: + EndDialog(hW, FALSE); + break; + + } + return FALSE; +} +void ListPatches (HWND hW) { + + int i, tmpi, filesize, totalPatch=0, totalSize=0; + char tmpStr[MAX_PATH], *fileData; + WIN32_FIND_DATA FindData; + HANDLE Find; + FILE *fp; + + //clear listbox's + SendDlgItemMessage(hW, IDC_PATCHCRCLIST, (UINT)LB_RESETCONTENT, (WPARAM)NULL, (LPARAM)NULL); + SendDlgItemMessage(hW, IDC_PATCHNAMELIST, (UINT)LB_RESETCONTENT, (WPARAM)NULL, (LPARAM)NULL); + + //sprintf(tmpStr,"%s*.pnach", Config.PatchDir) + sprintf(tmpStr, "patches\\*.pnach"); + + Find = FindFirstFile(tmpStr, &FindData); + + do { + if (Find==INVALID_HANDLE_VALUE) break; + + sprintf(tmpStr,"%s", FindData.cFileName); + + //add file name to crc list + SendDlgItemMessage(hW, IDC_PATCHCRCLIST, (UINT) LB_ADDSTRING, (WPARAM)NULL, (LPARAM)tmpStr); + + //sprintf(tmpStr,"%s%s", Config.PatchDir, FindData.cFileName) + sprintf(tmpStr,"patches\\%s", FindData.cFileName); + + fp = fopen(tmpStr, "r"); + if (fp == NULL) break; + + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + totalSize += filesize; + fseek(fp, 0, SEEK_SET); + + fileData = (char *) malloc(filesize+1024); + sprintf(fileData,""); + + //read file + while((tmpi=fgetc(fp)) != EOF) + sprintf(fileData,"%s%c",fileData,tmpi); + + //small hack :p + for(i=0;ii;tmpi--) + fileData[tmpi] = fileData[tmpi-1]; + fileData[i] = '\r'; + fileData[i+1] = '\n'; + i++; + } + } + + SetDlgItemText(hW, IDC_PATCHTEXT, (LPCTSTR)fileData); + + sprintf(fileData,""); + fclose(fp); + + return TRUE; +} + + +//Left Trim (remove the spaces at the left of a string) +char * lTrim (char *s) { + int count=0,i,tmpi; + + for (i=0;i<(int)strlen(s); i++) { + if (s[i] == ' ') count++; + else { + for (tmpi=0;tmpi<(int)strlen(s);tmpi++) + s[tmpi] = s[tmpi+count]; + break; + } + } + return s; +} + + +BOOL Save_Patch_Proc( char * filename ) { + + OPENFILENAME ofn; + char szFileName[ 256 ]; + char szFileTitle[ 256 ]; + char * filter = "Patch Files (*.pnach)\0*.pnach\0ALL Files (*.*)\0*.*"; + + memset( &szFileName, 0, sizeof( szFileName ) ); + memset( &szFileTitle, 0, sizeof( szFileTitle ) ); + + ofn.lStructSize = sizeof( OPENFILENAME ); + ofn.hwndOwner = gApp.hWnd; + ofn.lpstrFilter = filter; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = 256; + ofn.lpstrInitialDir = NULL; + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = 256; + ofn.lpstrTitle = NULL; + ofn.lpstrDefExt = "TXT"; + ofn.Flags = OFN_EXPLORER | OFN_LONGNAMES | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST; + + if ( GetSaveFileName( &ofn ) ) { + + strcpy( filename, szFileName ); + + return TRUE; + } + else { + return FALSE; + } +} diff --git a/pcsx2/windows/RDebugger.cpp b/pcsx2/windows/RDebugger.cpp new file mode 100644 index 0000000000..3127e87836 --- /dev/null +++ b/pcsx2/windows/RDebugger.cpp @@ -0,0 +1,374 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "Win32.h" + +#include +#include "Debugger.h" +#include "RDebugger.h" +#include "Common.h" +#include "R3000A.h" +#include "../rdebug/deci2.h" + +u32 port=8510; +SOCKET serversocket, remote; +char message[1024]; //message to add to listbox + +volatile long runStatus=STOP; +int runCode=0, runCount=1; +HANDLE runEvent=NULL; + +DECI2_DBGP_BRK ebrk[32], + ibrk[32]; +int ebrk_count=0, + ibrk_count=0; + +int debuggedCpu=0; //default is to debug EE cpu; IOP=1 +u32 breakAddress=0; //disabled; ie. you cannot use address 0 for a breakpoint +u32 breakCycle=0; //disabled; ie. you cannot stop after 0 cycles + +int CreateSocket(HWND hDlg, int port){ + WSADATA wsadata; + SOCKADDR_IN saServer; + + if (WSAStartup( MAKEWORD(1, 1), &wsadata) || + ((serversocket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET)){ + MessageBox(hDlg, "Could not create socket\n[Is TCP/IP installed? WinSock 1.1 or above?]", 0, MB_OK); + return FALSE; + } + sprintf(message, "[PCSX2] %s status=%s", wsadata.szDescription, wsadata.szSystemStatus); + ListBox_AddString(GetDlgItem(hDlg, IDC_COMMUNICATION), message); + + saServer.sin_family = AF_INET; + saServer.sin_addr.S_un.S_addr = INADDR_ANY; // accept any address + saServer.sin_port = htons(port); // port to listen to + + if (bind(serversocket, (LPSOCKADDR)&saServer, sizeof(struct sockaddr))==SOCKET_ERROR){ + sprintf(message, "Could not bind to port %d\n" + "[Is there another server running on that port?]", port); + MessageBox(hDlg, message, 0, MB_OK); + closesocket(serversocket); + return FALSE; + } + sprintf(message, "[PCSX2] Port %d is opened", port); + ListBox_AddString(GetDlgItem(hDlg, IDC_COMMUNICATION), message); + + // SOMAXCONN connections in queque? maybe 1 is enough... + if (listen(serversocket, SOMAXCONN) == SOCKET_ERROR){ + sprintf(message, "Listening for a connection failed\n" + "[dunno?]"); + MessageBox(hDlg, message, 0, MB_OK); + closesocket(serversocket); + return FALSE; + } + sprintf(message, "[PCSX2] Listening for a connection to establish..."); + ListBox_AddString(GetDlgItem(hDlg, IDC_COMMUNICATION), message); + + cpuRegs.CP0.n.EPC=cpuRegs.pc; + psxRegs.CP0.n.EPC=psxRegs.pc; + sprintf(message, "%08X", cpuRegs.pc); + Edit_SetText(GetDlgItem(hDlg, IDC_EEPC), message); + sprintf(message, "%08X", psxRegs.pc); + Edit_SetText(GetDlgItem(hDlg, IDC_IOPPC), message); + sprintf(message, "%d", cpuRegs.cycle); + Edit_SetText(GetDlgItem(hDlg, IDC_EECYCLE), message); + sprintf(message, "%d", psxRegs.cycle); + Edit_SetText(GetDlgItem(hDlg, IDC_IOPCYCLE), message); + Button_SetCheck(GetDlgItem(hDlg, IDC_DEBUGEE), (debuggedCpu==0)); + Button_SetCheck(GetDlgItem(hDlg, IDC_DEBUGIOP), (debuggedCpu==1)); + Edit_LimitText(GetDlgItem(hDlg, IDC_STOPAT), 8); //8 hex digits + Edit_LimitText(GetDlgItem(hDlg, IDC_STOPAFTER), 10);//10 decimal digits + sprintf(message, "%08X", breakAddress); + Edit_SetText(GetDlgItem(hDlg, IDC_STOPAT), message); + sprintf(message, "%d", breakCycle); + Edit_SetText(GetDlgItem(hDlg, IDC_STOPAFTER), message); + + Button_Enable(GetDlgItem(hDlg, IDC_DEBUGIOP), FALSE);//////////////////////// + + return TRUE; +} + +int readData(u8 *buffer){ + int r, count=0; + u8 *p=buffer; + + memset(buffer, 0, BUFFERSIZE); + while (((count+= r = recv(remote, (char*)p, BUFFERSIZE, 0))!=INVALID_SOCKET) && + (count<*(u16*)buffer)) + p+=r; + + if (r==INVALID_SOCKET) + return 0; + + return count; +} + +int writeData(const u8 *result){ + int r;/*, i; + static char l[300], p[10]; + DECI2_HEADER *header=(DECI2_HEADER*)result; +*/ + r = send(remote, (const char*)result, *(u16*)result, 0); + if (r==SOCKET_ERROR) + return 0; +/* + sprintf(l, "size=%d, src=%c dst=%c proto=0x%04X ", + header->length-8, header->source, header->destination, header->protocol); + for (i=8; i<*(u16*)result; i++){ + sprintf(p, "%02X ", result[i]); + strcat(l, p); + } + Msgbox::Alert(l); +*/ + return r; +} + +DWORD WINAPI ServingFunction(LPVOID lpParam){ + static u8 buffer[BUFFERSIZE], //a big buffer + result[BUFFERSIZE]; //a big buffer + static TCHAR eepc[9], ioppc[9], eecy[15], iopcy[15]; + SOCKADDR_IN saClient; + HWND hDlg=(HWND)lpParam; + int size=sizeof(struct sockaddr); + int exit=FALSE; + + if ((remote = accept(serversocket, (struct sockaddr*)&saClient, &size))==INVALID_SOCKET){ + ListBox_AddString(GetDlgItem(hDlg, IDC_COMMUNICATION), "[PCSX2] Commmunication lost. THE END"); + return FALSE; + } + sprintf(message, "[PCSX2] Connected to %d.%d.%d.%d on remote port %d", + saClient.sin_addr.S_un.S_un_b.s_b1, + saClient.sin_addr.S_un.S_un_b.s_b2, + saClient.sin_addr.S_un.S_un_b.s_b3, + saClient.sin_addr.S_un.S_un_b.s_b4, + saClient.sin_port); + ListBox_AddString(GetDlgItem(hDlg, IDC_COMMUNICATION), message); + ListBox_AddString(GetDlgItem(hDlg, IDC_COMMUNICATION), "[PCSX2] Start serving..."); + connected=1;//from this point on, all log stuff goes to ttyp + + //sendBREAK('E', 0, 0xff, 0x21, 1); //do not enable this unless you know what you are doing! + while (!exit && readData(buffer)){ + DECI2_HEADER *header=(DECI2_HEADER*)buffer; + + switch(header->protocol){ + case 0x0000:exit=TRUE; break; + case PROTO_DCMP:D2_DCMP(buffer, result, message); break; +// case 0x0120:D2_DRFP_EE(buffer, result, message); break; +// case 0x0121:D2_DRFP_IOP(buffer, result, message); break; +// case 0x0122:break; + case PROTO_IDBGP:D2_DBGP(buffer, result, message, eepc, ioppc, eecy, iopcy); break; +// case 0x0140:break; + case PROTO_ILOADP:D2_ILOADP(buffer, result, message); break; + case PROTO_EDBGP:D2_DBGP(buffer, result, message, eepc, ioppc, eecy, iopcy);break; +// case 0x0240:break; + case PROTO_NETMP:D2_NETMP(buffer, result, message); break; + default: + sprintf(message, "[DECI2 %c->%c/%04X] Protocol=0x%04X", + header->source, header->destination, + header->length, header->protocol); + ListBox_AddString(GetDlgItem(hDlg, IDC_COMMUNICATION), message); + continue; + } + if (exit==FALSE){ + ListBox_AddString(GetDlgItem(hDlg, IDC_COMMUNICATION), message); + Edit_SetText(GetDlgItem(hDlg, IDC_EEPC), eepc); + Edit_SetText(GetDlgItem(hDlg, IDC_IOPPC), ioppc); + Edit_SetText(GetDlgItem(hDlg, IDC_EECYCLE), eecy); + Edit_SetText(GetDlgItem(hDlg, IDC_IOPCYCLE), iopcy); + } + } + connected=0; + + ListBox_AddString(GetDlgItem(hDlg, IDC_COMMUNICATION), "[PCSX2] Connection closed. THE END"); + return TRUE; +} + +DWORD WINAPI Run2(LPVOID lpParam){ + HWND hDlg=(HWND)lpParam; + static char pc[9]; + int i; + + while (1){ + if (runStatus==RUN){ + if (PSMu32(cpuRegs.pc)==0x0000000D){ + sendBREAK('E', 0, runCode, 0x22, runCount); + InterlockedExchange(&runStatus, STOP); + continue; + } + if ((runCode==2) && (//next + ((PSMu32(cpuRegs.pc) & 0xFC000000)==0x0C000000) ||//JAL + ((PSMu32(cpuRegs.pc) & 0xFC00003F)==0x00000009) ||//JALR + ((PSMu32(cpuRegs.pc) & 0xFC00003F)==0x0000000C) //SYSCALL + )){u32 tmppc=cpuRegs.pc, skip=(PSMu32(cpuRegs.pc) & 0xFC00003F)==0x0000000C ? 4 : 8; + while (cpuRegs.pc!=tmppc+skip) + Cpu->Step(); + }else + Cpu->Step(); //use this with breakpoints & step-by-step +// Cpu->ExecuteBlock(); //use this to run faster, but not for stepping +// sprintf(pc, "%08X", cpuRegs.pc);Edit_SetText(GetDlgItem(hDlg, IDC_EEPC), pc); +// sprintf(pc, "%08X", psxRegs.pc);Edit_SetText(GetDlgItem(hDlg, IDC_IOPPC), pc); +// sprintf(pc, "%d", cpuRegs.cycle);Edit_SetText(GetDlgItem(hDlg, IDC_EECYCLE), pc); +// sprintf(pc, "%d", psxRegs.cycle);Edit_SetText(GetDlgItem(hDlg, IDC_IOPCYCLE), pc); + if (runCount!=0 && --runCount==0){ + sendBREAK('E', 0, runCode, 0x23, runCount); + InterlockedExchange(&runStatus, STOP); + continue; + } + if ((breakAddress) && (breakAddress==cpuRegs.pc)){ + sendBREAK('E', 0, runCode, 0x21, runCount); + InterlockedExchange(&runStatus, STOP); + continue; + } + if ((breakCycle) && (breakCycle==cpuRegs.cycle)){ + sendBREAK('E', 0, runCode, 0x21, runCount); + InterlockedExchange(&runStatus, STOP); + breakCycle=0; + Edit_SetText(GetDlgItem(hDlg, IDC_STOPAFTER), "0"); + continue; + } + for (i=0; i +#include +#include + +#define BUFFERSIZE (128*1024) + +extern LRESULT WINAPI RemoteDebuggerParamsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +extern LRESULT WINAPI RemoteDebuggerProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); diff --git a/pcsx2/windows/SamplProf.cpp b/pcsx2/windows/SamplProf.cpp new file mode 100644 index 0000000000..d2e02c4d96 --- /dev/null +++ b/pcsx2/windows/SamplProf.cpp @@ -0,0 +1,325 @@ + +#include "PrecompiledHeader.h" + +#ifndef _DEBUG + +#include "SamplProf.h" +#include +#include + +using namespace std; + +DWORD GetModuleFromPtr(IN void* ptr,OUT LPSTR lpFilename,IN DWORD nSize) +{ + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery(ptr,&mbi,sizeof(mbi)); + return GetModuleFileName((HMODULE)mbi.AllocationBase,lpFilename,nSize); +} +struct Module +{ + uptr base; + uptr end; + uptr len; + string name; + u32 ticks; + + Module(const char* name, const void* ptr) + { + if (name!=0) + this->name=name; + FromAddress(ptr,name==0); + ticks=0; + } + Module(const char* name, const void* b, u32 s) + { + this->name=name; + FromValues(b,s); + ticks=0; + } + bool operator<(const Module &other) const + { + return ticks>other.ticks; + } + string ToString(u32 total_ticks) + { + return name + ": " + to_string(ticks*100/(float)total_ticks) + " "; + } + bool Inside(uptr val) { return val>=base && val<=end; } + void FromAddress(const void* ptr,bool getname) + { + char filename[512]; + char filename2[512]; + static const void* ptr_old=0; + + if (ptr_old==ptr) + return; + ptr_old=ptr; + + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery(ptr,&mbi,sizeof(mbi)); + base=(u32)mbi.AllocationBase; + GetModuleFileName((HMODULE)mbi.AllocationBase,filename,512); + len=(u8*)mbi.BaseAddress-(u8*)mbi.AllocationBase+mbi.RegionSize; + + if (getname) + { + name=filename; + size_t last=name.find_last_of('\\'); + last=last==name.npos?0:last+1; + name=name.substr(last); + } + + for(;;) + { + VirtualQuery(((u8*)base)+len,&mbi,sizeof(mbi)); + if (!(mbi.Type&MEM_IMAGE)) + break; + + if (!GetModuleFileName((HMODULE)mbi.AllocationBase,filename2,512)) + break; + + if (strcmp(filename,filename2)!=0) + break; + len+=mbi.RegionSize; + } + + + end=base+len-1; + } + void FromValues(const void* b,u32 s) + { + base= (uptr)b; + len=s; + end=base+len-1; + } +}; + +typedef map MapType; + +static vector ProfModules; +static MapType ProfUnknownHash; + +static HANDLE hEmuThread = NULL; +static HANDLE hMtgsThread = NULL; +static HANDLE hProfThread = NULL; + +static CRITICAL_SECTION ProfModulesLock; + +static volatile bool ProfRunning=false; + +static bool _registeredName( const char* name ) +{ + for( vector::const_iterator + iter = ProfModules.begin(), + end = ProfModules.end(); itername.compare( name ) == 0 ) + return true; + } + return false; +} + +void ProfilerRegisterSource(const char* Name, const void* buff, u32 sz) +{ + if( ProfRunning ) + EnterCriticalSection( &ProfModulesLock ); + + if( !_registeredName( Name ) ) + ProfModules.push_back( Module(Name, buff, sz) ); + + if( ProfRunning ) + LeaveCriticalSection( &ProfModulesLock ); +} + +void ProfilerRegisterSource(const char* Name, const void* function) +{ + if( ProfRunning ) + EnterCriticalSection( &ProfModulesLock ); + + if( !_registeredName( Name ) ) + ProfModules.push_back( Module(Name,function) ); + + if( ProfRunning ) + LeaveCriticalSection( &ProfModulesLock ); +} + +void ProfilerTerminateSource( const char* Name ) +{ + for( vector::const_iterator + iter = ProfModules.begin(), + end = ProfModules.end(); itername.compare( Name ) == 0 ) + { + ProfModules.erase( iter ); + break; + } + } +} + +static bool DispatchKnownModules( uint Eip ) +{ + bool retval = false; + EnterCriticalSection( &ProfModulesLock ); + + size_t i; + for(i=0;i::iterator iter=ProfUnknownHash.find(modulenam); + if (iter!=ProfUnknownHash.end()) + { + iter->second.ticks++; + return; + } + + Module tmp((sz==0) ? modulenam.c_str() : NULL, (void*)Eip); + tmp.ticks++; + + ProfUnknownHash.insert(MapType::value_type(modulenam, tmp)); +} + +int __stdcall ProfilerThread(void* nada) +{ + ProfUnknownHash.clear(); + u32 tick_count=0; + + while(ProfRunning) + { + Sleep(5); + + if (tick_count>500) + { + string rv="|"; + u32 subtotal=0; + for (size_t i=0;i lst; + for (MapType::iterator i=ProfUnknownHash.begin();i!=ProfUnknownHash.end();i++) + { + lst.push_back(i->second); + } + + sort(lst.begin(),lst.end()); + for (size_t i=0;i\n",rv.c_str()); + + tick_count=0; + + ProfUnknownHash.clear(); + } + + tick_count++; + + CONTEXT ctx; + ctx.ContextFlags = CONTEXT_FULL; + GetThreadContext(hEmuThread,&ctx); + + if( !DispatchKnownModules( ctx.Eip ) ) + { + MapUnknownSource( ctx.Eip ); + } + + if( hMtgsThread != NULL ) + { + GetThreadContext(hMtgsThread,&ctx); + if( DispatchKnownModules( ctx.Eip ) ) + continue; + } + } + + return -1; +} + +void ProfilerInit() +{ + if (ProfRunning) + return; + + //Console::Msg( "Profiler Thread Initializing..." ); + ProfRunning=true; + DuplicateHandle(GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + &(HANDLE)hEmuThread, + 0, + FALSE, + DUPLICATE_SAME_ACCESS); + + InitializeCriticalSection( &ProfModulesLock ); + + hProfThread=CreateThread(0,0,(LPTHREAD_START_ROUTINE)ProfilerThread,0,0,0); + SetThreadPriority(hProfThread,THREAD_PRIORITY_HIGHEST); + //Console::WriteLn( " Done!" ); +} + +void ProfilerTerm() +{ + //Console::Msg( "Profiler Terminating..." ); + if (!ProfRunning) + return; + + ProfRunning=false; + + if( hProfThread != NULL ) + { + ResumeThread(hProfThread); + WaitForSingleObject(hProfThread,INFINITE); + CloseHandle(hProfThread); + } + + if( hEmuThread != NULL ) + CloseHandle( hEmuThread ); + + if( hMtgsThread != NULL ) + CloseHandle( hMtgsThread ); + + DeleteCriticalSection( &ProfModulesLock ); + //Console::WriteLn( " Done!" ); +} + +void ProfilerSetEnabled(bool Enabled) +{ + if (!ProfRunning) + { + if( !Enabled ) return; + ProfilerInit(); + } + + if (Enabled) + ResumeThread(hProfThread); + else + SuspendThread(hProfThread); +} + +#endif diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj new file mode 100644 index 0000000000..85688821c1 --- /dev/null +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -0,0 +1,2805 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pcsx2/windows/VCprojects/pthreads_2008.vcproj b/pcsx2/windows/VCprojects/pthreads_2008.vcproj new file mode 100644 index 0000000000..8afb6a7157 --- /dev/null +++ b/pcsx2/windows/VCprojects/pthreads_2008.vcproj @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pcsx2/windows/VCprojects/vsprops/common.vsprops b/pcsx2/windows/VCprojects/vsprops/common.vsprops new file mode 100644 index 0000000000..5869ad8a41 --- /dev/null +++ b/pcsx2/windows/VCprojects/vsprops/common.vsprops @@ -0,0 +1,36 @@ + + + + + + + diff --git a/pcsx2/windows/VCprojects/vsprops/debug.vsprops b/pcsx2/windows/VCprojects/vsprops/debug.vsprops new file mode 100644 index 0000000000..af6f81ca44 --- /dev/null +++ b/pcsx2/windows/VCprojects/vsprops/debug.vsprops @@ -0,0 +1,18 @@ + + + + + diff --git a/pcsx2/windows/VCprojects/vsprops/devbuild.vsprops b/pcsx2/windows/VCprojects/vsprops/devbuild.vsprops new file mode 100644 index 0000000000..5713920648 --- /dev/null +++ b/pcsx2/windows/VCprojects/vsprops/devbuild.vsprops @@ -0,0 +1,11 @@ + + + + diff --git a/pcsx2/windows/VCprojects/vsprops/postBuild.tmpl b/pcsx2/windows/VCprojects/vsprops/postBuild.tmpl new file mode 100644 index 0000000000..4f7d7bd309 --- /dev/null +++ b/pcsx2/windows/VCprojects/vsprops/postBuild.tmpl @@ -0,0 +1,21 @@ +@echo off +rem +rem Usage: postBuild.cmd SourcePath DestDir DestFile DestExt +rem +rem SourcePath - $(TargetPath) - Fully qualified path of the generated target file. +rem DestDir - $(SolutionDir) - Directory of the destination, usually the same as the solution. +rem DestFile - Base filename of the target/dest, without extension! +rem DestExt - Extension of the target/dest! + +set pcsxoutdir=%2\bin +set pcsxoutname=%pcsxoutdir%\%3-r$WCREV$$WCMODS?m:$%4 + +IF NOT EXIST %pcsxoutdir% ( + md %pcsxoutdir% +) + +copy /Y %1 %pcsxoutname% +if ERRORLEVEL 0 ( + echo Target copied to %pcsxoutname% +) +set ERRORLEVEL=0 diff --git a/pcsx2/windows/VCprojects/vsprops/postBuild.unknown b/pcsx2/windows/VCprojects/vsprops/postBuild.unknown new file mode 100644 index 0000000000..199dce4e3c --- /dev/null +++ b/pcsx2/windows/VCprojects/vsprops/postBuild.unknown @@ -0,0 +1,21 @@ +@echo off +rem +rem Usage: postBuild.cmd SourcePath DestDir DestFile DestExt +rem +rem SourcePath - $(TargetPath) - Fully qualified path of the generated target file. +rem DestDir - $(SolutionDir) - Directory of the destination, usually the same as the solution. +rem DestFile - Base filename of the target/dest, without extension! +rem DestExt - Extension of the target/dest! + +set pcsxoutdir=%2\bin +set pcsxoutname=%pcsxoutdir%%3%4 + +IF NOT EXIST %pcsxoutdir% ( + md %pcsxoutdir% +) + +copy /Y %1 %pcsxoutname% +if ERRORLEVEL 0 ( + echo Target copied to %pcsxoutname% +) +set ERRORLEVEL=0 diff --git a/pcsx2/windows/VCprojects/vsprops/preBuild.cmd b/pcsx2/windows/VCprojects/vsprops/preBuild.cmd new file mode 100644 index 0000000000..fea16bd143 --- /dev/null +++ b/pcsx2/windows/VCprojects/vsprops/preBuild.cmd @@ -0,0 +1,20 @@ +@echo off + +rem Usage: preBuild.cmd ProjectSrcDir VspropsDir +rem +rem ProjectSrcDir - $(ProjectDir)\.. - Directory of project source code. +rem VspropsDir - $(PrjectDir)\vsprops - Directory of this script and its counterparts. + +SubWCRev.exe %1 %2\svnrev_template.h %1\svnrev.h +if not ERRORLEVEL 0 ( + echo Automatic revision update unavailable, using generic template instead. + echo You can safely ignore this message - see svnrev.h for details. + copy /Y %2\svnrev_unknown.h %1\svnrev.h + copy /Y %2\postBuild.unknown %2\postBuild.cmd +) else ( + SubWCRev.exe %1 %2\postBuild.tmpl %2\postBuild.cmd +) + +rem Clear the error level -- this allows compilation to continue if SubWCRev failed. + +set ERRORLEVEL=0 diff --git a/pcsx2/windows/VCprojects/vsprops/release.vsprops b/pcsx2/windows/VCprojects/vsprops/release.vsprops new file mode 100644 index 0000000000..bfc1fe43b7 --- /dev/null +++ b/pcsx2/windows/VCprojects/vsprops/release.vsprops @@ -0,0 +1,27 @@ + + + + + + diff --git a/pcsx2/windows/VCprojects/vsprops/svnrev_template.h b/pcsx2/windows/VCprojects/vsprops/svnrev_template.h new file mode 100644 index 0000000000..afab6c4fd1 --- /dev/null +++ b/pcsx2/windows/VCprojects/vsprops/svnrev_template.h @@ -0,0 +1,18 @@ +// svnrev_template.h --> svnrev.h +// +// This file acts as a template for the automatic SVN revision/version tag. +// It is used by the utility SubWCrev.exe to create an "svnrev.h" file for +// whichever project is being compiled (as indicated by command line options +// passed to SubWCRev.exe during the project's pre-build step). +// +// The SubWCRev.exe utility is part of TortoiseSVN and requires several DLLs +// installed by TortoiseSVN, so it will only be available if you have TortoiseSVN +// installed on your system. If you do not have it installed, a generic template +// is used instead (see svnrev_generic.h). Having TortoiseSVN is handy but not +// necessary. If you do not have it installed, everything will still compile +// fine except without the SVN revision tagged to the application/dll version. +// +// TortoiseSVN can be downloaded from http://tortoisesvn.tigris.org + +#define SVN_REV $WCREV$ +#define SVN_MODS $WCMODS?1:0$ \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/vsprops/svnrev_unknown.h b/pcsx2/windows/VCprojects/vsprops/svnrev_unknown.h new file mode 100644 index 0000000000..a2a3703186 --- /dev/null +++ b/pcsx2/windows/VCprojects/vsprops/svnrev_unknown.h @@ -0,0 +1,23 @@ +// svnrev_genric.h --> svnrev.h +// +// This file acts as a placebo for people who do not have TortoiseSVN installed. +// It provides "empty" revision information to the Pcsx2 Playground projects in +// the absence of real revisions derived from the repository being built. +// +// This file does not affect application/dll builds in any significant manner, +// other than the lack of automatic revision tags inserted into the app (which +// is very convenient but hardly necessary). +// +// See svn_template.h for more information on how the process of revision +// templating works. +// +// If you would like to enable automatic revisin tagging, TortoiseSVN can be +// downloaded from http://tortoisesvn.tigris.org + +#define SVN_REV_UNKNOWN + +// The following defines are included so that code will still compile even if it +// doesn't check for the SVN_REV_UNKNOWN define. + +#define SVN_REV 0 +#define SVN_MODS "" \ No newline at end of file diff --git a/pcsx2/windows/Win32.h b/pcsx2/windows/Win32.h new file mode 100644 index 0000000000..5b2d6e1b72 --- /dev/null +++ b/pcsx2/windows/Win32.h @@ -0,0 +1,106 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef _PCSX2_WIN32_H__ +#define _PCSX2_WIN32_H__ + +#include +#include + +#include "Misc.h" +#include "resource.h" + +#define COMPILEDATE __DATE__ + +// --->> Ini Configuration [ini.c] + +extern char g_WorkingFolder[g_MaxPath]; +extern const char* g_CustomConfigFile; + +bool LoadConfig(); +void SaveConfig(); + +// <<--- END Ini Configuration [ini.c] + +// --->> Patch Browser Stuff (in the event we ever use it + +void ListPatches (HWND hW); +int ReadPatch (HWND hW, char fileName[1024]); +char * lTrim (char *s); +BOOL Save_Patch_Proc( char * filename ); + +// <<--- END Patch Browser + +struct AppData +{ + HWND hWnd; // Main window handle + HINSTANCE hInstance; // Application instance + HMENU hMenu; // Main window menu +}; + +LRESULT WINAPI MainWndProc(HWND, UINT, WPARAM, LPARAM); +void CreateMainWindow(int nCmdShow); +void RunGui(); + +BOOL Pcsx2Configure(HWND hWnd); +void InitLanguages(); +char *GetLanguageNext(); +void CloseLanguages(); +void ChangeLanguage(char *lang); + +void SysRestorableReset(); + +void WinClose(); +void States_Load( const string& file, int num=-1 ); +void States_Save( const string& file, int num=-1 ); +void States_Load(int num); +void States_Save(int num); +void OnStates_LoadOther(); +void OnStates_SaveOther(); +int ParseCommandLine( int tokenCount, TCHAR *const *const tokens ); +void RunExecute( const char* elf_file, bool use_bios=false ); +void ExecuteCpu(); +void strcatz(char *dst, char *src); + +BOOL CALLBACK PatchBDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam); +BOOL CALLBACK CpuDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam); +BOOL CALLBACK AdvancedOptionsProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam); +BOOL CALLBACK HacksProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +extern AppData gApp; +extern HWND hStatusWnd; +extern PcsxConfig winConfig; // local storage of the configuration options. +extern bool g_ReturnToGui; // set to exit the execution of the emulator and return control to the GUI +extern bool g_EmulationInProgress; // Set TRUE if a game is actively running (set to false on reset) + +extern int UseGui; +extern int nDisableSC; // screensaver +extern unsigned int langsMax; + +extern MemoryAlloc* g_RecoveryState; +extern MemoryAlloc* g_gsRecoveryState; +extern const char* g_pRunGSState; +extern int g_SaveGSStream; + + +// sets the contents of the Pcsx2 status bar... +void StatusBar_Notice( const std::string& text ); +void StatusBar_SetMsg( const std::string& text ); + +#endif + diff --git a/pcsx2/windows/WinConsole.cpp b/pcsx2/windows/WinConsole.cpp new file mode 100644 index 0000000000..f109ee7de0 --- /dev/null +++ b/pcsx2/windows/WinConsole.cpp @@ -0,0 +1,144 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "System.h" +#include "Debug.h" + +namespace Console +{ + static HANDLE hConsole = NULL; + + static const int tbl_color_codes[] = + { + 0 // black + , FOREGROUND_RED | FOREGROUND_INTENSITY + , FOREGROUND_GREEN | FOREGROUND_INTENSITY + , FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY + , FOREGROUND_BLUE | FOREGROUND_INTENSITY + , FOREGROUND_RED | FOREGROUND_BLUE + , FOREGROUND_GREEN | FOREGROUND_BLUE + , FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY + }; + + void SetTitle( const string& title ) + { + if( !hConsole ) return; + SetConsoleTitle( title.c_str() ); + } + + void Open() + { + COORD csize; + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + SMALL_RECT srect; + + if( hConsole ) return; + AllocConsole(); + SetConsoleTitle(_("Ps2 Output")); + + csize.X = 100; + csize.Y = 2048; + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), csize); + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbiInfo); + + srect = csbiInfo.srWindow; + srect.Right = srect.Left + 99; + srect.Bottom = srect.Top + 64; + SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), TRUE, &srect); + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + } + + void Close() + { + if( hConsole == NULL ) return; + FreeConsole(); + hConsole = NULL; + } + + __forceinline void __fastcall SetColor( Colors color ) + { + SetConsoleTextAttribute( hConsole, tbl_color_codes[color] ); + } + + __forceinline void ClearColor() + { + SetConsoleTextAttribute( hConsole, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE ); + } + + + // Writes a newline to the console. + __forceinline bool __fastcall Newline() + { + if (hConsole != NULL) + { + DWORD tmp; + WriteConsole(hConsole, "\r\n", 2, &tmp, 0); + } + + if (emuLog != NULL) + { + fputs("\n", emuLog); + fflush( emuLog ); + } + + return false; + } + + // Writes an unformatted string of text to the console (fast!) + // No newline is appended. + __forceinline bool __fastcall Write( const char* fmt ) + { + if (hConsole != NULL) + { + DWORD tmp; + WriteConsole(hConsole, fmt, (DWORD)strlen(fmt), &tmp, 0); + } + + // No flushing here -- only flush after newlines. + if (emuLog != NULL) + fputs(fmt, emuLog); + + return false; + } +} + +namespace Msgbox +{ + bool Alert( const char* fmt ) + { + MessageBox(0, fmt, _("Pcsx2 Msg"), 0); + return false; + } + + // Pops up an alert Dialog Box. + // Replacement for SysMessage. + bool Alert( const char* fmt, VARG_PARAM dummy, ... ) + { + varg_assert(); + + va_list list; + va_start(list,dummy); + MessageBox(0, vfmt_string(fmt,list).c_str(), _("Pcsx2 Msg"), 0); + va_end(list); + + return false; + } +} diff --git a/pcsx2/windows/WinMain.cpp b/pcsx2/windows/WinMain.cpp new file mode 100644 index 0000000000..2d406f771d --- /dev/null +++ b/pcsx2/windows/WinMain.cpp @@ -0,0 +1,1161 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "win32.h" + +#include +#include +#include + +#include + +#include "Common.h" +#include "debugger.h" +#include "rdebugger.h" +#include "AboutDlg.h" +#include "McdsDlg.h" + +#include "Patch.h" +#include "cheats/cheats.h" + +#include "Paths.h" +#include "SamplProf.h" + +#include "implement.h" // pthreads-win32 defines for startup/shutdown + +unsigned int langsMax; + + +struct _langs { + TCHAR lang[256]; +}; + +_langs *langs = NULL; + +void strcatz(char *dst, char *src) +{ + int len = strlen(dst) + 1; + strcpy(dst + len, src); +} + +//2002-09-20 (Florin) +BOOL APIENTRY CmdlineProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);//forward def +//------------------- + +TESTRUNARGS g_TestRun; + +static const char* phelpmsg = + "pcsx2 [options] [file]\n\n" + "-cfg [file] {configuration file}\n" + "-efile [efile] {0 - reset, 1 - runcd (default), 2 - loadelf}\n" + "-help {display this help file}\n" + "-nogui {Don't use gui when launching}\n" + "-loadgs [file] {Loads a gsstate}\n" + "\n" + +#ifdef PCSX2_DEVBUILD + "Testing Options: \n" + "\t-frame [frame] {game will run up to this frame before exiting}\n" + "\t-image [name] {path and base name of image (do not include the .ext)}\n" + "\t-jpg {save images to jpg format}\n" + "\t-log [name] {log path to save log file in}\n" + "\t-logopt [hex] {log options in hex (see debug.h) }\n" + "\t-numimages [num] {after hitting frame, this many images will be captures every 20 frames}\n" + "\t-test {Triggers testing mode (only for dev builds)}\n" + "\n" +#endif + + "Load Plugins:\n" + "\t-cdvd [dllpath] {specify the dll load path of the CDVD plugin}\n" + "\t-gs [dllpath] {specify the dll load path of the GS plugin}\n" + "\t-spu [dllpath] {specify the dll load path of the SPU2 plugin}\n" + "\n"; + +/// This code is courtesy of http://alter.org.ua/en/docs/win/args/ +static PTCHAR* _CommandLineToArgv( const TCHAR *CmdLine, int* _argc ) +{ + PTCHAR* argv; + PTCHAR _argv; + ULONG len; + ULONG argc; + TCHAR a; + ULONG i, j; + + BOOLEAN in_QM; + BOOLEAN in_TEXT; + BOOLEAN in_SPACE; + + len = _tcslen( CmdLine ); + i = ((len+2)/2)*sizeof(PVOID) + sizeof(PVOID); + + argv = (PTCHAR*)GlobalAlloc(GMEM_FIXED, + i + (len+2)*sizeof(a)); + + _argv = (PTCHAR)(((PUCHAR)argv)+i); + + argc = 0; + argv[argc] = _argv; + in_QM = FALSE; + in_TEXT = FALSE; + in_SPACE = TRUE; + i = 0; + j = 0; + + while( a = CmdLine[i] ) { + if(in_QM) { + if(a == '\"') { + in_QM = FALSE; + } else { + _argv[j] = a; + j++; + } + } else { + switch(a) { + case '\"': + in_QM = TRUE; + in_TEXT = TRUE; + if(in_SPACE) { + argv[argc] = _argv+j; + argc++; + } + in_SPACE = FALSE; + break; + case ' ': + case '\t': + case '\n': + case '\r': + if(in_TEXT) { + _argv[j] = '\0'; + j++; + } + in_TEXT = FALSE; + in_SPACE = TRUE; + break; + default: + in_TEXT = TRUE; + if(in_SPACE) { + argv[argc] = _argv+j; + argc++; + } + _argv[j] = a; + j++; + in_SPACE = FALSE; + break; + } + } + i++; + } + _argv[j] = '\0'; + argv[argc] = NULL; + + (*_argc) = argc; + return argv; +} + +void WinClose() +{ + SysClose(); + + // Don't check Config.Profiler here -- the Profiler will know if it's running or not. + ProfilerTerm(); + + ReleasePlugins(); + Console::Close(); + + pthread_win32_process_detach_np(); + + SysShutdownDynarecs(); + SysShutdownMem(); +} + +BOOL SysLoggedSetLockPagesPrivilege ( HANDLE hProcess, BOOL bEnable); + +// Returns TRUE if the test run mode was activaated (game was run and has been exited) +static bool TestRunMode() +{ + if( IsDevBuild && (g_TestRun.enabled || g_TestRun.ptitle != NULL) ) + { + // run without ui + UseGui = 0; + PCSX2_MEM_PROTECT_BEGIN(); + RunExecute( g_TestRun.efile ? g_TestRun.ptitle : NULL ); + PCSX2_MEM_PROTECT_END(); + return true; + } + return false; +} + +void WinRun( int nCmdShow ) +{ + // Load the command line overrides for plugins. + // Back up the user's preferences in winConfig. + + memcpy( &winConfig, &Config, sizeof( PcsxConfig ) ); + + if( g_TestRun.pgsdll ) + { + _tcscpy_s( Config.GS, g_MaxPath, g_TestRun.pgsdll ); + Console::Notice( "* GS plugin override: \n\t%s\n", params Config.GS ); + } + if( g_TestRun.pcdvddll ) + { + _tcscpy_s( Config.CDVD, g_MaxPath, g_TestRun.pcdvddll ); + Console::Notice( "* CDVD plugin override: \n\t%s\n", params Config.CDVD ); + } + if( g_TestRun.pspudll ) + { + _tcscpy_s( Config.SPU2, g_MaxPath, g_TestRun.pspudll ); + Console::Notice( "* SPU2 plugin override: \n\t%s\n", params Config.SPU2 ); + } + + // [TODO] : Add the other plugin overrides here... + +#ifndef _DEBUG + if( Config.Profiler ) + ProfilerInit(); +#endif + + InitCPUTicks(); + + while (LoadPlugins() == -1) + { + if (Pcsx2Configure(NULL) == FALSE) return; + } + + if( TestRunMode() ) return; + +#ifdef PCSX2_DEVBUILD + if( g_pRunGSState ) { + LoadGSState(g_pRunGSState); + return; + } +#endif + + CreateMainWindow( nCmdShow ); + + if( Config.PsxOut ) + { + // output the help commands + Console::SetColor( Console::Color_White ); + + Console::WriteLn( "Hotkeys:" ); + + Console::WriteLn( + "\tF1 - save state\n" + "\t(Shift +) F2 - cycle states\n" + "\tF3 - load state" + ); + + DevCon::WriteLn( + //"\tF10 - dump performance counters\n" + "\tF11 - save GS state\n" + //"\tF12 - dump hardware registers" + ); + Console::ClearColor(); + } + + RunGui(); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + char *lang; + int i; + + CreateDirectory(LOGS_DIR, NULL); + + InitCommonControls(); + pInstance=hInstance; + FirstShow=true; // this is used by cheats.cpp to search for stuff (broken?) + + pthread_win32_process_attach_np(); + + gApp.hInstance = hInstance; + gApp.hMenu = NULL; + gApp.hWnd = NULL; + +#ifdef ENABLE_NLS + bindtextdomain(PACKAGE, "Langs\\"); + textdomain(PACKAGE); +#endif + + memzero_obj(g_TestRun); + + _getcwd( g_WorkingFolder, g_MaxPath ); + + int argc; + TCHAR *const *const argv = _CommandLineToArgv( lpCmdLine, &argc ); + + if( argv == NULL ) + { + Msgbox::Alert( "A fatal error occured while attempting to parse the command line.\n" ); + return 2; + } + + switch( ParseCommandLine( argc, argv ) ) + { + case 1: // display help and exit: + printf( "%s", phelpmsg ); + MessageBox( NULL, phelpmsg, "Pcsx2 Help", MB_OK); + + case -1: // exit... + return 0; + } + + try + { + bool needsToConfig = !LoadConfig(); + + // Enumerate available translations + + if (needsToConfig || Config.Lang[0] == 0) { + strcpy(Config.Lang, "en_US"); + } + + langs = (_langs*)malloc(sizeof(_langs)); + strcpy(langs[0].lang, "en_US"); + InitLanguages(); i=1; + while ((lang = GetLanguageNext()) != NULL) { + langs = (_langs*)realloc(langs, sizeof(_langs)*(i+1)); + strcpy(langs[i].lang, lang); + i++; + } + CloseLanguages(); + langsMax = i; + + // automatically and always open the console for first-time uses (no ini file) + if(needsToConfig || Config.PsxOut ) + Console::Open(); + + // Important! Always allocate dynarecs before loading plugins, to give the dynarecs + // the best possible chance of claiming ideal memory space! + + SysInit(); + + if( needsToConfig ) + { + // Prompt the user for a valid configuration before starting the program. + Msgbox::Alert( _( "Pcsx2 needs to be configured." ) ); + Pcsx2Configure( NULL ); + LoadConfig(); // forces re-loading of language and stuff. + } + + if( Config.PsxOut ) + Console::Open(); + else + Console::Close(); + + WinRun( nCmdShow ); + } + catch( Exception::BaseException& ex ) + { + Msgbox::Alert( + "An unhandled or unrecoverable exception occurred, with the message:\n\n" + "%s" + "\n\nPcsx2 will now close. More details may be available via the emuLog.txt file.", params + ex.cMessage() + ); + } + catch( std::exception& ex ) + { + Msgbox::Alert( + "An unhandled or unrecoverable exception occurred, with the message:\n\n" + "%s" + "\n\nPcsx2 will now close. More details may be available via the emuLog.txt file.", params + ex.what() + ); + } + + WinClose(); + + return 0; +} + +static std::string str_Default( "default" ); + +void RunGui() +{ + MSG msg; + + PCSX2_MEM_PROTECT_BEGIN(); + + LoadPatch(str_Default); + + while( true ) + { + if( PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) != 0 ) + { + if( msg.message == WM_QUIT ) return; + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + Sleep(10); + } + + PCSX2_MEM_PROTECT_END(); +} + +BOOL Open_File_Proc( std::string& outstr ) +{ + OPENFILENAME ofn; + char szFileName[ g_MaxPath ]; + char szFileTitle[ g_MaxPath ]; + char * filter = "ELF Files (*.ELF)\0*.ELF\0ALL Files (*.*)\0*.*\0"; + + memzero_obj( szFileName ); + memzero_obj( szFileTitle ); + + ofn.lStructSize = sizeof( OPENFILENAME ); + ofn.hwndOwner = gApp.hWnd; + ofn.lpstrFilter = filter; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = g_MaxPath; + ofn.lpstrInitialDir = NULL; + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = g_MaxPath; + ofn.lpstrTitle = NULL; + ofn.lpstrDefExt = "ELF"; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_EXPLORER; + + if (GetOpenFileName(&ofn)) { + struct stat buf; + + if (stat(szFileName, &buf) != 0) { + return FALSE; + } + + outstr = szFileName; + return TRUE; + } + + return FALSE; +} + +//2002-09-20 (Florin) +BOOL APIENTRY CmdlineProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + SetWindowText(hDlg, _("Program arguments")); + + Button_SetText(GetDlgItem(hDlg, IDOK), _("OK")); + Button_SetText(GetDlgItem(hDlg, IDCANCEL), _("Cancel")); + Static_SetText(GetDlgItem(hDlg, IDC_TEXT), _("Fill in the command line arguments for opened program:")); + Static_SetText(GetDlgItem(hDlg, IDC_TIP), _("Tip: If you don't know what to write\nleave it blank")); + + SetDlgItemText(hDlg, IDC_CMDLINE, args); + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK) + { + char tmp[256]; + + GetDlgItemText(hDlg, IDC_CMDLINE, tmp, 256); + + strcpy_s(args, 256, tmp); + + EndDialog(hDlg, TRUE); + } else if (LOWORD(wParam) == IDCANCEL) { + EndDialog(hDlg, TRUE); + } + return TRUE; + } + + return FALSE; +} + + +#ifdef PCSX2_DEVBUILD + +BOOL APIENTRY LogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { + int i; + switch (message) { + case WM_INITDIALOG: + for (i=0; i<32; i++) + if (varLog & (1<= ID_LANGS && LOWORD(wParam) <= (ID_LANGS + langsMax)) { + DestroyWindow(gApp.hWnd); + ChangeLanguage(langs[LOWORD(wParam) - ID_LANGS].lang); + CreateMainWindow(SW_SHOWNORMAL); + return TRUE; + } + } + return TRUE; + + case WM_DESTROY: + DeleteObject(hbitmap_background); + PostQuitMessage(0); + gApp.hWnd = NULL; + return FALSE; + + // Explicit handling of WM_CLOSE. + // This is Windows default behavior, but we handle it here sot hat we might add a + // user confirmation prompt prior to exit (someday!) + case WM_CLOSE: + DestroyWindow( hWnd ); + return TRUE; + + case WM_SYSCOMMAND: + if( nDisableSC && (wParam== SC_SCREENSAVE || wParam == SC_MONITORPOWER) ) { + return FALSE; + } + break; + } + + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +int Slots[5] = { -1, -1, -1, -1, -1 }; + +void ResetMenuSlots() { + int i; + + for (i=0; i<5; i++) { + if (Slots[i] == -1) + EnableMenuItem(GetSubMenu(gApp.hMenu, 0), ID_FILE_STATES_LOAD_SLOT1+i, MF_GRAYED); + else + EnableMenuItem(GetSubMenu(gApp.hMenu, 0), ID_FILE_STATES_LOAD_SLOT1+i, MF_ENABLED); + } +} + +// fixme - this looks like the beginnings of a dynamic "list of valid saveslots" +// feature. Too bad it's never called and CheckState was old/dead code. +/*void UpdateMenuSlots() { + char str[g_MaxPath]; + int i; + + for (i=0; i<5; i++) { + sprintf_s (str, g_MaxPath, "sstates\\%8.8X.%3.3d", ElfCRC, i); + Slots[i] = CheckState(str); + } +}*/ + + +#define _ADDSUBMENU(menu, menun, string) \ + submenu[menun] = CreatePopupMenu(); \ + AppendMenu(menu, MF_STRING | MF_POPUP, (UINT)submenu[menun], string); + +#define ADDSUBMENU(menun, string) \ + _ADDSUBMENU(gApp.hMenu, menun, string); + +#define ADDSUBMENUS(submn, menun, string) \ + submenu[menun] = CreatePopupMenu(); \ + InsertMenu(submenu[submn], 0, MF_BYPOSITION | MF_STRING | MF_POPUP, (UINT)submenu[menun], string); + +#define ADDMENUITEM(menun, string, id) \ + item.fType = MFT_STRING; \ + item.fMask = MIIM_STATE | MIIM_TYPE | MIIM_ID; \ + item.fState = MFS_ENABLED; \ + item.wID = id; \ + sprintf(buf, string); \ + InsertMenuItem(submenu[menun], 0, TRUE, &item); + +#define ADDMENUITEMC(menun, string, id) \ + item.fType = MFT_STRING; \ + item.fMask = MIIM_STATE | MIIM_TYPE | MIIM_ID; \ + item.fState = MFS_ENABLED | MFS_CHECKED; \ + item.wID = id; \ + sprintf(buf, string); \ + InsertMenuItem(submenu[menun], 0, TRUE, &item); + +#define ADDSEPARATOR(menun) \ + item.fMask = MIIM_TYPE; \ + item.fType = MFT_SEPARATOR; \ + InsertMenuItem(submenu[menun], 0, TRUE, &item); + +void CreateMainMenu() { + MENUITEMINFO item; + HMENU submenu[5]; + char buf[256]; + int i; + + item.cbSize = sizeof(MENUITEMINFO); + item.dwTypeData = buf; + item.cch = 256; + + gApp.hMenu = CreateMenu(); + + //submenu = CreatePopupMenu(); + //AppendMenu(gApp.hMenu, MF_STRING | MF_POPUP, (UINT)submenu, _("&File")); + ADDSUBMENU(0, _("&File")); + ADDMENUITEM(0, _("E&xit"), ID_FILE_EXIT); + ADDSEPARATOR(0); + ADDSUBMENUS(0, 1, _("&States")); + ADDSEPARATOR(0); + ADDMENUITEM(0, _("&Open ELF File"), ID_FILEOPEN); + ADDMENUITEM(0, _("&Run CD/DVD"), ID_FILE_RUNCD); + ADDSUBMENUS(1, 3, _("&Save")); + ADDSUBMENUS(1, 2, _("&Load")); + ADDMENUITEM(2, _("&Other..."), ID_FILE_STATES_LOAD_OTHER); + ADDMENUITEM(2, _("Slot &4"), ID_FILE_STATES_LOAD_SLOT5); + ADDMENUITEM(2, _("Slot &3"), ID_FILE_STATES_LOAD_SLOT4); + ADDMENUITEM(2, _("Slot &2"), ID_FILE_STATES_LOAD_SLOT3); + ADDMENUITEM(2, _("Slot &1"), ID_FILE_STATES_LOAD_SLOT2); + ADDMENUITEM(2, _("Slot &0"), ID_FILE_STATES_LOAD_SLOT1); + ADDMENUITEM(3, _("&Other..."), ID_FILE_STATES_SAVE_OTHER); + ADDMENUITEM(3, _("Slot &4"), ID_FILE_STATES_SAVE_SLOT5); + ADDMENUITEM(3, _("Slot &3"), ID_FILE_STATES_SAVE_SLOT4); + ADDMENUITEM(3, _("Slot &2"), ID_FILE_STATES_SAVE_SLOT3); + ADDMENUITEM(3, _("Slot &1"), ID_FILE_STATES_SAVE_SLOT2); + ADDMENUITEM(3, _("Slot &0"), ID_FILE_STATES_SAVE_SLOT1); + + ADDSUBMENU(0, _("&Run")); + + ADDSUBMENUS(0, 1, _("&Process Priority")); + ADDMENUITEM(1, _("&Low"), ID_PROCESSLOW ); + ADDMENUITEM(1, _("High"), ID_PROCESSHIGH); + ADDMENUITEM(1, _("Normal"), ID_PROCESSNORMAL); + ADDMENUITEM(0,_("&Arguments"), ID_RUN_CMDLINE); + ADDMENUITEM(0,_("Re&set"), ID_RUN_RESET); + ADDMENUITEM(0,_("E&xecute"), ID_RUN_EXECUTE); + + ADDSUBMENU(0,_("&Config")); + ADDMENUITEM(0,_("Advanced"), ID_ADVANCED_OPTIONS); + ADDMENUITEM(0,_("Speed &Hacks"), ID_HACKS); + ADDMENUITEM(0,_("Gamefixes"), ID_GAMEFIXES); + ADDMENUITEM(0,_("&Patches"), ID_PATCHBROWSER); + ADDMENUITEM(0,_("C&pu"), ID_CONFIG_CPU); + ADDMENUITEM(0,_("&Memcards"), ID_CONFIG_MEMCARDS); + ADDSEPARATOR(0); + ADDMENUITEM(0,_("Fire&Wire"), ID_CONFIG_FW); + ADDMENUITEM(0,_("U&SB"), ID_CONFIG_USB); + ADDMENUITEM(0,_("D&ev9"), ID_CONFIG_DEV9); + ADDMENUITEM(0,_("C&dvdrom"), ID_CONFIG_CDVDROM); + ADDMENUITEM(0,_("&Sound"), ID_CONFIG_SOUND); + ADDMENUITEM(0,_("C&ontrollers"), ID_CONFIG_CONTROLLERS); + ADDMENUITEM(0,_("&Graphics"), ID_CONFIG_GRAPHICS); + ADDSEPARATOR(0); + ADDMENUITEM(0,_("&Configure"), ID_CONFIG_CONFIGURE); + + ADDSUBMENU(0,_("&Language")); + + for (i=langsMax-1; i>=0; i--) { + if (!strcmp(Config.Lang, langs[i].lang)) { + ADDMENUITEMC(0,ParseLang(langs[i].lang), ID_LANGS + i); + } else { + ADDMENUITEM(0,ParseLang(langs[i].lang), ID_LANGS + i); + } + } + +#ifdef PCSX2_DEVBUILD + ADDSUBMENU(0, _("&Debug")); + ADDMENUITEM(0,_("&Logging"), ID_DEBUG_LOGGING); + ADDMENUITEM(0,_("Memory Dump"), ID_DEBUG_MEMORY_DUMP); + ADDMENUITEM(0,_("&Remote Debugging"), ID_DEBUG_REMOTEDEBUGGING); + ADDMENUITEM(0,_("Enter &Debugger..."), ID_DEBUG_ENTERDEBUGGER); +#endif + + ADDSUBMENU(0, _("&Misc")); + ADDMENUITEM(0,_("Print cdvd &Info"), ID_CDVDPRINT); + ADDMENUITEM(0,_("Close GS Window on Esc"), ID_CLOSEGS); + ADDSEPARATOR(0); +#ifndef _DEBUG + ADDMENUITEM(0,_("Enable &Profiler"), ID_PROFILER); +#endif + ADDMENUITEM(0,_("Enable &Patches"), ID_PATCHES); + ADDMENUITEM(0,_("Enable &Console"), ID_CONSOLE); + ADDSEPARATOR(0); + ADDMENUITEM(0,_("Patch &Finder..."), ID_CHEAT_FINDER_SHOW); + ADDMENUITEM(0,_("Patch &Browser..."), ID_CHEAT_BROWSER_SHOW); + + + ADDSUBMENU(0, _("&Help")); + ADDMENUITEM(0,_("&Compatibility List..."), ID_HELP_HELP); + ADDMENUITEM(0,_("&About..."), ID_HELP_ABOUT); + + if( !IsDevBuild ) + EnableMenuItem(GetSubMenu(gApp.hMenu, 4), ID_DEBUG_LOGGING, MF_GRAYED); +} + +void CreateMainWindow(int nCmdShow) { + WNDCLASS wc; + HWND hWnd; + char buf[256]; + char COMPILER[20]=""; + BITMAP bm; + RECT rect; + int w, h; + + g_ReturnToGui = true; + +#ifdef _MSC_VER + sprintf(COMPILER, "(VC%d)", (_MSC_VER+100)/200);//hacky:) works for VC6 & VC.NET +#elif __BORLANDC__ + sprintf(COMPILER, "(BC)"); +#endif + /* Load Background Bitmap from the ressource */ + hbitmap_background = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(SPLASH_LOGO)); + + wc.lpszClassName = "PCSX2 Main"; + wc.lpfnWndProc = MainWndProc; + wc.style = 0; + wc.hInstance = gApp.hInstance; + wc.hIcon = LoadIcon(gApp.hInstance, MAKEINTRESOURCE(IDI_ICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_MENUTEXT); + wc.lpszMenuName = 0; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + + RegisterClass(&wc); + GetObject(hbitmap_background, sizeof(bm), &bm); + + { + const char* pvm = "VTLB"; + +#ifdef PCSX2_DEVBUILD + sprintf(buf, _("PCSX2 %s - %s Compile Date - %s %s"), PCSX2_VERSION, pvm, COMPILEDATE, COMPILER); +#else + sprintf(buf, _("PCSX2 %s - %s"), PCSX2_VERSION, pvm); +#endif + } + + hWnd = CreateWindow( + "PCSX2 Main", + buf, WS_OVERLAPPED | WS_SYSMENU, + 20, 20, 320, 240, + NULL, NULL, + gApp.hInstance, NULL + ); + + gApp.hWnd = hWnd; + ResetMenuSlots(); + CreateMainMenu(); + + SetMenu(gApp.hWnd, gApp.hMenu); + if(Config.ThPriority==THREAD_PRIORITY_NORMAL) CheckMenuItem(gApp.hMenu,ID_PROCESSNORMAL,MF_CHECKED); + if(Config.ThPriority==THREAD_PRIORITY_HIGHEST) CheckMenuItem(gApp.hMenu,ID_PROCESSHIGH,MF_CHECKED); + if(Config.ThPriority==THREAD_PRIORITY_LOWEST) CheckMenuItem(gApp.hMenu,ID_PROCESSLOW,MF_CHECKED); + if(Config.PsxOut) CheckMenuItem(gApp.hMenu,ID_CONSOLE,MF_CHECKED); + if(Config.Patch) CheckMenuItem(gApp.hMenu,ID_PATCHES,MF_CHECKED); + if(Config.Profiler) CheckMenuItem(gApp.hMenu,ID_PROFILER,MF_CHECKED); + if(Config.cdvdPrint)CheckMenuItem(gApp.hMenu,ID_CDVDPRINT,MF_CHECKED); + if(Config.closeGSonEsc)CheckMenuItem(gApp.hMenu,ID_CLOSEGS,MF_CHECKED); + + hStatusWnd = CreateStatusWindow(WS_CHILD | WS_VISIBLE, "", hWnd, 100); + + w = bm.bmWidth; h = bm.bmHeight; + GetWindowRect(hStatusWnd, &rect); + h+= rect.bottom - rect.top; + GetMenuItemRect(hWnd, gApp.hMenu, 0, &rect); + h+= rect.bottom - rect.top; + MoveWindow(hWnd, 60, 60, w, h, TRUE); + SendMessage( hStatusWnd, WM_SIZE, 0, 0 ); + + StatusBar_SetMsg("F1 - save, F2 - next state, Shift+F2 - prev state, F3 - load, F8 - snapshot"); + + ShowWindow(hWnd, nCmdShow); + SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); +} + + +WIN32_FIND_DATA lFindData; +HANDLE lFind; +int lFirst; + +void InitLanguages() { + lFind = FindFirstFile("Langs\\*", &lFindData); + lFirst = 1; +} + +char *GetLanguageNext() { + for (;;) { + if (!strcmp(lFindData.cFileName, ".")) { + if (FindNextFile(lFind, &lFindData) == FALSE) + return NULL; + continue; + } + if (!strcmp(lFindData.cFileName, "..")) { + if (FindNextFile(lFind, &lFindData) == FALSE) + return NULL; + continue; + } + break; + } + if (lFirst == 0) { + if (FindNextFile(lFind, &lFindData) == FALSE) + return NULL; + } else lFirst = 0; + if (lFind==INVALID_HANDLE_VALUE) return NULL; + + return lFindData.cFileName; +} + +void CloseLanguages() { + if (lFind!=INVALID_HANDLE_VALUE) FindClose(lFind); +} + +void ChangeLanguage(char *lang) { + strcpy_s(Config.Lang, lang); + SaveConfig(); + LoadConfig(); +} diff --git a/pcsx2/windows/WinSysExec.cpp b/pcsx2/windows/WinSysExec.cpp new file mode 100644 index 0000000000..2219ee55e0 --- /dev/null +++ b/pcsx2/windows/WinSysExec.cpp @@ -0,0 +1,830 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "win32.h" + +#include +#include + +#include "Common.h" +//#include "PsxCommon.h" +#include "VUmicro.h" + +#include "iR5900.h" + +int UseGui = 1; +int nDisableSC = 0; // screensaver + +MemoryAlloc* g_RecoveryState = NULL; +MemoryAlloc* g_gsRecoveryState = NULL; + + +bool g_ReturnToGui = false; // set to exit the execution of the emulator and return control to the GUI +bool g_EmulationInProgress = false; // Set TRUE if a game is actively running (set to false on reset) + +// This instance is not modified by command line overrides so +// that command line plugins and stuff won't be saved into the +// user's conf file accidentally. +PcsxConfig winConfig; // local storage of the configuration options. + +HWND hStatusWnd = NULL; +AppData gApp; + +const char* g_pRunGSState = NULL; + + +#define CmdSwitchIs( text ) ( stricmp( command, text ) == 0 ) + +// For issuing notices to both the status bar and the console at the same time. +// Single-line text only please! Mutli-line msgs should be directed to the +// console directly, thanks. +void StatusBar_Notice( const std::string& text ) +{ + // mirror output to the console! + Console::Status( text.c_str() ); + + // don't try this in GCC folks! + SendMessage(hStatusWnd, SB_SETTEXT, 0, (LPARAM)text.c_str() ); +} + +// Sets the status bar message without mirroring the output to the console. +void StatusBar_SetMsg( const std::string& text ) +{ + // don't try this in GCC folks! + SendMessage(hStatusWnd, SB_SETTEXT, 0, (LPARAM)text.c_str() ); +} + +// returns 1 if the user requested help (show help and exit) +// returns zero on success. +// returns -1 on failure (bad command line argument) +int ParseCommandLine( int tokenCount, TCHAR *const *const tokens ) +{ + int tidx = 0; + g_TestRun.efile = 0; + + while( tidx < tokenCount ) + { + const TCHAR* command = tokens[tidx++]; + + if( command[0] != '-' ) + { + g_TestRun.ptitle = command; + printf("opening file %s\n", command); + continue; + } + + // jump past the '-' switch char, and skip if this is a dud switch: + command++; + if( command[0] == 0 ) continue; + + if( CmdSwitchIs( "help" ) ) + { + return -1; + } + else if( CmdSwitchIs( "nogui" ) ) { + UseGui = 0; + } +#ifdef PCSX2_DEVBUILD + else if( CmdSwitchIs( "jpg" ) ) { + g_TestRun.jpgcapture = 1; + } +#endif + else + { + const TCHAR* param; + if( tidx >= tokenCount ) break; + + // None of the parameter-less toggles flagged. + // Check for switches that require one or more parameters here: + + param = tokens[tidx++]; + + if( CmdSwitchIs( "cfg" ) ) { + g_CustomConfigFile = param; + } + + else if( CmdSwitchIs( "efile" ) ) { + g_TestRun.efile = !!atoi( param ); + } + else if( CmdSwitchIs( "loadgs" ) ) { + g_pRunGSState = param; + } + + // Options to configure plugins: + + else if( CmdSwitchIs( "gs" ) ) { + g_TestRun.pgsdll = param; + } + else if( CmdSwitchIs( "cdvd" ) ) { + g_TestRun.pcdvddll = param; + } + else if( CmdSwitchIs( "spu" ) ) { + g_TestRun.pspudll = param; + } + +#ifdef PCSX2_DEVBUILD + else if( CmdSwitchIs( "image" ) ) { + g_TestRun.pimagename = param; + } + else if( CmdSwitchIs( "log" ) ) { + g_TestRun.plogname = param; + } + else if( CmdSwitchIs( "logopt" ) ) { + if( param[0] == '0' && param[1] == 'x' ) param += 2; + sscanf(param, "%x", &varLog); + } + else if( CmdSwitchIs( "frame" ) ) { + g_TestRun.frame = atoi( param ); + } + else if( CmdSwitchIs( "numimages" ) ) { + g_TestRun.numimages = atoi( param ); + } +#endif + } + } + return 0; +} + +void SysPrintf(const char *fmt, ...) +{ + va_list list; + char msg[512]; + + va_start(list,fmt); + vsprintf_s(msg,fmt,list); + msg[511] = '\0'; + va_end(list); + + Console::Write( msg ); +} + +static void __fastcall KeyEvent(keyEvent* ev); + +__forceinline void SysUpdate() { + + keyEvent* ev1 = PAD1keyEvent(); + keyEvent* ev2 = PAD2keyEvent(); + + KeyEvent( (ev1 != NULL) ? ev1 : ev2); +} + +static void TryRecoverFromGsState() +{ + if( g_gsRecoveryState != NULL ) + { + s32 dummylen; + + memLoadingState eddie( *g_gsRecoveryState ); + eddie.FreezePlugin( "GS", gsSafeFreeze ); + eddie.Freeze( dummylen ); // reads the length value recorded earlier. + eddie.gsFreeze(); + } +} + +void ExecuteCpu() +{ + // Make sure any left-over recovery states are cleaned up. + safe_delete( g_RecoveryState ); + + // Just in case they weren't initialized earlier (no harm in calling this multiple times) + if (OpenPlugins(NULL) == -1) return; + + // this needs to be called for every new game! + // (note: sometimes launching games through bios will give a crc of 0) + + if( GSsetGameCRC != NULL ) + GSsetGameCRC(ElfCRC, g_ZeroGSOptions); + + TryRecoverFromGsState(); + + safe_delete( g_gsRecoveryState ); + + // ... and hide the window. Ugly thing. + + ShowWindow( gApp.hWnd, SW_HIDE ); + + g_EmulationInProgress = true; + g_ReturnToGui = false; + + // Optimization: We hardcode two versions of the EE here -- one for recs and one for ints. + // This is because recs are performance critical, and being able to inline them into the + // function here helps a small bit (not much but every small bit counts!). + + timeBeginPeriod( 1 ); + + if( CHECK_EEREC ) + { + while( !g_ReturnToGui ) + { + recExecute(); + SysUpdate(); + } + } + else + { + while( !g_ReturnToGui ) + { + Cpu->Execute(); + SysUpdate(); + } + } + + timeEndPeriod( 1 ); + + ShowWindow( gApp.hWnd, SW_SHOW ); + SetForegroundWindow( gApp.hWnd ); +} + +// Runs and ELF image directly (ISO or ELF program or BIN) +// Used by Run::FromCD and such +void RunExecute( const char* elf_file, bool use_bios ) +{ + SetThreadPriority(GetCurrentThread(), Config.ThPriority); + SetPriorityClass(GetCurrentProcess(), Config.ThPriority == THREAD_PRIORITY_HIGHEST ? ABOVE_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS); + nDisableSC = 1; + + try + { + cpuReset(); + } + catch( Exception::BaseException& ex ) + { + Msgbox::Alert( ex.cMessage() ); + return; + } + + if (OpenPlugins(g_TestRun.ptitle) == -1) + return; + + if( elf_file == NULL ) + { + if(g_RecoveryState != NULL) + { + try + { + memLoadingState( *g_RecoveryState ).FreezeAll(); + } + catch( std::runtime_error& ex ) + { + Msgbox::Alert( + "Gamestate recovery failed. Your game progress will be lost (sorry!)\n" + "\nError: %s\n", params ex.what() ); + + // Take the user back to the GUI... + safe_delete( g_RecoveryState ); + ClosePlugins(); + return; + } + } + else if( g_gsRecoveryState == NULL ) + { + // Not recovering a state, so need to execute the bios and load the ELF information. + // (note: gsRecoveries are done from ExecuteCpu) + + // if the elf_file is null we use the CDVD elf file. + // But if the elf_file is an empty string then we boot the bios instead. + + char ename[g_MaxPath]; + ename[0] = 0; + if( !use_bios ) + GetPS2ElfName( ename ); + + loadElfFile( ename ); + } + } + else + { + // Custom ELF specified (not using CDVD). + // Run the BIOS and load the ELF. + + loadElfFile( elf_file ); + } + + ExecuteCpu(); +} + +class RecoveryMemSavingState : public memSavingState, Sealed +{ +public: + virtual ~RecoveryMemSavingState() { } + RecoveryMemSavingState() : memSavingState( *g_RecoveryState ) + { + } + + void gsFreeze() + { + if( g_gsRecoveryState != NULL ) + { + // just copy the data from src to dst. + // the normal savestate doesn't expect a length prefix for internal structures, + // so don't copy that part. + const u32 pluginlen = *((u32*)g_gsRecoveryState->GetPtr()); + const u32 gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4)); + memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(pluginlen+8), gslen ); + m_idx += gslen; + } + else + memSavingState::gsFreeze(); + } + + void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) ) + { + if( (freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL) ) + { + // Gs data is already in memory, so just copy from src to dest: + // length of the GS data is stored as the first u32, so use that to run the copy: + const u32 len = *((u32*)g_gsRecoveryState->GetPtr()); + memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(), len+4 ); + m_idx += len+4; + } + else + memSavingState::FreezePlugin( name, freezer ); + } +}; + +class RecoveryZipSavingState : public gzSavingState, Sealed +{ +public: + virtual ~RecoveryZipSavingState() { } + RecoveryZipSavingState( const string& filename ) : gzSavingState( filename ) + { + } + + void gsFreeze() + { + if( g_gsRecoveryState != NULL ) + { + // read data from the gsRecoveryState allocation instead of the GS, since the gs + // info was invalidated when the plugin was shut down. + + // the normal savestate doesn't expect a length prefix for internal structures, + // so don't copy that part. + + u32& pluginlen = *((u32*)g_gsRecoveryState->GetPtr(0)); + u32& gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4)); + gzwrite( m_file, g_gsRecoveryState->GetPtr(pluginlen+4), gslen ); + } + else + gzSavingState::gsFreeze(); + } + + void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) ) + { + if( (freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL) ) + { + // Gs data is already in memory, so just copy from there into the gzip file. + // length of the GS data is stored as the first u32, so use that to run the copy: + u32& len = *((u32*)g_gsRecoveryState->GetPtr()); + gzwrite( m_file, g_gsRecoveryState->GetPtr(), len+4 ); + } + else + gzSavingState::FreezePlugin( name, freezer ); + } +}; + +void States_Load( const string& file, int num ) +{ + if( !Path::isFile( file ) ) + { + Console::Notice( "Saveslot %d is empty.", params num ); + return; + } + + try + { + char Text[g_MaxPath]; + gzLoadingState joe( file ); // this'll throw an StateLoadError_Recoverable. + + // Make sure the cpu and plugins are ready to be state-ified! + cpuReset(); + OpenPlugins( NULL ); + + joe.FreezeAll(); + + if( num != -1 ) + sprintf (Text, _("*PCSX2*: Loaded State %d"), num); + else + sprintf (Text, _("*PCSX2*: Loaded State %s"), file); + + StatusBar_Notice( Text ); + + if( GSsetGameCRC != NULL ) + GSsetGameCRC(ElfCRC, g_ZeroGSOptions); + } + catch( Exception::StateLoadError_Recoverable& ex) + { + if( num != -1 ) + Console::Notice( "Could not load savestate from slot %d.\n\n%s", params num, ex.cMessage() ); + else + Console::Notice( "Could not load savestate file: %s.\n\n%s", params file, ex.cMessage() ); + + // At this point the cpu hasn't been reset, so we can return + // control to the user safely... (that's why we use a console notice instead of a popup) + + return; + } + catch( Exception::BaseException& ex ) + { + // The emulation state is ruined. Might as well give them a popup and start the gui. + + string message; + + if( num != -1 ) + ssprintf( message, + "Encountered an error while loading savestate from slot %d.\n", num ); + else + ssprintf( message, + "Encountered an error while loading savestate from file: %s.\n", file ); + + if( g_EmulationInProgress ) + message += "Since the savestate load was incomplete, the emulator has been reset.\n"; + + message += "\nError: " + ex.Message(); + + Msgbox::Alert( message.c_str() ); + SysClose(); + return; + } + + // Start emulating! + ExecuteCpu(); +} + +void States_Save( const string& file, int num ) +{ + try + { + string text; + RecoveryZipSavingState( file ).FreezeAll(); + if( num != -1 ) + ssprintf( text, _( "State saved to slot %d" ), num ); + else + ssprintf( text, _( "State saved to file: %s" ), file ); + + StatusBar_Notice( text ); + } + catch( Exception::BaseException& ex ) + { + string message; + + if( num != -1 ) + ssprintf( message, "An error occurred while trying to save to slot %d\n", num ); + else + ssprintf( message, "An error occurred while trying to save to file: %s\n", file ); + + message += "Your emulation state has not been saved!\n\nError: " + ex.Message(); + + Console::Error( message.c_str() ); + } +} + +void States_Load(int num) +{ + States_Load( SaveState::GetFilename( num ), num ); +} + +void States_Save(int num) +{ + if( g_RecoveryState != NULL ) + { + // State is already saved into memory, and the emulator (and in-progress flag) + // have likely been cleared out. So save from the Recovery buffer instead of + // doing a "standard" save: + + string text; + SaveState::GetFilename( text, num ); + gzFile fileptr = gzopen( text.c_str(), "wb" ); + if( fileptr == NULL ) + { + Msgbox::Alert( _("File permissions error while trying to save to slot %d"), params num ); + return; + } + gzwrite( fileptr, &g_SaveVersion, sizeof( u32 ) ); + gzwrite( fileptr, g_RecoveryState->GetPtr(), g_RecoveryState->GetSizeInBytes() ); + gzclose( fileptr ); + return; + } + + if( !g_EmulationInProgress ) + { + Msgbox::Alert( "You need to start a game first before you can save it's state." ); + return; + } + + States_Save( SaveState::GetFilename( num ), num ); +} + +void OnStates_LoadOther() +{ + OPENFILENAME ofn; + char szFileName[g_MaxPath]; + char szFileTitle[g_MaxPath]; + char szFilter[g_MaxPath]; + + memzero_obj( szFileName ); + memzero_obj( szFileTitle ); + + strcpy(szFilter, _("PCSX2 State Format")); + strcatz(szFilter, "*.*;*.*"); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = gApp.hWnd; + ofn.lpstrFilter = szFilter; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = g_MaxPath; + ofn.lpstrInitialDir = NULL; + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = g_MaxPath; + ofn.lpstrTitle = NULL; + ofn.lpstrDefExt = "EXE"; + ofn.Flags = OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_EXPLORER; + + if (GetOpenFileName ((LPOPENFILENAME)&ofn)) + States_Load( szFileName ); +} + +void OnStates_SaveOther() +{ + OPENFILENAME ofn; + char szFileName[g_MaxPath]; + char szFileTitle[g_MaxPath]; + char szFilter[g_MaxPath]; + + memzero_obj( szFileName ); + memzero_obj( szFileTitle ); + + strcpy(szFilter, _("PCSX2 State Format")); + strcatz(szFilter, "*.*;*.*"); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = gApp.hWnd; + ofn.lpstrFilter = szFilter; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = g_MaxPath; + ofn.lpstrInitialDir = NULL; + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = g_MaxPath; + ofn.lpstrTitle = NULL; + ofn.lpstrDefExt = "EXE"; + ofn.Flags = OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_EXPLORER; + + if (GetOpenFileName ((LPOPENFILENAME)&ofn)) + States_Save( szFileName ); +} + +class JustGsSavingState : public memSavingState, Sealed +{ +public: + virtual ~JustGsSavingState() { } + JustGsSavingState() : memSavingState( *g_gsRecoveryState ) + { + } + + // This special override saves the gs info to m_idx+4, and then goes back and + // writes in the length of data saved. + void gsFreeze() + { + int oldmidx = m_idx; + m_idx += 4; + memSavingState::gsFreeze(); + if( IsSaving() ) + { + s32& len = *((s32*)m_memory.GetPtr( oldmidx )); + len = (m_idx - oldmidx)-4; + } + } +}; + +static int shiftkey = 0; +static void __fastcall KeyEvent(keyEvent* ev) +{ + if (ev == NULL) return; + if (ev->evt == KEYRELEASE) { + switch (ev->key) { + case VK_SHIFT: shiftkey = 0; break; + } + GSkeyEvent(ev); return; + } + if (ev->evt != KEYPRESS) + return; + + //some pad plugins don't give a key released event for shift, so this is needed + //shiftkey = GetKeyState(VK_SHIFT)&0x8000; + //Well SSXPad breaks with your code, thats why my code worked and your makes reg dumping impossible + //So i suggest you fix the plugins that dont. + + switch (ev->key) { + case VK_SHIFT: shiftkey = 1; break; + + case VK_F1: case VK_F2: case VK_F3: case VK_F4: + case VK_F5: case VK_F6: case VK_F7: case VK_F8: + case VK_F9: case VK_F10: case VK_F11: case VK_F12: + try + { + ProcessFKeys(ev->key-VK_F1 + 1, shiftkey); + } + catch( Exception::CpuStateShutdown& ) + { + // Woops! Something was unrecoverable. Bummer. + // Let's give the user a RunGui! + + g_EmulationInProgress = false; + g_ReturnToGui = true; + } + break; + + case VK_ESCAPE: +#ifdef PCSX2_DEVBUILD + if( g_SaveGSStream >= 3 ) { + // gs state + g_SaveGSStream = 4; + break; + } +#endif + + g_ReturnToGui = true; + if( CHECK_ESCAPE_HACK ) + { + //PostMessage(GetForegroundWindow(), WM_CLOSE, 0, 0); + DestroyWindow( gApp.hWnd ); + } + else + { + if( !UseGui ) { + // not using GUI and user just quit, so exit + WinClose(); + } + + if( Config.closeGSonEsc ) + { + safe_delete( g_gsRecoveryState ); + safe_delete( g_RecoveryState ); + g_gsRecoveryState = new MemoryAlloc(); + JustGsSavingState eddie; + eddie.FreezePlugin( "GS", gsSafeFreeze ) ; + eddie.gsFreeze(); + PluginsResetGS(); + } + + ClosePlugins(); + nDisableSC = 0; + } + break; + + default: + GSkeyEvent(ev); + break; + } +} + +static bool sinit=false; + + +void SysRestorableReset() +{ + // already reset? and saved? + if( !g_EmulationInProgress ) return; + if( g_RecoveryState != NULL ) return; + + try + { + g_RecoveryState = new MemoryAlloc( "Memory Savestate Recovery" ); + RecoveryMemSavingState().FreezeAll(); + safe_delete( g_gsRecoveryState ); + g_EmulationInProgress = false; + } + catch( Exception::RuntimeError& ex ) + { + Msgbox::Alert( + "Pcsx2 gamestate recovery failed. Some options may have been reverted to protect your game's state.\n" + "Error: %s", params ex.cMessage() ); + safe_delete( g_RecoveryState ); + } +} + +void SysReset() +{ + if (!sinit) return; + + // fixme - this code sets the statusbar but never returns control to the window message pump + // so the status bar won't receive the WM_PAINT messages needed to update itself anyway. + // Oops! (air) + + StatusBar_Notice(_("Resetting...")); + Console::SetTitle(_("Resetting...")); + + g_EmulationInProgress = false; + safe_delete( g_RecoveryState ); + safe_delete( g_gsRecoveryState ); + ResetPlugins(); + + ElfCRC = 0; + + // Note : No need to call cpuReset() here. It gets called automatically before the + // emulator resumes execution. + + StatusBar_Notice(_("Ready")); + Console::SetTitle(_("*PCSX2* Emulation state is reset.")); +} + +bool SysInit() +{ + if( sinit ) return true; + sinit = true; + + CreateDirectory(MEMCARDS_DIR, NULL); + CreateDirectory(SSTATES_DIR, NULL); + + if( IsDevBuild && emuLog == NULL && g_TestRun.plogname != NULL ) + emuLog = fopen(g_TestRun.plogname, "w"); + + if( emuLog == NULL ) + emuLog = fopen(LOGS_DIR "\\emuLog.txt","w"); + + PCSX2_MEM_PROTECT_BEGIN(); + SysDetect(); + if( !SysAllocateMem() ) + return false; // critical memory allocation failure; + + SysAllocateDynarecs(); + PCSX2_MEM_PROTECT_END(); + + return true; +} + +// completely shuts down the emulator's cpu state, and unloads all plugins from memory. +void SysClose() { + if (!sinit) return; + cpuShutdown(); + ClosePlugins(); + ReleasePlugins(); + sinit=false; +} + + +static const char *err = N_("Error Loading Symbol"); +static int errval; + +void *SysLoadLibrary(const char *lib) { + return LoadLibrary(lib); +} + +void *SysLoadSym(void *lib, const char *sym) { + void *tmp = GetProcAddress((HINSTANCE)lib, sym); + if (tmp == NULL) errval = GetLastError(); + else errval = 0; + return tmp; +} + +const char *SysLibError() { + if( errval ) + { + static char perr[4096]; + + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),NULL,perr,4095,NULL); + + errval = 0; + return _(perr); + } + return NULL; +} + +void SysCloseLibrary(void *lib) { + FreeLibrary((HINSTANCE)lib); +} + +void *SysMmap(uptr base, u32 size) { + void *mem; + + mem = VirtualAlloc((void*)base, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + return mem; +} + +void SysMunmap(uptr base, u32 size) +{ + if( base == NULL ) return; + VirtualFree((void*)base, size, MEM_DECOMMIT); + VirtualFree((void*)base, 0, MEM_RELEASE); +} diff --git a/pcsx2/windows/WinThreads.cpp b/pcsx2/windows/WinThreads.cpp new file mode 100644 index 0000000000..a47d3c3d25 --- /dev/null +++ b/pcsx2/windows/WinThreads.cpp @@ -0,0 +1,152 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "System.h" +#include "Threading.h" +#include "ix86/ix86.h" + +#ifdef _WIN32 +#include "implement.h" // win32 pthreads implementations. +#endif + +namespace Threading +{ + void CountLogicalCores( int LogicalCoresPerPhysicalCPU, int PhysicalCoresPerPhysicalCPU ) + { + DWORD vProcessCPUs; + DWORD vSystemCPUs; + + cpuinfo.LogicalCores = 1; + + if( !GetProcessAffinityMask (GetCurrentProcess (), + &vProcessCPUs, &vSystemCPUs) ) return; + + int CPUs = 0; + DWORD bit; + + for (bit = 1; bit != 0; bit <<= 1) + { + if (vSystemCPUs & bit) + CPUs++; + } + + cpuinfo.LogicalCores = CPUs; + if( LogicalCoresPerPhysicalCPU > CPUs) // for 1-socket HTT-disabled machines + LogicalCoresPerPhysicalCPU = CPUs; + + cpuinfo.PhysicalCores = ( CPUs / LogicalCoresPerPhysicalCPU ) * PhysicalCoresPerPhysicalCPU; + ptw32_smp_system = ( cpuinfo.LogicalCores > 1 ) ? TRUE : FALSE; + } + + __forceinline void Timeslice() + { + Sleep(0); + } + + // For use in spin/wait loops, Acts as a hint to Intel CPUs and should, in theory + // improve performance and reduce cpu power consumption. + __forceinline void SpinWait() + { + __asm { pause }; + } + + void* Thread::_internal_callback( void* itsme ) + { + jASSUME( itsme != NULL ); + + Thread& owner = *((Thread*)itsme); + pthread_win32_thread_attach_np(); + + try + { + owner.m_returncode = owner.Callback(); + } + catch( std::exception& ex ) + { + Console::Error( "Thread terminated abnormally with error:\n\t%s", params ex.what() ); + owner.m_returncode = -1; + } + + owner.m_terminated = true; + pthread_win32_thread_detach_np(); + + return NULL; + } + + // Note: assuming multicore is safer because it forces the interlocked routines to use + // the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not + // having the LOCK prefix is very bad indeed. + + ////////////////////////////////////////////////////////////////////// + // Win32 versions of InterlockedExchange. + // These are much faster than the Win32 Kernel versions thanks to inlining. + + __forceinline long pcsx2_InterlockedExchange( volatile long* target, long srcval ) + { + // Use the pthreads-win32 implementation... + return ptw32_InterlockedExchange( target, srcval ); + } + + __forceinline long pcsx2_InterlockedCompareExchange( volatile long* target, long srcval, long comp ) + { + // Use the pthreads-win32 implementation... + return ptw32_InterlockedCompareExchange( target, srcval, comp ); + } + + __forceinline long pcsx2_InterlockedExchangeAdd( volatile long* target, long srcval ) + { + long result; + + // Use our own implementation... + // Pcsx2 won't use threads unless it's a multicore cpu, so no need to use + // the optimized single-core method. + + if( true ) //ptw32_smp_system ) + { + __asm + { + //PUSH ecx + mov ecx,dword ptr [target] + mov eax,dword ptr [srcval] + lock xadd dword ptr [ecx],eax + mov dword ptr [result], eax + //POP ecx + } + } + else + { + __asm + { + //PUSH ecx + //PUSH edx + mov ecx,dword ptr [target] + //L1: + mov eax,dword ptr [srcval] + xadd dword ptr [ecx],eax + //jnz L1 + mov dword ptr [result], eax + //POP edx + //POP ecx + } + } + return result; + } +} diff --git a/pcsx2/windows/WinVM.cpp b/pcsx2/windows/WinVM.cpp new file mode 100644 index 0000000000..b80b02d1a1 --- /dev/null +++ b/pcsx2/windows/WinVM.cpp @@ -0,0 +1,470 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "win32.h" + + +#ifdef PCSX2_VIRTUAL_MEM + +// virtual memory/privileges +#include "ntsecapi.h" + +static wchar_t s_szUserName[255]; + +LRESULT WINAPI UserNameProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + SetWindowPos(hDlg, HWND_TOPMOST, 200, 100, 0, 0, SWP_NOSIZE); + return TRUE; + + case WM_COMMAND: + switch(wParam) { + case IDOK: + { + wchar_t str[255]; + GetWindowTextW(GetDlgItem(hDlg, IDC_USER_NAME), str, 255); + swprintf(s_szUserName, 255, L"%hs", &str); + EndDialog(hDlg, TRUE ); + return TRUE; + } + + case IDCANCEL: + EndDialog(hDlg, FALSE ); + return TRUE; + } + break; + } + return FALSE; +} + +BOOL InitLsaString( + PLSA_UNICODE_STRING pLsaString, + LPCWSTR pwszString +) +{ + DWORD dwLen = 0; + + if (NULL == pLsaString) + return FALSE; + + if (NULL != pwszString) + { + dwLen = wcslen(pwszString); + if (dwLen > 0x7ffe) // String is too large + return FALSE; + } + + // Store the string. + pLsaString->Buffer = (WCHAR *)pwszString; + pLsaString->Length = (USHORT)dwLen * sizeof(WCHAR); + pLsaString->MaximumLength= (USHORT)(dwLen+1) * sizeof(WCHAR); + + return TRUE; +} + +PLSA_TRANSLATED_SID2 GetSIDInformation (LPWSTR AccountName,LSA_HANDLE PolicyHandle) +{ + LSA_UNICODE_STRING lucName; + PLSA_TRANSLATED_SID2 ltsTranslatedSID; + PLSA_REFERENCED_DOMAIN_LIST lrdlDomainList; + //LSA_TRUST_INFORMATION myDomain; + NTSTATUS ntsResult; + PWCHAR DomainString = NULL; + + // Initialize an LSA_UNICODE_STRING with the name. + if (!InitLsaString(&lucName, AccountName)) + { + wprintf(L"Failed InitLsaString\n"); + return NULL; + } + + ntsResult = LsaLookupNames2( + PolicyHandle, // handle to a Policy object + 0, + 1, // number of names to look up + &lucName, // pointer to an array of names + &lrdlDomainList, // receives domain information + <sTranslatedSID // receives relative SIDs + ); + if (0 != ntsResult) + { + wprintf(L"Failed LsaLookupNames - %lu \n", + LsaNtStatusToWinError(ntsResult)); + return NULL; + } + + // Get the domain the account resides in. +// myDomain = lrdlDomainList->Domains[ltsTranslatedSID->DomainIndex]; +// DomainString = (PWCHAR) LocalAlloc(LPTR, myDomain.Name.Length + 1); +// wcsncpy(DomainString, myDomain.Name.Buffer, myDomain.Name.Length); + + // Display the relative Id. +// wprintf(L"Relative Id is %lu in domain %ws.\n", +// ltsTranslatedSID->RelativeId, +// DomainString); + + LsaFreeMemory(lrdlDomainList); + + return ltsTranslatedSID; +} + +BOOL AddPrivileges(PSID AccountSID, LSA_HANDLE PolicyHandle, BOOL bAdd) +{ + LSA_UNICODE_STRING lucPrivilege; + NTSTATUS ntsResult; + + // Create an LSA_UNICODE_STRING for the privilege name(s). + if (!InitLsaString(&lucPrivilege, L"SeLockMemoryPrivilege")) + { + wprintf(L"Failed InitLsaString\n"); + return FALSE; + } + + if( bAdd ) { + ntsResult = LsaAddAccountRights( + PolicyHandle, // An open policy handle. + AccountSID, // The target SID. + &lucPrivilege, // The privilege(s). + 1 // Number of privileges. + ); + } + else { + ntsResult = LsaRemoveAccountRights( + PolicyHandle, // An open policy handle. + AccountSID, // The target SID + FALSE, + &lucPrivilege, // The privilege(s). + 1 // Number of privileges. + ); + } + + if (ntsResult == 0) + { + wprintf(L"Privilege added.\n"); + } + else + { + int err = LsaNtStatusToWinError(ntsResult); + char str[255]; + _snprintf(str, 255, "Privilege was not added - %lu \n", LsaNtStatusToWinError(ntsResult)); + MessageBox(NULL, str, "Privilege error", MB_OK); + return FALSE; + } + + return TRUE; +} + +#define TARGET_SYSTEM_NAME L"mysystem" +LSA_HANDLE GetPolicyHandle() +{ + LSA_OBJECT_ATTRIBUTES ObjectAttributes; + WCHAR SystemName[] = TARGET_SYSTEM_NAME; + USHORT SystemNameLength; + LSA_UNICODE_STRING lusSystemName; + NTSTATUS ntsResult; + LSA_HANDLE lsahPolicyHandle; + + // Object attributes are reserved, so initialize to zeroes. + ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); + + //Initialize an LSA_UNICODE_STRING to the server name. + SystemNameLength = wcslen(SystemName); + lusSystemName.Buffer = SystemName; + lusSystemName.Length = SystemNameLength * sizeof(WCHAR); + lusSystemName.MaximumLength = (SystemNameLength+1) * sizeof(WCHAR); + + // Get a handle to the Policy object. + ntsResult = LsaOpenPolicy( + NULL, //Name of the target system. + &ObjectAttributes, //Object attributes. + POLICY_ALL_ACCESS, //Desired access permissions. + &lsahPolicyHandle //Receives the policy handle. + ); + + if (ntsResult != 0) + { + // An error occurred. Display it as a win32 error code. + wprintf(L"OpenPolicy returned %lu\n", + LsaNtStatusToWinError(ntsResult)); + return NULL; + } + return lsahPolicyHandle; +} + + +/***************************************************************** + LoggedSetLockPagesPrivilege: a function to obtain, if possible, or + release the privilege of locking physical pages. + + Inputs: + + HANDLE hProcess: Handle for the process for which the + privilege is needed + + BOOL bEnable: Enable (TRUE) or disable? + + Return value: TRUE indicates success, FALSE failure. + +*****************************************************************/ +BOOL SysLoggedSetLockPagesPrivilege ( HANDLE hProcess, BOOL bEnable) +{ + struct { + u32 Count; + LUID_AND_ATTRIBUTES Privilege [1]; + } Info; + + HANDLE Token; + BOOL Result; + + // Open the token. + + Result = OpenProcessToken ( hProcess, + TOKEN_ADJUST_PRIVILEGES, + & Token); + + if( Result != TRUE ) { + Console::Error( "VirtualMemory Error > Cannot open process token." ); + return FALSE; + } + + // Enable or disable? + + Info.Count = 1; + if( bEnable ) + { + Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; + } + else + { + Info.Privilege[0].Attributes = SE_PRIVILEGE_REMOVED; + } + + // Get the LUID. + Result = LookupPrivilegeValue ( NULL, + SE_LOCK_MEMORY_NAME, + &(Info.Privilege[0].Luid)); + + if( Result != TRUE ) + { + Console::Error( "VirtualMemory Error > Cannot get privilege value for %s.", params SE_LOCK_MEMORY_NAME ); + return FALSE; + } + + // Adjust the privilege. + + Result = AdjustTokenPrivileges ( Token, FALSE, + (PTOKEN_PRIVILEGES) &Info, + 0, NULL, NULL); + + // Check the result. + if( Result != TRUE ) + { + Console::Error( "VirtualMemory Error > Cannot adjust token privileges, error %u.", params GetLastError() ); + return FALSE; + } + else + { + if( GetLastError() != ERROR_SUCCESS ) + { + + BOOL bSuc = FALSE; + LSA_HANDLE policy; + PLSA_TRANSLATED_SID2 ltsTranslatedSID; + +// if( !DialogBox(gApp.hInstance, MAKEINTRESOURCE(IDD_USERNAME), gApp.hWnd, (DLGPROC)UserNameProc) ) +// return FALSE; + DWORD len = sizeof(s_szUserName); + GetUserNameW(s_szUserName, &len); + + policy = GetPolicyHandle(); + + if( policy != NULL ) { + + ltsTranslatedSID = GetSIDInformation(s_szUserName, policy); + + if( ltsTranslatedSID != NULL ) { + bSuc = AddPrivileges(ltsTranslatedSID->Sid, policy, bEnable); + LsaFreeMemory(ltsTranslatedSID); + } + + LsaClose(policy); + } + + if( bSuc ) { + // Get the LUID. + LookupPrivilegeValue ( NULL, SE_LOCK_MEMORY_NAME, &(Info.Privilege[0].Luid)); + + bSuc = AdjustTokenPrivileges ( Token, FALSE, (PTOKEN_PRIVILEGES) &Info, 0, NULL, NULL); + } + + if( bSuc ) { + if( MessageBox(NULL, "PCSX2 just changed your SE_LOCK_MEMORY privilege in order to gain access to physical memory.\n" + "Log off/on and run pcsx2 again. Do you want to log off?\n", + "Privilege changed query", MB_YESNO) == IDYES ) { + ExitWindows(EWX_LOGOFF, 0); + } + } + else { + MessageBox(NULL, "Failed adding SE_LOCK_MEMORY privilege, please check the local policy.\n" + "Go to security settings->Local Policies->User Rights. There should be a \"Lock pages in memory\".\n" + "Add your user to that and log off/on. This enables pcsx2 to run at real-time by allocating physical memory.\n" + "Also can try Control Panel->Local Security Policy->... (this does not work on Windows XP Home)\n" + "(zerofrog)\n", "Virtual Memory Access Denied", MB_OK); + return FALSE; + } + } + } + + CloseHandle( Token ); + + return TRUE; +} + +static u32 s_dwPageSize = 0; +int SysPhysicalAlloc(u32 size, PSMEMORYBLOCK* pblock) +{ +//#ifdef WIN32_FILE_MAPPING +// assert(0); +//#endif + ULONG_PTR NumberOfPagesInitial; // initial number of pages requested + int PFNArraySize; // memory to request for PFN array + BOOL bResult; + + assert( pblock != NULL ); + memset(pblock, 0, sizeof(PSMEMORYBLOCK)); + + if( s_dwPageSize == 0 ) { + SYSTEM_INFO sSysInfo; // useful system information + GetSystemInfo(&sSysInfo); // fill the system information structure + s_dwPageSize = sSysInfo.dwPageSize; + + if( s_dwPageSize != 0x1000 ) { + Msgbox::Alert("Error! OS page size must be 4Kb!\n" + "If for some reason the OS cannot have 4Kb pages, then run the TLB build."); + return -1; + } + } + + // Calculate the number of pages of memory to request. + pblock->NumberPages = (size+s_dwPageSize-1)/s_dwPageSize; + PFNArraySize = pblock->NumberPages * sizeof (ULONG_PTR); + + pblock->aPFNs = (uptr*)HeapAlloc (GetProcessHeap (), 0, PFNArraySize); + + if (pblock->aPFNs == NULL) { + Console::Error("Failed to allocate on heap."); + goto eCleanupAndExit; + } + + // Allocate the physical memory. + NumberOfPagesInitial = pblock->NumberPages; + bResult = AllocateUserPhysicalPages( GetCurrentProcess(), (PULONG_PTR)&pblock->NumberPages, (PULONG_PTR)pblock->aPFNs ); + + if( bResult != TRUE ) + { + Console::Error("Virtual Memory Error %u > Cannot allocate physical pages.", params GetLastError() ); + goto eCleanupAndExit; + } + + if( NumberOfPagesInitial != pblock->NumberPages ) + { + Console::Error("Virtual Memory > Physical allocation failed!\n\tAllocated only %p of %p pages.", params pblock->NumberPages, NumberOfPagesInitial ); + goto eCleanupAndExit; + } + + pblock->aVFNs = (uptr*)HeapAlloc(GetProcessHeap(), 0, PFNArraySize); + + return 0; + +eCleanupAndExit: + SysPhysicalFree(pblock); + return -1; +} + +void SysPhysicalFree(PSMEMORYBLOCK* pblock) +{ + assert( pblock != NULL ); + + // Free the physical pages. + FreeUserPhysicalPages( GetCurrentProcess(), (PULONG_PTR)&pblock->NumberPages, (PULONG_PTR)pblock->aPFNs ); + + if( pblock->aPFNs != NULL ) HeapFree(GetProcessHeap(), 0, pblock->aPFNs); + if( pblock->aVFNs != NULL ) HeapFree(GetProcessHeap(), 0, pblock->aVFNs); + memset(pblock, 0, sizeof(PSMEMORYBLOCK)); +} + +int SysVirtualPhyAlloc(void* base, u32 size, PSMEMORYBLOCK* pblock) +{ + BOOL bResult; + int i; + + LPVOID lpMemReserved = VirtualAlloc( base, size, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE ); + if( lpMemReserved == NULL || base != lpMemReserved ) + { + Console::WriteLn("VirtualMemory Error %d > Cannot reserve memory at 0x%8.8x(%x).", params base, lpMemReserved, GetLastError()); + goto eCleanupAndExit; + } + + // Map the physical memory into the window. + bResult = MapUserPhysicalPages( base, (ULONG_PTR)pblock->NumberPages, (PULONG_PTR)pblock->aPFNs ); + + for(i = 0; i < pblock->NumberPages; ++i) + pblock->aVFNs[i] = (uptr)base + 0x1000*i; + + if( bResult != TRUE ) + { + Console::WriteLn("VirtualMemory Error %u > MapUserPhysicalPages failed to map.", params GetLastError() ); + goto eCleanupAndExit; + } + + return 0; + +eCleanupAndExit: + SysVirtualFree(base, size); + return -1; +} + +void SysVirtualFree(void* lpMemReserved, u32 size) +{ + // unmap + if( MapUserPhysicalPages( lpMemReserved, (size+s_dwPageSize-1)/s_dwPageSize, NULL ) != TRUE ) + { + Console::WriteLn("VirtualMemory Error %u > MapUserPhysicalPages failed to unmap", params GetLastError() ); + return; + } + + // Free virtual memory. + VirtualFree( lpMemReserved, 0, MEM_RELEASE ); +} + +int SysMapUserPhysicalPages(void* Addr, uptr NumPages, uptr* pfn, int pageoffset) +{ + BOOL bResult = MapUserPhysicalPages(Addr, NumPages, (PULONG_PTR)(pfn+pageoffset)); + +#ifdef _DEBUG + //if( !bResult ) + //__Log("Failed to map user pages: 0x%x:0x%x, error = %d\n", Addr, NumPages, GetLastError()); +#endif + + return bResult; +} + +#else + +#endif diff --git a/pcsx2/windows/afxresmw.h b/pcsx2/windows/afxresmw.h new file mode 100644 index 0000000000..2e5a124bb3 --- /dev/null +++ b/pcsx2/windows/afxresmw.h @@ -0,0 +1,23 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 Team + * Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + * + * 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 +#include + +#define IDC_STATIC (-1) diff --git a/pcsx2/windows/cheats/browser.cpp b/pcsx2/windows/cheats/browser.cpp new file mode 100644 index 0000000000..3a40fa22ea --- /dev/null +++ b/pcsx2/windows/cheats/browser.cpp @@ -0,0 +1,1129 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "../Win32.h" + +#include +#include + +using namespace std; + +#include "../cheatscpp.h" + +#include "PS2Edefs.h" +#include "Memory.h" +#include "Elfheader.h" +#include "cheats.h" +#include "../../patch.h" + +HWND hWndBrowser; + + +/// ADDED CODE /// +void CreateListBox(HWND hWnd) +{ + // Setting the ListView style + LRESULT lStyle = SendMessage(GetDlgItem(hWnd,IDC_PATCHES), LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + SendMessage(GetDlgItem(hWnd,IDC_PATCHES), LVM_SETEXTENDEDLISTVIEWSTYLE, 0, lStyle | LVS_EX_FULLROWSELECT); + + // Adding colummns to the ListView + LVCOLUMN cols[7]={ + {LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM,0,70,"Address",0,0,0}, + {LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM,0,35,"CPU",1,0,0}, + {LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM,0,70,"Data",2,0,0}, + {LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM,0,50,"Enabled",3,0,0}, + {LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM,0,45,"Group",4,0,0}, + {LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM,0,80,"PlaceToPatch",5,0,0}, + {LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM,0,50,"Type",6,0,0} + }; + + ListView_InsertColumn(GetDlgItem(hWnd,IDC_PATCHES),0,&cols[0]); + ListView_InsertColumn(GetDlgItem(hWnd,IDC_PATCHES),1,&cols[1]); + ListView_InsertColumn(GetDlgItem(hWnd,IDC_PATCHES),2,&cols[2]); + ListView_InsertColumn(GetDlgItem(hWnd,IDC_PATCHES),3,&cols[3]); + ListView_InsertColumn(GetDlgItem(hWnd,IDC_PATCHES),4,&cols[4]); + ListView_InsertColumn(GetDlgItem(hWnd,IDC_PATCHES),5,&cols[5]); + ListView_InsertColumn(GetDlgItem(hWnd,IDC_PATCHES),6,&cols[6]); +} + +void RefreshListBox(HWND hWnd) +{ + // First delete all items + ListView_DeleteAllItems(GetDlgItem(hWnd,IDC_PATCHES)); + + char Address[100], CPU[100], Data[100], Enabled[100], Group[100], PlaceToPatch[100], Type[100]; + + LVITEM item[7]={ + {LVIF_TEXT|LVIF_STATE,0,0,0,0,Address,0,0,0,0}, + {LVIF_TEXT|LVIF_STATE,0,1,0,0,CPU,0,0,0,0}, + {LVIF_TEXT|LVIF_STATE,0,2,0,0,Data,0,0,0,0}, + {LVIF_TEXT|LVIF_STATE,0,3,0,0,Enabled,0,0,0,0}, + {LVIF_TEXT|LVIF_STATE,0,4,0,0,Group,0,0,0,0}, + {LVIF_TEXT|LVIF_STATE,0,5,0,0,PlaceToPatch,0,0,0,0}, + {LVIF_TEXT|LVIF_STATE,0,6,0,0,Type,0,0,0,0} + }; + + char datatype[4][10]={"byte", "short", "word", "double"}; + char cpucore[2][10]={"EE", "IOP"}; + + // Adding items + for (int i = patchnumber-1; i >= 0; i--) + { + sprintf(Address, "0x%.8x", patch[i].addr); + sprintf(CPU, "%s", cpucore[patch[i].cpu-1]); + sprintf(Data, "0x%.8x", patch[i].data); + sprintf(Enabled, "%s", patch[i].enabled?"Yes":"No"); + sprintf(Group, "%d", patch[i].group); + sprintf(PlaceToPatch, "%d", patch[i].placetopatch); + sprintf(Type, "%s", datatype[patch[i].type-1]); + + ListView_InsertItem(GetDlgItem(hWnd,IDC_PATCHES),&item[0]); + ListView_SetItem(GetDlgItem(hWnd,IDC_PATCHES),&item[1]); + ListView_SetItem(GetDlgItem(hWnd,IDC_PATCHES),&item[2]); + ListView_SetItem(GetDlgItem(hWnd,IDC_PATCHES),&item[3]); + ListView_SetItem(GetDlgItem(hWnd,IDC_PATCHES),&item[4]); + ListView_SetItem(GetDlgItem(hWnd,IDC_PATCHES),&item[5]); + ListView_SetItem(GetDlgItem(hWnd,IDC_PATCHES),&item[6]); + } +} + + +// Decryption code for GS2v3-4/GS5 from maxconvert 0.71 +unsigned char gsv3_bseeds1[256] = { +0x6E, 0x3C, 0x01, 0x30, 0x45, 0xDA, 0xA1, 0x77, 0x6A, 0x41, 0xFC, 0xC6, 0x00, 0xEA, 0x2F, 0x23, +0xD9, 0x6F, 0x79, 0x39, 0x7E, 0xCC, 0x9A, 0x4E, 0x1E, 0xF4, 0xA7, 0x3D, 0x05, 0x75, 0xD0, 0x36, +0x9E, 0x8D, 0xF8, 0x8B, 0x96, 0x7A, 0xBF, 0x49, 0x62, 0x4F, 0x2A, 0xB8, 0xAF, 0x60, 0x80, 0x0D, +0x99, 0x89, 0xA9, 0xED, 0xB6, 0xD5, 0x7B, 0x54, 0x56, 0x65, 0x5C, 0xB3, 0xD3, 0x11, 0xDF, 0x27, +0x10, 0x71, 0x91, 0x3E, 0x25, 0x52, 0x46, 0x15, 0x3A, 0x31, 0x51, 0x81, 0xC5, 0xB1, 0xBE, 0x43, +0x95, 0x7C, 0x83, 0x53, 0x38, 0x88, 0x21, 0x4C, 0x9B, 0x7F, 0x90, 0xB9, 0xDB, 0x55, 0x33, 0xB7, +0xAD, 0x61, 0xD8, 0xE1, 0xE4, 0x20, 0xDC, 0x5F, 0xA4, 0x67, 0x26, 0x97, 0x2E, 0xAC, 0x7D, 0x24, +0xBD, 0x22, 0x04, 0x6C, 0xC0, 0x73, 0xE0, 0x32, 0x0C, 0xA3, 0xEE, 0x2B, 0xA0, 0x8A, 0x5B, 0xF3, +0xA6, 0xD1, 0x68, 0x8E, 0xAE, 0xC7, 0x9C, 0x82, 0xB4, 0xF9, 0xF6, 0xC4, 0x1B, 0xAB, 0x57, 0xCE, +0xEF, 0x69, 0xF7, 0x74, 0xFF, 0xA2, 0x6D, 0xF5, 0xB2, 0x0A, 0x37, 0x1F, 0xEC, 0x06, 0x5D, 0x0F, +0xB0, 0x08, 0xD7, 0xE3, 0x85, 0x58, 0x1A, 0x9F, 0x1D, 0x84, 0xE9, 0xEB, 0x0E, 0x66, 0x64, 0x40, +0x4A, 0x44, 0x35, 0x92, 0x3B, 0x86, 0xF0, 0xF2, 0xAA, 0x47, 0xCB, 0x02, 0xB5, 0xDD, 0xD2, 0x13, +0x16, 0x07, 0xC2, 0xE5, 0x17, 0xA5, 0x5E, 0xCA, 0xD6, 0xC9, 0x87, 0x2D, 0xC1, 0xCD, 0x76, 0x50, +0x1C, 0xE2, 0x8F, 0x29, 0xD4, 0xDE, 0xA8, 0xE7, 0x14, 0xFB, 0xC3, 0xE6, 0xFD, 0x5A, 0x48, 0xCF, +0x98, 0x42, 0x63, 0x4D, 0xBB, 0xE8, 0x70, 0xF1, 0x12, 0x78, 0x0B, 0xFA, 0xBA, 0x18, 0x93, 0x9D, +0x6B, 0x28, 0x2C, 0x09, 0x59, 0xFE, 0xC8, 0x34, 0x03, 0x94, 0x72, 0x8C, 0x3F, 0x4B, 0x19, 0xBC +}; + +unsigned char gsv3_bseeds2[25] = { 0x12, 0x18, 0x07, 0x0A, 0x17, 0x16, 0x10, 0x00, +0x05, 0x0D, 0x04, 0x02, 0x09, 0x08, 0x0E, 0x0F, 0x13, 0x11, 0x01, 0x15, +0x03, 0x06, 0x0C, 0x0B, 0x14 +}; + +unsigned char gsv3_bseeds3[64] = {0x1D, 0x01, 0x33, 0x0B, 0x23, 0x22, 0x2C, 0x24, +0x0E, 0x2A, 0x2D, 0x0F, 0x2B, 0x20, 0x08, 0x34, 0x17, 0x1B, 0x36, 0x0C, +0x12, 0x1F, 0x35, 0x27, 0x0A, 0x31, 0x11, 0x05, 0x1E, 0x32, 0x14, 0x02, +0x21, 0x03, 0x04, 0x0D, 0x09, 0x16, 0x2E, 0x1A, 0x26, 0x25, 0x2F, 0x07, +0x1C, 0x29, 0x18, 0x30, 0x10, 0x15, 0x13, 0x19, 0x06, 0x38, 0x28, 0x00, +0x37, 0x48, 0x48, 0x7F, 0x45, 0x45, 0x45, 0x7F +}; + +unsigned char gsv3_bseeds4[64] = { 0x10, 0x01, 0x18, 0x3C, 0x33, 0x29, 0x00, 0x06, +0x07, 0x02, 0x04, 0x13, 0x0B, 0x28, 0x15, 0x08, 0x1F, 0x3E, 0x0C, 0x05, +0x11, 0x1D, 0x1E, 0x27, 0x1A, 0x17, 0x38, 0x23, 0x2D, 0x37, 0x03, 0x2C, +0x2A, 0x1B, 0x22, 0x39, 0x25, 0x14, 0x24, 0x34, 0x20, 0x2F, 0x36, 0x3F, +0x3A, 0x0E, 0x0F, 0x2B, 0x32, 0x31, 0x21, 0x12, 0x26, 0x35, 0x30, 0x3B, +0x09, 0x19, 0x16, 0x0D, 0x0A, 0x2E, 0x3D, 0x1C +}; + +unsigned short gsv3_hseeds[4] = { 0x6C27, 0x1D38, 0x7FE1, 0x0000}; + +unsigned char gsv3_bseeds101[256] = { +0x0C, 0x02, 0xBB, 0xF8, 0x72, 0x1C, 0x9D, 0xC1, 0xA1, 0xF3, 0x99, 0xEA, 0x78, 0x2F, 0xAC, 0x9F, +0x40, 0x3D, 0xE8, 0xBF, 0xD8, 0x47, 0xC0, 0xC4, 0xED, 0xFE, 0xA6, 0x8C, 0xD0, 0xA8, 0x18, 0x9B, +0x65, 0x56, 0x71, 0x0F, 0x6F, 0x44, 0x6A, 0x3F, 0xF1, 0xD3, 0x2A, 0x7B, 0xF2, 0xCB, 0x6C, 0x0E, +0x03, 0x49, 0x77, 0x5E, 0xF7, 0xB2, 0x1F, 0x9A, 0x54, 0x13, 0x48, 0xB4, 0x01, 0x1B, 0x43, 0xFC, +0xAF, 0x09, 0xE1, 0x4F, 0xB1, 0x04, 0x46, 0xB9, 0xDE, 0x27, 0xB0, 0xFD, 0x57, 0xE3, 0x17, 0x29, +0xCF, 0x4A, 0x45, 0x53, 0x37, 0x5D, 0x38, 0x8E, 0xA5, 0xF4, 0xDD, 0x7E, 0x3A, 0x9E, 0xC6, 0x67, +0x2D, 0x61, 0x28, 0xE2, 0xAE, 0x39, 0xAD, 0x69, 0x82, 0x91, 0x08, 0xF0, 0x73, 0x96, 0x00, 0x11, +0xE6, 0x41, 0xFA, 0x75, 0x93, 0x1D, 0xCE, 0x07, 0xE9, 0x12, 0x25, 0x36, 0x51, 0x6E, 0x14, 0x59, +0x2E, 0x4B, 0x87, 0x52, 0xA9, 0xA4, 0xB5, 0xCA, 0x55, 0x31, 0x7D, 0x23, 0xFB, 0x21, 0x83, 0xD2, +0x5A, 0x42, 0xB3, 0xEE, 0xF9, 0x50, 0x24, 0x6B, 0xE0, 0x30, 0x16, 0x58, 0x86, 0xEF, 0x20, 0xA7, +0x7C, 0x06, 0x95, 0x79, 0x68, 0xC5, 0x80, 0x1A, 0xD6, 0x32, 0xB8, 0x8D, 0x6D, 0x60, 0x84, 0x2C, +0xA0, 0x4D, 0x98, 0x3B, 0x88, 0xBC, 0x34, 0x5F, 0x2B, 0x5B, 0xEC, 0xE4, 0xFF, 0x70, 0x4E, 0x26, +0x74, 0xCC, 0xC2, 0xDA, 0x8B, 0x4C, 0x0B, 0x85, 0xF6, 0xC9, 0xC7, 0xBA, 0x15, 0xCD, 0x8F, 0xDF, +0x1E, 0x81, 0xBE, 0x3C, 0xD4, 0x35, 0xC8, 0xA2, 0x62, 0x10, 0x05, 0x5C, 0x66, 0xBD, 0xD5, 0x3E, +0x76, 0x63, 0xD1, 0xA3, 0x64, 0xC3, 0xDB, 0xD7, 0xE5, 0xAA, 0x0D, 0xAB, 0x9C, 0x33, 0x7A, 0x90, +0xB6, 0xE7, 0xB7, 0x7F, 0x19, 0x97, 0x8A, 0x92, 0x22, 0x89, 0xEB, 0xD9, 0x0A, 0xDC, 0xF5, 0x94 +}; + + +u8 DecryptGS2v3(u32* address, u32* value, u8 ctrl) +{ + u32 command = *address & 0xFE000000; + u32 tmp = 0, tmp2 = 0, mask = 0, i = 0; + u8 bmask[4], flag; + u8 encFlag = (*address >> 25) & 0x7; + + if(ctrl > 0) + { + switch(ctrl) + { + case 2: + { + *address ^= (gsv3_hseeds[1] << 2); + *value ^= (gsv3_hseeds[1] << 13); + for(i = 0; i < 0x40; i++) + { + if(i < 32) + { + flag = (*value >> i) & 1; + } + else + { + flag = (*address >> (i - 32)) & 1; + } + + if(flag > 0) + { + if(gsv3_bseeds4[i] < 32) + { + tmp2 |= (1 << gsv3_bseeds4[i]); + } + else + { + tmp |= (1 << (gsv3_bseeds4[i] - 32)); + } + } + } + + *address = tmp ^ (gsv3_hseeds[2] << 3); + *value = tmp2 ^ gsv3_hseeds[2]; + break; + } + + case 4: + { + tmp = *address ^ (gsv3_hseeds[1] << 3); + bmask[0] = gsv3_bseeds1[(tmp >> 24) & 0xFF]; + bmask[1] = gsv3_bseeds1[(tmp >> 16) & 0xFF]; + bmask[2] = gsv3_bseeds1[(tmp >> 8) & 0xFF]; + bmask[3] = gsv3_bseeds1[tmp & 0xFF]; + tmp = (bmask[0] << 24) | (bmask[1] << 16) | (bmask[2] << 8) | bmask[3]; + *address = tmp ^ (gsv3_hseeds[2] << 16); + + tmp = *value ^ (gsv3_hseeds[1] << 9); + bmask[0] = gsv3_bseeds1[(tmp >> 24) & 0xFF]; + bmask[1] = gsv3_bseeds1[(tmp >> 16) & 0xFF]; + bmask[2] = gsv3_bseeds1[(tmp >> 8) & 0xFF]; + bmask[3] = gsv3_bseeds1[tmp & 0xFF]; + tmp = (bmask[0] << 24) | (bmask[1] << 16) | (bmask[2] << 8) | bmask[3]; + *value = tmp ^ (gsv3_hseeds[2] << 5); + break; + } + } + return 0; + } + + if(command >= 0x30000000 && command <= 0x6FFFFFFF) + { + if(encFlag == 1 || encFlag == 2) + { + ctrl = 2; + } + else if (encFlag == 3 || encFlag == 4) + { + ctrl = 4; + } + } + + switch(encFlag) + { + case 0: + { + break; + } + + case 1: + { + tmp = *address & 0x01FFFFFF; + tmp = tmp ^ (gsv3_hseeds[1] << 8); + mask = 0; + for(i = 0; i < 25; i++) + { + mask |= ((tmp & (1 << i)) >> i) << gsv3_bseeds2[i]; + } + tmp = mask ^ gsv3_hseeds[2]; + tmp |= command; + *address = tmp & 0xF1FFFFFF; + break; + } + + case 2: + { + if(encFlag == 2) + { + tmp = *address & 0x01FFFFFF; + *address = tmp ^ (gsv3_hseeds[1] << 1); + *value ^= (gsv3_hseeds[1] << 16); + tmp = 0; + tmp2 = 0; + for(i = 0; i < 0x39; i++) + { + if(i < 32) + { + flag = (*value >> i) & 1; + } + else + { + flag = (*address >> (i - 32)) & 1; + } + + if(flag > 0) + { + if(gsv3_bseeds3[i] < 32) + { + tmp2 |= (1 << gsv3_bseeds3[i]); + } + else + { + tmp |= (1 << (gsv3_bseeds3[i] - 32)); + } + } + } + *address = ((tmp ^ (gsv3_hseeds[2] << 8)) | command) & 0xF1FFFFFF; + *value = tmp2 ^ gsv3_hseeds[2]; + } + break; + } + + case 3: + { + tmp = *address ^ (gsv3_hseeds[1] << 8); + bmask[0] = gsv3_bseeds1[(tmp >> 8) & 0xFF]; + bmask[1] = gsv3_bseeds1[(tmp & 0xFF)]; + tmp = (tmp & 0xFFFF0000) ; + tmp |= (bmask[0] ^ (bmask[1] << 8)) ; + tmp ^= (gsv3_hseeds[2] << 4); + *address = tmp & 0xF1FFFFFF; + break; + } + + case 4: + { + tmp = *address ^ (gsv3_hseeds[1] << 8); + bmask[0] = gsv3_bseeds1[(tmp >> 8) & 0xFF]; + bmask[1] = gsv3_bseeds1[(tmp & 0xFF)]; + tmp = (tmp & 0xFFFF0000) ; + tmp |= (bmask[1] | (bmask[0] << 8)) ; + tmp ^= (gsv3_hseeds[2] << 4); + tmp2 = *address ^ (gsv3_hseeds[1] << 3); + *address = tmp & 0xF1FFFFFF; + tmp = *value ^ (gsv3_hseeds[1] << 9); + bmask[0] = gsv3_bseeds1[(tmp >> 24) & 0xFF]; + bmask[1] = gsv3_bseeds1[(tmp >> 16) & 0xFF]; + bmask[2] = gsv3_bseeds1[(tmp >> 8) & 0xFF]; + bmask[3] = gsv3_bseeds1[tmp & 0xFF]; + tmp = (bmask[0] << 24) | (bmask[1] << 16) | (bmask[2] << 8) | bmask[3]; + *value = tmp ^ (gsv3_hseeds[2] << 5); + break; + } + + case 5: + case 6: + case 7: + default: + break; + } + + return ctrl; + +} + +struct Cheat +{ + unsigned int address, value; +}; + +int ParseCheats(char *cur, std::vector &Cheats, HWND hWnd) +{ + // Parse the text and put the cheats to the vector + while(*cur!=NULL){ + Cheat C; + + // if there is endline advance 2 chars + if(*cur==0x0d && *(cur+1)==0x0a) + cur+=2; + // check if new start is null + if(*cur==NULL) + break; + + // get address and value and insert in the vector + int ret = sscanf(cur, "%X %X", &C.address, &C.value); + if(ret != 2) + { + MessageBox(hWnd,"Code conversion is probably wrong.","Error",0); + return 0; + } + Cheats.insert(Cheats.end(), C); + cur+=17; + } + return 1; +} + + +// Callback for the GameShark2 v3-4 cheat dialog +BOOL CALLBACK AddGS(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) +{ + int wmId,wmEvent; + static HWND hParent; + + switch(uMsg) + { + + case WM_PAINT: + return FALSE; + case WM_INITDIALOG: + hParent=(HWND)lParam; + break; + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + switch (wmId) + { + case IDCANCEL: + EndDialog(hWnd,1); + break; + + case IDC_CONVERT: // Convert the code + { + std::vector Cheats; + char chepa[256]; + + // Set ready text to false + SetWindowText(GetDlgItem(hWnd,IDC_READY), "0"); + + // Get the text from the edit control and load it + GetWindowText(GetDlgItem(hWnd,IDC_ADDR),chepa,256); + + // Parsing the cheats + ParseCheats(chepa, Cheats, hWnd); + + int control = 0; + *chepa=0; + // Decrypt multiline code + for(unsigned int i=0;i Cheats; + char ready[2], chepa[256]; + + // Check if we are ready to add first + GetWindowText(GetDlgItem(hWnd,IDC_READY),ready,2); + if(ready[0] == '0') + { + MessageBox(hWnd, "First convert the code.", "Error", 0); + break; + } + + // Get the text from the edit control and load it + GetWindowText(GetDlgItem(hWnd,IDC_CONVERTEDCODE),chepa,256); + + // Parsing the cheats + int ret = ParseCheats(chepa, Cheats, hWnd); + + if(ret == 1) + { + // Adding cheats + for(unsigned int i=0;i Cheats; + char chepa[256]; + + // Get the text from the edit control and load it + GetWindowText(GetDlgItem(hWnd,IDC_ADDR),chepa,256); + + if(strlen(chepa) == 0) + { + MessageBox(hWnd, "Add some cheats.", "Error", 0); + break; + } + + // Parsing the cheats + int ret = ParseCheats(chepa, Cheats, hWnd); + + if(ret == 1) + { + // Adding cheats + for(unsigned int i=0;i +#include + +#include "PS2Edefs.h" +#include "Memory.h" +#include "IopMem.h" + +#include "cheats.h" +#include "../../patch.h" + +class result +{ +public: + u32 address; + result(u32 addr): + address(addr) + { + } +}; + +char *sizenames[4]={"char","short","word","double"}; + +char mtext[100]; + +HINSTANCE pInstance; + +int TSelect; + +bool Unsigned; +int Source; +int Compare; +int Size; +int CompareTo; + +u64 CompareValue; + +std::vector results; + +//int mresults; + +bool FirstSearch; + +bool FirstShow; + +char olds[Ps2MemSize::Base]; + +char tn[100]; +char to[100]; +char tv[100]; + +u8 *mptr[2]; + +int msize[2]={0x02000000,0x00200000}; + +int lticks; + +HWND hWndFinder; + +LVCOLUMN cols[3]={ + {LVCF_TEXT|LVCF_WIDTH,0,60,"Address",0,0,0}, + {LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM,0,60,"Old V",1,0,0}, + {LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM,0,60,"New V",2,0,0} +}; + +LVITEM item[3]={ + {LVIF_TEXT|LVIF_STATE,0,0,0,0,tn,0,0,0,0}, + {LVIF_TEXT|LVIF_STATE,0,1,0,0,to,0,0,0,0}, + {LVIF_TEXT|LVIF_STATE,0,2,0,0,tv,0,0,0,0} +}; + +void DoEvents() +{ + MSG msg; + while(PeekMessage(&msg,0,0,0,PM_REMOVE)!=0) + DispatchMessage(&msg); +} + +void UpdateStatus() +{ + int nticks=GetTickCount(); + if((nticks-lticks)>250) + { + int nshown=ListView_GetItemCount(GetDlgItem(hWndFinder,IDC_RESULTS)); + sprintf(mtext,"%d matches found (%d shown).",results.size(),nshown); + SetWindowText(GetDlgItem(hWndFinder,IDC_MATCHES),mtext); + lticks=nticks; + DoEvents(); + } +} + +void SearchReset() +{ + memcpy(olds,mptr[Source],msize[Source]); + FirstSearch=true; + + results.clear(); + +} + +int AddResult(u32 addr) +{ + result nr=result(addr); + results.push_back(nr); + return 1; +} + +bool CompareAny(u64 val,u64 cto) +{ + + if(Unsigned) + { + switch(Size) + { + case 0: + val=(u8)val; + cto=(u8)cto; + break; + case 1: + val=(u16)val; + cto=(u16)cto; + break; + case 2: + val=(u32)val; + cto=(u32)cto; + break; + case 3: + break; + default:return false; + } + switch(Compare) + { + case 0: /* EQ */ + return val==cto; + case 1: /* GT */ + return val> cto; + case 2: /* LT */ + return val< cto; + case 3: /* GE */ + return val>=cto; + case 4: /* LE */ + return val<=cto; + default:/* NE */ + return val!=cto; + } + } + else + { + switch(Size) + { + case 0: + val=(s8)val; + cto=(s8)cto; + break; + case 1: + val=(s16)val; + cto=(s16)cto; + break; + case 2: + val=(s32)val; + cto=(s32)cto; + break; + case 3: + break; + default:return false; + } + switch(Compare) + { + case 0: /* EQ */ + return (s64)val==(s64)cto; + case 1: /* GT */ + return (s64)val> (s64)cto; + case 2: /* LT */ + return (s64)val< (s64)cto; + case 3: /* GE */ + return (s64)val>=(s64)cto; + case 4: /* LE */ + return (s64)val<=(s64)cto; + default:/* NE */ + return (s64)val!=(s64)cto; + } + } +} + +void SearchFirst() +{ + int MSize=1< oldr=std::vector(results); + + results.clear(); + + for(i=0;i", + Source?"IOP":"EE", + tn, + sizenames[Size]); + + SetWindowText(GetDlgItem(hWnd,IDC_ADDR),tn); + SetWindowText(GetDlgItem(hWnd,IDC_VALUE),tv); + SetWindowText(GetDlgItem(hWnd,IDC_NAME),to); + + break; + } + } + + + break; + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + switch (wmId) + { + case IDCANCEL: + EndDialog(hWnd,1); + break; + + case IDOK: + GetWindowText(GetDlgItem(hWnd,IDC_VALUE),tv,100); + value=_atoi64(tv); + AddPatch(1,Source+1,results[Selected].address,Size+1,value); + + EndDialog(hWnd,1); + break; + + + default: + return FALSE; + } + break; + default: + return FALSE; + } + return TRUE; +} + +void AddCheat(HINSTANCE hInstance, HWND hParent) +{ + INT_PTR retret=DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_ADD),hParent,(DLGPROC)AddCheatProc,(LPARAM)hParent); +} + +void AddResults(HWND hWnd) +{ + int i,mresults; + u64 sizemask=(1<<(1<32768) { + mresults=32768; + } + + ListView_DeleteAllItems(GetDlgItem(hWnd,IDC_RESULTS)); + + for(i=0;i32768) { + sprintf(mtext,"%d matches found (32768 shown).",results.size()); + SetWindowText(GetDlgItem(hWnd,IDC_MATCHES),mtext); + } + else { + sprintf(mtext,"%d matches found.",results.size()); + SetWindowText(GetDlgItem(hWnd,IDC_MATCHES),mtext); + } + +} + +BOOL CALLBACK FinderProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) +{ + int wmId,wmEvent; + LRESULT lStyle; + + switch(uMsg) + { + + case WM_PAINT: + INIT_CHECK(IDC_UNSIGNED,Unsigned); + return FALSE; + + case WM_INITDIALOG: + mptr[0]=psM; + mptr[1]=psxM; + + hWndFinder=hWnd; + + ENABLE_CONTROL(IDC_VALUE,false); + + GROUP_INIT(IDC_EE,Source); + GROUP_SELECT(IDC_EE); + GROUP_SELECT(IDC_IOP); + + GROUP_INIT(IDC_OLD,CompareTo); + GROUP_SELECT(IDC_OLD); + GROUP_SELECT(IDC_SET); + ENABLE_CONTROL(IDC_VALUE,(CompareTo!=0)); + + GROUP_INIT(IDC_EQ,Compare); + GROUP_SELECT(IDC_EQ); + GROUP_SELECT(IDC_GT); + GROUP_SELECT(IDC_LT); + GROUP_SELECT(IDC_GE); + GROUP_SELECT(IDC_LE); + GROUP_SELECT(IDC_NE); + + GROUP_INIT(IDC_8B,Size); + GROUP_SELECT(IDC_8B); + GROUP_SELECT(IDC_16B); + GROUP_SELECT(IDC_32B); + GROUP_SELECT(IDC_64B); + + INIT_CHECK(IDC_UNSIGNED,Unsigned); + + //Listview Init + lStyle = SendMessage(GetDlgItem(hWnd,IDC_RESULTS), LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); + SendMessage(GetDlgItem(hWnd,IDC_RESULTS), LVM_SETEXTENDEDLISTVIEWSTYLE, 0, lStyle | LVS_EX_FULLROWSELECT); + + ListView_InsertColumn(GetDlgItem(hWnd,IDC_RESULTS),0,&cols[0]); + ListView_InsertColumn(GetDlgItem(hWnd,IDC_RESULTS),1,&cols[1]); + ListView_InsertColumn(GetDlgItem(hWnd,IDC_RESULTS),2,&cols[2]); + + if(FirstShow) + { + SearchReset(); + SetWindowText(GetDlgItem(hWnd,IDC_MATCHES),"ready to search."); + FirstShow=false; + } + else { + AddResults(hWnd); + } + + break; + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + switch (wmId) + { + case IDCANCEL: + EndDialog(hWnd,1); + break; + + case IDC_ADD: + AddCheat(pInstance,hWnd); + break; + + case IDC_RESET: + ENABLE_CONTROL(IDC_EE, true); + ENABLE_CONTROL(IDC_IOP, true); + ENABLE_CONTROL(IDC_STATUS, true); + ENABLE_CONTROL(IDC_UNSIGNED,true); + ENABLE_CONTROL(IDC_8B, true); + ENABLE_CONTROL(IDC_16B, true); + ENABLE_CONTROL(IDC_32B, true); + ENABLE_CONTROL(IDC_64B, true); + SetWindowText(GetDlgItem(hWnd,IDC_MATCHES),"ready to search."); + SearchReset(); + ListView_DeleteAllItems(GetDlgItem(hWnd,IDC_RESULTS)); + break; + + case IDC_SEARCH: + GetWindowText(GetDlgItem(hWnd,IDC_VALUE),mtext,100); + CompareValue=atoi(mtext); + ENABLE_CONTROL(IDC_SEARCH, false); + ENABLE_CONTROL(IDC_RESET, false); + ENABLE_CONTROL(IDC_ADD, false); + ENABLE_CONTROL(IDCANCEL, false); + if(FirstSearch) { + ENABLE_CONTROL(IDC_EE, false); + ENABLE_CONTROL(IDC_IOP, false); + ENABLE_CONTROL(IDC_STATUS, false); + ENABLE_CONTROL(IDC_UNSIGNED,false); + ENABLE_CONTROL(IDC_8B, false); + ENABLE_CONTROL(IDC_16B, false); + ENABLE_CONTROL(IDC_32B, false); + ENABLE_CONTROL(IDC_64B, false); + SearchFirst(); + } + else SearchMore(); + + AddResults(hWnd); + + memcpy(olds,mptr[Source],msize[Source]); + + ENABLE_CONTROL(IDC_SEARCH, true); + ENABLE_CONTROL(IDC_RESET, true); + ENABLE_CONTROL(IDC_ADD, true); + ENABLE_CONTROL(IDCANCEL, true); + + break; + + HANDLE_CHECK(IDC_UNSIGNED,Unsigned); + + HANDLE_GROUP_ITEM(IDC_EE); + HANDLE_GROUP_ITEM(IDC_IOP); + BEGIN_GROUP_HANDLER(IDC_EE,Source); + GROUP_SELECT(IDC_EE); + GROUP_SELECT(IDC_IOP); + break; + + HANDLE_GROUP_ITEM(IDC_OLD); + HANDLE_GROUP_ITEM(IDC_SET); + BEGIN_GROUP_HANDLER(IDC_OLD,CompareTo); + GROUP_SELECT(IDC_OLD); + GROUP_SELECT(IDC_SET); + ENABLE_CONTROL(IDC_VALUE,(CompareTo!=0)); + break; + + HANDLE_GROUP_ITEM(IDC_EQ); + HANDLE_GROUP_ITEM(IDC_GT); + HANDLE_GROUP_ITEM(IDC_LT); + HANDLE_GROUP_ITEM(IDC_GE); + HANDLE_GROUP_ITEM(IDC_LE); + HANDLE_GROUP_ITEM(IDC_NE); + BEGIN_GROUP_HANDLER(IDC_EQ,Compare); + GROUP_SELECT(IDC_EQ); + GROUP_SELECT(IDC_GT); + GROUP_SELECT(IDC_LT); + GROUP_SELECT(IDC_GE); + GROUP_SELECT(IDC_LE); + GROUP_SELECT(IDC_NE); + break; + + HANDLE_GROUP_ITEM(IDC_8B); + HANDLE_GROUP_ITEM(IDC_16B); + HANDLE_GROUP_ITEM(IDC_32B); + HANDLE_GROUP_ITEM(IDC_64B); + BEGIN_GROUP_HANDLER(IDC_8B,Size); + GROUP_SELECT(IDC_8B); + GROUP_SELECT(IDC_16B); + GROUP_SELECT(IDC_32B); + GROUP_SELECT(IDC_64B); + break; + + default: + return FALSE; + } + break; + default: + return FALSE; + } + return TRUE; +} + +void ShowFinder(HINSTANCE hInstance, HWND hParent) +{ + INT_PTR ret=DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_FINDER),hParent,(DLGPROC)FinderProc,1); +} diff --git a/pcsx2/windows/cheats/cheats.h b/pcsx2/windows/cheats/cheats.h new file mode 100644 index 0000000000..edc5a9641e --- /dev/null +++ b/pcsx2/windows/cheats/cheats.h @@ -0,0 +1,39 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +#ifndef CHEATS_H_INCLUDED +#define CHEATS_H_INCLUDED + +#ifndef __cplusplus +//typedef enum ebool +//{ +// false, +// true +//} bool; +#define bool unsigned __int8 +#define false 0 +#define true 1 +#endif + +extern HINSTANCE pInstance; +extern bool FirstShow; + +void AddCheat(HINSTANCE hInstance, HWND hParent); +void ShowFinder(HINSTANCE hInstance, HWND hParent); +void ShowCheats(HINSTANCE hInstance, HWND hParent); + +#endif//CHEATS_H_INCLUDED diff --git a/pcsx2/windows/ini.cpp b/pcsx2/windows/ini.cpp new file mode 100644 index 0000000000..6550996f8d --- /dev/null +++ b/pcsx2/windows/ini.cpp @@ -0,0 +1,346 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2003 Pcsx2 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 "PrecompiledHeader.h" +#include "win32.h" + +#include "Common.h" +#include "Paths.h" + +static const u32 IniVersion = 100; + +const char* g_CustomConfigFile; +char g_WorkingFolder[g_MaxPath]; // Working folder at application startup + +// Returns TRUE if the user has invoked the -cfg command line option. +static bool hasCustomConfig() +{ + return (g_CustomConfigFile != NULL) && (g_CustomConfigFile[0] != 0); +} + +// Returns the FULL (absolute) path and filename of the configuration file. +static void GetConfigFilename( string& dest ) +{ + if( hasCustomConfig() ) + { + // Load a user-specified configuration. + // If the configuration isn't found, fail outright (see below) + + Path::Combine( dest, g_WorkingFolder, g_CustomConfigFile ); + } + else + { + // use the ini relative to the application's working directory. + // Our current working directory can change, so we use the one we detected + // at startup: + + Path::Combine( dest, g_WorkingFolder, CONFIG_DIR "\\pcsx2pg.ini" ); + } +} + +class IniFile +{ +protected: + string m_filename; + string m_section; + +public: + virtual ~IniFile() {} + IniFile() : m_filename(), m_section("Misc") + { + GetConfigFilename( m_filename ); + } + + void SetCurrentSection( const string& newsection ) + { + m_section = newsection; + } + + virtual void Entry( const string& var, string& value, const string& defvalue=string() )=0; + virtual void Entry( const string& var, char (&value)[g_MaxPath], const string& defvalue=string() )=0; + virtual void Entry( const string& var, int& value, const int defvalue=0 )=0; + virtual void Entry( const string& var, uint& value, const uint defvalue=0 )=0; + virtual void Entry( const string& var, bool& value, const bool defvalue=0 )=0; + virtual void EnumEntry( const string& var, int& value, const char* const* enumArray, const int defvalue=0 )=0; + + void DoConfig( PcsxConfig& Conf ) + { + SetCurrentSection( "Misc" ); + + Entry( "Patching", Conf.Patch, false ); + Entry( "GameFixes", Conf.GameFixes); +#ifdef PCSX2_DEVBUILD + Entry( "DevLogFlags", varLog ); +#endif + + //interface + SetCurrentSection( "Interface" ); + Entry( "Bios", Conf.Bios ); + Entry( "Language", Conf.Lang ); + Entry( "PluginsDir", Conf.PluginsDir, DEFAULT_PLUGINS_DIR ); + Entry( "BiosDir", Conf.BiosDir, DEFAULT_BIOS_DIR ); + Entry( "CloseGsOnEscape", Conf.closeGSonEsc, true ); + + SetCurrentSection( "Console" ); + Entry( "ConsoleWindow", Conf.PsxOut, true ); + Entry( "Profiler", Conf.Profiler, false ); + Entry( "CdvdVerbose", Conf.cdvdPrint, false ); + + Entry( "ThreadPriority", Conf.ThPriority, THREAD_PRIORITY_NORMAL ); + Entry( "Memorycard1", Conf.Mcd1, MEMCARDS_DIR "\\" DEFAULT_MEMCARD1 ); + Entry( "Memorycard2", Conf.Mcd2, MEMCARDS_DIR "\\" DEFAULT_MEMCARD2 ); + + SetCurrentSection( "Framelimiter" ); + Entry( "CustomFps", Conf.CustomFps ); + Entry( "FrameskipMode", Conf.CustomFrameSkip ); + Entry( "ConsecutiveFramesToRender", Conf.CustomConsecutiveFrames ); + Entry( "ConsecutiveFramesToSkip", Conf.CustomConsecutiveSkip ); + + // Plugins are saved from the winConfig struct. + // It contains the user config settings and not the + // runtime cmdline overrides. + + SetCurrentSection( "Plugins" ); + + Entry( "GS", Conf.GS ); + Entry( "SPU2", Conf.SPU2 ); + Entry( "CDVD", Conf.CDVD ); + Entry( "PAD1", Conf.PAD1 ); + Entry( "PAD2", Conf.PAD2 ); + Entry( "DEV9", Conf.DEV9 ); + Entry( "USB", Conf.USB ); + Entry( "FW", Conf.FW ); + + //cpu + SetCurrentSection( "Cpu" ); + Entry( "Options", Conf.Options, PCSX2_EEREC|PCSX2_VU0REC|PCSX2_VU1REC ); + Entry( "sseMXCSR", Conf.sseMXCSR, DEFAULT_sseMXCSR ); + Entry( "sseVUMXCSR", Conf.sseVUMXCSR, DEFAULT_sseVUMXCSR ); + Entry( "eeOptions", Conf.eeOptions, DEFAULT_eeOptions ); + Entry( "vuOptions", Conf.vuOptions, DEFAULT_vuOptions ); + Entry( "SpeedHacks", Conf.Hacks ); + } +}; + +class IniFileLoader : public IniFile +{ +protected: + MemoryAlloc m_workspace; + +public: + virtual ~IniFileLoader() {} + IniFileLoader() : IniFile(), + m_workspace( 4096, "IniFileLoader Workspace" ) + { + } + + void Entry( const string& var, string& value, const string& defvalue=string() ) + { + int retval = GetPrivateProfileString( + m_section.c_str(), var.c_str(), defvalue.c_str(), m_workspace.GetPtr(), m_workspace.GetLength(), m_filename.c_str() + ); + + if( retval >= m_workspace.GetLength() - 2 ) + Console::Notice( "Loadini Warning > Possible truncated value on key '%hs'", params &var ); + value = m_workspace.GetPtr(); + } + + void Entry( const string& var, char (&value)[g_MaxPath], const string& defvalue=string() ) + { + int retval = GetPrivateProfileString( + m_section.c_str(), var.c_str(), defvalue.c_str(), value, sizeof( value ), m_filename.c_str() + ); + + if( retval >= sizeof(value) - 2 ) + Console::Notice( "Loadini Warning > Possible truncated value on key '%hs'", params &var ); + } + + void Entry( const string& var, int& value, const int defvalue=0 ) + { + string retval; + Entry( var, retval, to_string( defvalue ) ); + value = atoi( retval.c_str() ); + } + + void Entry( const string& var, uint& value, const uint defvalue=0 ) + { + string retval; + Entry( var, retval, to_string( defvalue ) ); + value = atoi( retval.c_str() ); + } + + void Entry( const string& var, bool& value, const bool defvalue=false ) + { + string retval; + Entry( var, retval, defvalue ? "enabled" : "disabled" ); + value = (retval == "enabled"); + } + + void EnumEntry( const string& var, int& value, const char* const* enumArray, const int defvalue=0 ) + { + string retval; + Entry( var, retval, enumArray[defvalue] ); + + int i=0; + while( enumArray[i] != NULL && ( retval != enumArray[i] ) ) i++; + + if( enumArray[i] == NULL ) + { + Console::Notice( "Loadini Warning > Unrecognized value '%hs' on key '%hs'\n\tUsing the default setting of '%s'.", + params &retval, &var, enumArray[defvalue] ); + value = defvalue; + } + else + value = i; + } + +}; + +class IniFileSaver : public IniFile +{ +public: + virtual ~IniFileSaver() {} + IniFileSaver() : IniFile() + { + char versionStr[20]; + _itoa( IniVersion, versionStr, 10 ); + WritePrivateProfileString( "Misc", "IniVersion", versionStr, m_filename.c_str() ); + } + + void Entry( const string& var, const string& value, const string& defvalue=string() ) + { + WritePrivateProfileString( m_section.c_str(), var.c_str(), value.c_str(), m_filename.c_str() ); + } + + void Entry( const string& var, string& value, const string& defvalue=string() ) + { + WritePrivateProfileString( m_section.c_str(), var.c_str(), value.c_str(), m_filename.c_str() ); + } + + void Entry( const string& var, char (&value)[g_MaxPath], const string& defvalue=string() ) + { + WritePrivateProfileString( m_section.c_str(), var.c_str(), value, m_filename.c_str() ); + } + + void Entry( const string& var, int& value, const int defvalue=0 ) + { + Entry( var, to_string( value ) ); + } + + void Entry( const string& var, uint& value, const uint defvalue=0 ) + { + Entry( var, to_string( value ) ); + } + + void Entry( const string& var, bool& value, const bool defvalue=false ) + { + Entry( var, value ? "enabled" : "disabled" ); + } + + void EnumEntry( const string& var, int& value, const char* const* enumArray, const int defvalue=0 ) + { + Entry( var, enumArray[value] ); + } +}; + +bool LoadConfig() +{ + string szIniFile; + bool status = true; + + GetConfigFilename( szIniFile ); + + if( !Path::Exists( szIniFile ) ) + { + if( hasCustomConfig() ) + { + // using custom config, so fail outright: + throw Exception::FileNotFound( + "User-specified configuration file not found:\n\t%s\n\nPCSX2 will now exit." + ); + } + + // standard mode operation. Create the directory. + CreateDirectory( "inis", NULL ); + status = false; // inform caller that we're not configured. + } + else + { + // sanity check to make sure the user doesn't have some kind of + // crazy ass setup... why not! + + if( Path::isDirectory( szIniFile ) ) + throw Exception::Stream( + "Cannot open or create the Pcsx2 ini file because a directory of\n" + "the same name already exists! Please delete it or reinstall Pcsx2\n" + "fresh and try again." + ); + + // Ini Version check! ----> + // If the user's ini is old, give them a friendly warning that says they should + // probably delete it. + + char versionStr[20]; + u32 version; + + GetPrivateProfileString( "Misc", "IniVersion", NULL, versionStr, 20, szIniFile.c_str() ); + version = atoi( versionStr ); + if( version < IniVersion ) + { + // Warn the user of a version mismatch. + Msgbox::Alert( + "Configuration versions do not match. Pcsx2 may be unstable.\n" + "If you experience problems, delete the pcsx2-pg.ini file from the ini dir." + ); + + // save the new version -- gets rid of the warning on subsequent startups + _itoa( IniVersion, versionStr, 10 ); + WritePrivateProfileString( "Misc", "IniVersion", versionStr, szIniFile.c_str() ); + } + } + + IniFileLoader().DoConfig( Config ); + +#ifdef ENABLE_NLS + { + string text; + extern int _nl_msg_cat_cntr; + ssprintf(text, "LANGUAGE=%s", Config.Lang); + gettext_putenv(text.c_str()); + } +#endif + + return status; +} + +void SaveConfig() +{ + PcsxConfig tmpConf = Config; + + strcpy( tmpConf.GS, winConfig.GS ); + strcpy( tmpConf.SPU2, winConfig.SPU2 ); + strcpy( tmpConf.CDVD, winConfig.CDVD ); + strcpy( tmpConf.PAD1, winConfig.PAD1 ); + strcpy( tmpConf.PAD2, winConfig.PAD2 ); + strcpy( tmpConf.DEV9, winConfig.DEV9 ); + strcpy( tmpConf.USB, winConfig.USB ); + strcpy( tmpConf.FW, winConfig.FW ); + + IniFileSaver().DoConfig( tmpConf ); +} + diff --git a/pcsx2/windows/libs/gnu_gettext.lib b/pcsx2/windows/libs/gnu_gettext.lib new file mode 100644 index 0000000000000000000000000000000000000000..8b3885a56b2f9d31a261c89fbd0e31d15fc51c14 GIT binary patch literal 2916 zcmcIm%}*0S6#s1t6sieCjfr2dB!s{PNNJ%MHH)HwP(D)1#miW_1)4xhuow>{UOD)e zcr=_1C;tEshIsPefwS?z4d2_H*-nSjr35CKop~QSZ{GX8H#7au^1Fr27x7WMzD=dm zQxlV^#6&&Y9+UL=cv8;Z#Q}5!(LtcY0b=_==ds_rW&y7s(nvktEBgVB+)ud+F}JY1 zl+B5m*DJ5GbIZA9XHiX1+<#N( zZ&?ITv(`hx2nds~MvAP4(uZe8SYaqbL%!h8^YZlDhcqK+!%qAzB>!0 z!fxe#WxZ+-Wryv|+~T|^H9xnB!GH1!J}xmY;O!pIFa@Vj+1{Z|pY*li>`d8IX!0&2 zP`*2Wx9?5G@w{YPQN>BJ4lxkuq-q@I5Aa7eULANYDIOwtOW!M3^tDz-B9#j6jf@rX zD&w2CN8Tx{Mw#P!qS{BxoN>kF%=w}SIdbOQTt-|5<0rI}3vaKuku{;GN+La}#m+xf zevt(q@y4y7bc;@VHvo2F&Uu}vjpXl;Ja zaLr~cKDBMK#D|gjZZNQeL5E_QR8SiB9+54Mr>I?I#?olnw`A~$tM~08+2M0W1UIE= z%Amu&QpIg}^?J3lK4TO7^Zk!*F$rj%vLVHS>w*CX+ CYG_^n literal 0 HcmV?d00001 diff --git a/pcsx2/windows/libs/libintlmsc.h b/pcsx2/windows/libs/libintlmsc.h new file mode 100644 index 0000000000..25a96a998d --- /dev/null +++ b/pcsx2/windows/libs/libintlmsc.h @@ -0,0 +1,116 @@ +/* This file is part of a Windows32 DLL Interface to: + GNU gettext - internationalization aids + Copyright (C) 1996, 1998 Free Software Foundation, Inc. + + This file was written by Franco Bez + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2, 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser 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. */ + +/* REPLACEMENT FOR ORIGINAL LIBINTL.H for use with Windows32 */ + +#if !defined(__LIBINTL_H_INCLUDED) +#define __LIBINTL_H_INCLUDED + +#if defined(__cplusplus) +extern "C" { +#endif + +/* See if we allready know what we want static or dll linkage or none at all*/ +#if defined DONT_USE_GETTEXT || ( defined USE_SAFE_GETTEXT_DLL && defined USE_GETTEXT_STATIC ) || ( defined USE_GETTEXT_DLL && defined USE_SAFE_GETTEXT_DLL ) || ( defined USE_GETTEXT_DLL && defined USE_GETTEXT_STATIC ) +/* TWO IS HARDLY POSSIBLE */ +#undef USE_GETTEXT_DLL +#undef USE_GETTEXT_STATIC +#undef USE_SAFE_GETTEXT_DLL +#endif /* MORE THAN ONE - OR NONE AT ALL */ + +#if !defined USE_GETTEXT_DLL && !defined USE_SAFE_GETTEXT_DLL && !defined USE_GETTEXT_STATIC && !defined DONT_USE_GETTEXT +/* not explicitly defined so try to guess it - + if GNUC is used - we use static linkage by default + because at the moment this is the only plattform + for which a static lib is available + else we use the DLL built with GNUC */ +# if defined __GNUC__ +# define USE_GETTEXT_STATIC +# else +# define USE_GETTEXT_DLL +# endif /* __GNUC__ */ +#endif /* NONE */ + +/* NOW ONLY ONE OF + DONT_USE_GETTEXT , USE_GETTEXT_DLL , USE_SAFE_GETTEXT_DLL , USE_GETTEXT_STATIC + IS DEFINED */ + +#if defined USE_GETTEXT_DLL +/* exported functions in DLL gnu_gettext.dll + you should link with import library + -lgnu_gettext (for mingw32) OR gnu_gettext.lib (MSVC) */ +__declspec(dllimport) char *gettext(const char *__msgid); +__declspec(dllimport) char *dgettext(const char *__domainname,const char *__msgid); +__declspec(dllimport) char *dcgettext(const char *__domainname,const char *__msgid, int __category); +__declspec(dllimport) char *textdomain(const char *__domainname); +__declspec(dllimport) char *bindtextdomain(const char *__domainname,const char *__dirname); +/* calling _putenv from within the DLL */ +__declspec(dllexport) int gettext_putenv(const char *envstring); +#endif /* DLL */ + +#if defined USE_SAFE_GETTEXT_DLL +/* Uses DLL gnu_gettext.dll ONLY if present, otherwise NO translation will take place + you should link with "safe_gettext_dll.o -lstdc++" see README for safe_gettext_dll for Details */ +/* The safe gettext functions */ +extern char *gettext(const char *szMsgId); +extern char *dgettext(const char *szDomain,const char *szMsgId); +extern char *dcgettext(const char *szDomain,const char *szMsgId,int iCategory); +extern char *textdomain(const char *szDomain); +extern char *bindtextdomain(const char *szDomain,const char *szDirectory); +/* calling _putenv from within the DLL */ +extern int gettext_putenv(const char *envstring); +#endif /* SAFE DLL */ + +#if defined USE_GETTEXT_STATIC +/* exported functions in static library libintl.a + and supporting macros + you should link with -lintl (mingw32) */ +extern char *gettext__(const char *__msgid); +extern char *dgettext__(const char *__domainname,const char *__msgid); +extern char *dcgettext__(const char *__domainname,const char *__msgid, int __category); +extern char *textdomain__(const char *__domainname); +extern char *bindtextdomain__(const char *__domainname,const char *__dirname); +#define gettext(szMsgId) gettext__(szMsgId) +#define dgettext(szDomain,szMsgId) dgettext__(szDomain,szMsgId) +#define dcgettext(szDomain,szMsgId,iCategory) dcgettext__(szDomain,szMsgId,iCategory) +#define textdomain(szDomain) textdomain__(szDomain) +#define bindtextdomain(szDomain,szDirectory) bindtextdomain__(szDomain,szDirectory) +// dummy - for static linkage - calling _putenv from within the DLL +#define gettext_putenv(a) _putenv(a) +#endif /* STATIC */ + +#if defined DONT_USE_GETTEXT +/* DON'T USE GETTEXT AT ALL + MAKROS TO MAKE CODE COMPILE WELL, BUT GETTEXT WILL NOT BE USESD +*/ +# define gettext(Msgid) (Msgid) +# define dgettext(Domainname, Msgid) (Msgid) +# define dcgettext(Domainname, Msgid, Category) (Msgid) +# define textdomain(Domainname) ((char *) Domainname) +# define bindtextdomain(Domainname, Dirname) ((char *) Dirname) +// dummy - for static linkage - calling _putenv from within the DLL +# define gettext_putenv(a) _putenv(a) +#endif /* DON'T USE AT ALL */ + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif /*!defined(__LIBINTL_H_INCLUDED)*/ diff --git a/pcsx2/windows/memzero.h b/pcsx2/windows/memzero.h new file mode 100644 index 0000000000..c204fbc32a --- /dev/null +++ b/pcsx2/windows/memzero.h @@ -0,0 +1,598 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef _WIN_MEMZERO_H_ +#define _WIN_MEMZERO_H_ + +// These functions are meant for memset operations of constant length only. +// For dynamic length clears, use the C-compiler provided memset instead. + +// MemZero Code Strategies: +// I use a trick to help the MSVC compiler optimize it's asm code better. The compiler +// won't optimize local variables very well because it insists in storing them on the +// stack and then loading them out of the stack when I use them from inline ASM, and +// it won't allow me to use template parameters in inline asm code either. But I can +// assign the template parameters to enums, and then use the enums from asm code. +// Yeah, silly, but it works. :D (air) + +// All methods defined in this header use template in combination with the aforementioned +// enumerations to generate very efficient and compact inlined code. These optimized +// memsets work on the theory that most uses of memset involve static arrays and +// structures, which are constant in size, thus allowing us to generate optimal compile- +// time code for each use of the function. + +// Notes on XMM0's "storage" area (_xmm_backup): +// Unfortunately there's no way to guarantee alignment for this variable. If I use the +// __declspec(aligned(16)) decorator, MSVC fails to inline the function since stack +// alignment requires prep work. And for the same reason it's not possible to check the +// alignment of the stack at compile time, so I'm forced to use movups to store and +// retrieve xmm0. + + +// This is an implementation of the memzero_ptr fast memset routine (for zero-clears only). +template< size_t bytes > +static __forceinline void memzero_ptr( void *dest ) +{ + if( bytes == 0 ) return; + + // This function only works on 32-bit alignments. For anything else we just fall back + // on the compiler-provided implementation of memset... + + if( (bytes & 0x3) != 0 ) + { + memset( dest, 0, bytes ); + return; + } + + enum + { + remainder = bytes & 127, + bytes128 = bytes / 128 + }; + + // Initial check -- if the length is not a multiple of 16 then fall back on + // using rep movsd methods. Handling these unaligned clears in a more efficient + // manner isn't necessary in pcsx2 (meaning they aren't used in speed-critical + // scenarios). + + if( (bytes & 0xf) == 0 ) + { + u64 _xmm_backup[2]; + + if( ((uptr)dest & 0xf) != 0 ) + { + // UNALIGNED COPY MODE. + // For unaligned copies we have a threshold of at least 128 vectors. Anything + // less and it's probably better off just falling back on the rep movsd. + if( bytes128 > 128 ) + { + __asm + { + movups _xmm_backup,xmm0; + mov ecx,dest + pxor xmm0,xmm0 + mov eax,bytes128 + + align 16 + + _loop_6: + movups [ecx],xmm0; + movups [ecx+0x10],xmm0; + movups [ecx+0x20],xmm0; + movups [ecx+0x30],xmm0; + movups [ecx+0x40],xmm0; + movups [ecx+0x50],xmm0; + movups [ecx+0x60],xmm0; + movups [ecx+0x70],xmm0; + sub ecx,-128 + dec eax; + jnz _loop_6; + } + if( remainder != 0 ) + { + // Copy the remainder in reverse (using the decrementing eax as our indexer) + __asm + { + mov eax, remainder + + _loop_5: + movups [ecx+eax],xmm0; + sub eax,16; + jnz _loop_5; + } + } + __asm + { + movups xmm0,[_xmm_backup]; + } + return; + } + } + else if( bytes128 > 48 ) + { + // ALIGNED COPY MODE + // Data is aligned and the size of data is large enough to merit a nice + // fancy chunk of unrolled goodness: + + __asm + { + movups _xmm_backup,xmm0; + mov ecx,dest + pxor xmm0,xmm0 + mov eax,bytes128 + + align 16 + + _loop_8: + movaps [ecx],xmm0; + movaps [ecx+0x10],xmm0; + movaps [ecx+0x20],xmm0; + movaps [ecx+0x30],xmm0; + movaps [ecx+0x40],xmm0; + movaps [ecx+0x50],xmm0; + movaps [ecx+0x60],xmm0; + movaps [ecx+0x70],xmm0; + sub ecx,-128 + dec eax; + jnz _loop_8; + } + if( remainder != 0 ) + { + // Copy the remainder in reverse (using the decrementing eax as our indexer) + __asm + { + mov eax, remainder + + _loop_10: + movaps [ecx+eax],xmm0; + sub eax,16; + jnz _loop_10; + } + } + __asm + { + movups xmm0,[_xmm_backup]; + } + return; + } + } + + // This function only works on 32-bit alignments. + jASSUME( (bytes & 0x3) == 0 ); + jASSUME( ((uptr)dest & 0x3) == 0 ); + + enum + { + remdat = bytes>>2 + }; + + // This case statement handles 5 special-case sizes (small blocks) + // in addition to the generic large block that uses rep stosd. + + switch( remdat ) + { + case 1: + *(u32*)dest = 0; + return; + + case 2: + *(u64*)dest = 0; + return; + + case 3: + __asm + { + cld; + mov edi, dest + xor eax, eax + stosd + stosd + stosd + } + return; + + case 4: + __asm + { + cld; + mov edi, dest + xor eax, eax + stosd + stosd + stosd + stosd + } + return; + + case 5: + __asm + { + cld; + mov edi, dest + xor eax, eax + stosd + stosd + stosd + stosd + stosd + } + return; + + default: + __asm + { + cld; + mov ecx, remdat + mov edi, dest + xor eax, eax + rep stosd + } + return; + } +} + +// An optimized memset for 8 bit destination data. +template< u8 data, size_t bytes > +static __forceinline void memset_8( void *dest ) +{ + if( bytes == 0 ) return; + + if( (bytes & 0x3) != 0 ) + { + // unaligned data length. No point in doing an optimized inline version (too complicated!) + // So fall back on the compiler implementation: + + memset( dest, data, bytes ); + return; + } + + //u64 _xmm_backup[2]; + + /*static const size_t remainder = bytes & 127; + static const size_t bytes128 = bytes / 128; + if( bytes128 > 32 ) + { + // This function only works on 128-bit alignments. + jASSUME( (bytes & 0xf) == 0 ); + jASSUME( ((uptr)dest & 0xf) == 0 ); + + __asm + { + movups _xmm_backup,xmm0; + mov eax,bytes128 + mov ecx,dest + movss xmm0,data + + align 16 + + _loop_8: + movaps [ecx],xmm0; + movaps [ecx+0x10],xmm0; + movaps [ecx+0x20],xmm0; + movaps [ecx+0x30],xmm0; + movaps [ecx+0x40],xmm0; + movaps [ecx+0x50],xmm0; + movaps [ecx+0x60],xmm0; + movaps [ecx+0x70],xmm0; + sub ecx,-128 + dec eax; + jnz _loop_8; + } + if( remainder != 0 ) + { + // Copy the remainder in reverse (using the decrementing eax as our indexer) + __asm + { + mov eax, remainder + + _loop_10: + movaps [ecx+eax],xmm0; + sub eax,16; + jnz _loop_10; + } + } + __asm + { + movups xmm0,[_xmm_backup]; + } + }*/ + + // This function only works on 32-bit alignments of data copied. + jASSUME( (bytes & 0x3) == 0 ); + + enum + { + remdat = bytes>>2, + data32 = data + (data<<8) + (data<<16) + (data<<24) + }; + + // macro to execute the x86/32 "stosd" copies. + switch( remdat ) + { + case 1: + *(u32*)dest = data32; + return; + + case 2: + ((u32*)dest)[0] = data32; + ((u32*)dest)[1] = data32; + return; + + case 3: + __asm + { + cld; + mov edi, dest; + mov eax, data32; + stosd; + stosd; + stosd; + } + return; + + case 4: + __asm + { + cld; + mov edi, dest; + mov eax, data32; + stosd; + stosd; + stosd; + stosd; + } + return; + + case 5: + __asm + { + cld; + mov edi, dest; + mov eax, data32; + stosd; + stosd; + stosd; + stosd; + stosd; + } + return; + + default: + __asm + { + cld; + mov ecx, remdat; + mov edi, dest; + mov eax, data32; + rep stosd; + } + return; + } +} + +template< u16 data, size_t bytes > +static __forceinline void memset_16( void *dest ) +{ + if( bytes == 0 ) return; + + if( (bytes & 0x1) != 0 ) + throw Exception::LogicError( "Invalid parameter passed to memset_16 - data length is not a multiple of 16 or 32 bits." ); + + if( (bytes & 0x3) != 0 ) + { + // Unaligned data length. No point in doing an optimized inline version (too complicated with + // remainders and such). + + _memset16_unaligned( dest, data, bytes ); + return; + } + + //u64 _xmm_backup[2]; + + // This function only works on 32-bit alignments of data copied. + jASSUME( (bytes & 0x3) == 0 ); + + enum + { + remdat = bytes>>2, + data32 = data + (data<<16) + }; + + // macro to execute the x86/32 "stosd" copies. + switch( remdat ) + { + case 1: + *(u32*)dest = data32; + return; + + case 2: + ((u32*)dest)[0] = data32; + ((u32*)dest)[1] = data32; + return; + + case 3: + __asm + { + cld; + mov edi, dest; + mov eax, data32; + stosd; + stosd; + stosd; + } + return; + + case 4: + __asm + { + cld; + mov edi, dest; + mov eax, data32; + stosd; + stosd; + stosd; + stosd; + } + return; + + case 5: + __asm + { + cld; + mov edi, dest; + mov eax, data32; + stosd; + stosd; + stosd; + stosd; + stosd; + } + return; + + default: + __asm + { + cld; + mov ecx, remdat; + mov edi, dest; + mov eax, data32; + rep stosd; + } + return + } +} + +template< u32 data, size_t bytes > +static __forceinline void memset_32( void *dest ) +{ + if( bytes == 0 ) return; + + if( (bytes & 0x3) != 0 ) + throw Exception::LogicError( "Invalid parameter passed to memset_32 - data length is not a multiple of 32 bits." ); + + + //u64 _xmm_backup[2]; + + // This function only works on 32-bit alignments of data copied. + // If the data length is not a factor of 32 bits, the C++ optimizing compiler will + // probably just generate mysteriously broken code in Release builds. ;) + + jASSUME( (bytes & 0x3) == 0 ); + + enum + { + remdat = bytes>>2, + data32 = data + }; + + // macro to execute the x86/32 "stosd" copies. + switch( remdat ) + { + case 1: + *(u32*)dest = data32; + return; + + case 2: + ((u32*)dest)[0] = data32; + ((u32*)dest)[1] = data32; + return; + + case 3: + __asm + { + cld; + mov edi, dest; + mov eax, data32; + stosd; + stosd; + stosd; + } + return; + + case 4: + __asm + { + cld; + mov edi, dest; + mov eax, data32; + stosd; + stosd; + stosd; + stosd; + } + return; + + case 5: + __asm + { + cld; + mov edi, dest; + mov eax, data32; + stosd; + stosd; + stosd; + stosd; + stosd; + } + return; + + default: + __asm + { + cld; + mov ecx, remdat; + mov edi, dest; + mov eax, data32; + rep stosd; + } + return + } +} + +// This method can clear any object-like entity -- which is anything that is not a pointer. +// Structures, static arrays, etc. No need to include sizeof() crap, this does it automatically +// for you! +template< typename T > +static __forceinline void memzero_obj( T& object ) +{ + memzero_ptr( &object ); +} + +// This method clears an object with the given 8 bit value. +template< u8 data, typename T > +static __forceinline void memset8_obj( T& object ) +{ + memset_8( &object ); +} + +// This method clears an object with the given 16 bit value. +template< u16 data, typename T > +static __forceinline void memset16_obj( T& object ) +{ + memset_16( &object ); +} + +// This method clears an object with the given 32 bit value. +template< u32 data, typename T > +static __forceinline void memset32_obj( T& object ) +{ + memset_32( &object ); +} + +#endif + diff --git a/pcsx2/windows/pcsx2.rc b/pcsx2/windows/pcsx2.rc new file mode 100644 index 0000000000..77bb4a6f31 --- /dev/null +++ b/pcsx2/windows/pcsx2.rc @@ -0,0 +1,1718 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxresmw.h" ///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Romanian resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ROM) +#ifdef _WIN32 +LANGUAGE LANG_ROMANIAN, SUBLANG_DEFAULT +#pragma code_page(1250) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CMDLINE DIALOG 0, 0, 186, 82 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Program arguments" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,36,37,50,14 + PUSHBUTTON "Cancel",IDCANCEL,99,37,50,14 + CTEXT "Tip: If you don't know what to write\nleave it blank",IDC_TIP,7,56,172,19,WS_BORDER + EDITTEXT IDC_CMDLINE,7,17,172,14,ES_AUTOHSCROLL + LTEXT "Fill in the command line arguments for the opened program:",IDC_TEXT,7,7,174,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_CMDLINE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 75 + END +END +#endif // APSTUDIO_INVOKED + +#endif // Romanian resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// German (Germany) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) +#ifdef _WIN32 +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_GAMEFIXES DIALOGEX 0, 0, 278, 129 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Game Special Fixes" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,85,108,50,14 + PUSHBUTTON "Cancel",IDCANCEL,139,108,50,14 + CTEXT "Some games need special settings.\nConfigure them here.",IDC_STATIC,7,7,264,17 + GROUPBOX "PCSX2 Gamefixes",IDC_STATIC,7,30,264,92 + CONTROL "FPU Clamp Hack - Special fix for Tekken 5.",IDC_GAMEFIX3, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,46,249,10 + CONTROL "VU Branch Hack - Special fix for Magna Carta; Breaks Crash Bandicoot!",IDC_GAMEFIX4, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,74,252,10 + CONTROL "VU Add / Sub Hack - Special fix for Tri-Ace games!",IDC_GAMEFIX1, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,60,252,10 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_GAMEFIXES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 271 + VERTGUIDE, 12 + TOPMARGIN, 7 + BOTTOMMARGIN, 122 + END +END +#endif // APSTUDIO_INVOKED + +#endif // German (Germany) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CP0REGS DIALOG 0, 0, 267, 237 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "COP0 " +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_CP00,48,7,51,12,ES_READONLY + LTEXT "Index",-1,7,9,18,8 + EDITTEXT IDC_CP01,48,21,51,12,ES_READONLY + LTEXT "Random",-1,7,23,28,8 + EDITTEXT IDC_CP02,48,35,51,12,ES_READONLY + LTEXT "EntryLo0",-1,7,37,29,8 + EDITTEXT IDC_CP03,48,49,51,12,ES_READONLY + LTEXT "EntryLo1",-1,7,51,29,8 + EDITTEXT IDC_CP04,48,63,51,12,ES_READONLY + LTEXT "Context",-1,7,65,25,8 + EDITTEXT IDC_CP05,48,77,51,12,ES_READONLY + LTEXT "PageMask",-1,7,79,35,8 + EDITTEXT IDC_CP06,48,91,51,12,ES_READONLY + LTEXT "Wired",-1,7,93,20,8 + EDITTEXT IDC_CP07,48,105,51,12,ES_READONLY + EDITTEXT IDC_CP08,48,119,51,12,ES_READONLY + LTEXT "BadVAddr",-1,7,121,33,8 + EDITTEXT IDC_CP09,48,133,52,12,ES_READONLY + LTEXT "Count",-1,7,135,20,8 + EDITTEXT IDC_CP010,48,147,51,12,ES_READONLY + LTEXT "EntryHi",-1,7,149,24,8 + EDITTEXT IDC_CP011,48,161,52,12,ES_READONLY + LTEXT "Compare",-1,7,163,29,8 + EDITTEXT IDC_CP012,48,175,51,12,ES_READONLY + LTEXT "Status",-1,7,177,21,8 + EDITTEXT IDC_CP013,48,189,51,12,ES_READONLY + LTEXT "Cause",-1,7,191,21,8 + EDITTEXT IDC_CP014,48,203,51,12,ES_READONLY + LTEXT "EPC",-1,7,205,15,8 + EDITTEXT IDC_CP015,48,218,51,12,ES_READONLY + LTEXT "PRId",-1,7,219,17,8 + EDITTEXT IDC_CP016,191,7,51,12,ES_READONLY + LTEXT "Config",-1,145,9,21,8 + EDITTEXT IDC_CP017,191,21,51,12,ES_READONLY + EDITTEXT IDC_CP018,191,35,51,12,ES_READONLY + EDITTEXT IDC_CP019,191,49,51,12,ES_READONLY + EDITTEXT IDC_CP020,191,63,51,12,ES_READONLY + EDITTEXT IDC_CP021,191,77,51,12,ES_READONLY + EDITTEXT IDC_CP022,191,91,51,12,ES_READONLY + EDITTEXT IDC_CP023,191,105,51,12,ES_READONLY + EDITTEXT IDC_CP024,191,119,51,12,ES_READONLY + EDITTEXT IDC_CP025,191,133,51,12,ES_READONLY + EDITTEXT IDC_CP026,191,147,51,12,ES_READONLY + LTEXT "Debug",-1,147,122,22,8 + EDITTEXT IDC_CP027,191,161,51,12,ES_READONLY + LTEXT "Perf",-1,149,136,14,8 + EDITTEXT IDC_CP028,191,175,51,12,ES_READONLY + LTEXT "TagLo",-1,145,177,22,8 + EDITTEXT IDC_CP029,191,189,51,12,ES_READONLY + LTEXT "TagHi",-1,145,191,20,8 + EDITTEXT IDC_CP030,191,203,51,12,ES_READONLY + LTEXT "ErrorEPC",-1,145,205,30,8 + EDITTEXT IDC_CP031,191,218,51,12,ES_READONLY +END + +IDD_DEBUG DIALOGEX 0, 0, 341, 270 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Debugger" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LISTBOX IDC_DEBUG_DISASM,7,6,198,257,LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | WS_TABSTOP,WS_EX_TRANSPARENT + SCROLLBAR IDC_DEBUG_SCROLL,205,6,11,257,SBS_VERT + PUSHBUTTON "Go",IDC_DEBUG_GO,224,6,50,14 + PUSHBUTTON "S&tep (IOP)",IDC_DEBUG_STEP,224,183,50,14 + GROUPBOX "R5900",IDC_STATIC,221,39,114,63 + PUSHBUTTON "&Step Into",IDC_DEBUG_STEP_EE,224,50,50,14 + PUSHBUTTON "S&kip",IDC_DEBUG_SKIP,224,67,50,14 + PUSHBUTTON "Step to Cursor",IDC_DEBUG_STEP_TO_CURSOR,224,84,51,14 + PUSHBUTTON "Run &Over",IDC_DEBUG_STEP_OVER,277,50,52,14 + PUSHBUTTON "&Run to Cursor",IDC_DEBUG_RUN_TO_CURSOR,278,67,51,14 + PUSHBUTTON "Break",IDC_DEBUG_BREAK,278,84,51,14 + PUSHBUTTON "Bkpt on Exec",IDC_DEBUG_BP_EXEC,224,125,50,14 + PUSHBUTTON "Bkpt on Count",IDC_DEBUG_BP_COUNT,224,141,50,14 + PUSHBUTTON "Clear Bkpts",IDC_DEBUG_BP_CLEAR,224,158,50,14 + PUSHBUTTON "Un/Set Log",IDC_DEBUG_LOG,279,124,50,14 + PUSHBUTTON "Reset to PC",IDC_DEBUG_RESETTOPC,279,142,50,14 + PUSHBUTTON "Jump to ADDR",IDC_DEBUG_JUMP,279,158,50,14 + PUSHBUTTON "Dump code",IDC_DEBUG_DUMP,223,211,50,14 + PUSHBUTTON "Dump memory",IDC_DEBUG_MEMORY,223,228,50,14 + PUSHBUTTON "Cpu ops",IDC_CPUOP,223,244,50,14 + PUSHBUTTON "Close",IDC_DEBUG_CLOSE,280,244,50,14 +END + +IDD_DUMP DIALOGEX 0, 0, 386, 96 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Dump code" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + EDITTEXT IDC_DUMP_START,73,14,82,13 + EDITTEXT IDC_DUMP_END,73,29,82,13 + EDITTEXT IDC_DUMP_FNAME,73,50,82,13,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,87,74,50,14 + PUSHBUTTON "Cancel",IDCANCEL,227,73,50,14 + LTEXT "Start PC Address:",IDC_STATIC,19,17,52,8 + LTEXT "End PC Address:",IDC_STATIC,19,32,50,8 + LTEXT "Filename:",IDC_STATIC,19,53,31,8 + GROUPBOX "EE",IDC_STATIC,14,6,167,64 + EDITTEXT IDC_DUMP_STARTIOP,255,15,82,13 + EDITTEXT IDC_DUMP_ENDIOP,255,30,82,13 + EDITTEXT IDC_DUMP_FNAMEIOP,255,50,82,13,ES_AUTOHSCROLL + LTEXT "Start PC Address:",IDC_STATIC,201,18,52,8 + LTEXT "End PC Address:",IDC_STATIC,201,33,50,8 + LTEXT "Filename:",IDC_STATIC,201,54,31,8 + GROUPBOX "IOP",IDC_STATIC,195,7,167,63 +END + +IDD_GPREGS DIALOG 0, 0, 391, 279 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "R5900 Main registers" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_GPR0,22,7,166,12,ES_READONLY + LTEXT "R0",-1,7,9,10,8 + EDITTEXT IDC_GPR1,22,21,166,12,ES_READONLY + LTEXT "AT",-1,7,23,10,8 + EDITTEXT IDC_GPR2,22,35,166,12,ES_READONLY + LTEXT "V0",-1,7,37,10,8 + EDITTEXT IDC_GPR3,22,49,166,12,ES_READONLY + LTEXT "V1",-1,7,51,10,8 + EDITTEXT IDC_GPR4,22,63,166,12,ES_READONLY + LTEXT "A0",-1,7,65,10,8 + EDITTEXT IDC_GPR5,22,77,166,12,ES_READONLY + LTEXT "A1",-1,7,79,10,8 + EDITTEXT IDC_GPR6,22,91,166,12,ES_READONLY + LTEXT "A2",-1,7,93,10,8 + EDITTEXT IDC_GPR7,22,105,166,12,ES_READONLY + LTEXT "A3",-1,7,107,10,8 + EDITTEXT IDC_GPR8,22,119,166,12,ES_READONLY + LTEXT "T0",-1,7,121,10,8 + EDITTEXT IDC_GPR9,22,133,166,12,ES_READONLY + LTEXT "T1",-1,7,135,10,8 + EDITTEXT IDC_GPR10,22,147,166,12,ES_READONLY + LTEXT "T2",-1,7,149,10,8 + EDITTEXT IDC_GPR11,22,161,166,12,ES_READONLY + LTEXT "T3",-1,7,163,10,8 + EDITTEXT IDC_GPR12,22,175,166,12,ES_READONLY + LTEXT "T4",-1,7,177,10,8 + EDITTEXT IDC_GPR13,22,189,166,12,ES_READONLY + LTEXT "T5",-1,7,191,10,8 + EDITTEXT IDC_GPR14,22,203,166,12,ES_READONLY + LTEXT "T6",-1,7,205,10,8 + EDITTEXT IDC_GPR15,22,218,166,12,ES_READONLY + LTEXT "T7",-1,7,219,10,8 + EDITTEXT IDC_GPR16,214,7,165,12,ES_READONLY + LTEXT "S0",-1,196,10,10,8 + EDITTEXT IDC_GPR17,214,20,166,12,ES_READONLY + LTEXT "S1",-1,196,22,10,8 + EDITTEXT IDC_GPR18,214,35,166,12,ES_READONLY + LTEXT "S2",-1,196,36,10,8 + EDITTEXT IDC_GPR19,214,49,165,12,ES_READONLY + LTEXT "S3",-1,196,52,10,8 + EDITTEXT IDC_GPR20,214,63,165,12,ES_READONLY + LTEXT "S4",-1,196,66,10,8 + EDITTEXT IDC_GPR21,214,76,165,12,ES_READONLY + LTEXT "S5",-1,196,80,10,8 + EDITTEXT IDC_GPR22,214,91,165,12,ES_READONLY + LTEXT "S6",-1,196,94,10,8 + EDITTEXT IDC_GPR23,214,105,165,12,ES_READONLY + LTEXT "S7",-1,196,108,10,8 + EDITTEXT IDC_GPR24,214,119,165,12,ES_READONLY + LTEXT "T8",-1,196,121,10,8 + EDITTEXT IDC_GPR25,214,132,165,12,ES_READONLY + LTEXT "T9",-1,196,135,10,8 + EDITTEXT IDC_GPR26,214,147,165,12,ES_READONLY + LTEXT "K0",-1,196,149,10,8 + EDITTEXT IDC_GPR27,214,161,165,12,ES_READONLY + LTEXT "K1",-1,196,163,10,8 + EDITTEXT IDC_GPR28,214,175,165,12,ES_READONLY + LTEXT "GP",-1,196,178,11,8 + EDITTEXT IDC_GPR29,214,188,165,12,ES_READONLY + LTEXT "SP",-1,196,190,10,8 + EDITTEXT IDC_GPR30,214,203,165,12,ES_READONLY + LTEXT "S8",-1,196,205,10,8 + EDITTEXT IDC_GPR31,214,217,165,12,ES_READONLY + LTEXT "RA",-1,196,220,11,8 + EDITTEXT IDC_GPR_PC,20,260,57,12,ES_READONLY + LTEXT "PC",-1,7,263,10,8 + EDITTEXT IDC_GPR_HI,21,238,167,12,ES_READONLY + LTEXT "HI",-1,7,241,8,8 + LTEXT "LO",-1,196,241,10,8 + EDITTEXT IDC_GPR_LO,214,238,165,12,ES_READONLY +END + +IDD_JUMP DIALOG 0, 0, 175, 64 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Jump to specific address" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_JUMP_PC,73,14,82,13 + DEFPUSHBUTTON "OK",IDOK,25,43,50,14 + PUSHBUTTON "Cancel",IDCANCEL,101,42,50,14 + LTEXT "Enter new PC Address:",IDC_STATIC,3,18,70,8 +END + +IDD_MEMORY DIALOG 0, 0, 317, 270 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Memory" +FONT 8, "Courier" +BEGIN + EDITTEXT IDC_MEMORY_ADDR,54,7,97,12 + PUSHBUTTON "Close",IDC_MEMORY_CLOSE,266,7,42,14 + LTEXT "Address:",IDC_STATIC,14,8,34,8 + LISTBOX IDC_MEMORY_DUMP,3,29,300,169,LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_TABSTOP + SCROLLBAR IDC_MEM_SCROLL,303,29,9,172,SBS_VERT + GROUPBOX "Memory Patch",IDC_STATIC,3,204,307,57 + EDITTEXT IDC_ADDRESS_PATCH,124,220,97,12 + EDITTEXT IDC_DATA_PATCH,124,242,97,12 + LTEXT "Address",IDC_STATIC,84,222,40,8 + LTEXT "Data",IDC_STATIC,85,244,39,8 + PUSHBUTTON "Patch It",IDC_PATCH,238,230,64,16 + PUSHBUTTON "Raw dump",IDC_DUMPRAW,204,7,50,14 +END + +IDD_VU1INTEGER DIALOG 0, 0, 357, 263 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "VU1 Integer & Control Registers" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_VU1_VI00,29,7,89,12,WS_DISABLED + LTEXT "VI00",IDC_STATIC,7,9,16,8 + EDITTEXT IDC_VU1_VI01,29,21,89,12,WS_DISABLED + LTEXT "VI01",IDC_STATIC,7,23,16,8 + EDITTEXT IDC_VU1_VI02,29,35,89,12,WS_DISABLED + LTEXT "VI02",IDC_STATIC,7,37,16,8 + EDITTEXT IDC_VU1_VI03,29,49,89,12,WS_DISABLED + LTEXT "VI03",IDC_STATIC,7,51,16,8 + EDITTEXT IDC_VU1_VI04,29,63,89,12,WS_DISABLED + LTEXT "VI04",IDC_STATIC,7,65,16,8 + EDITTEXT IDC_VU1_VI05,29,77,89,12,WS_DISABLED + LTEXT "VI05",IDC_STATIC,7,79,16,8 + EDITTEXT IDC_VU1_VI06,29,91,89,12,WS_DISABLED + LTEXT "VI06",IDC_STATIC,7,93,16,8 + EDITTEXT IDC_VU1_VI07,29,105,89,12,WS_DISABLED + LTEXT "VI07",IDC_STATIC,7,107,16,8 + EDITTEXT IDC_VU1_VI08,29,119,89,12,WS_DISABLED + LTEXT "VI08",IDC_STATIC,7,121,16,8 + EDITTEXT IDC_VU1_VI09,29,133,89,12,WS_DISABLED + LTEXT "VI09",IDC_STATIC,7,135,16,8 + EDITTEXT IDC_VU1_VI10,29,147,89,12,WS_DISABLED + LTEXT "VI10",IDC_STATIC,7,149,16,8 + EDITTEXT IDC_VU1_VI11,29,161,89,12,WS_DISABLED + LTEXT "VI11",IDC_STATIC,7,163,16,8 + EDITTEXT IDC_VU1_VI12,29,175,89,12,WS_DISABLED + LTEXT "VI12",IDC_STATIC,7,177,16,8 + EDITTEXT IDC_VU1_VI13,29,189,89,12,WS_DISABLED + LTEXT "VI13",IDC_STATIC,7,191,16,8 + EDITTEXT IDC_VU1_VI14,29,203,89,12,WS_DISABLED + LTEXT "VI14",IDC_STATIC,7,205,16,8 + EDITTEXT IDC_VU1_VI15,29,218,89,12,WS_DISABLED + LTEXT "VI15",IDC_STATIC,7,219,16,8 + EDITTEXT IDC_VU1_VI16,172,7,89,12,WS_DISABLED + LTEXT "Status Flag",IDC_STATIC,123,9,36,8 + EDITTEXT IDC_VU1_VI17,172,21,89,12,WS_DISABLED + LTEXT "MAC Flag",IDC_STATIC,123,23,32,8 + EDITTEXT IDC_VU1_VI18,172,35,89,12,WS_DISABLED + LTEXT "Clipping Flag",IDC_STATIC,123,37,41,8 + EDITTEXT IDC_VU1_VI19,172,49,89,12,WS_DISABLED + LTEXT "",IDC_STATIC,123,51,8,8 + EDITTEXT IDC_VU1_VI20,172,63,89,12,WS_DISABLED + LTEXT "R register",IDC_STATIC,123,65,31,8 + EDITTEXT IDC_VU1_VI21,172,77,89,12,WS_DISABLED + LTEXT "I register",IDC_STATIC,123,79,28,8 + EDITTEXT IDC_VU1_VI22,172,91,89,12,WS_DISABLED + LTEXT "Q register",IDC_STATIC,123,93,31,8 + EDITTEXT IDC_VU1_VI23,172,105,89,12,WS_DISABLED + LTEXT "",IDC_STATIC,123,107,8,8 + EDITTEXT IDC_VU1_VI24,172,119,89,12,WS_DISABLED + LTEXT "",IDC_STATIC,123,121,8,8 + EDITTEXT IDC_VU1_VI25,172,133,89,12,WS_DISABLED + LTEXT "",IDC_STATIC,123,135,8,8 + EDITTEXT IDC_VU1_VI26,172,147,89,12,WS_DISABLED + LTEXT "TPC register",IDC_STATIC,123,149,40,8 + EDITTEXT IDC_VU1_VI27,172,161,89,12,WS_DISABLED + LTEXT "P register",IDC_STATIC,124,108,30,8 + EDITTEXT IDC_VU1_VI28,172,175,89,12,WS_DISABLED + EDITTEXT IDC_VU1_VI29,172,189,89,12,WS_DISABLED + EDITTEXT IDC_VU1_VI30,172,203,89,12,WS_DISABLED + LTEXT "",IDC_STATIC,123,205,8,8 + EDITTEXT IDC_VU1_VI31,172,218,89,12,WS_DISABLED + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDVERT | SS_SUNKEN,273,7,1,222 + LTEXT "Accumulator",IDC_STATIC,27,238,43,14 + EDITTEXT IDC_VU1_ACC,78,239,166,12,ES_READONLY +END + +IDD_VU0REGS DIALOG 0, 0, 391, 279 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "VUO Floating Point Registers" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_VU0_VF00,28,7,166,12,ES_READONLY + LTEXT "VF00",-1,7,9,18,8 + EDITTEXT IDC_VU0_VF01,28,21,166,12,ES_READONLY + LTEXT "VF01",-1,7,23,18,8 + EDITTEXT IDC_VU0_VF02,28,35,166,12,ES_READONLY + LTEXT "VF02",-1,7,37,18,8 + EDITTEXT IDC_VU0_VF03,28,49,166,12,ES_READONLY + LTEXT "VF03",-1,7,51,18,8 + EDITTEXT IDC_VU0_VF04,28,63,166,12,ES_READONLY + LTEXT "VF04",-1,7,65,18,8 + EDITTEXT IDC_VU0_VF05,28,77,166,12,ES_READONLY + LTEXT "VF05",-1,7,79,18,8 + EDITTEXT IDC_VU0_VF06,28,91,166,12,ES_READONLY + LTEXT "VF06",-1,7,93,18,8 + EDITTEXT IDC_VU0_VF07,28,105,166,12,ES_READONLY + LTEXT "VF07",-1,7,107,18,8 + EDITTEXT IDC_VU0_VF08,28,119,166,12,ES_READONLY + LTEXT "VF08",-1,7,121,18,8 + EDITTEXT IDC_VU0_VF09,28,133,166,12,ES_READONLY + LTEXT "VF09",-1,7,135,18,8 + EDITTEXT IDC_VU0_VF10,28,147,166,12,ES_READONLY + LTEXT "VF10",-1,7,149,18,8 + EDITTEXT IDC_VU0_VF11,28,161,166,12,ES_READONLY + LTEXT "VF11",-1,7,163,18,8 + EDITTEXT IDC_VU0_VF12,28,175,166,12,ES_READONLY + LTEXT "VF12",-1,7,177,18,8 + EDITTEXT IDC_VU0_VF13,28,189,166,12,ES_READONLY + LTEXT "VF13",-1,7,191,18,8 + EDITTEXT IDC_VU0_VF14,28,203,166,12,ES_READONLY + LTEXT "VF14",-1,7,205,18,8 + EDITTEXT IDC_VU0_VF15,28,218,166,12,ES_READONLY + LTEXT "VF15",-1,7,219,18,8 + EDITTEXT IDC_VU0_VF16,214,7,165,12,ES_READONLY + LTEXT "VF16",-1,196,10,18,8 + EDITTEXT IDC_VU0_VF17,214,20,166,12,ES_READONLY + LTEXT "VF17",-1,196,22,18,8 + EDITTEXT IDC_VU0_VF18,214,35,166,12,ES_READONLY + LTEXT "VF18",-1,196,36,18,8 + EDITTEXT IDC_VU0_VF19,214,49,165,12,ES_READONLY + LTEXT "VF19",-1,196,52,18,8 + EDITTEXT IDC_VU0_VF20,214,63,165,12,ES_READONLY + LTEXT "VF20",-1,196,66,18,8 + EDITTEXT IDC_VU0_VF21,214,76,165,12,ES_READONLY + LTEXT "VF21",-1,196,80,18,8 + EDITTEXT IDC_VU0_VF22,214,91,165,12,ES_READONLY + LTEXT "VF22",-1,196,94,18,8 + EDITTEXT IDC_VU0_VF23,214,105,165,12,ES_READONLY + LTEXT "VF23",-1,196,108,18,8 + EDITTEXT IDC_VU0_VF24,214,119,165,12,ES_READONLY + LTEXT "VF24",-1,196,121,18,8 + EDITTEXT IDC_VU0_VF25,214,132,165,12,ES_READONLY + LTEXT "VF25",-1,196,135,18,8 + EDITTEXT IDC_VU0_VF26,214,147,165,12,ES_READONLY + LTEXT "VF26",-1,196,149,18,8 + EDITTEXT IDC_VU0_VF27,214,161,165,12,ES_READONLY + LTEXT "VF27",-1,196,163,18,8 + EDITTEXT IDC_VU0_VF28,214,175,165,12,ES_READONLY + LTEXT "VF28",-1,196,178,18,8 + EDITTEXT IDC_VU0_VF29,214,188,165,12,ES_READONLY + LTEXT "VF29",-1,196,190,18,8 + EDITTEXT IDC_VU0_VF30,214,203,165,12,ES_READONLY + LTEXT "VF30",-1,196,205,18,8 + EDITTEXT IDC_VU0_VF31,214,217,165,12,ES_READONLY + LTEXT "VF31",-1,196,220,18,8 +END + +IDD_CP1REGS DIALOG 0, 0, 357, 237 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "COP1 Registers " +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_FP0,29,7,89,12,WS_DISABLED + LTEXT "FP0",IDC_STATIC,7,9,14,8 + EDITTEXT IDC_FP1,29,21,89,12,WS_DISABLED + LTEXT "FP1",IDC_STATIC,7,23,14,8 + EDITTEXT IDC_FP2,29,35,89,12,WS_DISABLED + LTEXT "FP2",IDC_STATIC,7,37,14,8 + EDITTEXT IDC_FP3,29,49,89,12,WS_DISABLED + LTEXT "FP3",IDC_STATIC,7,51,14,8 + EDITTEXT IDC_FP4,29,63,89,12,WS_DISABLED + LTEXT "FP4",IDC_STATIC,7,65,14,8 + EDITTEXT IDC_FP5,29,77,89,12,WS_DISABLED + LTEXT "FP5",IDC_STATIC,7,79,14,8 + EDITTEXT IDC_FP6,29,91,89,12,WS_DISABLED + LTEXT "FP6",IDC_STATIC,7,93,14,8 + EDITTEXT IDC_FP7,29,105,89,12,WS_DISABLED + LTEXT "FP7",IDC_STATIC,7,107,14,8 + EDITTEXT IDC_FP8,29,119,89,12,WS_DISABLED + LTEXT "FP8",IDC_STATIC,7,121,14,8 + EDITTEXT IDC_FP9,29,133,89,12,WS_DISABLED + LTEXT "FP9",IDC_STATIC,7,135,14,8 + EDITTEXT IDC_FP10,29,147,89,12,WS_DISABLED + LTEXT "FP10",IDC_STATIC,7,149,18,8 + EDITTEXT IDC_FP11,29,161,89,12,WS_DISABLED + LTEXT "FP11",IDC_STATIC,7,163,18,8 + EDITTEXT IDC_FP12,29,175,89,12,WS_DISABLED + LTEXT "FP12",IDC_STATIC,7,177,18,8 + EDITTEXT IDC_FP13,29,189,89,12,WS_DISABLED + LTEXT "FP13",IDC_STATIC,7,191,18,8 + EDITTEXT IDC_FP14,29,203,89,12,WS_DISABLED + LTEXT "FP14",IDC_STATIC,7,205,18,8 + EDITTEXT IDC_FP15,29,218,89,12,WS_DISABLED + LTEXT "FP15",IDC_STATIC,7,219,18,8 + EDITTEXT IDC_FP16,145,7,89,12,WS_DISABLED + LTEXT "FP16",IDC_STATIC,123,9,18,8 + EDITTEXT IDC_FP17,145,21,89,12,WS_DISABLED + LTEXT "FP17",IDC_STATIC,123,23,18,8 + EDITTEXT IDC_FP18,145,35,89,12,WS_DISABLED + LTEXT "FP18",IDC_STATIC,123,37,18,8 + EDITTEXT IDC_FP19,145,49,89,12,WS_DISABLED + LTEXT "FP19",IDC_STATIC,123,51,18,8 + EDITTEXT IDC_FP20,145,63,89,12,WS_DISABLED + LTEXT "FP20",IDC_STATIC,123,65,18,8 + EDITTEXT IDC_FP21,145,77,89,12,WS_DISABLED + LTEXT "FP21",IDC_STATIC,123,79,18,8 + EDITTEXT IDC_FP22,145,91,89,12,WS_DISABLED + LTEXT "FP22",IDC_STATIC,123,93,18,8 + EDITTEXT IDC_FP23,145,105,89,12,WS_DISABLED + LTEXT "FP23",IDC_STATIC,123,107,18,8 + EDITTEXT IDC_FP24,145,119,89,12,WS_DISABLED + LTEXT "FP24",IDC_STATIC,123,121,18,8 + EDITTEXT IDC_FP25,145,133,89,12,WS_DISABLED + LTEXT "FP25",IDC_STATIC,123,135,18,8 + EDITTEXT IDC_FP26,145,147,89,12,WS_DISABLED + LTEXT "FP26",IDC_STATIC,123,149,18,8 + EDITTEXT IDC_FP27,145,161,89,12,WS_DISABLED + LTEXT "FP27",IDC_STATIC,123,163,18,8 + EDITTEXT IDC_FP28,145,175,89,12,WS_DISABLED + LTEXT "FP28",IDC_STATIC,123,177,18,8 + EDITTEXT IDC_FP29,145,189,89,12,WS_DISABLED + LTEXT "FP29",IDC_STATIC,123,191,18,8 + EDITTEXT IDC_FP30,145,203,89,12,WS_DISABLED + LTEXT "FP30",IDC_STATIC,123,205,18,8 + EDITTEXT IDC_FP31,145,218,89,12,WS_DISABLED + LTEXT "FP31",IDC_STATIC,123,219,18,8 + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDVERT | SS_SUNKEN,244,8,1,223 + LTEXT "Implementation/Revision Reg",IDC_STATIC,253,9,94,8 + EDITTEXT IDC_FCR0,298,21,50,12,WS_DISABLED + LTEXT "FCR0",IDC_STATIC,253,23,19,8 + LTEXT "Control/Status Register",IDC_STATIC,253,37,74,8 + EDITTEXT IDC_FCR31,298,49,50,12,WS_DISABLED + LTEXT "FCR31",IDC_STATIC,253,51,23,8 + LTEXT "Accumulator",IDC_STATIC,256,68,43,14 + EDITTEXT IDC_FPU_ACC,299,68,49,12,ES_AUTOHSCROLL | WS_DISABLED +END + +IDD_VU0INTEGER DIALOG 0, 0, 357, 263 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "VUO Integer & Control Registers" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_VU0_VI00,29,7,89,12,WS_DISABLED + LTEXT "VI00",IDC_STATIC,7,9,16,8 + EDITTEXT IDC_VU0_VI01,29,21,89,12,WS_DISABLED + LTEXT "VI01",IDC_STATIC,7,23,16,8 + EDITTEXT IDC_VU0_VI02,29,35,89,12,WS_DISABLED + LTEXT "VI02",IDC_STATIC,7,37,16,8 + EDITTEXT IDC_VU0_VI03,29,49,89,12,WS_DISABLED + LTEXT "VI03",IDC_STATIC,7,51,16,8 + EDITTEXT IDC_VU0_VI04,29,63,89,12,WS_DISABLED + LTEXT "VI04",IDC_STATIC,7,65,16,8 + EDITTEXT IDC_VU0_VI05,29,77,89,12,WS_DISABLED + LTEXT "VI05",IDC_STATIC,7,79,16,8 + EDITTEXT IDC_VU0_VI06,29,91,89,12,WS_DISABLED + LTEXT "VI06",IDC_STATIC,7,93,16,8 + EDITTEXT IDC_VU0_VI07,29,105,89,12,WS_DISABLED + LTEXT "VI07",IDC_STATIC,7,107,16,8 + EDITTEXT IDC_VU0_VI08,29,119,89,12,WS_DISABLED + LTEXT "VI08",IDC_STATIC,7,121,16,8 + EDITTEXT IDC_VU0_VI09,29,133,89,12,WS_DISABLED + LTEXT "VI09",IDC_STATIC,7,135,16,8 + EDITTEXT IDC_VU0_VI10,29,147,89,12,WS_DISABLED + LTEXT "VI10",IDC_STATIC,7,149,16,8 + EDITTEXT IDC_VU0_VI11,29,161,89,12,WS_DISABLED + LTEXT "VI11",IDC_STATIC,7,163,16,8 + EDITTEXT IDC_VU0_VI12,29,175,89,12,WS_DISABLED + LTEXT "VI12",IDC_STATIC,7,177,16,8 + EDITTEXT IDC_VU0_VI13,29,189,89,12,WS_DISABLED + LTEXT "VI13",IDC_STATIC,7,191,16,8 + EDITTEXT IDC_VU0_VI14,29,203,89,12,WS_DISABLED + LTEXT "VI14",IDC_STATIC,7,205,16,8 + EDITTEXT IDC_VU0_VI15,29,218,89,12,WS_DISABLED + LTEXT "VI15",IDC_STATIC,7,219,16,8 + EDITTEXT IDC_VU0_VI16,172,7,89,12,WS_DISABLED + LTEXT "Status Flag",IDC_STATIC,123,9,36,8 + EDITTEXT IDC_VU0_VI17,172,21,89,12,WS_DISABLED + LTEXT "MAC Flag",IDC_STATIC,123,23,32,8 + EDITTEXT IDC_VU0_VI18,172,35,89,12,WS_DISABLED + LTEXT "Clipping Flag",IDC_STATIC,123,37,41,8 + EDITTEXT IDC_VU0_VI19,172,49,89,12,WS_DISABLED + LTEXT "",IDC_STATIC,123,51,8,8 + EDITTEXT IDC_VU0_VI20,172,63,89,12,WS_DISABLED + LTEXT "R register",IDC_STATIC,123,65,31,8 + EDITTEXT IDC_VU0_VI21,172,77,89,12,WS_DISABLED + LTEXT "I register",IDC_STATIC,123,79,28,8 + EDITTEXT IDC_VU0_VI22,172,91,89,12,WS_DISABLED + LTEXT "Q register",IDC_STATIC,123,93,31,8 + EDITTEXT IDC_VU0_VI23,172,105,89,12,WS_DISABLED + LTEXT "",IDC_STATIC,123,107,8,8 + EDITTEXT IDC_VU0_VI24,172,119,89,12,WS_DISABLED + LTEXT "",IDC_STATIC,123,121,8,8 + EDITTEXT IDC_VU0_VI25,172,133,89,12,WS_DISABLED + LTEXT "",IDC_STATIC,123,135,8,8 + EDITTEXT IDC_VU0_VI26,172,147,89,12,WS_DISABLED + LTEXT "TPC register",IDC_STATIC,123,149,40,8 + EDITTEXT IDC_VU0_VI27,172,161,89,12,WS_DISABLED + LTEXT "CMSAR0 reg",IDC_STATIC,123,163,42,8 + EDITTEXT IDC_VU0_VI28,172,175,89,12,WS_DISABLED + LTEXT "FBRST register",IDC_STATIC,123,177,49,8 + EDITTEXT IDC_VU0_VI29,172,189,89,12,WS_DISABLED + LTEXT "VPU-STAT reg",IDC_STATIC,123,191,48,8 + EDITTEXT IDC_VU0_VI30,172,203,89,12,WS_DISABLED + LTEXT "",IDC_STATIC,123,205,8,8 + EDITTEXT IDC_VU0_VI31,172,218,89,12,WS_DISABLED + LTEXT "CMSAR1 reg",IDC_STATIC,123,219,42,8 + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDVERT | SS_SUNKEN,273,7,1,222 + LTEXT "Accumulator",IDC_STATIC,27,238,43,14 + EDITTEXT IDC_VU0_ACC,78,239,166,12,ES_READONLY + LTEXT "P register",IDC_STATIC,124,108,30,8 +END + +IDD_VU1REGS DIALOG 0, 0, 391, 279 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "VU1Floating Point Registers" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_VU1_VF00,28,7,166,12,ES_READONLY + LTEXT "VF00",-1,7,9,18,8 + EDITTEXT IDC_VU1_VF01,28,21,166,12,ES_READONLY + LTEXT "VF01",-1,7,23,18,8 + EDITTEXT IDC_VU1_VF02,28,35,166,12,ES_READONLY + LTEXT "VF02",-1,7,37,18,8 + EDITTEXT IDC_VU1_VF03,28,49,166,12,ES_READONLY + LTEXT "VF03",-1,7,51,18,8 + EDITTEXT IDC_VU1_VF04,28,63,166,12,ES_READONLY + LTEXT "VF04",-1,7,65,18,8 + EDITTEXT IDC_VU1_VF05,28,77,166,12,ES_READONLY + LTEXT "VF05",-1,7,79,18,8 + EDITTEXT IDC_VU1_VF06,28,91,166,12,ES_READONLY + LTEXT "VF06",-1,7,93,18,8 + EDITTEXT IDC_VU1_VF07,28,105,166,12,ES_READONLY + LTEXT "VF07",-1,7,107,18,8 + EDITTEXT IDC_VU1_VF08,28,119,166,12,ES_READONLY + LTEXT "VF08",-1,7,121,18,8 + EDITTEXT IDC_VU1_VF09,28,133,166,12,ES_READONLY + LTEXT "VF09",-1,7,135,18,8 + EDITTEXT IDC_VU1_VF10,28,147,166,12,ES_READONLY + LTEXT "VF10",-1,7,149,18,8 + EDITTEXT IDC_VU1_VF11,28,161,166,12,ES_READONLY + LTEXT "VF11",-1,7,163,18,8 + EDITTEXT IDC_VU1_VF12,28,175,166,12,ES_READONLY + LTEXT "VF12",-1,7,177,18,8 + EDITTEXT IDC_VU1_VF13,28,189,166,12,ES_READONLY + LTEXT "VF13",-1,7,191,18,8 + EDITTEXT IDC_VU1_VF14,28,203,166,12,ES_READONLY + LTEXT "VF14",-1,7,205,18,8 + EDITTEXT IDC_VU1_VF15,28,218,166,12,ES_READONLY + LTEXT "VF15",-1,7,219,18,8 + EDITTEXT IDC_VU1_VF16,214,7,165,12,ES_READONLY + LTEXT "VF16",-1,196,10,18,8 + EDITTEXT IDC_VU1_VF17,214,20,166,12,ES_READONLY + LTEXT "VF17",-1,196,22,18,8 + EDITTEXT IDC_VU1_VF18,214,35,166,12,ES_READONLY + LTEXT "VF18",-1,196,36,18,8 + EDITTEXT IDC_VU1_VF19,214,49,165,12,ES_READONLY + LTEXT "VF19",-1,196,52,18,8 + EDITTEXT IDC_VU1_VF20,214,63,165,12,ES_READONLY + LTEXT "VF20",-1,196,66,18,8 + EDITTEXT IDC_VU1_VF21,214,76,165,12,ES_READONLY + LTEXT "VF21",-1,196,80,18,8 + EDITTEXT IDC_VU1_VF22,214,91,165,12,ES_READONLY + LTEXT "VF22",-1,196,94,18,8 + EDITTEXT IDC_VU1_VF23,214,105,165,12,ES_READONLY + LTEXT "VF23",-1,196,108,18,8 + EDITTEXT IDC_VU1_VF24,214,119,165,12,ES_READONLY + LTEXT "VF24",-1,196,121,18,8 + EDITTEXT IDC_VU1_VF25,214,132,165,12,ES_READONLY + LTEXT "VF25",-1,196,135,18,8 + EDITTEXT IDC_VU1_VF26,214,147,165,12,ES_READONLY + LTEXT "VF26",-1,196,149,18,8 + EDITTEXT IDC_VU1_VF27,214,161,165,12,ES_READONLY + LTEXT "VF27",-1,196,163,18,8 + EDITTEXT IDC_VU1_VF28,214,175,165,12,ES_READONLY + LTEXT "VF28",-1,196,178,18,8 + EDITTEXT IDC_VU1_VF29,214,188,165,12,ES_READONLY + LTEXT "VF29",-1,196,190,18,8 + EDITTEXT IDC_VU1_VF30,214,203,165,12,ES_READONLY + LTEXT "VF30",-1,196,205,18,8 + EDITTEXT IDC_VU1_VF31,214,217,165,12,ES_READONLY + LTEXT "VF31",-1,196,220,18,8 +END + +IDD_RDEBUGPARAMS DIALOGEX 0, 0, 174, 58 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Remote Debugging" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,7,37,50,14 + PUSHBUTTON "Cancel",IDCANCEL,117,37,50,14 + EDITTEXT IDC_PORT,127,7,40,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Accept connections on TCP/IP port:",IDC_STATIC,7,7,120,10 + CONTROL "Debug Bios",IDC_DEBUGBIOS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,22,50,10 +END + +IDD_RDEBUG DIALOGEX 0, 0, 254, 271 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Remote Debugging" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "E&xit",IDOK,199,251,48,14 + LISTBOX IDC_COMMUNICATION,7,7,240,211,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP + PUSHBUTTON "&Clear",IDC_CLEAR,199,229,48,14 + EDITTEXT IDC_EEPC,74,229,52,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_IOPPC,132,229,52,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "EE pc address:\t IOP pc address:",IDC_STATIC,77,219,113,9 + CONTROL "Debug EE",IDC_DEBUGEE,"Button",BS_AUTORADIOBUTTON,77,255,48,10 + CONTROL "Debug IOP",IDC_DEBUGIOP,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,132,254,50,11 + EDITTEXT IDC_STOPAT,7,229,56,13,ES_AUTOHSCROLL + EDITTEXT IDC_STOPAFTER,7,252,56,13,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Break at (hex addr):",IDC_STATIC,7,219,67,8 + LTEXT "Break after (cycles):",IDC_STATIC,7,243,66,8 + EDITTEXT IDC_EECYCLE,74,241,52,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_IOPCYCLE,132,241,52,12,ES_AUTOHSCROLL | ES_READONLY +END + +IDD_MCDCONF DIALOGEX 0, 0, 466, 261 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Memcard Manager" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,147,236,50,14 + PUSHBUTTON "Cancel",IDCANCEL,209,236,50,14 + EDITTEXT IDC_MCD1,10,208,160,14,ES_MULTILINE | ES_AUTOHSCROLL + EDITTEXT IDC_MCD2,259,208,160,14,ES_AUTOHSCROLL + PUSHBUTTON "Select Mcd",IDC_MCDSEL1,10,171,50,14 + PUSHBUTTON "Select Mcd",IDC_MCDSEL2,261,171,50,14 + GROUPBOX "Memory Card 2",IDC_FRAMEMCD2,258,5,194,157 + PUSHBUTTON "Format Mcd",IDC_FORMAT1,65,171,50,14 + GROUPBOX "Memory Card 1",IDC_FRAMEMCD1,5,5,195,156 + PUSHBUTTON "Format Mcd",IDC_FORMAT2,316,171,50,14 + PUSHBUTTON "Reload Mcd",IDC_RELOAD1,120,171,50,14 + PUSHBUTTON "Reload Mcd",IDC_RELOAD2,371,171,50,14 + PUSHBUTTON "-> Copy ->",IDC_COPYTO2,206,30,45,14 + PUSHBUTTON "<- Copy <-",IDC_COPYTO1,206,50,45,14 + PUSHBUTTON "Paste",IDC_PASTE,206,70,45,14 + PUSHBUTTON "<- Un/Delete",IDC_DELETE1,206,90,45,14 + PUSHBUTTON "Un/Delete ->",IDC_DELETE2,206,110,45,14 + CONTROL "",IDC_TREE1,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | NOT WS_VISIBLE | WS_BORDER | WS_TABSTOP,10,14,160,112 + CONTROL "",IDC_TREE2,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | NOT WS_VISIBLE | WS_BORDER | WS_TABSTOP,262,15,160,112 + CONTROL "",IDC_ICON1,"Static",SS_BLACKFRAME | SS_REALSIZEIMAGE | NOT WS_VISIBLE | WS_BORDER,165,141,88,82 + PUSHBUTTON "Load Icon",IDC_LOADICO1,10,190,50,14 + PUSHBUTTON "Save File",IDC_SAVE1,65,190,50,14 + PUSHBUTTON "Load Icon",IDC_LOADICO2,261,189,50,14 + PUSHBUTTON "Save File",IDC_SAVE2,317,189,50,14 + CONTROL "",IDC_ICON2,"Static",SS_BLACKFRAME | SS_REALSIZEIMAGE | NOT WS_VISIBLE | WS_BORDER,168,146,88,82 + CONTROL "",IDC_LIST1,"SysListView32",LVS_SINGLESEL | LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,10,14,185,143 + CONTROL "",IDC_LIST2,"SysListView32",LVS_SINGLESEL | LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,262,15,185,143 +END + +IDD_DUMPMEM DIALOG 0, 0, 175, 95 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Dump memory" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_DUMPMEM_START,73,14,82,13 + EDITTEXT IDC_DUMPMEM_END,73,29,82,13 + EDITTEXT IDC_DUMPMEM_FNAME,73,50,82,13,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,25,74,50,14 + PUSHBUTTON "Cancel",IDCANCEL,101,74,50,14 + LTEXT "Start Address:",IDC_STATIC,19,17,52,8 + LTEXT "End Address:",IDC_STATIC,19,32,50,8 + LTEXT "Filename:",IDC_STATIC,19,53,31,8 +END + +IDD_DIALOGBAR DIALOGEX 0, 0, 330, 16 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "TODO: layout dialog bar",IDC_STATIC,126,4,77,8 +END + +IDD_IOPREGS DIALOGEX 0, 0, 417, 295 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "IOP Main Registers" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_IOPGPR0,22,20,166,12,ES_READONLY + LTEXT "R0",-1,7,23,10,8 + EDITTEXT IDC_IOPGPR1,22,34,166,12,ES_READONLY + LTEXT "AT",-1,7,36,10,8 + EDITTEXT IDC_IOPGPR2,22,49,166,12,ES_READONLY + LTEXT "V0",-1,7,50,10,8 + EDITTEXT IDC_IOPGPR3,22,63,166,12,ES_READONLY + LTEXT "V1",-1,7,65,10,8 + EDITTEXT IDC_IOPGPR4,22,76,166,12,ES_READONLY + LTEXT "A0",-1,7,79,10,8 + EDITTEXT IDC_IOPGPR5,22,90,166,12,ES_READONLY + LTEXT "A1",-1,7,92,10,8 + EDITTEXT IDC_IOPGPR6,22,105,166,12,ES_READONLY + LTEXT "A2",-1,7,106,10,8 + EDITTEXT IDC_IOPGPR7,22,119,166,12,ES_READONLY + LTEXT "A3",-1,7,121,10,8 + EDITTEXT IDC_IOPGPR8,22,132,166,12,ES_READONLY + LTEXT "T0",-1,7,135,10,8 + EDITTEXT IDC_IOPGPR9,22,146,166,12,ES_READONLY + LTEXT "T1",-1,7,148,10,8 + EDITTEXT IDC_IOPGPR10,22,161,166,12,ES_READONLY + LTEXT "T2",-1,7,162,10,8 + EDITTEXT IDC_IOPGPR11,22,175,166,12,ES_READONLY + LTEXT "T3",-1,7,177,10,8 + EDITTEXT IDC_IOPGPR12,22,188,166,12,ES_READONLY + LTEXT "T4",-1,7,191,10,8 + EDITTEXT IDC_IOPGPR13,22,202,166,12,ES_READONLY + LTEXT "T5",-1,7,204,10,8 + EDITTEXT IDC_IOPGPR14,22,217,166,12,ES_READONLY + LTEXT "T6",-1,7,218,10,8 + EDITTEXT IDC_IOPGPR15,22,231,166,12,ES_READONLY + LTEXT "T7",-1,7,233,10,8 + EDITTEXT IDC_IOPGPR16,214,20,165,12,ES_READONLY + LTEXT "S0",-1,196,23,10,8 + EDITTEXT IDC_IOPGPR17,214,34,166,12,ES_READONLY + LTEXT "S1",-1,196,36,10,8 + EDITTEXT IDC_IOPGPR18,214,49,166,12,ES_READONLY + LTEXT "S2",-1,196,50,10,8 + EDITTEXT IDC_IOPGPR19,214,63,165,12,ES_READONLY + LTEXT "S3",-1,196,66,10,8 + EDITTEXT IDC_IOPGPR20,214,76,165,12,ES_READONLY + LTEXT "S4",-1,196,79,10,8 + EDITTEXT IDC_IOPGPR21,214,90,165,12,ES_READONLY + LTEXT "S5",-1,196,94,10,8 + EDITTEXT IDC_IOPGPR22,214,105,165,12,ES_READONLY + LTEXT "S6",-1,196,108,10,8 + EDITTEXT IDC_IOPGPR23,214,119,165,12,ES_READONLY + LTEXT "S7",-1,196,122,10,8 + EDITTEXT IDC_IOPGPR24,214,132,165,12,ES_READONLY + LTEXT "T8",-1,196,135,10,8 + EDITTEXT IDC_IOPGPR25,214,146,165,12,ES_READONLY + LTEXT "T9",-1,196,148,10,8 + EDITTEXT IDC_IOPGPR26,214,161,165,12,ES_READONLY + LTEXT "K0",-1,196,162,10,8 + EDITTEXT IDC_IOPGPR27,214,175,165,12,ES_READONLY + LTEXT "K1",-1,196,177,10,8 + EDITTEXT IDC_IOPGPR28,214,188,165,12,ES_READONLY + LTEXT "GP",-1,196,191,11,8 + EDITTEXT IDC_IOPGPR29,214,202,165,12,ES_READONLY + LTEXT "SP",-1,196,204,10,8 + EDITTEXT IDC_IOPGPR30,214,217,165,12,ES_READONLY + LTEXT "S8",-1,196,218,10,8 + EDITTEXT IDC_IOPGPR31,214,231,165,12,ES_READONLY + LTEXT "RA",-1,196,234,11,8 + EDITTEXT IDC_IOPGPR_PC,20,272,57,12,ES_READONLY + LTEXT "PC",-1,7,276,10,8 + EDITTEXT IDC_IOPGPR_HI,21,252,167,12,ES_READONLY + LTEXT "HI",-1,7,255,8,8 + LTEXT "LO",-1,196,255,10,8 + EDITTEXT IDC_IOPGPR_LO,214,252,165,12,ES_READONLY +END + +IDD_USERNAME DIALOGEX 0, 0, 186, 79 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Enter User Name" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,7,58,50,14 + PUSHBUTTON "Cancel",IDCANCEL,129,58,50,14 + LTEXT "Pcsx2 needs to allocate physical memory in order to execute faster. Enter the windows username you wish this privilege to be enabled for.",IDC_STATIC,7,7,172,25 + EDITTEXT IDC_USER_NAME,73,37,106,13,ES_AUTOHSCROLL + LTEXT "User Name:",IDC_STATIC,7,41,38,8 +END + +IDD_ADVANCED_OPTIONS DIALOGEX 0, 0, 543, 275 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Advanced Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,409,254,50,14 + PUSHBUTTON "Cancel",IDCANCEL,465,254,50,14 + RADIOBUTTON "Nearest",IDC_EE_ROUNDMODE0,20,36,44,16 + RADIOBUTTON "Negative",IDC_EE_ROUNDMODE1,64,36,47,16 + RADIOBUTTON "Positive",IDC_EE_ROUNDMODE2,111,36,45,16 + RADIOBUTTON "Chop / Zero",IDC_EE_ROUNDMODE3,156,36,54,16 + CONTROL " Flush to Zero",IDC_EE_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,105,58,13 + CONTROL " Denormals are Zero",IDC_EE_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,111,105,79,13 + CONTROL " Flush to Zero",IDC_VU_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,214,58,13 + CONTROL " Denormals are Zero",IDC_VU_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,111,214,81,13 + RADIOBUTTON "Nearest",IDC_VU_ROUNDMODE0,20,154,44,12 + RADIOBUTTON "Negative",IDC_VU_ROUNDMODE1,64,154,47,12 + RADIOBUTTON "Positive",IDC_VU_ROUNDMODE2,111,154,45,12 + RADIOBUTTON "Chop / Zero",IDC_VU_ROUNDMODE3,156,154,52,12 + PUSHBUTTON "Defaults",IDDEFAULT,346,254,50,14 + GROUPBOX "VU Recs Options",IDC_STATIC,7,128,250,122,BS_CENTER + GROUPBOX "EE Recs Options",IDC_STATIC,7,12,251,111,BS_CENTER + GROUPBOX "Round Mode",IDC_STATIC,11,141,236,32 + GROUPBOX "Round Mode",IDC_STATIC,11,26,236,36 + GROUPBOX "Help",IDC_STATIC,271,12,251,238,BS_CENTER + GROUPBOX "Clamp Mode",IDC_STATIC,11,178,236,31 + RADIOBUTTON "None",IDC_VU_CLAMPMODE0,20,189,44,12 + RADIOBUTTON "Normal",IDC_VU_CLAMPMODE1,64,189,47,12 + RADIOBUTTON "Extra",IDC_VU_CLAMPMODE2,111,189,45,12 + RADIOBUTTON "Extra + Preserve Sign",IDC_VU_CLAMPMODE3,156,189,85,12 + CONTROL " Set O and U Flags",IDC_VU_CHECK3,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,20,232,91,13 + CONTROL " Software Emulate DaZ",IDC_VU_CHECK4,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,111,232,116,13 + GROUPBOX "Clamp Mode",IDC_STATIC,11,67,236,31 + RADIOBUTTON "None",IDC_EE_CLAMPMODE0,20,76,44,16 + RADIOBUTTON "Normal",IDC_EE_CLAMPMODE1,64,76,47,16 + RADIOBUTTON "Extra + Preserve Sign",IDC_EE_CLAMPMODE2,111,76,91,16 + LTEXT "These options specify how your CPU rounds floating point values.\n\nTry changing the roundmode for EE if your game hangs, it could make it work again.",IDC_STATIC,287,33,216,35 + GROUPBOX "Round Mode",IDC_STATIC,281,22,235,51,BS_LEFT + GROUPBOX "Clamp Mode",IDC_STATIC,281,80,236,84,BS_LEFT + GROUPBOX "Other Options",IDC_STATIC,280,172,237,67,BS_LEFT + LTEXT "These options specify how PCSX2's recompilers will clamp Infinities and NaN (Not a Number) values in the opcode instructions.",IDC_STATIC,286,94,224,19 + LTEXT "Flush to Zero - Your CPU makes Floating Point Underflows become Zero, so it does less work. (Speed Up)\n\nDenormals are Zero - Your CPU makes Floating Point Denormals become Zero, so it does less work. (Speed Up)",IDC_STATIC,286,186,224,46 + LTEXT "None - No clamping. (Fastest Mode)\nNormal - Clamps the result.\nExtra - Clamps the operands, the result, and anywhere in between.\nExtra + Preserve Sign - Same as ""Extra"", except preserves NaN's sign when clamping the operands. (Slowest Mode)",IDC_STATIC,286,114,224,45 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_RDEBUGPARAMS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 167 + TOPMARGIN, 7 + BOTTOMMARGIN, 51 + END + + IDD_RDEBUG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 247 + TOPMARGIN, 7 + BOTTOMMARGIN, 265 + END + + IDD_MCDCONF, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 299 + TOPMARGIN, 7 + BOTTOMMARGIN, 178 + END + + IDD_DIALOGBAR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 323 + TOPMARGIN, 7 + BOTTOMMARGIN, 9 + END + + IDD_IOPREGS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 410 + TOPMARGIN, 7 + BOTTOMMARGIN, 284 + END + + IDD_USERNAME, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 72 + END + + IDD_ADVANCED_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 535 + VERTGUIDE, 11 + VERTGUIDE, 20 + VERTGUIDE, 64 + VERTGUIDE, 111 + VERTGUIDE, 156 + VERTGUIDE, 247 + BOTTOMMARGIN, 268 + HORZGUIDE, 12 + HORZGUIDE, 36 + HORZGUIDE, 214 + HORZGUIDE, 232 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_PS2SILVER BITMAP "ps2_silver.bmp" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +ABOUT_DIALOG DIALOGEX 0, 0, 431, 302 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_ACCEPTFILES +CAPTION "About PCSX2 - Playground" +FONT 8, "Microsoft Sans Serif", 400, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,205,282,50,14 + CTEXT "PCSX2, a PS2 Emulator...",IDC_PCSX_ABOUT_AUTHORS,9,10,135,127,0,WS_EX_TRANSPARENT + CTEXT "Greets to...",IDC_PCSX_ABOUT_GREETS,89,182,311,77 + GROUPBOX "",IDC_STATIC,5,3,145,138 + GROUPBOX "",IDC_STATIC,77,173,333,91 + CONTROL 132,IDC_PS2SILVER_RECT,"Static",SS_BITMAP,2,183,70,74 +END + +IDD_HACKS DIALOGEX 0, 0, 328, 263 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PCSX2 Speed Hacks" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Default Cycle Rate",IDC_EESYNC_DEFAULT,"Button",BS_AUTORADIOBUTTON,13,44,87,10 + CONTROL "Use x1.5 Cycle Rate",IDC_EESYNC1,"Button",BS_AUTORADIOBUTTON,13,79,87,10 + CONTROL "Use x2 Cycle Rate",IDC_EESYNC2,"Button",BS_AUTORADIOBUTTON,13,113,83,10 + CONTROL "Use x3 Cycle Rate",IDC_EESYNC3,"Button",BS_AUTORADIOBUTTON,13,147,80,10 + CONTROL "Enable IOP x2 Cycle Rate",IDC_IOPSYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,174,41,98,10 + CONTROL "WaitCycles Sync Hack",IDC_WAITCYCLES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,174,82,90,10 + CONTROL "Escape Hack - Use Esc key to fully exit PCSX2.",IDC_ESCHACK, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,234,180,10 + DEFPUSHBUTTON "OK",IDOK,217,242,50,14 + PUSHBUTTON "Cancel",IDCANCEL,271,242,50,14 + CTEXT "These hacks will speed up emulation but reduce emulation compatibility or cause visual errors. If you have problems, disable all these and try again!",IDC_HACKDESC,18,7,286,19 + GROUPBOX "EmotionEngine (EE) Sync Hacks",IDC_STATIC,7,31,159,180 + GROUPBOX "Miscellaneous",IDC_STATIC,7,220,194,33 + LTEXT "Important: X2 and X3 sync hacks *will* cause choppy/skippy audio on many FMV movies.",IDC_STATIC,13,183,149,25 + LTEXT "Bigger speedup, but causes flickering or missing geometry on many games.",IDC_STATIC,25,158,133,19 + LTEXT "Big speedup! Works well with many games.",IDC_STATIC,25,124,125,19 + LTEXT "Most compatible option - recommended for everyone with high-end machines.",IDC_STATIC,25,55,136,19 + LTEXT "Small speedup and works well with most games.",IDC_STATIC,186,53,134,22 + LTEXT "Small speedup. Works well with most games, but may cause certain games to crash or freeze up during bootup or stage changes.",IDC_STATIC,186,94,134,39 + LTEXT "Moderate speedup and works well with most games.",IDC_STATIC,25,90,129,19 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + ABOUT_DIALOG, DIALOG + BEGIN + RIGHTMARGIN, 429 + BOTTOMMARGIN, 296 + END + + IDD_HACKS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 321 + VERTGUIDE, 13 + TOPMARGIN, 7 + BOTTOMMARGIN, 256 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +SPLASH_LOGO BITMAP "..\\pcsxAbout.bmp" +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Spanish resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ESN) +#ifdef _WIN32 +LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_MODERN +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_FINDER DIALOGEX 3, 1, 262, 212 +STYLE DS_SETFONT | WS_CAPTION | WS_SYSMENU +CAPTION "Cheat Finder" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "Results",IDC_RESULTS,"SysListView32",LVS_REPORT | WS_TABSTOP,96,15,161,153,WS_EX_CLIENTEDGE + GROUPBOX "Values of size",IDC_STATIC,5,44,86,45 + CHECKBOX "Unsigned",IDC_UNSIGNED,11,74,70,10,NOT WS_TABSTOP + CONTROL "64bits",IDC_64B,"Button",BS_AUTORADIOBUTTON | WS_GROUP,48,64,33,10 + CONTROL "32bits",IDC_32B,"Button",BS_AUTORADIOBUTTON | WS_GROUP,48,54,33,10 + CONTROL "16bits",IDC_16B,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,64,33,10 + CONTROL "8bits",IDC_8B,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,54,33,10 + PUSHBUTTON "Close",IDCANCEL,208,192,49,15,NOT WS_TABSTOP + PUSHBUTTON "Search",IDC_SEARCH,96,192,49,15,NOT WS_TABSTOP + GROUPBOX "Compared to",IDC_STATIC,5,162,86,45 + EDITTEXT IDC_VALUE,21,192,65,12,ES_AUTOHSCROLL | NOT WS_TABSTOP + CONTROL "Specific Value",IDC_SET,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,182,75,10 + CONTROL "Old Value",IDC_OLD,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,172,75,10 + GROUPBOX "Search in",IDC_STATIC,5,5,86,35 + CONTROL "Smaller or equal",IDC_LE,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,139,75,10 + CONTROL "Greater or equal",IDC_GE,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,129,75,10 + CONTROL "Smaller",IDC_LT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,120,75,10 + CONTROL "Greater",IDC_GT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,110,75,10 + CONTROL "Equal",IDC_EQ,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,101,75,10 + PUSHBUTTON "Reset",IDC_RESET,96,172,49,15,NOT WS_TABSTOP + PUSHBUTTON "Add",IDC_ADD,208,172,49,15,NOT WS_TABSTOP + RTEXT "1 match found.",IDC_MATCHES,125,5,132,10,NOT WS_GROUP + LTEXT "Results:",IDC_STATIC,96,5,29,10,NOT WS_GROUP + CONTROL "EE RAM",IDC_EE,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,15,43,10 + CONTROL "IOP RAM",IDC_IOP,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,25,43,10 + CONTROL "Not equal",IDC_NE,"Button",BS_AUTORADIOBUTTON | WS_GROUP,11,148,75,10 + GROUPBOX "being",IDC_STATIC,5,92,86,67 +END + +IDD_ADD DIALOGEX 3, 1, 145, 74 +STYLE DS_SETFONT | WS_CAPTION | WS_SYSMENU +CAPTION "Add Cheat" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + PUSHBUTTON "OK",IDOK,37,54,49,15,NOT WS_TABSTOP + PUSHBUTTON "Cancel",IDCANCEL,91,54,49,15,NOT WS_TABSTOP + LTEXT "Cheat Name",IDC_STATIC,5,30,75,10,NOT WS_GROUP + LTEXT "New Value",IDC_STATIC,69,5,70,10,NOT WS_GROUP + LTEXT "Address",IDC_STATIC,5,5,59,10,NOT WS_GROUP + EDITTEXT IDC_NAME,5,39,134,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_TABSTOP + EDITTEXT IDC_VALUE,69,15,70,12,ES_AUTOHSCROLL | NOT WS_TABSTOP + EDITTEXT IDC_ADDR,5,15,59,12,ES_AUTOHSCROLL | NOT WS_TABSTOP +END + +IDD_CHEATS DIALOGEX 0, 0, 345, 198 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Patch List" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Exit",IDCANCEL,288,175,50,14 + CONTROL "",IDC_GROUPS,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_EDITLABELS | TVS_CHECKBOXES | NOT WS_VISIBLE | WS_TABSTOP,7,7,230,126,WS_EX_CLIENTEDGE + CONTROL "",IDC_PATCHES,"SysListView32",LVS_REPORT | WS_TABSTOP,7,7,270,183,WS_EX_CLIENTEDGE + PUSHBUTTON "Enable/Disable",IDC_ENABLEBUTTON,287,7,51,14 + PUSHBUTTON "Add GS2v3-4",IDC_ADDGS,287,62,51,14 + PUSHBUTTON "Edit Patch",IDC_EDITPATCH,287,43,51,14 + PUSHBUTTON "Add Patch",IDC_ADDPATCH,287,25,51,14 + PUSHBUTTON "Add RAW",IDC_ADDRAW,287,81,51,14 + DEFPUSHBUTTON "OK",IDOK,288,155,50,14,NOT WS_VISIBLE + PUSHBUTTON "pnach Writer",IDC_PNACHWRITER,287,100,51,14 + PUSHBUTTON "Skip MPEG",IDC_SKIPMPEG,287,119,51,14 +END + +IDD_ADDGS DIALOGEX 3, 1, 145, 117 +STYLE DS_SETFONT | WS_CAPTION | WS_SYSMENU +CAPTION "Add Cheat" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + PUSHBUTTON "Add",IDOK,91,98,49,15,NOT WS_TABSTOP + PUSHBUTTON "Convert",IDC_CONVERT,38,98,49,15,NOT WS_TABSTOP + EDITTEXT IDC_CONVERTEDCODE,5,58,134,33,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | NOT WS_TABSTOP + LTEXT "Converted code:",-1,5,46,75,10,NOT WS_GROUP + LTEXT "GS2v3-4(GSv5 remove first line):",-1,5,5,123,10,NOT WS_GROUP + LTEXT "Static",IDC_ADDRESS,1,101,8,7,NOT WS_VISIBLE + LTEXT "Static",IDC_VALUE,126,47,8,7,NOT WS_VISIBLE + LTEXT "0",IDC_READY,17,104,9,6,NOT WS_VISIBLE + EDITTEXT IDC_ADDR,5,15,132,28,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | NOT WS_TABSTOP +END + +IDD_EDITPATCH DIALOGEX 3, 1, 145, 154 +STYLE DS_SETFONT | WS_CAPTION | WS_SYSMENU +CAPTION "Edit Patch" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + PUSHBUTTON "Cancel",IDCANCEL,91,135,49,15,NOT WS_TABSTOP + PUSHBUTTON "Save",IDC_SAVE,38,135,49,15,NOT WS_TABSTOP + LTEXT "Address:",IDC_STATIC,5,4,50,10 + EDITTEXT IDC_ADDRESS,58,3,69,12,ES_AUTOHSCROLL + LTEXT "Data:",IDC_STATIC,5,22,50,10 + EDITTEXT IDC_DATA,58,21,69,12,ES_AUTOHSCROLL + LTEXT "Group:",IDC_STATIC,5,39,50,10 + EDITTEXT IDC_GROUP,58,38,69,12,ES_AUTOHSCROLL + CONTROL "",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_ARROWKEYS,132,36,11,20 + LTEXT "CPU:",IDC_STATIC,5,58,50,10 + LTEXT "Enabled:",IDC_STATIC,5,76,50,10 + LTEXT "PlaceToPatch:",IDC_STATIC,5,94,50,10 + LTEXT "Type:",IDC_STATIC,5,111,50,10 + COMBOBOX IDC_CPU,57,56,70,37,CBS_DROPDOWNLIST | CBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_ENABLED,57,74,70,38,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_PLACETOPATCH,57,92,70,35,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_TYPE,57,109,70,54,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +END + +IDD_ADDRAW DIALOGEX 3, 1, 145, 71 +STYLE DS_SETFONT | WS_CAPTION | WS_SYSMENU +CAPTION "Add Cheat" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + PUSHBUTTON "Add",IDOK,91,51,49,15,NOT WS_TABSTOP + LTEXT "Enter RAW codes:",-1,5,5,123,10,NOT WS_GROUP + EDITTEXT IDC_ADDR,5,15,132,28,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | NOT WS_TABSTOP + PUSHBUTTON "Cancel",IDCANCEL,38,51,49,15,NOT WS_TABSTOP +END + +IDD_PNACHWRITER DIALOGEX 3, 1, 146, 161 +STYLE DS_SETFONT | WS_CAPTION | WS_SYSMENU +CAPTION "pnach Writer" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + PUSHBUTTON "Cancel",IDCANCEL,91,141,49,15,NOT WS_TABSTOP + PUSHBUTTON "Save",IDC_SAVE,38,141,49,15,NOT WS_TABSTOP + LTEXT "CRC:",IDC_STATIC,5,5,50,10 + EDITTEXT IDC_CRC,58,4,69,12,ES_AUTOHSCROLL + LTEXT "Comment:",IDC_STATIC,5,22,50,10 + EDITTEXT IDC_COMMENT,58,21,69,12,ES_AUTOHSCROLL + LTEXT "Gametitle:",IDC_STATIC,5,39,50,10 + EDITTEXT IDC_GAMETITLE,58,38,69,12,ES_AUTOHSCROLL + LTEXT "Round Mode:",IDC_STATIC,5,123,50,10 + LTEXT "Fastmemory:",IDC_STATIC,5,76,50,10 + COMBOBOX IDC_ROUND2,102,122,39,63,CBS_DROPDOWNLIST | CBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_ROUND1,57,122,41,79,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Check1",IDC_FASTMEMORY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,58,76,9,8 + LTEXT "Zerogs:",IDC_STATIC,5,54,50,10 + EDITTEXT IDC_ZEROGS,57,54,69,12,ES_AUTOHSCROLL + LTEXT "Path3 Hack:",IDC_STATIC,6,91,50,10 + CONTROL "Check1",IDC_PATH3HACK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,58,91,9,8 + LTEXT "vunanmode:",IDC_STATIC,5,107,50,10 + CONTROL "Check1",IDC_VUNANMODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,58,107,9,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_CHEATS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 338 + TOPMARGIN, 7 + BOTTOMMARGIN, 191 + END + + IDD_ADDGS, DIALOG + BEGIN + BOTTOMMARGIN, 74 + END + + IDD_EDITPATCH, DIALOG + BEGIN + VERTGUIDE, 58 + VERTGUIDE, 127 + BOTTOMMARGIN, 150 + HORZGUIDE, 19 + HORZGUIDE, 36 + HORZGUIDE, 54 + HORZGUIDE, 72 + HORZGUIDE, 90 + HORZGUIDE, 108 + HORZGUIDE, 124 + HORZGUIDE, 126 + END + + IDD_ADDRAW, DIALOG + BEGIN + BOTTOMMARGIN, 66 + END + + IDD_PNACHWRITER, DIALOG + BEGIN + RIGHTMARGIN, 145 + VERTGUIDE, 58 + VERTGUIDE, 127 + BOTTOMMARGIN, 157 + HORZGUIDE, 19 + HORZGUIDE, 36 + HORZGUIDE, 54 + HORZGUIDE, 72 + HORZGUIDE, 90 + HORZGUIDE, 108 + HORZGUIDE, 124 + HORZGUIDE, 126 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxresmw.h""\r\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Spanish resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Spanish (Argentina) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ESS) +#ifdef _WIN32 +LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_ARGENTINA +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONFIG DIALOGEX 0, 0, 317, 244 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Dialog" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + COMBOBOX IDC_LISTGS,10,15,145,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure...",IDC_CONFIGGS,10,35,45,12 + PUSHBUTTON "Test...",IDC_TESTGS,60,35,45,12 + PUSHBUTTON "About...",IDC_ABOUTGS,110,35,45,12 + COMBOBOX IDC_LISTSPU2,166,15,145,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure...",IDC_CONFIGSPU2,165,35,45,12 + PUSHBUTTON "Test...",IDC_TESTSPU2,215,35,45,12 + PUSHBUTTON "About...",IDC_ABOUTSPU2,265,35,43,12 + COMBOBOX IDC_LISTCDVD,10,105,145,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure...",IDC_CONFIGCDVD,10,125,43,12 + PUSHBUTTON "Test...",IDC_TESTCDVD,60,125,45,12 + PUSHBUTTON "About...",IDC_ABOUTCDVD,110,125,45,12 + COMBOBOX IDC_LISTBIOS,165,196,145,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,207,223,50,14 + PUSHBUTTON "Cancel",IDCANCEL,260,223,50,14 + CTEXT "Graphics",IDC_GRAPHICS,50,5,62,10,SS_CENTERIMAGE + CTEXT "Sound",IDC_SOUND,200,5,71,10,SS_CENTERIMAGE + CTEXT "Cdvdrom",IDC_CDVDROM,55,95,56,10,SS_CENTERIMAGE + CTEXT "Bios",IDC_BIOS,208,185,61,10,SS_CENTERIMAGE + COMBOBOX IDC_LISTPAD1,10,60,145,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure...",IDC_CONFIGPAD1,10,80,45,12 + PUSHBUTTON "Test...",IDC_TESTPAD1,60,80,45,12 + PUSHBUTTON "About...",IDC_ABOUTPAD1,110,80,45,12 + CTEXT "Second Controller",IDC_SECONDCONTROLLER,191,50,90,10,SS_CENTERIMAGE + COMBOBOX IDC_LISTPAD2,166,60,145,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure...",IDC_CONFIGPAD2,165,80,45,12 + PUSHBUTTON "Test...",IDC_TESTPAD2,215,80,45,12 + PUSHBUTTON "About...",IDC_ABOUTPAD2,265,80,45,12 + CTEXT "First Controller",IDC_FIRSTCONTROLLER,41,50,82,10,SS_CENTERIMAGE + PUSHBUTTON "Set Bios Directory",IDC_BIOSDIR,14,223,134,14 + PUSHBUTTON "Set Plugins Directory",IDC_PLUGINSDIR,14,204,134,14 + COMBOBOX IDC_LISTDEV9,165,105,145,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure...",IDC_CONFIGDEV9,165,125,43,12 + PUSHBUTTON "Test...",IDC_TESTDEV9,215,125,45,12 + PUSHBUTTON "About...",IDC_ABOUTDEV9,265,125,45,12 + CTEXT "Dev9",IDC_DEV9,221,95,30,10,SS_CENTERIMAGE + COMBOBOX IDC_LISTUSB,10,151,145,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure...",IDC_CONFIGUSB,10,170,43,12 + PUSHBUTTON "Test...",IDC_TESTUSB,60,170,45,12 + PUSHBUTTON "About...",IDC_ABOUTUSB,110,170,45,12 + CTEXT "Usb",IDC_USB,59,140,47,10,SS_CENTERIMAGE + COMBOBOX IDC_LISTFW,165,151,145,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure...",IDC_CONFIGFW,165,169,43,12 + PUSHBUTTON "Test...",IDC_TESTFW,214,169,45,12 + PUSHBUTTON "About...",IDC_ABOUTFW,265,169,45,12 + CTEXT "FireWire",IDC_FW,206,141,60,8 +END + +IDD_BPEXEC DIALOG 0, 0, 182, 61 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "BreakPoint on Exec" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_EXECBP,92,10,82,13 + DEFPUSHBUTTON "OK",IDOK,30,40,50,14 + PUSHBUTTON "Cancel",IDCANCEL,100,40,50,14 + LTEXT "Enter BreakPoint Address:",IDC_STATIC,5,13,84,8 +END + +IDD_BPCNT DIALOG 0, 0, 182, 61 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "BreakPoint on Count" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_CNTBP,92,10,82,13 + DEFPUSHBUTTON "OK",IDOK,30,40,50,14 + PUSHBUTTON "Cancel",IDCANCEL,100,40,50,14 + LTEXT "Enter BreakPoint Count:",IDC_STATIC,5,13,77,8 +END + +IDD_ADVANCED DIALOGEX 0, 0, 177, 69 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Advanced" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,65,47,50,14 + PUSHBUTTON "Cancel",IDCANCEL,121,47,50,14 + CONTROL "Enable Reg Caching",IDC_REGCACHING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,15,81,10 + GROUPBOX "",IDC_STATIC,2,7,168,38 + PUSHBUTTON "Reset",IDC_ADVRESET,5,47,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 310 + TOPMARGIN, 7 + BOTTOMMARGIN, 237 + HORZGUIDE, 169 + END + + IDD_BPEXEC, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 175 + TOPMARGIN, 6 + BOTTOMMARGIN, 54 + END + + IDD_BPCNT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 175 + TOPMARGIN, 7 + BOTTOMMARGIN, 54 + END + + IDD_ADVANCED, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 170 + TOPMARGIN, 7 + BOTTOMMARGIN, 61 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON ICON "Cdrom02.ico" +#endif // Spanish (Argentina) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Greek resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ELL) +#ifdef _WIN32 +LANGUAGE LANG_GREEK, SUBLANG_DEFAULT +#pragma code_page(1253) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PATCHBROWSER DIALOGEX 0, 0, 487, 282 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Patches Browser" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "New Patch",IDC_NEWPATCH,112,257,60,14 + EDITTEXT IDC_PATCHTEXT,113,95,354,153,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL + PUSHBUTTON "Save Patch",IDC_SAVEPATCH,186,257,60,14,WS_DISABLED + PUSHBUTTON "Refresh List",IDC_REFRESHPATCHLIST,21,257,60,14 + LISTBOX IDC_PATCHCRCLIST,19,95,69,153,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LISTBOX IDC_PATCHNAMELIST,17,34,452,50,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LTEXT "Search game name patch:",IDC_GAMENAMESEARCH,17,16,101,9 + EDITTEXT IDC_SEARCHPATCHTEXT,109,14,295,12,ES_AUTOHSCROLL + PUSHBUTTON "Exit",IDC_EXITPB,411,257,59,14 +END + +IDD_LOGGING DIALOGEX 0, 0, 263, 177 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Logging" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,206,156,50,14 + CONTROL "Cpu log",IDC_CPULOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,5,53,10 + CONTROL "COP0 log",IDC_COP0LOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,20,59,10 + CONTROL "FPU log",IDC_FPULOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,35,55,10 + CONTROL "MMI log",IDC_MMILOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,50,55,10 + CONTROL "VUO log",IDC_VU0LOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,65,56,10 + CONTROL "Bios log",IDC_BIOSLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,80,54,10 + CONTROL "DMA log",IDC_DMALOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,95,57,10 + CONTROL "HW log",IDC_HWLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,110,53,10 + CONTROL "Unknown Memory log",IDC_MEMLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,5,84,10 + CONTROL "Elf log",IDC_ELFLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,20,35,10 + CONTROL "VIF log",IDC_VIFLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,35,38,10 + CONTROL "Scratch pad log",IDC_SPRLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,50,66,10 + CONTROL "Gif log",IDC_GIFLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,65,49,10 + CONTROL "SIF log",IDC_SIFLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,80,51,10 + GROUPBOX "IOP",IDC_STATIC,161,7,95,127 + CONTROL "IOP cpu log",IDC_IOPLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,165,15,67,10 + CONTROL "HW log",IDC_IOPHWLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,165,30,53,10 + CONTROL "Bios log",IDC_IOPBIOSLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,165,45,41,10 + CONTROL "Unknown Memory log",IDC_IOPMEMLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,165,60,85,10 + CONTROL "IPU log",IDC_IPULOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,95,39,10 + CONTROL "DMA log",IDC_IOPDMALOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,165,75,67,10 + CONTROL "PAD log",IDC_IOPPADLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,165,90,53,10 + CONTROL "CDR log",IDC_IOPCDRLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,165,104,43,10 + CONTROL "VUMicro log",IDC_VUMICROLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,110,55,10 + CONTROL "RPC services log",IDC_RPCSERVICES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,125,67,10 + CONTROL "Log to STDOUT",IDC_STDOUTPUTLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,125,68,10 + CONTROL "Symbols log",IDC_SYMLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,140,53,10 + CONTROL "Counters log",IDC_IOPCNTLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,165,118,55,10 + CONTROL "EE Counters log",IDC_EECNTLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,140,64,11 +END + +IDD_IOP_DEBUG DIALOGEX 0, 0, 358, 107 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Debug" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LISTBOX IDC_DEBUG_DISASM_IOP,7,7,330,92,LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | WS_TABSTOP,WS_EX_TRANSPARENT + SCROLLBAR IDC_DEBUG_SCROLL_IOP,340,7,11,93,SBS_VERT +END + +IDD_CPUDLG DIALOGEX 0, 0, 563, 321 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "EERec - EE/IOP recompiler (need MMX/SSE/SSE2)",IDC_CPU_EEREC, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,100,265,18 + CONTROL "VU0rec - enable recompiler for VU0 unit",IDC_CPU_VU0REC, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,14,127,219,18 + CONTROL "VU1rec - enable recompiler for VU1 unit",IDC_CPU_VU1REC, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,14,143,214,18 + CONTROL "Multi threaded GS mode (MTGS)\n (faster on dual core/HT procs, requires pcsx2 restart)",IDC_CPU_GSMULTI, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,7,166,231,30 + CONTROL "Normal - All frames are rendered as fast as possible.",IDC_CPU_FL_NORMAL, + "Button",BS_AUTORADIOBUTTON | BS_MULTILINE | WS_GROUP,309,17,221,17 + CONTROL "Limit - Force frames to normal speeds if too fast.\n (You can set a custom FPS limit below.)",IDC_CPU_FL_LIMIT, + "Button",BS_AUTORADIOBUTTON | BS_MULTILINE,309,33,222,15 + CONTROL "Frame Skip - In order to achieve normal speeds,\n some frames are skipped (fast).\n Fps displayed counts skipped frames too.",IDC_CPU_FL_SKIP, + "Button",BS_AUTORADIOBUTTON | BS_MULTILINE,309,48,221,28 + CONTROL "VU Skip - Same as 'Frame Skip', but tries to skip more.\n Artifacts might be present, but will be faster.",IDC_CPU_FL_SKIPVU, + "Button",BS_AUTORADIOBUTTON | BS_MULTILINE,309,75,220,25 + DEFPUSHBUTTON "OK",IDOK,210,298,61,14 + PUSHBUTTON "Cancel",IDCANCEL,282,298,61,14,NOT WS_TABSTOP + LTEXT "CPU Vendor",IDC_VENDORNAME,12,23,88,8 + LTEXT "Family",IDC_FAMILYNAME,12,41,88,8 + LTEXT "Cpu Speed",IDC_CPUSPEEDNAME,12,60,88,8 + LTEXT "",IDC_VENDORINPUT,112,24,124,8 + LTEXT "",IDC_FAMILYINPUT,112,41,124,8 + LTEXT "",IDC_FEATURESINPUT,111,79,124,8 + LTEXT "",IDC_CPUSPEEDINPUT,111,61,124,8 + GROUPBOX "VU Recompilers - All options are set by default",IDC_CPU_VUGROUP,7,119,265,46 + LTEXT "Features",IDC_FEATURESNAME,12,78,88,8 + GROUPBOX "",IDC_STATIC,7,7,265,90 + LTEXT "Custom FPS Limit (0=auto):",IDC_CUSTOM_FPS,327,113,124,12 + EDITTEXT IDC_CUSTOMFPS,456,113,53,13,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_CUSTOM_FRAMESKIP,456,130,53,13,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_CUSTOM_CONSECUTIVE_FRAMES,456,148,53,13,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Skip Frames when slower than:\n(See Note 1)",IDC_FRAMESKIP_LABEL1,327,129,106,20 + LTEXT "Consecutive Frames before skipping:\n(See Note 2)",IDC_FRAMESKIP_LABEL2,327,149,121,17 + GROUPBOX "Frame Limiting (F4 key switches the mode in-game!)",IDC_FRAMELIMIT,301,7,250,280 + GROUPBOX "Detailed Settings",IDC_FRAMELIMIT_OPTIONS,310,99,234,185 + LTEXT "*Note 1: Will only skip when slower than this fps number.\n (0 = Auto) ; (9999 = Forced-Frameskip regardless of speed.)\n (e.g. If set to 45, will only skip when slower than 45fps.)",IDC_FRAMESKIP_LABEL3,318,189,217,26 + LTEXT "*Note 2: Will render this number of consecutive frames before\n skipping the next frame. (0=default)\n (e.g. If set to 2, will render 2 frames before skipping 1.)",IDC_FRAMESKIP_LABEL4,318,216,217,25 + EDITTEXT IDC_CUSTOM_CONSECUTIVE_SKIP,456,166,53,13,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Consecutive Frames to skip:\n(See Note 3)",IDC_FRAMESKIP_LABEL5,327,167,121,17 + LTEXT "*Note 3: Will skip this number of frames before\n rendering the next sequence of frames. (0=default)\n (e.g. If set to 2, will skip 2 consecutive frames whenever its time\n to skip.)",IDC_FRAMESKIP_LABEL6,318,244,217,32 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_LOGGING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 256 + TOPMARGIN, 7 + BOTTOMMARGIN, 170 + END + + IDD_IOP_DEBUG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 351 + TOPMARGIN, 7 + BOTTOMMARGIN, 100 + END + + IDD_CPUDLG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 558 + TOPMARGIN, 7 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Greek resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + ///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/pcsx2/windows/ps2_silver.bmp b/pcsx2/windows/ps2_silver.bmp new file mode 100644 index 0000000000000000000000000000000000000000..cb58820cdf1d734c0f8889bde4b6023e9feb4e12 GIT binary patch literal 37974 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 + + +We are pleased to announce the availability of a new release of +Pthreads-win32, an Open Source Software implementation of the +Threads component of the POSIX 1003.1 2001 Standard for Microsoft's +Win32 environment. Some functions from other sections of POSIX +1003.1 2001 are also supported including semaphores and scheduling +functions. + +Some common non-portable functions are also implemented for +additional compatibility, as are a few functions specific +to pthreads-win32 for easier integration with Win32 applications. + +Pthreads-win32 is free software, distributed under the GNU Lesser +General Public License (LGPL). + + +Acknowledgements +---------------- +This library is based originally on a Win32 pthreads +implementation contributed by John Bossom . + +The implementation of Condition Variables uses algorithms developed +by Alexander Terekhov and Louis Thomas. + +The implementation of POSIX mutexes has been improved by Thomas Pfaff +and later by Alexander Terekhov. + +The implementation of Spinlocks and Barriers was contributed +by Ross Johnson. + +The implementation of read/write locks was contributed by +Aurelio Medina and improved by Alexander Terekhov. + +Many others have contributed significant time and effort to solve crutial +problems in order to make the library workable, robust and reliable. + +Thanks to Xavier Leroy for granting permission to use and modify his +LinuxThreads manual pages. + +Thanks to The Open Group for making the Single Unix Specification +publicly available - many of the manual pages included in the package +were extracted from it. + +There is also a separate CONTRIBUTORS file. This file and others are +on the web site: + + http://sources.redhat.com/pthreads-win32 + +As much as possible, the ChangeLog file acknowledges contributions to the +code base in more detail. + + +Changes since the last release +------------------------------ +These are now documented in the NEWS file. +See the ChangeLog file also. + + +Known Bugs +---------- +These are now documented in the BUGS file. + + +Level of standards conformance +------------------------------ + +The following POSIX 1003.1 2001 options are defined and set to 200112L: + + _POSIX_THREADS + _POSIX_THREAD_SAFE_FUNCTIONS + _POSIX_THREAD_ATTR_STACKSIZE + _POSIX_THREAD_PRIORITY_SCHEDULING + _POSIX_SEMAPHORES + _POSIX_READER_WRITER_LOCKS + _POSIX_SPIN_LOCKS + _POSIX_BARRIERS + + +The following POSIX 1003.1 2001 options are defined and set to -1: + + _POSIX_THREAD_ATTR_STACKADDR + _POSIX_THREAD_PRIO_INHERIT + _POSIX_THREAD_PRIO_PROTECT + _POSIX_THREAD_PROCESS_SHARED + + +The following POSIX 1003.1 2001 limits are defined and set: + + _POSIX_THREAD_THREADS_MAX + _POSIX_SEM_VALUE_MAX + _POSIX_SEM_NSEMS_MAX + _POSIX_THREAD_KEYS_MAX + _POSIX_THREAD_DESTRUCTOR_ITERATIONS + PTHREAD_STACK_MIN + PTHREAD_THREADS_MAX + SEM_VALUE_MAX + SEM_NSEMS_MAX + PTHREAD_KEYS_MAX + PTHREAD_DESTRUCTOR_ITERATIONS + + +The following functions are implemented: + + --------------------------- + PThreads + --------------------------- + pthread_attr_init + pthread_attr_destroy + pthread_attr_getdetachstate + pthread_attr_getstackaddr + pthread_attr_getstacksize + pthread_attr_setdetachstate + pthread_attr_setstackaddr + pthread_attr_setstacksize + + pthread_create + pthread_detach + pthread_equal + pthread_exit + pthread_join + pthread_once + pthread_self + + pthread_cancel + pthread_cleanup_pop + pthread_cleanup_push + pthread_setcancelstate + pthread_setcanceltype + pthread_testcancel + + --------------------------- + Thread Specific Data + --------------------------- + pthread_key_create + pthread_key_delete + pthread_setspecific + pthread_getspecific + + --------------------------- + Mutexes + --------------------------- + pthread_mutexattr_init + pthread_mutexattr_destroy + pthread_mutexattr_getpshared + pthread_mutexattr_setpshared + pthread_mutexattr_gettype + pthread_mutexattr_settype (types: PTHREAD_MUTEX_DEFAULT + PTHREAD_MUTEX_NORMAL + PTHREAD_MUTEX_ERRORCHECK + PTHREAD_MUTEX_RECURSIVE ) + pthread_mutex_init + pthread_mutex_destroy + pthread_mutex_lock + pthread_mutex_trylock + pthread_mutex_timedlock + pthread_mutex_unlock + + --------------------------- + Condition Variables + --------------------------- + pthread_condattr_init + pthread_condattr_destroy + pthread_condattr_getpshared + pthread_condattr_setpshared + + pthread_cond_init + pthread_cond_destroy + pthread_cond_wait + pthread_cond_timedwait + pthread_cond_signal + pthread_cond_broadcast + + --------------------------- + Read/Write Locks + --------------------------- + pthread_rwlock_init + pthread_rwlock_destroy + pthread_rwlock_tryrdlock + pthread_rwlock_trywrlock + pthread_rwlock_rdlock + pthread_rwlock_timedrdlock + pthread_rwlock_rwlock + pthread_rwlock_timedwrlock + pthread_rwlock_unlock + pthread_rwlockattr_init + pthread_rwlockattr_destroy + pthread_rwlockattr_getpshared + pthread_rwlockattr_setpshared + + --------------------------- + Spin Locks + --------------------------- + pthread_spin_init + pthread_spin_destroy + pthread_spin_lock + pthread_spin_unlock + pthread_spin_trylock + + --------------------------- + Barriers + --------------------------- + pthread_barrier_init + pthread_barrier_destroy + pthread_barrier_wait + pthread_barrierattr_init + pthread_barrierattr_destroy + pthread_barrierattr_getpshared + pthread_barrierattr_setpshared + + --------------------------- + Semaphores + --------------------------- + sem_init + sem_destroy + sem_post + sem_wait + sem_trywait + sem_timedwait + sem_getvalue (# free if +ve, # of waiters if -ve) + sem_open (returns an error ENOSYS) + sem_close (returns an error ENOSYS) + sem_unlink (returns an error ENOSYS) + + --------------------------- + RealTime Scheduling + --------------------------- + pthread_attr_getschedparam + pthread_attr_setschedparam + pthread_attr_getinheritsched + pthread_attr_setinheritsched + pthread_attr_getschedpolicy (only supports SCHED_OTHER) + pthread_attr_setschedpolicy (only supports SCHED_OTHER) + pthread_getschedparam + pthread_setschedparam + pthread_getconcurrency + pthread_setconcurrency + pthread_attr_getscope + pthread_attr_setscope (only supports PTHREAD_SCOPE_SYSTEM) + sched_get_priority_max + sched_get_priority_min + sched_rr_get_interval (returns an error ENOTSUP) + sched_setscheduler (only supports SCHED_OTHER) + sched_getscheduler (only supports SCHED_OTHER) + sched_yield + + --------------------------- + Signals + --------------------------- + pthread_sigmask + pthread_kill (only supports zero sig value, + for thread validity checking) + + --------------------------- + Non-portable routines (see the README.NONPORTABLE file for usage) + --------------------------- + pthread_getw32threadhandle_np + pthread_timechange_handler_np + pthread_delay_np + pthread_mutexattr_getkind_np + pthread_mutexattr_setkind_np (types: PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ADAPTIVE_NP, + PTHREAD_MUTEX_TIMED_NP) + pthread_num_processors_np + pthread_win32_process_attach_np (Required when statically linking + the library) + pthread_win32_process_detach_np (Required when statically linking + the library) + pthread_win32_thread_attach_np (Required when statically linking + the library) + pthread_win32_thread_detach_np (Required when statically linking + the library) + + --------------------------- + Static Initializers + --------------------------- + PTHREAD_ONCE_INIT + PTHREAD_MUTEX_INITIALIZER + PTHREAD_RECURSIVE_MUTEX_INITIALIZER + PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + PTHREAD_ERRORCHECK_MUTEX_INITIALIZER + PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP + PTHREAD_COND_INITIALIZER + PTHREAD_RWLOCK_INITIALIZER + PTHREAD_SPINLOCK_INITIALIZER + + --------------------------- + Thread-Safe C Runtime Library (macros) + --------------------------- + strtok_r + asctime_r + ctime_r + gmtime_r + localtime_r + rand_r + + +The following functions are not implemented: + + --------------------------- + RealTime Scheduling + --------------------------- + pthread_mutex_getprioceiling + pthread_mutex_setprioceiling + pthread_mutex_attr_getprioceiling + pthread_mutex_attr_getprotocol + pthread_mutex_attr_setprioceiling + pthread_mutex_attr_setprotocol + + --------------------------- + Fork Handlers + --------------------------- + pthread_atfork + + --------------------------- + Stdio + --------------------------- + flockfile + ftrylockfile + funlockfile + getc_unlocked + getchar_unlocked + putc_unlocked + putchar_unlocked + + --------------------------- + Thread-Safe C Runtime Library + --------------------------- + readdir_r + getgrgid_r + getgrnam_r + getpwuid_r + getpwnam_r + + --------------------------- + Signals + --------------------------- + sigtimedwait + sigwait + sigwaitinfo + + --------------------------- + General + --------------------------- + sysconf + +The library includes two non-API functions for creating cancellation +points in applications and libraries: + + pthreadCancelableWait + pthreadCancelableTimedWait + + +Availability +------------ + +The prebuilt DLL, export libs (for both MSVC and Mingw32), and the header +files (pthread.h, semaphore.h, sched.h) are available along with the +complete source code. + +The source code can be found at: + + ftp://sources.redhat.com/pub/pthreads-win32 + +and as individual source code files at + + ftp://sources.redhat.com/pub/pthreads-win32/source + +The pre-built DLL, export libraries and include files can be found at: + + ftp://sources.redhat.com/pub/pthreads-win32/dll-latest + + + +Mailing List +------------ + +There is a mailing list for discussing pthreads on Win32. To join, +send email to: + + pthreads-win32-subscribe@sourceware.cygnus.com + + +Application Development Environments +------------------------------------ + +See the README file for more information. + +MSVC: +MSVC using SEH works. Distribute pthreadVSE.dll with your application. +MSVC using C++ EH works. Distribute pthreadVCE.dll with your application. +MSVC using C setjmp/longjmp works. Distribute pthreadVC.dll with your application. + + +Mingw32: +See the FAQ, Questions 6 and 10. + +Mingw using C++ EH works. Distribute pthreadGCE.dll with your application. +Mingw using C setjmp/longjmp works. Distribute pthreadGC.dll with your application. + + +Cygwin: (http://sourceware.cygnus.com/cygwin/) +Developers using Cygwin will not need pthreads-win32 since it has POSIX threads +support. Refer to its documentation for details and extent. + + +UWIN: +UWIN is a complete Unix-like environment for Windows from AT&T. Pthreads-win32 +doesn't currently support UWIN (and vice versa), but that may change in the +future. + +Generally: +For convenience, the following pre-built files are available on the FTP site +(see Availability above): + + pthread.h - for POSIX 1c threads + semaphore.h - for POSIX 1b semaphores + sched.h - for POSIX 1b scheduling + pthreadVCE.dll - built with MSVC++ compiler using C++ EH + pthreadVCE.lib + pthreadVC.dll - built with MSVC compiler using C setjmp/longjmp + pthreadVC.lib + pthreadVSE.dll - built with MSVC compiler using SEH + pthreadVSE.lib + pthreadGCE.dll - built with Mingw32 G++ 2.95.2-1 + pthreadGC.dll - built with Mingw32 GCC 2.95.2-1 using setjmp/longjmp + libpthreadGCE.a - derived from pthreadGCE.dll + libpthreadGC.a - derived from pthreadGC.dll + gcc.dll - needed if distributing applications that use + pthreadGCE.dll (but see the FAQ Q 10 for the latest + related information) + +These are the only files you need in order to build POSIX threads +applications for Win32 using either MSVC or Mingw32. + +See the FAQ file in the source tree for additional information. + + +Documentation +------------- + +For the authoritative reference, see the online POSIX +standard reference at: + + http://www.OpenGroup.org + +For POSIX Thread API programming, several reference books are +available: + + Programming with POSIX Threads + David R. Butenhof + Addison-Wesley (pub) + + Pthreads Programming + By Bradford Nichols, Dick Buttlar & Jacqueline Proulx Farrell + O'Reilly (pub) + +On the web: see the links at the bottom of the pthreads-win32 site: + + http://sources.redhat.com/pthreads-win32/ + + Currently, there is no documentation included in the package apart + from the copious comments in the source code. + + + +Enjoy! + +Ross Johnson diff --git a/pcsx2/windows/pthreads/BUGS b/pcsx2/windows/pthreads/BUGS new file mode 100644 index 0000000000..71451aa540 --- /dev/null +++ b/pcsx2/windows/pthreads/BUGS @@ -0,0 +1,133 @@ +---------- +Known bugs +---------- + +1. Not strictly a bug, more of a gotcha. + + Under MS VC++ (only tested with version 6.0), a term_func + set via the standard C++ set_terminate() function causes the + application to abort. + + Notes from the MSVC++ manual: + 1) A term_func() should call exit(), otherwise + abort() will be called on return to the caller. + A call to abort() raises SIGABRT and the default signal handler + for all signals terminates the calling program with + exit code 3. + 2) A term_func() must not throw an exception. Therefore + term_func() should not call pthread_exit(), which + works by throwing an exception (pthreadVCE or pthreadVSE) + or by calling longjmp (pthreadVC). + + Workaround: avoid using pthread_exit() in C++ applications. Exit + threads by dropping through the end of the thread routine. + +2. Cancellation problems in optimised code + - Milan Gardian + + This is suspected to be a compiler bug in VC6.0, and also seen in + VC7.0 and VS .NET 2003. The GNU C++ compiler does not have a problem + with this, and it has been reported that the Intel C++ 8.1 compiler + and Visual C++ 2005 Express Edition Beta2 pass tests\semaphore4.c + (which exposes the bug). + + Workaround [rpj - 2 Feb 2002] + ----------------------------- + [Please note: this workaround did not solve a similar problem in + snapshot-2004-11-03 or later, even though similar symptoms were seen. + tests\semaphore4.c fails in that snapshot for the VCE version of the + DLL.] + + The problem disappears when /Ob0 is used, i.e. /O2 /Ob0 works OK, + but if you want to use inlining optimisation you can be much more + specific about where it's switched off and on by using a pragma. + + So the inlining optimisation is interfering with the way that cleanup + handlers are run. It appears to relate to auto-inlining of class methods + since this is the only auto inlining that is performed at /O1 optimisation + (functions with the "inline" qualifier are also inlined, but the problem + doesn't appear to involve any such functions in the library or testsuite). + + In order to confirm the inlining culprit, the following use of pragmas + eliminate the problem but I don't know how to make it transparent, putting + it in, say, pthread.h where pthread_cleanup_push defined as a macro. + + #pragma inline_depth(0) + pthread_cleanup_push(handlerFunc, (void *) &arg); + + /* ... */ + + pthread_cleanup_pop(0); + #pragma inline_depth() + + Note the empty () pragma value after the pop macro. This resets depth to the + default. Or you can specify a non-zero depth here. + + The pragma is also needed (and now used) within the library itself wherever + cleanup handlers are used (condvar.c and rwlock.c). + + Use of these pragmas allows compiler optimisations /O1 and /O2 to be + used for either or both the library and applications. + + Experimenting further, I found that wrapping the actual cleanup handler + function with #pragma auto_inline(off|on) does NOT work. + + MSVC6.0 doesn't appear to support the C99 standard's _Pragma directive, + however, later versions may. This form is embeddable inside #define + macros, which would be ideal because it would mean that it could be added + to the push/pop macro definitions in pthread.h and hidden from the + application programmer. + + [/rpj] + + Original problem description + ---------------------------- + + The cancellation (actually, cleanup-after-cancel) tests fail when using VC + (professional) optimisation switches (/O1 or /O2) in pthreads library. I + have not investigated which concrete optimisation technique causes this + problem (/Og, /Oi, /Ot, /Oy, /Ob1, /Gs, /Gf, /Gy, etc.), but here is a + summary of builds and corresponding failures: + + * pthreads VSE (optimised tests): OK + * pthreads VCE (optimised tests): Failed "cleanup1" test (runtime) + + * pthreads VSE (DLL in CRT, optimised tests): OK + * pthreads VCE (DLL in CRT, optimised tests): Failed "cleanup1" test + (runtime) + + Please note that while in VSE version of the pthreads library the + optimisation does not really have any impact on the tests (they pass OK), in + VCE version addition of optimisation (/O2 in this case) causes the tests to + fail uniformly - either in "cleanup0" or "cleanup1" test cases. + + Please note that all the tests above use default pthreads DLL (no + optimisations, linked with either static or DLL CRT, based on test type). + Therefore the problem lies not within the pthreads DLL but within the + compiled client code (the application using pthreads -> involvement of + "pthread.h"). + + I think the message of this section is that usage of VCE version of pthreads + in applications relying on cancellation/cleanup AND using optimisations for + creation of production code is highly unreliable for the current version of + the pthreads library. + +3. The Borland Builder 5.5 version of the library produces memory read exceptions +in some tests. + +4. pthread_barrier_wait() can deadlock if the number of potential calling +threads for a particular barrier is greater than the barrier count parameter +given to pthread_barrier_init() for that barrier. + +This is due to the very lightweight implementation of pthread-win32 barriers. +To cope with more than "count" possible waiters, barriers must effectively +implement all the same safeguards as condition variables, making them much +"heavier" than at present. + +The workaround is to ensure that no more than "count" threads attempt to wait +at the barrier. + +5. Canceling a thread blocked on pthread_once appears not to work in the MSVC++ +version of the library "pthreadVCE.dll". The test case "once3.c" hangs. I have no +clues on this at present. All other versions pass this test ok - pthreadsVC.dll, +pthreadsVSE.dll, pthreadsGC.dll and pthreadsGCE.dll. diff --git a/pcsx2/windows/pthreads/CONTRIBUTORS b/pcsx2/windows/pthreads/CONTRIBUTORS new file mode 100644 index 0000000000..2f70e385c0 --- /dev/null +++ b/pcsx2/windows/pthreads/CONTRIBUTORS @@ -0,0 +1,129 @@ +Contributors (in approximate order of appearance) + +[See also the ChangeLog file where individuals are +attributed in log entries. Likewise in the FAQ file.] + +Ben Elliston bje at cygnus dot com + Initiated the project; + setup the project infrastructure (CVS, web page, etc.); + early prototype routines. +Ross Johnson rpj at callisto dot canberra dot edu dot au + early prototype routines; + ongoing project coordination/maintenance; + implementation of spin locks and barriers; + various enhancements; + bug fixes; + documentation; + testsuite. +Robert Colquhoun rjc at trump dot net dot au + Early bug fixes. +John E. Bossom John dot Bossom at cognos dot com + Contributed substantial original working implementation; + bug fixes; + ongoing guidance and standards interpretation. +Anders Norlander anorland at hem2 dot passagen dot se + Early enhancements and runtime checking for supported + Win32 routines. +Tor Lillqvist tml at iki dot fi + General enhancements; + early bug fixes to condition variables. +Scott Lightner scott at curriculum dot com + Bug fix. +Kevin Ruland Kevin dot Ruland at anheuser-busch dot com + Various bug fixes. +Mike Russo miker at eai dot com + Bug fix. +Mark E. Armstrong avail at pacbell dot net + Bug fixes. +Lorin Hochstein lmh at xiphos dot ca + general bug fixes; bug fixes to condition variables. +Peter Slacik Peter dot Slacik at tatramed dot sk + Bug fixes. +Mumit Khan khan at xraylith dot wisc dot edu + Fixes to work with Mingw32. +Milan Gardian mg at tatramed dot sk + Bug fixes and reports/analyses of obscure problems. +Aurelio Medina aureliom at crt dot com + First implementation of read-write locks. +Graham Dumpleton Graham dot Dumpleton at ra dot pad dot otc dot telstra dot com dot au + Bug fix in condition variables. +Tristan Savatier tristan at mpegtv dot com + WinCE port. +Erik Hensema erik at hensema dot xs4all dot nl + Bug fixes. +Rich Peters rpeters at micro-magic dot com +Todd Owen towen at lucidcalm dot dropbear dot id dot au + Bug fixes to dll loading. +Jason Nye jnye at nbnet dot nb dot ca + Implementation of async cancelation. +Fred Forester fforest at eticomm dot net +Kevin D. Clark kclark at cabletron dot com +David Baggett dmb at itasoftware dot com + Bug fixes. +Paul Redondo paul at matchvision dot com +Scott McCaskill scott at 3dfx dot com + Bug fixes. +Jef Gearhart jgearhart at tpssys dot com + Bug fix. +Arthur Kantor akantor at bexusa dot com + Mutex enhancements. +Steven Reddie smr at essemer dot com dot au + Bug fix. +Alexander Terekhov TEREKHOV at de dot ibm dot com + Re-implemented and improved read-write locks; + (with Louis Thomas) re-implemented and improved + condition variables; + enhancements to semaphores; + enhancements to mutexes; + new mutex implementation in 'futex' style; + suggested a robust implementation of pthread_once + similar to that implemented by V.Kliathcko; + system clock change handling re CV timeouts; + bug fixes. +Thomas Pfaff tpfaff at gmx dot net + Changes to make C version usable with C++ applications; + re-implemented mutex routines to avoid Win32 mutexes + and TryEnterCriticalSection; + procedure to fix Mingw32 thread-safety issues. +Franco Bez franco dot bez at gmx dot de + procedure to fix Mingw32 thread-safety issues. +Louis Thomas lthomas at arbitrade dot com + (with Alexander Terekhov) re-implemented and improved + condition variables. +David Korn dgk at research dot att dot com + Ported to UWIN. +Phil Frisbie, Jr. phil at hawksoft dot com + Bug fix. +Ralf Brese Ralf dot Brese at pdb4 dot siemens dot de + Bug fix. +prionx at juno dot com prionx at juno dot com + Bug fixes. +Max Woodbury mtew at cds dot duke dot edu + POSIX versioning conditionals; + reduced namespace pollution; + idea to separate routines to reduce statically + linked image sizes. +Rob Fanner rfanner at stonethree dot com + Bug fix. +Michael Johnson michaelj at maine dot rr dot com + Bug fix. +Nicolas Barry boozai at yahoo dot com + Bug fixes. +Piet van Bruggen pietvb at newbridges dot nl + Bug fix. +Makoto Kato raven at oldskool dot jp + AMD64 port. +Panagiotis E. Hadjidoukas peh at hpclab dot ceid dot upatras dot gr + Contributed the QueueUserAPCEx package which + makes preemptive async cancelation possible. +Will Bryant will dot bryant at ecosm dot com + Borland compiler patch and makefile. +Anuj Goyal anuj dot goyal at gmail dot com + Port to Digital Mars compiler. +Gottlob Frege gottlobfrege at gmail dot com + re-implemented pthread_once (version 2) + (pthread_once cancellation added by rpj). +Vladimir Kliatchko vladimir at kliatchko dot com + reimplemented pthread_once with the same form + as described by A.Terekhov (later version 2); + implementation of MCS (Mellor-Crummey/Scott) locks. diff --git a/pcsx2/windows/pthreads/COPYING b/pcsx2/windows/pthreads/COPYING new file mode 100644 index 0000000000..af8e3db912 --- /dev/null +++ b/pcsx2/windows/pthreads/COPYING @@ -0,0 +1,150 @@ + pthreads-win32 - a POSIX threads library for Microsoft Windows + + +This file is Copyrighted +------------------------ + + This file is covered under the following Copyright: + + Copyright (C) 2001,2006 Ross P. Johnson + All rights reserved. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Pthreads-win32 is covered by the GNU Lesser General Public License +------------------------------------------------------------------ + + Pthreads-win32 is open software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation version 2.1 of the + License. + + Pthreads-win32 is several binary link libraries, several modules, + associated interface definition files and scripts used to control + its compilation and installation. + + Pthreads-win32 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 Lesser General Public License for more details. + + A copy of the GNU Lesser General Public License is distributed with + pthreads-win32 under the filename: + + COPYING.LIB + + You should have received a copy of the version 2.1 GNU Lesser General + Public License with pthreads-win32; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place + Suite 330 + Boston, MA 02111-1307 + USA + + The contact addresses for pthreads-win32 is as follows: + + Web: http://sources.redhat.com/pthreads-win32 + Email: Ross Johnson + Please use: Firstname.Lastname@homemail.com.au + + + +Pthreads-win32 copyrights and exception files +--------------------------------------------- + + With the exception of the files listed below, Pthreads-win32 + is covered under the following GNU Lesser General Public License + Copyrights: + + Pthreads-win32 - POSIX Threads Library for Win32 + Copyright(C) 1998 John E. Bossom + Copyright(C) 1999,2006 Pthreads-win32 contributors + + The current list of contributors is contained + in the file CONTRIBUTORS included with the source + code distribution. The current list of CONTRIBUTORS + can also be seen at the following WWW location: + http://sources.redhat.com/pthreads-win32/contributors.html + + Contact Email: Ross Johnson + Please use: Firstname.Lastname@homemail.com.au + + These files are not covered under one of the Copyrights listed above: + + COPYING + COPYING.LIB + tests/rwlock7.c + + This file, COPYING, is distributed under the Copyright found at the + top of this file. It is important to note that you may distribute + verbatim copies of this file but you may not modify this file. + + The file COPYING.LIB, which contains a copy of the version 2.1 + GNU Lesser General Public License, is itself copyrighted by the + Free Software Foundation, Inc. Please note that the Free Software + Foundation, Inc. does NOT have a copyright over Pthreads-win32, + only the COPYING.LIB that is supplied with pthreads-win32. + + The file tests/rwlock7.c is derived from code written by + Dave Butenhof for his book 'Programming With POSIX(R) Threads'. + The original code was obtained by free download from his website + http://home.earthlink.net/~anneart/family/Threads/source.html + and did not contain a copyright or author notice. It is assumed to + be freely distributable. + + In all cases one may use and distribute these exception files freely. + And because one may freely distribute the LGPL covered files, the + entire pthreads-win32 source may be freely used and distributed. + + + +General Copyleft and License info +--------------------------------- + + For general information on Copylefts, see: + + http://www.gnu.org/copyleft/ + + For information on GNU Lesser General Public Licenses, see: + + http://www.gnu.org/copyleft/lesser.html + http://www.gnu.org/copyleft/lesser.txt + + +Why pthreads-win32 did not use the GNU General Public License +------------------------------------------------------------- + + The goal of the pthreads-win32 project has been to + provide a quality and complete implementation of the POSIX + threads API for Microsoft Windows within the limits imposed + by virtue of it being a stand-alone library and not + linked directly to other POSIX compliant libraries. For + example, some functions and features, such as those based + on POSIX signals, are missing. + + Pthreads-win32 is a library, available in several different + versions depending on supported compilers, and may be used + as a dynamically linked module or a statically linked set of + binary modules. It is not an application on it's own. + + It was fully intended that pthreads-win32 be usable with + commercial software not covered by either the GPL or the LGPL + licenses. Pthreads-win32 has many contributors to it's + code base, many of whom have done so because they have + used the library in commercial or proprietry software + projects. + + Releasing pthreads-win32 under the LGPL ensures that the + library can be used widely, while at the same time ensures + that bug fixes and improvements to the pthreads-win32 code + itself is returned to benefit all current and future users + of the library. + + Although pthreads-win32 makes it possible for applications + that use POSIX threads to be ported to Win32 platforms, the + broader goal of the project is to encourage the use of open + standards, and in particular, to make it just a little easier + for developers writing Win32 applications to consider + widening the potential market for their products. diff --git a/pcsx2/windows/pthreads/FAQ b/pcsx2/windows/pthreads/FAQ new file mode 100644 index 0000000000..ee8968ba4c --- /dev/null +++ b/pcsx2/windows/pthreads/FAQ @@ -0,0 +1,403 @@ + ========================================= + PTHREADS-WIN32 Frequently Asked Questions + ========================================= + +INDEX +----- + +Q 1 What is it? + +Q 2 Which of the several dll versions do I use? + or, + What are all these pthread*.dll and pthread*.lib files? + +Q 3 What is the library naming convention? + +Q 4 Cleanup code default style or: it used to work when I built + the library myself, but now it doesn't - why? + +Q 5 Why is the default library version now less exception-friendly? + +Q 6 Should I use Cygwin or Mingw32 as a development environment? + +Q 7 Now that pthreads-win32 builds under Mingw32, why do I get + memory access violations (segfaults)? + +Q 8 How do I use pthread.dll for Win32 (Visual C++ 5.0) + +Q 9 Cancelation doesn't work for me, why? + +Q 10 How do I generate pthreadGCE.dll and libpthreadw32.a for use + with Mingw32? + +============================================================================= + +Q 1 What is it? +--- + +Pthreads-win32 is an Open Source Software implementation of the +Threads component of the POSIX 1003.1c 1995 Standard for Microsoft's +Win32 environment. Some functions from POSIX 1003.1b are also +supported including semaphores. Other related functions include +the set of read-write lock functions. The library also supports +some of the functionality of the Open Group's Single Unix +specification, version 2, namely mutex types. + +See the file "ANNOUNCE" for more information including standards +conformance details and list of supported routines. + + +------------------------------------------------------------------------------ + +Q 2 Which of the several dll versions do I use? +--- or, + What are all these pthread*.dll and pthread*.lib files? + +Simply, you only use one of them, but you need to choose carefully. + +The most important choice you need to make is whether to use a +version that uses exceptions internally, or not (there are versions +of the library that use exceptions as part of the thread +cancelation and cleanup implementation, and one that uses +setjmp/longjmp instead). + +There is some contension amongst POSIX threads experts as +to how POSIX threads cancelation and exit should work +with languages that include exceptions and handlers, e.g. +C++ and even C (Microsoft's Structured Exceptions). + +The issue is: should cancelation of a thread in, say, +a C++ application cause object destructors and C++ exception +handlers to be invoked as the stack unwinds during thread +exit, or not? + +There seems to be more opinion in favour of using the +standard C version of the library (no EH) with C++ applications +since this appears to be the assumption commercial pthreads +implementations make. Therefore, if you use an EH version +of pthreads-win32 then you may be under the illusion that +your application will be portable, when in fact it is likely to +behave very differently linked with other pthreads libraries. + +Now you may be asking: why have you kept the EH versions of +the library? + +There are a couple of reasons: +- there is division amongst the experts and so the code may + be needed in the future. (Yes, it's in the repository and we + can get it out anytime in the future, but ...) +- pthreads-win32 is one of the few implementations, and possibly + the only freely available one, that has EH versions. It may be + useful to people who want to play with or study application + behaviour under these conditions. + + +------------------------------------------------------------------------------ + +Q 3 What is the library naming convention? +--- + +Because the library is being built using various exception +handling schemes and compilers - and because the library +may not work reliably if these are mixed in an application, +each different version of the library has it's own name. + +Note 1: the incompatibility is really between EH implementations +of the different compilers. It should be possible to use the +standard C version from either compiler with C++ applications +built with a different compiler. If you use an EH version of +the library, then you must use the same compiler for the +application. This is another complication and dependency that +can be avoided by using only the standard C library version. + +Note 2: if you use a standard C pthread*.dll with a C++ +application, then any functions that you define that are +intended to be called via pthread_cleanup_push() must be +__cdecl. + +Note 3: the intention is to also name either the VC or GC +version (it should be arbitrary) as pthread.dll, including +pthread.lib and libpthread.a as appropriate. + +In general: + pthread[VG]{SE,CE,C}.dll + pthread[VG]{SE,CE,C}.lib + +where: + [VG] indicates the compiler + V - MS VC + G - GNU C + + {SE,CE,C} indicates the exception handling scheme + SE - Structured EH + CE - C++ EH + C - no exceptions - uses setjmp/longjmp + +For example: + pthreadVSE.dll (MSVC/SEH) + pthreadGCE.dll (GNUC/C++ EH) + pthreadGC.dll (GNUC/not dependent on exceptions) + +The GNU library archive file names have changed to: + + libpthreadGCE.a + libpthreadGC.a + + +------------------------------------------------------------------------------ + +Q 4 Cleanup code default style or: it used to work when I built +--- the library myself, but now it doesn't - why? + +Up to and including snapshot 2001-07-12, if not defined, the cleanup +style was determined automatically from the compiler used, and one +of the following was defined accordingly: + + __CLEANUP_SEH MSVC only + __CLEANUP_CXX C++, including MSVC++, GNU G++ + __CLEANUP_C C, including GNU GCC, not MSVC + +These defines determine the style of cleanup (see pthread.h) and, +most importantly, the way that cancelation and thread exit (via +pthread_exit) is performed (see the routine ptw32_throw() in private.c). + +In short, the exceptions versions of the library throw an exception +when a thread is canceled or exits (via pthread_exit()), which is +caught by a handler in the thread startup routine, so that the +the correct stack unwinding occurs regardless of where the thread +is when it's canceled or exits via pthread_exit(). + +After snapshot 2001-07-12, unless your build explicitly defines (e.g. +via a compiler option) __CLEANUP_SEH, __CLEANUP_CXX, or __CLEANUP_C, then +the build now ALWAYS defaults to __CLEANUP_C style cleanup. This style +uses setjmp/longjmp in the cancelation and pthread_exit implementations, +and therefore won't do stack unwinding even when linked to applications +that have it (e.g. C++ apps). This is for consistency with most/all +commercial Unix POSIX threads implementations. + +Although it was not clearly documented before, it is still necessary to +build your application using the same __CLEANUP_* define as was +used for the version of the library that you link with, so that the +correct parts of pthread.h are included. That is, the possible +defines require the following library versions: + + __CLEANUP_SEH pthreadVSE.dll + __CLEANUP_CXX pthreadVCE.dll or pthreadGCE.dll + __CLEANUP_C pthreadVC.dll or pthreadGC.dll + +THE POINT OF ALL THIS IS: if you have not been defining one of these +explicitly, then the defaults have been set according to the compiler +and language you are using, as described at the top of this +section. + +THIS NOW CHANGES, as has been explained above. For example: + +If you were building your application with MSVC++ i.e. using C++ +exceptions (rather than SEH) and not explicitly defining one of +__CLEANUP_*, then __CLEANUP_C++ was defined for you in pthread.h. +You should have been linking with pthreadVCE.dll, which does +stack unwinding. + +If you now build your application as you had before, pthread.h will now +set __CLEANUP_C as the default style, and you will need to link +with pthreadVC.dll. Stack unwinding will now NOT occur when a +thread is canceled, nor when the thread calls pthread_exit(). + +Your application will now most likely behave differently to previous +versions, and in non-obvious ways. Most likely is that local +objects may not be destroyed or cleaned up after a thread +is canceled. + +If you want the same behaviour as before, then you must now define +__CLEANUP_C++ explicitly using a compiler option and link with +pthreadVCE.dll as you did before. + + +------------------------------------------------------------------------------ + +Q 5 Why is the default library version now less exception-friendly? +--- + +Because most commercial Unix POSIX threads implementations don't allow you to +choose to have stack unwinding. (Compaq's TRU64 Unix is possibly an exception.) + +Therefore, providing it in pthread-win32 as a default could be dangerous +and non-portable. We still provide the choice but you must now consciously +make it. + +WHY NOT REMOVE THE EXCEPTIONS VERSIONS OF THE LIBRARY ALTOGETHER? +There are a few reasons: +- because there are well respected POSIX threads people who believe + that POSIX threads implementations should be exceptions-aware and + do the expected thing in that context. (There are equally respected + people who believe it should not be easily accessible, if it's there + at all.) +- because pthreads-win32 is one of the few implementations that has + the choice, perhaps the only freely available one, and so offers + a laboratory to people who may want to explore the effects; +- although the code will always be around somewhere for anyone who + wants it, once it's removed from the current version it will not be + nearly as visible to people who may have a use for it. + + +------------------------------------------------------------------------------ + +Q 6 Should I use Cygwin or Mingw32 as a development environment? +--- + +Important: see Q7 also. + +Use Mingw32 with the MSVCRT library to build applications that use +the pthreads DLL. + +Cygwin's own internal support for POSIX threads is growing. +Consult that project's documentation for more information. + +------------------------------------------------------------------------------ + +Q 7 Now that pthreads-win32 builds under Mingw32, why do I get +--- memory access violations (segfaults)? + +The latest Mingw32 package has thread-safe exception handling (see Q10). +Also, see Q6 above. + +------------------------------------------------------------------------------ + +Q 8 How do I use pthread.dll for Win32 (Visual C++ 5.0) +--- + +> +> I'm a "rookie" when it comes to your pthread implementation. I'm currently +> desperately trying to install the prebuilt .dll file into my MSVC compiler. +> Could you please provide me with explicit instructions on how to do this (or +> direct me to a resource(s) where I can acquire such information)? +> +> Thank you, +> + +You should have a .dll, .lib, .def, and three .h files. It is recommended +that you use pthreadVC.dll, rather than pthreadVCE.dll or pthreadVSE.dll +(see Q2 above). + +The .dll can go in any directory listed in your PATH environment +variable, so putting it into C:\WINDOWS should work. + +The .lib file can go in any directory listed in your LIB environment +variable. + +The .h files can go in any directory listed in your INCLUDE +environment variable. + +Or you might prefer to put the .lib and .h files into a new directory +and add its path to LIB and INCLUDE. You can probably do this easiest +by editing the file:- + +C:\Program Files\DevStudio\vc\bin\vcvars32.bat + +The .def file isn't used by anything in the pre-compiled version but +is included for information. + +Cheers. +Ross + +------------------------------------------------------------------------------ + +Q 9 Cancelation doesn't work for me, why? +--- + +> I'm investigating a problem regarding thread cancelation. The thread I want +> to cancel has PTHREAD_CANCEL_ASYNCHRONOUS, however, this piece of code +> blocks on the join(): +> +> if ((retv = Pthread_cancel( recvThread )) == 0) +> { +> retv = Pthread_join( recvThread, 0 ); +> } +> +> Pthread_* are just macro's; they call pthread_*. +> +> The thread recvThread seems to block on a select() call. It doesn't get +> cancelled. +> +> Two questions: +> +> 1) is this normal behaviour? +> +> 2) if not, how does the cancel mechanism work? I'm not very familliar to +> win32 programming, so I don't really understand how the *Event() family of +> calls work. + +The answer to your first question is, normal POSIX behaviour would +be to asynchronously cancel the thread. However, even that doesn't +guarantee cancelation as the standard only says it should be +cancelled as soon as possible. + +Snapshot 99-11-02 or earlier only partially supports asynchronous cancellation. +Snapshots since then simulate async cancelation by poking the address of +a cancelation routine into the PC of the threads context. This requires +the thread to be resumed in some way for the cancelation to actually +proceed. This is not true async cancelation, but it is as close as we've +been able to get to it. + +If the thread you're trying to cancel is blocked (for instance, it could be +waiting for data from the network), it will only get cancelled when it unblocks +(when the data arrives). For true pre-emptive cancelation in these cases, +pthreads-win32 from snapshot 2004-05-16 can automatically recognise and use the +QueueUserAPCEx package by Panagiotis E. Hadjidoukas. This package is available +from the pthreads-win32 ftp site and is included in the pthreads-win32 +self-unpacking zip from 2004-05-16 onwards. + +Using deferred cancelation would normally be the way to go, however, +even though the POSIX threads standard lists a number of C library +functions that are defined as deferred cancelation points, there is +no hookup between those which are provided by Windows and the +pthreads-win32 library. + +Incidently, it's worth noting for code portability that the older POSIX +threads standards cancelation point lists didn't include "select" because +(as I read in Butenhof) it wasn't part of POSIX. However, it does appear in +the SUSV3. + +Effectively, the only mandatory cancelation points that pthreads-win32 +recognises are those the library implements itself, ie. + + pthread_testcancel + pthread_cond_wait + pthread_cond_timedwait + pthread_join + sem_wait + sem_timedwait + pthread_delay_np + +The following routines from the non-mandatory list in SUSV3 are +cancelation points in pthreads-win32: + + pthread_rwlock_wrlock + pthread_rwlock_timedwrlock + +The following routines from the non-mandatory list in SUSV3 are not +cancelation points in pthreads-win32: + + pthread_rwlock_rdlock + pthread_rwlock_timedrdlock + +Pthreads-win32 also provides two functions that allow you to create +cancelation points within your application, but only for cases where +a thread is going to block on a Win32 handle. These are: + + pthreadCancelableWait(HANDLE waitHandle) /* Infinite wait */ + + pthreadCancelableTimedWait(HANDLE waitHandle, DWORD timeout) + +------------------------------------------------------------------------------ + + +Q 10 How do I create thread-safe applications using +---- pthreadGCE.dll, libpthreadw32.a and Mingw32? + +This should not be a problem with recent versions of MinGW32. + +For early versions, see Thomas Pfaff's email at: +http://sources.redhat.com/ml/pthreads-win32/2002/msg00000.html +------------------------------------------------------------------------------ + diff --git a/pcsx2/windows/pthreads/MAINTAINERS b/pcsx2/windows/pthreads/MAINTAINERS new file mode 100644 index 0000000000..5b04e43f88 --- /dev/null +++ b/pcsx2/windows/pthreads/MAINTAINERS @@ -0,0 +1,4 @@ +CVS Repository maintainers + +Ross Johnson rpj@ise.canberra.edu.au +Ben Elliston bje@cygnus.com diff --git a/pcsx2/windows/pthreads/NEWS b/pcsx2/windows/pthreads/NEWS new file mode 100644 index 0000000000..f44636d5a1 --- /dev/null +++ b/pcsx2/windows/pthreads/NEWS @@ -0,0 +1,1110 @@ +RELEASE 2.8.0 +------------- +(2006-12-22) + +General +------- +New bug fixes in this release since 2.7.0 have not been applied to the +version 1.x.x series. It is probably time to drop version 1. + +Testing and verification +------------------------ +This release has not yet been tested on SMP architechtures. All tests pass +on a uni-processor system. + +Bug fixes +--------- +Sem_destroy could return EBUSY even though no threads were waiting on the +semaphore. Other races around invalidating semaphore structs (internally) +have been removed as well. + +New tests +--------- +semaphore5.c - tests the bug fix referred to above. + + +RELEASE 2.7.0 +------------- +(2005-06-04) + +General +------- +All new features in this release have been back-ported in release 1.11.0, +including the incorporation of MCS locks in pthread_once, however, versions +1 and 2 remain incompatible even though they are now identical in +performance and functionality. + +Testing and verification +------------------------ +This release has been tested (passed the test suite) on both uni-processor +and multi-processor systems. +- Tim Theisen + +Bug fixes +--------- +Pthread_once has been re-implemented to remove priority boosting and other +complexity to improve robustness. Races for Win32 handles that are not +recycle-unique have been removed. The general form of pthread_once is now +the same as that suggested earlier by Alexander Terekhov, but instead of the +'named mutex', a queue-based lock has been implemented which has the required +properties of dynamic self initialisation and destruction. This lock is also +efficient. The ABI is unaffected in as much as the size of pthread_once_t has +not changed and PTHREAD_ONCE_INIT has not changed, however, applications that +peek inside pthread_once_t, which is supposed to be opaque, will break. +- Vladimir Kliatchko + +New features +------------ +* Support for Mingw cross development tools added to GNUmakefile. +Mingw cross tools allow building the libraries on Linux. +- Mikael Magnusson + + +RELEASE 2.6.0 +------------- +(2005-05-19) + +General +------- +All of the bug fixes and new features in this release have been +back-ported in release 1.10.0. + +Testing and verification +------------------------ +This release has been tested (passed the test suite) on both uni-processor +and multi-processor systems. Thanks to Tim Theisen at TomoTherapy for +exhaustively running the MP tests and for providing crutial observations +and data when faults are detected. + +Bugs fixed +---------- + +* pthread_detach() now reclaims remaining thread resources if called after +the target thread has terminated. Previously, this routine did nothing in +this case. + +New tests +--------- + +* detach1.c - tests that pthread_detach properly invalidates the target +thread, which indicates that the thread resources have been reclaimed. + + +RELEASE 2.5.0 +------------- +(2005-05-09) + +General +------- + +The package now includes a reference documentation set consisting of +HTML formatted Unix-style manual pages that have been edited for +consistency with Pthreads-w32. The set can also be read online at: +http://sources.redhat.com/pthreads-win32/manual/index.html + +Thanks again to Tim Theisen for running the test suite pre-release +on an MP system. + +All of the bug fixes and new features in this release have been +back-ported in release 1.9.0. + +Bugs fixed +---------- + +* Thread Specific Data (TSD) key management has been ammended to +eliminate a source of (what was effectively) resource leakage (a HANDLE +plus memory for each key destruct routine/thread association). This was +not a true leak because these resources were eventually reclaimed when +pthread_key_delete was run AND each thread referencing the key had exited. +The problem was that these two conditions are often not met until very +late, and often not until the process is about to exit. + +The ammended implementation avoids the need for the problematic HANDLE +and reclaims the memory as soon as either the key is deleted OR the +thread exits, whichever is first. + +Thanks to Richard Hughes at Aculab for identifying and locating the leak. + +* TSD key destructors are now processed up to PTHREAD_DESTRUCTOR_ITERATIONS +times instead of just once. PTHREAD_DESTRUCTOR_ITERATIONS has been +defined in pthread.h for some time but not used. + +* Fix a semaphore accounting race between sem_post/sem_post_multiple +and sem_wait cancellation. This is the same issue as with +sem_timedwait that was fixed in the last release. + +* sem_init, sem_post, and sem_post_multiple now check that the +semaphore count never exceeds _POSIX_SEM_VALUE_MAX. + +* Although sigwait() is nothing more than a no-op, it should at least +be a cancellation point to be consistent with the standard. + +New tests +--------- + +* stress1.c - attempts to expose problems in condition variable +and semaphore timed wait logic. This test was inspired by Stephan +Mueller's sample test code used to identify the sem_timedwait bug +from the last release. It's not a part of the regular test suite +because it can take awhile to run. To run it: +nmake clean VC-stress + +* tsd2.c - tests that key destructors are re-run if the tsd key value is +not NULL after the destructor routine has run. Also tests that +pthread_setspecific() and pthread_getspecific() are callable from +destructors. + + +RELEASE 2.4.0 +------------- +(2005-04-26) + +General +------- + +There is now no plan to release a version 3.0.0 to fix problems in +pthread_once(). Other possible implementations of pthread_once +will still be investigated for a possible future release in an attempt +to reduce the current implementation's complexity. + +All of the bug fixes and new features in this release have been +back-ported for release 1.8.0. + +Bugs fixed +---------- + +* Fixed pthread_once race (failures on an MP system). Thanks to +Tim Theisen for running exhaustive pre-release testing on his MP system +using a range of compilers: + VC++ 6 + VC++ 7.1 + Intel C++ version 8.0 +All tests passed. +Some minor speed improvements were also done. + +* Fix integer overrun error in pthread_mutex_timedlock() - missed when +sem_timedwait() was fixed in release 2.2.0. This routine no longer returns +ENOTSUP when NEED_SEM is defined - it is supported (NEED_SEM is only +required for WinCE versions prior to 3.0). + +* Fix timeout bug in sem_timedwait(). +- Thanks to Stephan Mueller for reporting, providing diagnostic output +and test code. + +* Fix several problems in the NEED_SEM conditionally included code. +NEED_SEM included code is provided for systems that don't implement W32 +semaphores, such as WinCE prior to version 3.0. An alternate implementation +of POSIX semaphores is built using W32 events for these systems when +NEED_SEM is defined. This code has been completely rewritten in this +release to reuse most of the default POSIX semaphore code, and particularly, +to implement all of the sem_* routines supported by pthreads-win32. Tim +Theisen also run the test suite over the NEED_SEM code on his MP system. All +tests passed. + +* The library now builds without errors for the Borland Builder 5.5 compiler. + +New features +------------ + +* pthread_mutex_timedlock() and all sem_* routines provided by +pthreads-win32 are now implemented for WinCE versions prior to 3.0. Those +versions did not implement W32 semaphores. Define NEED_SEM in config.h when +building the library for these systems. + +Known issues in this release +---------------------------- + +* pthread_once is too complicated - but it works as far as testing can +determine.. + +* The Borland version of the dll fails some of the tests with a memory read +exception. The cause is not yet known but a compiler bug has not been ruled +out. + + +RELEASE 2.3.0 +------------- +(2005-04-12) + +General +------- + +Release 1.7.0 is a backport of features and bug fixes new in +this release. See earlier notes under Release 2.0.0/General. + +Bugs fixed +---------- + +* Fixed pthread_once potential for post once_routine cancellation +hanging due to starvation. See comments in pthread_once.c. +Momentary priority boosting is used to ensure that, after a +once_routine is cancelled, the thread that will run the +once_routine is not starved by higher priority waiting threads at +critical times. Priority boosting occurs only AFTER a once_routine +cancellation, and is applied only to that once_control. The +once_routine is run at the thread's normal base priority. + +New tests +--------- + +* once4.c: Aggressively tests pthread_once() under realtime +conditions using threads with varying priorities. Windows' +random priority boosting does not occur for threads with realtime +priority levels. + + +RELEASE 2.2.0 +------------- +(2005-04-04) + +General +------- + +* Added makefile targets to build static link versions of the library. +Both MinGW and MSVC. Please note that this does not imply any change +to the LGPL licensing, which still imposes psecific conditions on +distributing software that has been statically linked with this library. + +* There is a known bug in pthread_once(). Cancellation of the init_routine +exposes a potential starvation (i.e. deadlock) problem if a waiting thread +has a higher priority than the initting thread. This problem will be fixed +in version 3.0.0 of the library. + +Bugs fixed +---------- + +* Fix integer overrun error in sem_timedwait(). +Kevin Lussier + +* Fix preprocessor directives for static linking. +Dimitar Panayotov + + +RELEASE 2.1.0 +------------- +(2005-03-16) + +Bugs fixed +---------- + +* Reverse change to pthread_setcancelstate() in 2.0.0. + + +RELEASE 2.0.0 +------------- +(2005-03-16) + +General +------- + +This release represents an ABI change and the DLL version naming has +incremented from 1 to 2, e.g. pthreadVC2.dll. + +Version 1.4.0 back-ports the new functionality included in this +release. Please distribute DLLs built from that version with updates +to applications built on pthreads-win32 version 1.x.x. + +The package naming has changed, replacing the snapshot date with +the version number + descriptive information. E.g. this +release is "pthreads-w32-2-0-0-release". + +Bugs fixed +---------- + +* pthread_setcancelstate() no longer checks for a pending +async cancel event if the library is using alertable async +cancel. See the README file (Prerequisites section) for info +on adding alertable async cancelation. + +New features +------------ + +* pthread_once() now supports init_routine cancellability. + +New tests +--------- + +* Agressively test pthread_once() init_routine cancellability. + + +SNAPSHOT 2005-03-08 +------------------- +Version 1.3.0 + +Bug reports (fixed) +------------------- + +* Implicitly created threads leave Win32 handles behind after exiting. +- Dmitrii Semii + +* pthread_once() starvation problem. +- Gottlob Frege + +New tests +--------- + +* More intense testing of pthread_once(). + + +SNAPSHOT 2005-01-25 +------------------- +Version 1.2.0 + +Bug fixes +--------- + +* Attempted acquisition of a recursive mutex could cause waiting threads +to not be woken when the mutex was released. +- Ralf Kubis + +* Various package omissions have been fixed. + + +SNAPSHOT 2005-01-03 +------------------- +Version 1.1.0 + +Bug fixes +--------- + +* Unlocking recursive or errorcheck mutexes would sometimes +unexpectedly return an EPERM error (bug introduced in +snapshot-2004-11-03). +- Konstantin Voronkov + + +SNAPSHOT 2004-11-22 +------------------- +Version 1.0.0 + +This snapshot primarily fixes the condvar bug introduced in +snapshot-2004-11-03. DLL versioning has also been included to allow +applications to runtime check the Microsoft compatible DLL version +information, and to extend the DLL naming system for ABI and major +(non-backward compatible) API changes. See the README file for details. + +Bug fixes +--------- + +* Condition variables no longer deadlock (bug introduced in +snapshot-2004-11-03). +- Alexander Kotliarov and Nicolas at saintmac + +* DLL naming extended to avoid 'DLL hell' in the future, and to +accommodate the ABI change introduced in snapshot-2004-11-03. Snapshot +2004-11-03 will be removed from FTP sites. + +New features +------------ + +* A Microsoft-style version resource has been added to the DLL for +applications that wish to check DLL compatibility at runtime. + +* Pthreads-win32 DLL naming has been extended to allow incompatible DLL +versions to co-exist in the same filesystem. See the README file for details, +but briefly: while the version information inside the DLL will change with +each release from now on, the DLL version names will only change if the new +DLL is not backward compatible with older applications. + +The versioning scheme has been borrowed from GNU Libtool, and the DLL +naming scheme is from Cygwin. Provided the Libtool-style numbering rules are +honoured, the Cygwin DLL naming scheme automatcally ensures that DLL name +changes are minimal and that applications will not load an incompatible +pthreads-win32 DLL. + +Those who use the pre-built DLLs will find that the DLL/LIB names have a new +suffix (1) in this snapshot. E.g. pthreadVC1.dll etc. + +* The POSIX thread ID reuse uniqueness feature introduced in the last snapshot +has been kept as default, but the behaviour can now be controlled when the DLL +is built to effectively switch it off. This makes the library much more +sensitive to applications that assume that POSIX thread IDs are unique, i.e. +are not strictly compliant with POSIX. See the PTW32_THREAD_ID_REUSE_INCREMENT +macro comments in config.h for details. + +Other changes +------------- +Certain POSIX macros have changed. + +These changes are intended to conform to the Single Unix Specification version 3, +which states that, if set to 0 (zero) or not defined, then applications may use +sysconf() to determine their values at runtime. Pthreads-win32 does not +implement sysconf(). + +The following macros are no longer undefined, but defined and set to -1 +(not implemented): + + _POSIX_THREAD_ATTR_STACKADDR + _POSIX_THREAD_PRIO_INHERIT + _POSIX_THREAD_PRIO_PROTECT + _POSIX_THREAD_PROCESS_SHARED + +The following macros are defined and set to 200112L (implemented): + + _POSIX_THREADS + _POSIX_THREAD_SAFE_FUNCTIONS + _POSIX_THREAD_ATTR_STACKSIZE + _POSIX_THREAD_PRIORITY_SCHEDULING + _POSIX_SEMAPHORES + _POSIX_READER_WRITER_LOCKS + _POSIX_SPIN_LOCKS + _POSIX_BARRIERS + +The following macros are defined and set to appropriate values: + + _POSIX_THREAD_THREADS_MAX + _POSIX_SEM_VALUE_MAX + _POSIX_SEM_NSEMS_MAX + PTHREAD_DESTRUCTOR_ITERATIONS + PTHREAD_KEYS_MAX + PTHREAD_STACK_MIN + PTHREAD_THREADS_MAX + + +SNAPSHOT 2004-11-03 +------------------- + +DLLs produced from this snapshot cannot be used with older applications without +recompiling the application, due to a change to pthread_t to provide unique POSIX +thread IDs. + +Although this snapshot passes the extended test suite, many of the changes are +fairly major, and some applications may show different behaviour than previously, +so adopt with care. Hopefully, any changed behaviour will be due to the library +being better at it's job, not worse. + +Bug fixes +--------- + +* pthread_create() no longer accepts NULL as the thread reference arg. +A segfault (memory access fault) will result, and no thread will be +created. + +* pthread_barrier_wait() no longer acts as a cancelation point. + +* Fix potential race condition in pthread_once() +- Tristan Savatier + +* Changes to pthread_cond_destroy() exposed some coding weaknesses in several +test suite mini-apps because pthread_cond_destroy() now returns EBUSY if the CV +is still in use. + +New features +------------ + +* Added for compatibility: +PTHREAD_RECURSIVE_MUTEX_INITIALIZER, +PTHREAD_ERRORCHECK_MUTEX_INITIALIZER, +PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, +PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP + +* Initial support for Digital Mars compiler +- Anuj Goyal + +* Faster Mutexes. These have been been rewritten following a model provided by +Alexander Terekhov that reduces kernel space checks, and eliminates some additional +critical sections used to manage a race between timedlock expiration and unlock. +Please be aware that the new mutexes do not enforce strict absolute FIFO scheduling +of mutexes, however any out-of-order lock acquisition should be very rare. + +* Faster semaphores. Following a similar model to mutexes above, these have been +rewritten to use preliminary users space checks. + +* sem_getvalue() now returns the number of waiters. + +* The POSIX thread ID now has much stronger uniqueness characteristics. The library +garrantees not to reuse the same thread ID for at least 2^(wordsize) thread +destruction/creation cycles. + +New tests +--------- + +* semaphore4.c: Tests cancelation of the new sem_wait(). + +* semaphore4t.c: Likewise for sem_timedwait(). + +* rwlock8.c: Tests and times the slow execution paths of r/w locks, and the CVs, +mutexes, and semaphores that they're built on. + + +SNAPSHOT 2004-05-16 +------------------- + +Attempt to add Watcom to the list of compilers that can build the library. +This failed in the end due to it's non-thread-aware errno. The library +builds but the test suite fails. See README.Watcom for more details. + +Bug fixes +--------- +* Bug and memory leak in sem_init() +- Alex Blanco + +* ptw32_getprocessors() now returns CPU count of 1 for WinCE. +- James Ewing + +* pthread_cond_wait() could be canceled at a point where it should not +be cancelable. Fixed. +- Alexander Terekhov + +* sem_timedwait() had an incorrect timeout calculation. +- Philippe Di Cristo + +* Fix a memory leak left behind after threads are destroyed. +- P. van Bruggen + +New features +------------ +* Ported to AMD64. +- Makoto Kato + +* True pre-emptive asynchronous cancelation of threads. This is optional +and requires that Panagiotis E. Hadjidoukas's QueueUserAPCEx package be +installed. This package is included in the pthreads-win32 self-unpacking +Zip archive starting from this snapshot. See the README.txt file inside +the package for installation details. + +Note: If you don't use async cancelation in your application, or don't need +to cancel threads that are blocked on system resources such as network I/O, +then the default non-preemptive async cancelation is probably good enough. +However, pthreads-win32 auto-detects the availability of these components +at run-time, so you don't need to rebuild the library from source if you +change your mind later. + +All of the advice available in books and elsewhere on the undesirability +of using async cancelation in any application still stands, but this +feature is a welcome addition with respect to the library's conformance to +the POSIX standard. + +SNAPSHOT 2003-09-18 +------------------- + +Cleanup of thread priority management. In particular, setting of thread +priority now attempts to map invalid Win32 values within the range returned +by sched_get_priority_min/max() to useful values. See README.NONPORTABLE +under "Thread priority". + +Bug fixes +--------- +* pthread_getschedparam() now returns the priority given by the most recent +call to pthread_setschedparam() or established by pthread_create(), as +required by the standard. Previously, pthread_getschedparam() incorrectly +returned the running thread priority at the time of the call, which may have +been adjusted or temporarily promoted/demoted. + +* sched_get_priority_min() and sched_get_priority_max() now return -1 on error +and set errno. Previously, they incorrectly returned the error value directly. + + +SNAPSHOT 2003-09-04 +------------------- + +Bug fixes +--------- +* ptw32_cancelableWait() now allows cancelation of waiting implicit POSIX +threads. + +New test +-------- +* cancel8.c tests cancelation of Win32 threads waiting at a POSIX cancelation +point. + + +SNAPSHOT 2003-09-03 +------------------- + +Bug fixes +--------- +* pthread_self() would free the newly created implicit POSIX thread handle if +DuplicateHandle failed instead of recycle it (very unlikely). + +* pthread_exit() was neither freeing nor recycling the POSIX thread struct +for implicit POSIX threads. + +New feature - Cancelation of/by Win32 (non-POSIX) threads +--------------------------------------------------------- +Since John Bossom's original implementation, the library has allowed non-POSIX +initialised threads (Win32 threads) to call pthreads-win32 routines and +therefore interact with POSIX threads. This is done by creating an on-the-fly +POSIX thread ID for the Win32 thread that, once created, allows fully +reciprical interaction. This did not extend to thread cancelation (async or +deferred). Now it does. + +Any thread can be canceled by any other thread (Win32 or POSIX) if the former +thread's POSIX pthread_t value is known. It's TSD destructors and POSIX +cleanup handlers will be run before the thread exits with an exit code of +PTHREAD_CANCELED (retrieved with GetExitCodeThread()). + +This allows a Win32 thread to, for example, call POSIX CV routines in the same way +that POSIX threads would/should, with pthread_cond_wait() cancelability and +cleanup handlers (pthread_cond_wait() is a POSIX cancelation point). + +By adding cancelation, Win32 threads should now be able to call all POSIX +threads routines that make sense including semaphores, mutexes, condition +variables, read/write locks, barriers, spinlocks, tsd, cleanup push/pop, +cancelation, pthread_exit, scheduling, etc. + +Note that these on-the-fly 'implicit' POSIX thread IDs are initialised as detached +(not joinable) with deferred cancelation type. The POSIX thread ID will be created +automatically by any POSIX routines that need a POSIX handle (unless the routine +needs a pthread_t as a parameter of course). A Win32 thread can discover it's own +POSIX thread ID by calling pthread_self(), which will create the handle if +necessary and return the pthread_t value. + +New tests +--------- +Test the above new feature. + + +SNAPSHOT 2003-08-19 +------------------- + +This snapshot fixes some accidental corruption to new test case sources. +There are no changes to the library source code. + + +SNAPSHOT 2003-08-15 +------------------- + +Bug fixes +--------- + +* pthread.dsp now uses correct compile flags (/MD). +- Viv + +* pthread_win32_process_detach_np() fixed memory leak. +- Steven Reddie + +* pthread_mutex_destroy() fixed incorrect return code. +- Nicolas Barry + +* pthread_spin_destroy() fixed memory leak. +- Piet van Bruggen + +* Various changes to tighten arg checking, and to work with later versions of +MinGW32 and MsysDTK. + +* pthread_getschedparam() etc, fixed dangerous thread validity checking. +- Nicolas Barry + +* POSIX thread handles are now reused and their memory is not freed on thread exit. +This allows for stronger thread validity checking. + +New standard routine +-------------------- + +* pthread_kill() added to provide thread validity checking to applications. +It does not accept any non zero values for the signal arg. + +New test cases +-------------- + +* New test cases to confirm validity checking, pthread_kill(), and thread reuse. + + +SNAPSHOT 2003-05-10 +------------------- + +Bug fixes +--------- + +* pthread_mutex_trylock() now returns correct error values. +pthread_mutex_destroy() will no longer destroy a recursively locked mutex. +pthread_mutex_lock() is no longer inadvertantly behaving as a cancelation point. +- Thomas Pfaff + +* pthread_mutex_timedlock() no longer occasionally sets incorrect mutex +ownership, causing deadlocks in some applications. +- Robert Strycek and Alexander Terekhov + + +SNAPSHOT 2002-11-04 +------------------- + +Bug fixes +--------- + +* sem_getvalue() now returns the correct value under Win NT and WinCE. +- Rob Fanner + +* sem_timedwait() now uses tighter checks for unreasonable +abstime values - that would result in unexpected timeout values. + +* ptw32_cond_wait_cleanup() no longer mysteriously consumes +CV signals but may produce more spurious wakeups. It is believed +that the sem_timedwait() call is consuming a CV signal that it +shouldn't. +- Alexander Terekhov + +* Fixed a memory leak in ptw32_threadDestroy() for implicit threads. + +* Fixed potential for deadlock in pthread_cond_destroy(). +A deadlock could occur for statically declared CVs (PTHREAD_COND_INITIALIZER), +when one thread is attempting to destroy the condition variable while another +is attempting to dynamically initialize it. +- Michael Johnson + + +SNAPSHOT 2002-03-02 +------------------- + +Cleanup code default style. (IMPORTANT) +---------------------------------------------------------------------- +Previously, if not defined, the cleanup style was determined automatically +from the compiler/language, and one of the following was defined accordingly: + + __CLEANUP_SEH MSVC only + __CLEANUP_CXX C++, including MSVC++, GNU G++ + __CLEANUP_C C, including GNU GCC, not MSVC + +These defines determine the style of cleanup (see pthread.h) and, +most importantly, the way that cancelation and thread exit (via +pthread_exit) is performed (see the routine ptw32_throw() in private.c). + +In short, the exceptions versions of the library throw an exception +when a thread is canceled or exits (via pthread_exit()), which is +caught by a handler in the thread startup routine, so that the +the correct stack unwinding occurs regardless of where the thread +is when it's canceled or exits via pthread_exit(). + +In this and future snapshots, unless the build explicitly defines (e.g. +via a compiler option) __CLEANUP_SEH, __CLEANUP_CXX, or __CLEANUP_C, then +the build NOW always defaults to __CLEANUP_C style cleanup. This style +uses setjmp/longjmp in the cancelation and pthread_exit implementations, +and therefore won't do stack unwinding even when linked to applications +that have it (e.g. C++ apps). This is for consistency with most +current commercial Unix POSIX threads implementations. Compaq's TRU64 +may be an exception (no pun intended) and possible future trend. + +Although it was not clearly documented before, it is still necessary to +build your application using the same __CLEANUP_* define as was +used for the version of the library that you link with, so that the +correct parts of pthread.h are included. That is, the possible +defines require the following library versions: + + __CLEANUP_SEH pthreadVSE.dll + __CLEANUP_CXX pthreadVCE.dll or pthreadGCE.dll + __CLEANUP_C pthreadVC.dll or pthreadGC.dll + +E.g. regardless of whether your app is C or C++, if you link with +pthreadVC.lib or libpthreadGC.a, then you must define __CLEANUP_C. + + +THE POINT OF ALL THIS IS: if you have not been defining one of these +explicitly, then the defaults as described at the top of this +section were being used. + +THIS NOW CHANGES, as has been explained above, but to try to make this +clearer here's an example: + +If you were building your application with MSVC++ i.e. using C++ +exceptions and not explicitly defining one of __CLEANUP_*, then +__CLEANUP_C++ was automatically defined for you in pthread.h. +You should have been linking with pthreadVCE.dll, which does +stack unwinding. + +If you now build your application as you had before, pthread.h will now +automatically set __CLEANUP_C as the default style, and you will need to +link with pthreadVC.dll. Stack unwinding will now NOT occur when a thread +is canceled, or the thread calls pthread_exit(). + +Your application will now most likely behave differently to previous +versions, and in non-obvious ways. Most likely is that locally +instantiated objects may not be destroyed or cleaned up after a thread +is canceled. + +If you want the same behaviour as before, then you must now define +__CLEANUP_C++ explicitly using a compiler option and link with +pthreadVCE.dll as you did before. + + +WHY ARE WE MAKING THE DEFAULT STYLE LESS EXCEPTION-FRIENDLY? +Because no commercial Unix POSIX threads implementation allows you to +choose to have stack unwinding. Therefore, providing it in pthread-win32 +as a default is dangerous. We still provide the choice but unless +you consciously choose to do otherwise, your pthreads applications will +now run or crash in similar ways irrespective of the threads platform +you use. Or at least this is the hope. + + +WHY NOT REMOVE THE EXCEPTIONS VERSIONS OF THE LIBRARY ALTOGETHER? +There are a few reasons: +- because there are well respected POSIX threads people who believe + that POSIX threads implementations should be exceptions aware and + do the expected thing in that context. (There are equally respected + people who believe it should not be easily accessible, if it's there + at all, for unconditional conformity to other implementations.) +- because pthreads-win32 is one of the few implementations that has + the choice, perhaps the only freely available one, and so offers + a laboratory to people who may want to explore the effects; +- although the code will always be around somewhere for anyone who + wants it, once it's removed from the current version it will not be + nearly as visible to people who may have a use for it. + + +Source module splitting +----------------------- +In order to enable smaller image sizes to be generated +for applications that link statically with the library, +most routines have been separated out into individual +source code files. + +This is being done in such a way as to be backward compatible. +The old source files are reused to congregate the individual +routine files into larger translation units (via a bunch of +# includes) so that the compiler can still optimise wherever +possible, e.g. through inlining, which can only be done +within the same translation unit. + +It is also possible to build the entire library by compiling +the single file named "pthread.c", which just #includes all +the secondary congregation source files. The compiler +may be able to use this to do more inlining of routines. + +Although the GNU compiler is able to produce libraries with +the necessary separation (the -ffunction-segments switch), +AFAIK, the MSVC and other compilers don't have this feature. + +Finally, since I use makefiles and command-line compilation, +I don't know what havoc this reorganisation may wreak amongst +IDE project file users. You should be able to continue +using your existing project files without modification. + + +New non-portable functions +-------------------------- +pthread_num_processors_np(): + Returns the number of processors in the system that are + available to the process, as determined from the processor + affinity mask. + +pthread_timechange_handler_np(): + To improve tolerance against operator or time service initiated + system clock changes. + + This routine can be called by an application when it + receives a WM_TIMECHANGE message from the system. At present + it broadcasts all condition variables so that waiting threads + can wake up and re-evaluate their conditions and restart + their timed waits if required. + - Suggested by Alexander Terekhov + + +Platform dependence +------------------- +As Win95 doesn't provide one, the library now contains +it's own InterlockedCompareExchange() routine, which is used +whenever Windows doesn't provide it. InterlockedCompareExchange() +is used to implement spinlocks and barriers, and also in mutexes. +This routine relies on the CMPXCHG machine instruction which +is not available on i386 CPUs. This library (from snapshot +20010712 onwards) is therefore no longer supported on i386 +processor platforms. + + +New standard routines +--------------------- +For source code portability only - rwlocks cannot be process shared yet. + + pthread_rwlockattr_init() + pthread_rwlockattr_destroy() + pthread_rwlockattr_setpshared() + pthread_rwlockattr_getpshared() + +As defined in the new POSIX standard, and the Single Unix Spec version 3: + + sem_timedwait() + pthread_mutex_timedlock() - Alexander Terekhov and Thomas Pfaff + pthread_rwlock_timedrdlock() - adapted from pthread_rwlock_rdlock() + pthread_rwlock_timedwrlock() - adapted from pthread_rwlock_wrlock() + + +pthread.h no longer includes windows.h +-------------------------------------- +[Not yet for G++] + +This was done to prevent conflicts. + +HANDLE, DWORD, and NULL are temporarily defined within pthread.h if +they are not already. + + +pthread.h, sched.h and semaphore.h now use dllexport/dllimport +-------------------------------------------------------------- +Not only to avoid the need for the pthread.def file, but to +improve performance. Apparently, declaring functions with dllimport +generates a direct call to the function and avoids the overhead +of a stub function call. + +Bug fixes +--------- +* Fixed potential NULL pointer dereferences in pthread_mutexattr_init, +pthread_mutexattr_getpshared, pthread_barrierattr_init, +pthread_barrierattr_getpshared, and pthread_condattr_getpshared. +- Scott McCaskill + +* Removed potential race condition in pthread_mutex_trylock and +pthread_mutex_lock; +- Alexander Terekhov + +* The behaviour of pthread_mutex_trylock in relation to +recursive mutexes was inconsistent with commercial implementations. +Trylock would return EBUSY if the lock was owned already by the +calling thread regardless of mutex type. Trylock now increments the +recursion count and returns 0 for RECURSIVE mutexes, and will +return EDEADLK rather than EBUSY for ERRORCHECK mutexes. This is +consistent with Solaris. +- Thomas Pfaff + +* Found a fix for the library and workaround for applications for +the known bug #2, i.e. where __CLEANUP_CXX or __CLEANUP_SEH is defined. +See the "Known Bugs in this snapshot" section below. + +This could be made transparent to applications by replacing the macros that +define the current C++ and SEH versions of pthread_cleanup_push/pop +with the C version, but AFAIK cleanup handlers would not then run in the +correct sequence with destructors and exception cleanup handlers when +an exception occurs. + +* Cancelation once started in a thread cannot now be inadvertantly +double canceled. That is, once a thread begins it's cancelation run, +cancelation is disabled and a subsequent cancel request will +return an error (ESRCH). + +* errno: An incorrect compiler directive caused a local version +of errno to be used instead of the Win32 errno. Both instances are +thread-safe but applications checking errno after a pthreads-win32 +call would be wrong. Fixing this also fixed a bad compiler +option in the testsuite (/MT should have been /MD) which is +needed to link with the correct library MSVCRT.LIB. + + +SNAPSHOT 2001-07-12 +------------------- + +To be added + + +SNAPSHOT 2001-07-03 +------------------- + +To be added + + +SNAPSHOT 2000-08-13 +------------------- + +New: +- Renamed DLL and LIB files: + pthreadVSE.dll (MS VC++/Structured EH) + pthreadVSE.lib + pthreadVCE.dll (MS VC++/C++ EH) + pthreadVCE.lib + pthreadGCE.dll (GNU G++/C++ EH) + libpthreadw32.a + + Both your application and the pthread dll should use the + same exception handling scheme. + +Bugs fixed: +- MSVC++ C++ exception handling. + +Some new tests have been added. + + +SNAPSHOT 2000-08-10 +------------------- + +New: +- asynchronous cancelation on X86 (Jason Nye) +- Makefile compatible with MS nmake to replace + buildlib.bat +- GNUmakefile for Mingw32 +- tests/Makefile for MS nmake replaces runall.bat +- tests/GNUmakefile for Mingw32 + +Bugs fixed: +- kernel32 load/free problem +- attempt to hide internel exceptions from application + exception handlers (__try/__except and try/catch blocks) +- Win32 thread handle leakage bug + (David Baggett/Paul Redondo/Eyal Lebedinsky) + +Some new tests have been added. + + +SNAPSHOT 1999-11-02 +------------------- + +Bugs fixed: +- ctime_r macro had an incorrect argument (Erik Hensema), +- threads were not being created + PTHREAD_CANCEL_DEFERRED. This should have + had little effect as deferred is the only + supported type. (Ross Johnson). + +Some compatibility improvements added, eg. +- pthread_setcancelstate accepts NULL pointer + for the previous value argument. Ditto for + pthread_setcanceltype. This is compatible + with Solaris but should not affect + standard applications (Erik Hensema) + +Some new tests have been added. + + +SNAPSHOT 1999-10-17 +------------------- + +Bug fix - Cancelation of threads waiting on condition variables +now works properly (Lorin Hochstein and Peter Slacik) + + +SNAPSHOT 1999-08-12 +------------------- + +Fixed exception stack cleanup if calling pthread_exit() +- (Lorin Hochstein and John Bossom). + +Fixed bugs in condition variables - (Peter Slacik): + - additional contention checks + - properly adjust number of waiting threads after timed + condvar timeout. + + +SNAPSHOT 1999-05-30 +------------------- + +Some minor bugs have been fixed. See the ChangeLog file for details. + +Some more POSIX 1b functions are now included but ony return an +error (ENOSYS) if called. They are: + + sem_open + sem_close + sem_unlink + sem_getvalue + + +SNAPSHOT 1999-04-07 +------------------- + +Some POSIX 1b functions which were internally supported are now +available as exported functions: + + sem_init + sem_destroy + sem_wait + sem_trywait + sem_post + sched_yield + sched_get_priority_min + sched_get_priority_max + +Some minor bugs have been fixed. See the ChangeLog file for details. + + +SNAPSHOT 1999-03-16 +------------------- + +Initial release. + diff --git a/pcsx2/windows/pthreads/README b/pcsx2/windows/pthreads/README new file mode 100644 index 0000000000..8c3af6097b --- /dev/null +++ b/pcsx2/windows/pthreads/README @@ -0,0 +1,593 @@ +PTHREADS-WIN32 +============== + +Pthreads-win32 is free software, distributed under the GNU Lesser +General Public License (LGPL). See the file 'COPYING.LIB' for terms +and conditions. Also see the file 'COPYING' for information +specific to pthreads-win32, copyrights and the LGPL. + + +What is it? +----------- + +Pthreads-win32 is an Open Source Software implementation of the +Threads component of the POSIX 1003.1c 1995 Standard (or later) +for Microsoft's Win32 environment. Some functions from POSIX +1003.1b are also supported including semaphores. Other related +functions include the set of read-write lock functions. The +library also supports some of the functionality of the Open +Group's Single Unix specification, version 2, namely mutex types, +plus some common and pthreads-win32 specific non-portable +routines (see README.NONPORTABLE). + +See the file "ANNOUNCE" for more information including standards +conformance details and the list of supported and unsupported +routines. + + +Prerequisites +------------- +MSVC or GNU C (MinGW32 MSys development kit) + To build from source. + +QueueUserAPCEx by Panagiotis E. Hadjidoukas + For true async cancelation of threads (including blocked threads). + This is a DLL and Windows driver that provides pre-emptive APC + by forcing threads into an alertable state when the APC is queued. + Both the DLL and driver are provided with the pthreads-win32.exe + self-unpacking ZIP, and on the pthreads-win32 FTP site (in source + and pre-built forms). Currently this is a separate LGPL package to + pthreads-win32. See the README in the QueueUserAPCEx folder for + installation instructions. + + Pthreads-win32 will automatically detect if the QueueUserAPCEx DLL + QuserEx.DLL is available and whether the driver AlertDrv.sys is + loaded. If it is not available, pthreads-win32 will simulate async + cancelation, which means that it can async cancel only threads that + are runnable. The simulated async cancellation cannot cancel blocked + threads. + + +Library naming +-------------- + +Because the library is being built using various exception +handling schemes and compilers - and because the library +may not work reliably if these are mixed in an application, +each different version of the library has it's own name. + +Note 1: the incompatibility is really between EH implementations +of the different compilers. It should be possible to use the +standard C version from either compiler with C++ applications +built with a different compiler. If you use an EH version of +the library, then you must use the same compiler for the +application. This is another complication and dependency that +can be avoided by using only the standard C library version. + +Note 2: if you use a standard C pthread*.dll with a C++ +application, then any functions that you define that are +intended to be called via pthread_cleanup_push() must be +__cdecl. + +Note 3: the intention was to also name either the VC or GC +version (it should be arbitrary) as pthread.dll, including +pthread.lib and libpthread.a as appropriate. This is no longer +likely to happen. + +Note 4: the compatibility number was added so that applications +can differentiate between binary incompatible versions of the +libs and dlls. + +In general: + pthread[VG]{SE,CE,C}c.dll + pthread[VG]{SE,CE,C}c.lib + +where: + [VG] indicates the compiler + V - MS VC, or + G - GNU C + + {SE,CE,C} indicates the exception handling scheme + SE - Structured EH, or + CE - C++ EH, or + C - no exceptions - uses setjmp/longjmp + + c - DLL compatibility number indicating ABI and API + compatibility with applications built against + any snapshot with the same compatibility number. + See 'Version numbering' below. + +The name may also be suffixed by a 'd' to indicate a debugging version +of the library. E.g. pthreadVC2d.lib. Debugging versions contain +additional information for debugging (symbols etc) and are often not +optimised in any way (compiled with optimisation turned off). + +For example: + pthreadVSE.dll (MSVC/SEH) + pthreadGCE.dll (GNUC/C++ EH) + pthreadGC.dll (GNUC/not dependent on exceptions) + pthreadVC1.dll (MSVC/not dependent on exceptions - not binary + compatible with pthreadVC.dll) + pthreadVC2.dll (MSVC/not dependent on exceptions - not binary + compatible with pthreadVC1.dll or pthreadVC.dll) + +The GNU library archive file names have correspondingly changed to: + + libpthreadGCEc.a + libpthreadGCc.a + + +Versioning numbering +-------------------- + +Version numbering is separate from the snapshot dating system, and +is the canonical version identification system embedded within the +DLL using the Microsoft version resource system. The versioning +system chosen follows the GNU Libtool system. See +http://www.gnu.org/software/libtool/manual.html section 6.2. + +See the resource file 'version.rc'. + +Microsoft version numbers use 4 integers: + + 0.0.0.0 + +Pthreads-win32 uses the first 3 following the Libtool convention. +The fourth is commonly used for the build number, but will be reserved +for future use. + + current.revision.age.0 + +The numbers are changed as follows: + +1. If the library source code has changed at all since the last update, + then increment revision (`c:r:a' becomes `c:r+1:a'). +2. If any interfaces have been added, removed, or changed since the last + update, increment current, and set revision to 0. +3. If any interfaces have been added since the last public release, then + increment age. +4. If any interfaces have been removed or changed since the last public + release, then set age to 0. + + +DLL compatibility numbering is an attempt to ensure that applications +always load a compatible pthreads-win32 DLL by using a DLL naming system +that is consistent with the version numbering system. It also allows +older and newer DLLs to coexist in the same filesystem so that older +applications can continue to be used. For pre .NET Windows systems, +this inevitably requires incompatible versions of the same DLLs to have +different names. + +Pthreads-win32 has adopted the Cygwin convention of appending a single +integer number to the DLL name. The number used is based on the library +version number and is computed as 'current' - 'age'. + +(See http://home.att.net/~perlspinr/libversioning.html for a nicely +detailed explanation.) + +Using this method, DLL name/s will only change when the DLL's +backwards compatibility changes. Note that the addition of new +'interfaces' will not of itself change the DLL's compatibility for older +applications. + + +Which of the several dll versions to use? +----------------------------------------- +or, +--- +What are all these pthread*.dll and pthread*.lib files? +------------------------------------------------------- + +Simple, use either pthreadGCv.* if you use GCC, or pthreadVCv.* if you +use MSVC - where 'v' is the DLL versioning (compatibility) number. + +Otherwise, you need to choose carefully and know WHY. + +The most important choice you need to make is whether to use a +version that uses exceptions internally, or not. There are versions +of the library that use exceptions as part of the thread +cancelation and exit implementation. The default version uses +setjmp/longjmp. + +There is some contension amongst POSIX threads experts as +to how POSIX threads cancelation and exit should work +with languages that use exceptions, e.g. C++ and even C +(Microsoft's Structured Exceptions). + +The issue is: should cancelation of a thread in, say, +a C++ application cause object destructors and C++ exception +handlers to be invoked as the stack unwinds during thread +exit, or not? + +There seems to be more opinion in favour of using the +standard C version of the library (no EH) with C++ applications +for the reason that this appears to be the assumption commercial +pthreads implementations make. Therefore, if you use an EH version +of pthreads-win32 then you may be under the illusion that +your application will be portable, when in fact it is likely to +behave differently when linked with other pthreads libraries. + +Now you may be asking: then why have you kept the EH versions of +the library? + +There are a couple of reasons: +- there is division amongst the experts and so the code may + be needed in the future. Yes, it's in the repository and we + can get it out anytime in the future, but it would be difficult + to find. +- pthreads-win32 is one of the few implementations, and possibly + the only freely available one, that has EH versions. It may be + useful to people who want to play with or study application + behaviour under these conditions. + +Notes: + +[If you use either pthreadVCE or pthreadGCE] + +1. [See also the discussion in the FAQ file - Q2, Q4, and Q5] + +If your application contains catch(...) blocks in your POSIX +threads then you will need to replace the "catch(...)" with the macro +"PtW32Catch", eg. + + #ifdef PtW32Catch + PtW32Catch { + ... + } + #else + catch(...) { + ... + } + #endif + +Otherwise neither pthreads cancelation nor pthread_exit() will work +reliably when using versions of the library that use C++ exceptions +for cancelation and thread exit. + +This is due to what is believed to be a C++ compliance error in VC++ +whereby you may not have multiple handlers for the same exception in +the same try/catch block. GNU G++ doesn't have this restriction. + + +Other name changes +------------------ + +All snapshots prior to and including snapshot 2000-08-13 +used "_pthread_" as the prefix to library internal +functions, and "_PTHREAD_" to many library internal +macros. These have now been changed to "ptw32_" and "PTW32_" +respectively so as to not conflict with the ANSI standard's +reservation of identifiers beginning with "_" and "__" for +use by compiler implementations only. + +If you have written any applications and you are linking +statically with the pthreads-win32 library then you may have +included a call to _pthread_processInitialize. You will +now have to change that to ptw32_processInitialize. + + +Cleanup code default style +-------------------------- + +Previously, if not defined, the cleanup style was determined automatically +from the compiler used, and one of the following was defined accordingly: + + __CLEANUP_SEH MSVC only + __CLEANUP_CXX C++, including MSVC++, GNU G++ + __CLEANUP_C C, including GNU GCC, not MSVC + +These defines determine the style of cleanup (see pthread.h) and, +most importantly, the way that cancelation and thread exit (via +pthread_exit) is performed (see the routine ptw32_throw()). + +In short, the exceptions versions of the library throw an exception +when a thread is canceled, or exits via pthread_exit(). This exception is +caught by a handler in the thread startup routine, so that the +the correct stack unwinding occurs regardless of where the thread +is when it's canceled or exits via pthread_exit(). + +In this snapshot, unless the build explicitly defines (e.g. via a +compiler option) __CLEANUP_SEH, __CLEANUP_CXX, or __CLEANUP_C, then +the build NOW always defaults to __CLEANUP_C style cleanup. This style +uses setjmp/longjmp in the cancelation and pthread_exit implementations, +and therefore won't do stack unwinding even when linked to applications +that have it (e.g. C++ apps). This is for consistency with most/all +commercial Unix POSIX threads implementations. + +Although it was not clearly documented before, it is still necessary to +build your application using the same __CLEANUP_* define as was +used for the version of the library that you link with, so that the +correct parts of pthread.h are included. That is, the possible +defines require the following library versions: + + __CLEANUP_SEH pthreadVSE.dll + __CLEANUP_CXX pthreadVCE.dll or pthreadGCE.dll + __CLEANUP_C pthreadVC.dll or pthreadGC.dll + +It is recommended that you let pthread.h use it's default __CLEANUP_C +for both library and application builds. That is, don't define any of +the above, and then link with pthreadVC.lib (MSVC or MSVC++) and +libpthreadGC.a (MinGW GCC or G++). The reason is explained below, but +another reason is that the prebuilt pthreadVCE.dll is currently broken. +Versions built with MSVC++ later than version 6 may not be broken, but I +can't verify this yet. + +WHY ARE WE MAKING THE DEFAULT STYLE LESS EXCEPTION-FRIENDLY? +Because no commercial Unix POSIX threads implementation allows you to +choose to have stack unwinding. Therefore, providing it in pthread-win32 +as a default is dangerous. We still provide the choice but unless +you consciously choose to do otherwise, your pthreads applications will +now run or crash in similar ways irrespective of the pthreads platform +you use. Or at least this is the hope. + + +Building under VC++ using C++ EH, Structured EH, or just C +---------------------------------------------------------- + +From the source directory run nmake without any arguments to list +help information. E.g. + +$ nmake + +Microsoft (R) Program Maintenance Utility Version 6.00.8168.0 +Copyright (C) Microsoft Corp 1988-1998. All rights reserved. + +Run one of the following command lines: +nmake clean VCE (to build the MSVC dll with C++ exception handling) +nmake clean VSE (to build the MSVC dll with structured exception handling) +nmake clean VC (to build the MSVC dll with C cleanup code) +nmake clean VCE-inlined (to build the MSVC inlined dll with C++ exception handling) +nmake clean VSE-inlined (to build the MSVC inlined dll with structured exception handling) +nmake clean VC-inlined (to build the MSVC inlined dll with C cleanup code) +nmake clean VC-static (to build the MSVC static lib with C cleanup code) +nmake clean VCE-debug (to build the debug MSVC dll with C++ exception handling) +nmake clean VSE-debug (to build the debug MSVC dll with structured exception handling) +nmake clean VC-debug (to build the debug MSVC dll with C cleanup code) +nmake clean VCE-inlined-debug (to build the debug MSVC inlined dll with C++ exception handling) +nmake clean VSE-inlined-debug (to build the debug MSVC inlined dll with structured exception handling) +nmake clean VC-inlined-debug (to build the debug MSVC inlined dll with C cleanup code) +nmake clean VC-static-debug (to build the debug MSVC static lib with C cleanup code) + + +The pre-built dlls are normally built using the *-inlined targets. + +You can run the testsuite by changing to the "tests" directory and +running nmake. E.g.: + +$ cd tests +$ nmake + +Microsoft (R) Program Maintenance Utility Version 6.00.8168.0 +Copyright (C) Microsoft Corp 1988-1998. All rights reserved. + +Run one of the following command lines: +nmake clean VC (to test using VC dll with VC (no EH) applications) +nmake clean VCX (to test using VC dll with VC++ (EH) applications) +nmake clean VCE (to test using the VCE dll with VC++ EH applications) +nmake clean VSE (to test using VSE dll with VC (SEH) applications) +nmake clean VC-bench (to benchtest using VC dll with C bench app) +nmake clean VCX-bench (to benchtest using VC dll with C++ bench app) +nmake clean VCE-bench (to benchtest using VCE dll with C++ bench app) +nmake clean VSE-bench (to benchtest using VSE dll with SEH bench app) +nmake clean VC-static (to test using VC static lib with VC (no EH) applications) + + +Building under Mingw32 +---------------------- + +The dll can be built easily with recent versions of Mingw32. +(The distributed versions are built using Mingw32 and MsysDTK +from www.mingw32.org.) + +From the source directory, run make for help information. E.g.: + +$ make +Run one of the following command lines: +make clean GC (to build the GNU C dll with C cleanup code) +make clean GCE (to build the GNU C dll with C++ exception handling) +make clean GC-inlined (to build the GNU C inlined dll with C cleanup code) +make clean GCE-inlined (to build the GNU C inlined dll with C++ exception handling) +make clean GC-static (to build the GNU C inlined static lib with C cleanup code) +make clean GC-debug (to build the GNU C debug dll with C cleanup code) +make clean GCE-debug (to build the GNU C debug dll with C++ exception handling) +make clean GC-inlined-debug (to build the GNU C inlined debug dll with C cleanup code) +make clean GCE-inlined-debug (to build the GNU C inlined debug dll with C++ exception handling) +make clean GC-static-debug (to build the GNU C inlined static debug lib with C cleanup code) + + +The pre-built dlls are normally built using the *-inlined targets. + +You can run the testsuite by changing to the "tests" directory and +running make for help information. E.g.: + +$ cd tests +$ make +Run one of the following command lines: +make clean GC (to test using GC dll with C (no EH) applications) +make clean GCX (to test using GC dll with C++ (EH) applications) +make clean GCE (to test using GCE dll with C++ (EH) applications) +make clean GC-bench (to benchtest using GNU C dll with C cleanup code) +make clean GCE-bench (to benchtest using GNU C dll with C++ exception handling) +make clean GC-static (to test using GC static lib with C (no EH) applications) + + +Building under Linux using the Mingw32 cross development tools +-------------------------------------------------------------- + +You can build the library without leaving Linux by using the Mingw32 cross +development toolchain. See http://www.libsdl.org/extras/win32/cross/ for +tools and info. The GNUmakefile contains some support for this, for example: + +make CROSS=i386-mingw32msvc- clean GC-inlined + +will build pthreadGCn.dll and libpthreadGCn.a (n=version#), provided your +cross-tools/bin directory is in your PATH (or use the cross-make.sh script +at the URL above). + + +Building the library as a statically linkable library +----------------------------------------------------- + +General: PTW32_STATIC_LIB must be defined for both the library build and the +application build. The makefiles supplied and used by the following 'make' +command lines will define this for you. + +MSVC (creates pthreadVCn.lib as a static link lib): + +nmake clean VC-static + + +MinGW32 (creates libpthreadGCn.a as a static link lib): + +make clean GC-static + + +Define PTW32_STATIC_LIB when building your application. Also, your +application must call a two non-portable routines to initialise the +some state on startup and cleanup before exit. One other routine needs +to be called to cleanup after any Win32 threads have called POSIX API +routines. See README.NONPORTABLE or the html reference manual pages for +details on these routines: + +BOOL pthread_win32_process_attach_np (void); +BOOL pthread_win32_process_detach_np (void); +BOOL pthread_win32_thread_attach_np (void); // Currently a no-op +BOOL pthread_win32_thread_detach_np (void); + + +The tests makefiles have the same targets but only check that the +static library is statically linkable. They don't run the full +testsuite. To run the full testsuite, build the dlls and run the +dll test targets. + + +Building the library under Cygwin +--------------------------------- + +Cygwin is implementing it's own POSIX threads routines and these +will be the ones to use if you develop using Cygwin. + + +Ready to run binaries +--------------------- + +For convenience, the following ready-to-run files can be downloaded +from the FTP site (see under "Availability" below): + + pthread.h + semaphore.h + sched.h + pthreadVC.dll - built with MSVC compiler using C setjmp/longjmp + pthreadVC.lib + pthreadVCE.dll - built with MSVC++ compiler using C++ EH + pthreadVCE.lib + pthreadVSE.dll - built with MSVC compiler using SEH + pthreadVSE.lib + pthreadGC.dll - built with Mingw32 GCC + libpthreadGC.a - derived from pthreadGC.dll + pthreadGCE.dll - built with Mingw32 G++ + libpthreadGCE.a - derived from pthreadGCE.dll + +As of August 2003 pthreads-win32 pthreadG* versions are built and tested +using the MinGW + MsysDTK environment current as of that date or later. +The following file MAY be needed for older MinGW environments. + + gcc.dll - needed to build and run applications that use + pthreadGCE.dll. + + +Building applications with GNU compilers +---------------------------------------- + +If you're using pthreadGC.dll: + +With the three header files, pthreadGC.dll and libpthreadGC.a in the +same directory as your application myapp.c, you could compile, link +and run myapp.c under Mingw32 as follows: + + gcc -o myapp.exe myapp.c -I. -L. -lpthreadGC + myapp + +Or put pthreadGC.dll in an appropriate directory in your PATH, +put libpthreadGC.a in your system lib directory, and +put the three header files in your system include directory, +then use: + + gcc -o myapp.exe myapp.c -lpthreadGC + myapp + + +If you're using pthreadGCE.dll: + +With the three header files, pthreadGCE.dll, gcc.dll and libpthreadGCE.a +in the same directory as your application myapp.c, you could compile, +link and run myapp.c under Mingw32 as follows: + + gcc -x c++ -o myapp.exe myapp.c -I. -L. -lpthreadGCE + myapp + +Or put pthreadGCE.dll and gcc.dll in an appropriate directory in +your PATH, put libpthreadGCE.a in your system lib directory, and +put the three header files in your system include directory, +then use: + + gcc -x c++ -o myapp.exe myapp.c -lpthreadGCE + myapp + + +Availability +------------ + +The complete source code in either unbundled, self-extracting +Zip file, or tar/gzipped format can be found at: + + ftp://sources.redhat.com/pub/pthreads-win32 + +The pre-built DLL, export libraries and matching pthread.h can +be found at: + + ftp://sources.redhat.com/pub/pthreads-win32/dll-latest + +Home page: + + http://sources.redhat.com/pthreads-win32/ + + +Mailing list +------------ + +There is a mailing list for discussing pthreads on Win32. +To join, send email to: + + pthreads-win32-subscribe@sources.redhat.com + +Unsubscribe by sending mail to: + + pthreads-win32-unsubscribe@sources.redhat.com + + +Acknowledgements +---------------- + +See the ANNOUNCE file for acknowledgements. +See the 'CONTRIBUTORS' file for the list of contributors. + +As much as possible, the ChangeLog file attributes +contributions and patches that have been incorporated +in the library to the individuals responsible. + +Finally, thanks to all those who work on and contribute to the +POSIX and Single Unix Specification standards. The maturity of an +industry can be measured by it's open standards. + +---- +Ross Johnson + + + + + + + + + diff --git a/pcsx2/windows/pthreads/README.NONPORTABLE b/pcsx2/windows/pthreads/README.NONPORTABLE new file mode 100644 index 0000000000..aa43297e10 --- /dev/null +++ b/pcsx2/windows/pthreads/README.NONPORTABLE @@ -0,0 +1,285 @@ +This file documents non-portable functions and other issues. + +Non-portable functions included in pthreads-win32 +------------------------------------------------- + +BOOL +pthread_win32_test_features_np(int mask) + + This routine allows an application to check which + run-time auto-detected features are available within + the library. + + The possible features are: + + PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE + Return TRUE if the native version of + InterlockedCompareExchange() is being used. + PTW32_ALERTABLE_ASYNC_CANCEL + Return TRUE is the QueueUserAPCEx package + QUSEREX.DLL is available and the AlertDrv.sys + driver is loaded into Windows, providing + alertable (pre-emptive) asyncronous threads + cancelation. If this feature returns FALSE + then the default async cancel scheme is in + use, which cannot cancel blocked threads. + + Features may be Or'ed into the mask parameter, in which case + the routine returns TRUE if any of the Or'ed features would + return TRUE. At this stage it doesn't make sense to Or features + but it may some day. + + +void * +pthread_timechange_handler_np(void *) + + To improve tolerance against operator or time service + initiated system clock changes. + + This routine can be called by an application when it + receives a WM_TIMECHANGE message from the system. At + present it broadcasts all condition variables so that + waiting threads can wake up and re-evaluate their + conditions and restart their timed waits if required. + + It has the same return type and argument type as a + thread routine so that it may be called directly + through pthread_create(), i.e. as a separate thread. + + Parameters + + Although a parameter must be supplied, it is ignored. + The value NULL can be used. + + Return values + + It can return an error EAGAIN to indicate that not + all condition variables were broadcast for some reason. + Otherwise, 0 is returned. + + If run as a thread, the return value is returned + through pthread_join(). + + The return value should be cast to an integer. + + +HANDLE +pthread_getw32threadhandle_np(pthread_t thread); + + Returns the win32 thread handle that the POSIX + thread "thread" is running as. + + Applications can use the win32 handle to set + win32 specific attributes of the thread. + + +int +pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr, int kind) + +int +pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr, int *kind) + + These two routines are included for Linux compatibility + and are direct equivalents to the standard routines + pthread_mutexattr_settype + pthread_mutexattr_gettype + + pthread_mutexattr_setkind_np accepts the following + mutex kinds: + PTHREAD_MUTEX_FAST_NP + PTHREAD_MUTEX_ERRORCHECK_NP + PTHREAD_MUTEX_RECURSIVE_NP + + These are really just equivalent to (respectively): + PTHREAD_MUTEX_NORMAL + PTHREAD_MUTEX_ERRORCHECK + PTHREAD_MUTEX_RECURSIVE + +int +pthread_delay_np (const struct timespec *interval); + + This routine causes a thread to delay execution for a specific period of time. + This period ends at the current time plus the specified interval. The routine + will not return before the end of the period is reached, but may return an + arbitrary amount of time after the period has gone by. This can be due to + system load, thread priorities, and system timer granularity. + + Specifying an interval of zero (0) seconds and zero (0) nanoseconds is + allowed and can be used to force the thread to give up the processor or to + deliver a pending cancelation request. + + This routine is a cancelation point. + + The timespec structure contains the following two fields: + + tv_sec is an integer number of seconds. + tv_nsec is an integer number of nanoseconds. + + Return Values + + If an error condition occurs, this routine returns an integer value + indicating the type of error. Possible return values are as follows: + + 0 Successful completion. + [EINVAL] The value specified by interval is invalid. + +int +pthread_num_processors_np + + This routine (found on HPUX systems) returns the number of processors + in the system. This implementation actually returns the number of + processors available to the process, which can be a lower number + than the system's number, depending on the process's affinity mask. + +BOOL +pthread_win32_process_attach_np (void); + +BOOL +pthread_win32_process_detach_np (void); + +BOOL +pthread_win32_thread_attach_np (void); + +BOOL +pthread_win32_thread_detach_np (void); + + These functions contain the code normally run via dllMain + when the library is used as a dll but which need to be + called explicitly by an application when the library + is statically linked. + + You will need to call pthread_win32_process_attach_np() before + you can call any pthread routines when statically linking. + You should call pthread_win32_process_detach_np() before + exiting your application to clean up. + + pthread_win32_thread_attach_np() is currently a no-op, but + pthread_win32_thread_detach_np() is needed to clean up + the implicit pthread handle that is allocated to a Win32 thread if + it calls certain pthreads routines. Call this routine when the + Win32 thread exits. + + These functions invariably return TRUE except for + pthread_win32_process_attach_np() which will return FALSE + if pthreads-win32 initialisation fails. + +int +pthreadCancelableWait (HANDLE waitHandle); + +int +pthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout); + + These two functions provide hooks into the pthread_cancel + mechanism that will allow you to wait on a Windows handle + and make it a cancellation point. Both functions block + until either the given w32 handle is signaled, or + pthread_cancel has been called. It is implemented using + WaitForMultipleObjects on 'waitHandle' and a manually + reset w32 event used to implement pthread_cancel. + + +Non-portable issues +------------------- + +Thread priority + + POSIX defines a single contiguous range of numbers that determine a + thread's priority. Win32 defines priority classes and priority + levels relative to these classes. Classes are simply priority base + levels that the defined priority levels are relative to such that, + changing a process's priority class will change the priority of all + of it's threads, while the threads retain the same relativity to each + other. + + A Win32 system defines a single contiguous monotonic range of values + that define system priority levels, just like POSIX. However, Win32 + restricts individual threads to a subset of this range on a + per-process basis. + + The following table shows the base priority levels for combinations + of priority class and priority value in Win32. + + Process Priority Class Thread Priority Level + ----------------------------------------------------------------- + 1 IDLE_PRIORITY_CLASS THREAD_PRIORITY_IDLE + 1 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE + 1 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE + 1 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE + 1 HIGH_PRIORITY_CLASS THREAD_PRIORITY_IDLE + 2 IDLE_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + 3 IDLE_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + 4 IDLE_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + 4 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + 5 IDLE_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + 5 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + 5 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + 6 IDLE_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + 6 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + 6 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + 7 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + 7 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + 7 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + 8 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + 8 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + 8 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + 8 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + 9 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + 9 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + 9 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + 10 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + 10 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + 11 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + 11 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + 11 HIGH_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + 12 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + 12 HIGH_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + 13 HIGH_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + 14 HIGH_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + 15 HIGH_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + 15 HIGH_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + 15 IDLE_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + 15 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + 15 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + 15 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + 16 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_IDLE + 17 REALTIME_PRIORITY_CLASS -7 + 18 REALTIME_PRIORITY_CLASS -6 + 19 REALTIME_PRIORITY_CLASS -5 + 20 REALTIME_PRIORITY_CLASS -4 + 21 REALTIME_PRIORITY_CLASS -3 + 22 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + 23 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + 24 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + 25 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + 26 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + 27 REALTIME_PRIORITY_CLASS 3 + 28 REALTIME_PRIORITY_CLASS 4 + 29 REALTIME_PRIORITY_CLASS 5 + 30 REALTIME_PRIORITY_CLASS 6 + 31 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + + Windows NT: Values -7, -6, -5, -4, -3, 3, 4, 5, and 6 are not supported. + + + As you can see, the real priority levels available to any individual + Win32 thread are non-contiguous. + + An application using pthreads-win32 should not make assumptions about + the numbers used to represent thread priority levels, except that they + are monotonic between the values returned by sched_get_priority_min() + and sched_get_priority_max(). E.g. Windows 95, 98, NT, 2000, XP make + available a non-contiguous range of numbers between -15 and 15, while + at least one version of WinCE (3.0) defines the minimum priority + (THREAD_PRIORITY_LOWEST) as 5, and the maximum priority + (THREAD_PRIORITY_HIGHEST) as 1. + + Internally, pthreads-win32 maps any priority levels between + THREAD_PRIORITY_IDLE and THREAD_PRIORITY_LOWEST to THREAD_PRIORITY_LOWEST, + or between THREAD_PRIORITY_TIME_CRITICAL and THREAD_PRIORITY_HIGHEST to + THREAD_PRIORITY_HIGHEST. Currently, this also applies to + REALTIME_PRIORITY_CLASSi even if levels -7, -6, -5, -4, -3, 3, 4, 5, and 6 + are supported. + + If it wishes, a Win32 application using pthreads-win32 can use the Win32 + defined priority macros THREAD_PRIORITY_IDLE through + THREAD_PRIORITY_TIME_CRITICAL. diff --git a/pcsx2/windows/pthreads/TODO b/pcsx2/windows/pthreads/TODO new file mode 100644 index 0000000000..f798e769e5 --- /dev/null +++ b/pcsx2/windows/pthreads/TODO @@ -0,0 +1,7 @@ + Things that aren't done yet + --------------------------- + +1. Implement PTHREAD_PROCESS_SHARED for semaphores, mutexes, + condition variables, read/write locks, barriers. + + diff --git a/pcsx2/windows/pthreads/attr.c b/pcsx2/windows/pthreads/attr.c new file mode 100644 index 0000000000..0d0b5600ab --- /dev/null +++ b/pcsx2/windows/pthreads/attr.c @@ -0,0 +1,53 @@ +/* + * attr.c + * + * Description: + * This translation unit agregates operations on thread attribute objects. + * It is used for inline optimisation. + * + * The included modules are used separately when static executable sizes + * must be minimised. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +#include "pthread_attr_init.c" +#include "pthread_attr_destroy.c" +#include "pthread_attr_getdetachstate.c" +#include "pthread_attr_setdetachstate.c" +#include "pthread_attr_getstackaddr.c" +#include "pthread_attr_setstackaddr.c" +#include "pthread_attr_getstacksize.c" +#include "pthread_attr_setstacksize.c" +#include "pthread_attr_getscope.c" +#include "pthread_attr_setscope.c" diff --git a/pcsx2/windows/pthreads/barrier.c b/pcsx2/windows/pthreads/barrier.c new file mode 100644 index 0000000000..f95f4bde98 --- /dev/null +++ b/pcsx2/windows/pthreads/barrier.c @@ -0,0 +1,47 @@ +/* + * barrier.c + * + * Description: + * This translation unit implements barrier primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +#include "pthread_barrier_init.c" +#include "pthread_barrier_destroy.c" +#include "pthread_barrier_wait.c" +#include "pthread_barrierattr_init.c" +#include "pthread_barrierattr_destroy.c" +#include "pthread_barrierattr_getpshared.c" +#include "pthread_barrierattr_setpshared.c" diff --git a/pcsx2/windows/pthreads/cancel.c b/pcsx2/windows/pthreads/cancel.c new file mode 100644 index 0000000000..28cf4ebc5e --- /dev/null +++ b/pcsx2/windows/pthreads/cancel.c @@ -0,0 +1,44 @@ +/* + * cancel.c + * + * Description: + * POSIX thread functions related to thread cancellation. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +#include "pthread_setcancelstate.c" +#include "pthread_setcanceltype.c" +#include "pthread_testcancel.c" +#include "pthread_cancel.c" diff --git a/pcsx2/windows/pthreads/cleanup.c b/pcsx2/windows/pthreads/cleanup.c new file mode 100644 index 0000000000..8f65c5ee5d --- /dev/null +++ b/pcsx2/windows/pthreads/cleanup.c @@ -0,0 +1,148 @@ +/* + * cleanup.c + * + * Description: + * This translation unit implements routines associated + * with cleaning up threads. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +/* + * The functions ptw32_pop_cleanup and ptw32_push_cleanup + * are implemented here for applications written in C with no + * SEH or C++ destructor support. + */ + +ptw32_cleanup_t * +ptw32_pop_cleanup (int execute) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function pops the most recently pushed cleanup + * handler. If execute is nonzero, then the cleanup handler + * is executed if non-null. + * + * PARAMETERS + * execute + * if nonzero, execute the cleanup handler + * + * + * DESCRIPTION + * This function pops the most recently pushed cleanup + * handler. If execute is nonzero, then the cleanup handler + * is executed if non-null. + * NOTE: specify 'execute' as nonzero to avoid duplication + * of common cleanup code. + * + * RESULTS + * N/A + * + * ------------------------------------------------------ + */ +{ + ptw32_cleanup_t *cleanup; + + cleanup = (ptw32_cleanup_t *) pthread_getspecific (ptw32_cleanupKey); + + if (cleanup != NULL) + { + if (execute && (cleanup->routine != NULL)) + { + + (*cleanup->routine) (cleanup->arg); + + } + + pthread_setspecific (ptw32_cleanupKey, (void *) cleanup->prev); + + } + + return (cleanup); + +} /* ptw32_pop_cleanup */ + + +void +ptw32_push_cleanup (ptw32_cleanup_t * cleanup, + ptw32_cleanup_callback_t routine, void *arg) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function pushes a new cleanup handler onto the thread's stack + * of cleanup handlers. Each cleanup handler pushed onto the stack is + * popped and invoked with the argument 'arg' when + * a) the thread exits by calling 'pthread_exit', + * b) when the thread acts on a cancellation request, + * c) or when the thread calls pthread_cleanup_pop with a nonzero + * 'execute' argument + * + * PARAMETERS + * cleanup + * a pointer to an instance of pthread_cleanup_t, + * + * routine + * pointer to a cleanup handler, + * + * arg + * parameter to be passed to the cleanup handler + * + * + * DESCRIPTION + * This function pushes a new cleanup handler onto the thread's stack + * of cleanup handlers. Each cleanup handler pushed onto the stack is + * popped and invoked with the argument 'arg' when + * a) the thread exits by calling 'pthread_exit', + * b) when the thread acts on a cancellation request, + * c) or when the thrad calls pthread_cleanup_pop with a nonzero + * 'execute' argument + * NOTE: pthread_push_cleanup, ptw32_pop_cleanup must be paired + * in the same lexical scope. + * + * RESULTS + * pthread_cleanup_t * + * pointer to the previous cleanup + * + * ------------------------------------------------------ + */ +{ + cleanup->routine = routine; + cleanup->arg = arg; + + cleanup->prev = (ptw32_cleanup_t *) pthread_getspecific (ptw32_cleanupKey); + + pthread_setspecific (ptw32_cleanupKey, (void *) cleanup); + +} /* ptw32_push_cleanup */ diff --git a/pcsx2/windows/pthreads/condvar.c b/pcsx2/windows/pthreads/condvar.c new file mode 100644 index 0000000000..449235c5ad --- /dev/null +++ b/pcsx2/windows/pthreads/condvar.c @@ -0,0 +1,50 @@ +/* + * condvar.c + * + * Description: + * This translation unit implements condition variables and their primitives. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + */ + +#include "pthread.h" +#include "implement.h" + +#include "ptw32_cond_check_need_init.c" +#include "pthread_condattr_init.c" +#include "pthread_condattr_destroy.c" +#include "pthread_condattr_getpshared.c" +#include "pthread_condattr_setpshared.c" +#include "pthread_cond_init.c" +#include "pthread_cond_destroy.c" +#include "pthread_cond_wait.c" +#include "pthread_cond_signal.c" diff --git a/pcsx2/windows/pthreads/config.h b/pcsx2/windows/pthreads/config.h new file mode 100644 index 0000000000..2ea14f16c0 --- /dev/null +++ b/pcsx2/windows/pthreads/config.h @@ -0,0 +1,134 @@ +/* config.h */ + +#ifndef PTW32_CONFIG_H +#define PTW32_CONFIG_H + +/********************************************************************* + * Defaults: see target specific redefinitions below. + *********************************************************************/ + +/* We're building the pthreads-win32 library */ +#define PTW32_BUILD + +/* Do we know about the C type sigset_t? */ +#undef HAVE_SIGSET_T + +/* Define if you have the header file. */ +#define HAVE_SIGNAL_H + +/* Define if you have the Borland TASM32 or compatible assembler. */ +#undef HAVE_TASM32 + +/* Define if you don't have Win32 DuplicateHandle. (eg. WinCE) */ +#undef NEED_DUPLICATEHANDLE + +/* Define if you don't have Win32 _beginthreadex. (eg. WinCE) */ +#undef NEED_CREATETHREAD + +/* Define if you don't have Win32 errno. (eg. WinCE) */ +#undef NEED_ERRNO + +/* Define if you don't have Win32 calloc. (eg. WinCE) */ +#undef NEED_CALLOC + +/* Define if you don't have Win32 ftime. (eg. WinCE) */ +#undef NEED_FTIME + +/* Define if you don't have Win32 semaphores. (eg. WinCE 2.1 or earlier) */ +#undef NEED_SEM + +/* Define if you need to convert string parameters to unicode. (eg. WinCE) */ +#undef NEED_UNICODE_CONSTS + +/* Define if your C (not C++) compiler supports "inline" functions. */ +#define HAVE_C_INLINE + +/* Do we know about type mode_t? */ +#undef HAVE_MODE_T + +/* Define if you have the timespec struct */ +#undef HAVE_STRUCT_TIMESPEC + +/* Define if you don't have the GetProcessAffinityMask() */ +#undef NEED_PROCESS_AFFINITY_MASK + +/* +# ---------------------------------------------------------------------- +# The library can be built with some alternative behaviour to better +# facilitate development of applications on Win32 that will be ported +# to other POSIX systems. +# +# Nothing described here will make the library non-compliant and strictly +# compliant applications will not be affected in any way, but +# applications that make assumptions that POSIX does not guarantee are +# not strictly compliant and may fail or misbehave with some settings. +# +# PTW32_THREAD_ID_REUSE_INCREMENT +# Purpose: +# POSIX says that applications should assume that thread IDs can be +# recycled. However, Solaris (and some other systems) use a [very large] +# sequence number as the thread ID, which provides virtual uniqueness. +# This provides a very high but finite level of safety for applications +# that are not meticulous in tracking thread lifecycles e.g. applications +# that call functions which target detached threads without some form of +# thread exit synchronisation. +# +# Usage: +# Set to any value in the range: 0 <= value < 2^wordsize. +# Set to 0 to emulate reusable thread ID behaviour like Linux or *BSD. +# Set to 1 for unique thread IDs like Solaris (this is the default). +# Set to some factor of 2^wordsize to emulate smaller word size types +# (i.e. will wrap sooner). This might be useful to emulate some embedded +# systems. +# +# define PTW32_THREAD_ID_REUSE_INCREMENT 0 +# +# ---------------------------------------------------------------------- + */ +#undef PTW32_THREAD_ID_REUSE_INCREMENT + + +/********************************************************************* + * Target specific groups + * + * If you find that these are incorrect or incomplete please report it + * to the pthreads-win32 maintainer. Thanks. + *********************************************************************/ +#ifdef WINCE +#define NEED_DUPLICATEHANDLE +#define NEED_CREATETHREAD +#define NEED_ERRNO +#define NEED_CALLOC +#define NEED_FTIME +//#define NEED_SEM +#define NEED_UNICODE_CONSTS +#define NEED_PROCESS_AFFINITY_MASK +#endif + +#ifdef _UWIN +#define HAVE_MODE_T +#define HAVE_STRUCT_TIMESPEC +#endif + +#ifdef __GNUC__ +#define HAVE_C_INLINE +#endif + +#ifdef __MINGW32__ +#define HAVE_MODE_T +#endif + +#ifdef __BORLANDC__ +#endif + +#ifdef __WATCOMC__ +#endif + +#ifdef __DMC__ +#define HAVE_SIGNAL_H +#define HAVE_C_INLINE +#endif + + + +#endif diff --git a/pcsx2/windows/pthreads/create.c b/pcsx2/windows/pthreads/create.c new file mode 100644 index 0000000000..78ea45ea8f --- /dev/null +++ b/pcsx2/windows/pthreads/create.c @@ -0,0 +1,305 @@ +/* + * create.c + * + * Description: + * This translation unit implements routines associated with spawning a new + * thread. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#ifndef _UWIN +#include +#endif + +int +pthread_create (pthread_t * tid, + const pthread_attr_t * attr, + void *(*start) (void *), void *arg) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function creates a thread running the start function, + * passing it the parameter value, 'arg'. The 'attr' + * argument specifies optional creation attributes. + * The identity of the new thread is returned + * via 'tid', which should not be NULL. + * + * PARAMETERS + * tid + * pointer to an instance of pthread_t + * + * attr + * optional pointer to an instance of pthread_attr_t + * + * start + * pointer to the starting routine for the new thread + * + * arg + * optional parameter passed to 'start' + * + * + * DESCRIPTION + * This function creates a thread running the start function, + * passing it the parameter value, 'arg'. The 'attr' + * argument specifies optional creation attributes. + * The identity of the new thread is returned + * via 'tid', which should not be the NULL pointer. + * + * RESULTS + * 0 successfully created thread, + * EINVAL attr invalid, + * EAGAIN insufficient resources. + * + * ------------------------------------------------------ + */ +{ + pthread_t thread; + ptw32_thread_t * tp; + register pthread_attr_t a; + HANDLE threadH = 0; + int result = EAGAIN; + int run = PTW32_TRUE; + ThreadParms *parms = NULL; + long stackSize; + int priority; + pthread_t self; + + /* + * Before doing anything, check that tid can be stored through + * without invoking a memory protection error (segfault). + * Make sure that the assignment below can't be optimised out by the compiler. + * This is assured by conditionally assigning *tid again at the end. + */ + tid->x = 0; + + if (attr != NULL) + { + a = *attr; + } + else + { + a = NULL; + } + + if ((thread = ptw32_new ()).p == NULL) + { + goto FAIL0; + } + + tp = (ptw32_thread_t *) thread.p; + + priority = tp->sched_priority; + + if ((parms = (ThreadParms *) malloc (sizeof (*parms))) == NULL) + { + goto FAIL0; + } + + parms->tid = thread; + parms->start = start; + parms->arg = arg; + +#if defined(HAVE_SIGSET_T) + + /* + * Threads inherit their initial sigmask from their creator thread. + */ + self = pthread_self(); + tp->sigmask = ((ptw32_thread_t *)self.p)->sigmask; + +#endif /* HAVE_SIGSET_T */ + + + if (a != NULL) + { + stackSize = a->stacksize; + tp->detachState = a->detachstate; + priority = a->param.sched_priority; + +#if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL) + /* WinCE */ +#else + /* Everything else */ + + /* + * Thread priority must be set to a valid system level + * without altering the value set by pthread_attr_setschedparam(). + */ + + /* + * PTHREAD_EXPLICIT_SCHED is the default because Win32 threads + * don't inherit their creator's priority. They are started with + * THREAD_PRIORITY_NORMAL (win32 value). The result of not supplying + * an 'attr' arg to pthread_create() is equivalent to defaulting to + * PTHREAD_EXPLICIT_SCHED and priority THREAD_PRIORITY_NORMAL. + */ + if (PTHREAD_INHERIT_SCHED == a->inheritsched) + { + /* + * If the thread that called pthread_create() is a Win32 thread + * then the inherited priority could be the result of a temporary + * system adjustment. This is not the case for POSIX threads. + */ +#if ! defined(HAVE_SIGSET_T) + self = pthread_self (); +#endif + priority = ((ptw32_thread_t *) self.p)->sched_priority; + } + +#endif + + } + else + { + /* + * Default stackSize + */ + stackSize = PTHREAD_STACK_MIN; + } + + tp->state = run ? PThreadStateInitial : PThreadStateSuspended; + + tp->keys = NULL; + + /* + * Threads must be started in suspended mode and resumed if necessary + * after _beginthreadex returns us the handle. Otherwise we set up a + * race condition between the creating and the created threads. + * Note that we also retain a local copy of the handle for use + * by us in case thread.p->threadH gets NULLed later but before we've + * finished with it here. + */ + +#if ! defined (__MINGW32__) || defined (__MSVCRT__) || defined (__DMC__) + + tp->threadH = + threadH = + (HANDLE) _beginthreadex ((void *) NULL, /* No security info */ + (unsigned) stackSize, /* default stack size */ + ptw32_threadStart, + parms, + (unsigned) + CREATE_SUSPENDED, + (unsigned *) &(tp->thread)); + + if (threadH != 0) + { + if (a != NULL) + { + (void) ptw32_setthreadpriority (thread, SCHED_OTHER, priority); + } + + if (run) + { + ResumeThread (threadH); + } + } + +#else /* __MINGW32__ && ! __MSVCRT__ */ + + /* + * This lock will force pthread_threadStart() to wait until we have + * the thread handle and have set the priority. + */ + (void) pthread_mutex_lock (&tp->cancelLock); + + tp->threadH = + threadH = + (HANDLE) _beginthread (ptw32_threadStart, (unsigned) stackSize, /* default stack size */ + parms); + + /* + * Make the return code match _beginthreadex's. + */ + if (threadH == (HANDLE) - 1L) + { + tp->threadH = threadH = 0; + } + else + { + if (!run) + { + /* + * beginthread does not allow for create flags, so we do it now. + * Note that beginthread itself creates the thread in SUSPENDED + * mode, and then calls ResumeThread to start it. + */ + SuspendThread (threadH); + } + + if (a != NULL) + { + (void) ptw32_setthreadpriority (thread, SCHED_OTHER, priority); + } + } + + (void) pthread_mutex_unlock (&tp->cancelLock); + +#endif /* __MINGW32__ && ! __MSVCRT__ */ + + result = (threadH != 0) ? 0 : EAGAIN; + + /* + * Fall Through Intentionally + */ + + /* + * ------------ + * Failure Code + * ------------ + */ + +FAIL0: + if (result != 0) + { + + ptw32_threadDestroy (thread); + tp = NULL; + + if (parms != NULL) + { + free (parms); + } + } + else + { + *tid = thread; + } + +#ifdef _UWIN + if (result == 0) + pthread_count++; +#endif + return (result); + +} /* pthread_create */ diff --git a/pcsx2/windows/pthreads/dll.c b/pcsx2/windows/pthreads/dll.c new file mode 100644 index 0000000000..067c98f0dd --- /dev/null +++ b/pcsx2/windows/pthreads/dll.c @@ -0,0 +1,92 @@ +/* + * dll.c + * + * Description: + * This translation unit implements DLL initialisation. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef PTW32_STATIC_LIB + +#include "pthread.h" +#include "implement.h" + +#ifdef _MSC_VER +/* + * lpvReserved yields an unreferenced formal parameter; + * ignore it + */ +#pragma warning( disable : 4100 ) +#endif + +#ifdef __cplusplus +/* + * Dear c++: Please don't mangle this name. -thanks + */ +extern "C" +#endif /* __cplusplus */ + BOOL WINAPI +DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) +{ + BOOL result = PTW32_TRUE; + + switch (fdwReason) + { + + case DLL_PROCESS_ATTACH: + result = pthread_win32_process_attach_np (); + break; + + case DLL_THREAD_ATTACH: + /* + * A thread is being created + */ + result = pthread_win32_thread_attach_np (); + break; + + case DLL_THREAD_DETACH: + /* + * A thread is exiting cleanly + */ + result = pthread_win32_thread_detach_np (); + break; + + case DLL_PROCESS_DETACH: + (void) pthread_win32_thread_detach_np (); + result = pthread_win32_process_detach_np (); + break; + } + + return (result); + +} /* DllMain */ + +#endif /* PTW32_STATIC_LIB */ diff --git a/pcsx2/windows/pthreads/errno.c b/pcsx2/windows/pthreads/errno.c new file mode 100644 index 0000000000..11fdd8af42 --- /dev/null +++ b/pcsx2/windows/pthreads/errno.c @@ -0,0 +1,94 @@ +/* + * errno.c + * + * Description: + * This translation unit implements routines associated with spawning a new + * thread. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#if defined(NEED_ERRNO) + +#include "pthread.h" +#include "implement.h" + +static int reallyBad = ENOMEM; + +/* + * Re-entrant errno. + * + * Each thread has it's own errno variable in pthread_t. + * + * The benefit of using the pthread_t structure + * instead of another TSD key is TSD keys are limited + * on Win32 to 64 per process. Secondly, to implement + * it properly without using pthread_t you'd need + * to dynamically allocate an int on starting the thread + * and store it manually into TLS and then ensure that you free + * it on thread termination. We get all that for free + * by simply storing the errno on the pthread_t structure. + * + * MSVC and Mingw32 already have their own thread-safe errno. + * + * #if defined( _REENTRANT ) || defined( _MT ) + * #define errno *_errno() + * + * int *_errno( void ); + * #else + * extern int errno; + * #endif + * + */ + +int * +_errno (void) +{ + pthread_t self; + int *result; + + if ((self = pthread_self ()) == NULL) + { + /* + * Yikes! unable to allocate a thread! + * Throw an exception? return an error? + */ + result = &reallyBad; + } + else + { + result = &(self->ptErrno); + } + + return (result); + +} /* _errno */ + +#endif /* (NEED_ERRNO) */ diff --git a/pcsx2/windows/pthreads/exit.c b/pcsx2/windows/pthreads/exit.c new file mode 100644 index 0000000000..28c7196f2a --- /dev/null +++ b/pcsx2/windows/pthreads/exit.c @@ -0,0 +1,44 @@ +/* + * exit.c + * + * Description: + * This translation unit implements routines associated with exiting from + * a thread. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#ifndef _UWIN +# include +#endif + +#include "pthread_exit.c" diff --git a/pcsx2/windows/pthreads/fork.c b/pcsx2/windows/pthreads/fork.c new file mode 100644 index 0000000000..97d9ec23ae --- /dev/null +++ b/pcsx2/windows/pthreads/fork.c @@ -0,0 +1,39 @@ +/* + * fork.c + * + * Description: + * Implementation of fork() for POSIX threads. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + +#include "pthread.h" +#include "implement.h" diff --git a/pcsx2/windows/pthreads/global.c b/pcsx2/windows/pthreads/global.c new file mode 100644 index 0000000000..8d8a245d6a --- /dev/null +++ b/pcsx2/windows/pthreads/global.c @@ -0,0 +1,117 @@ +/* + * global.c + * + * Description: + * This translation unit instantiates data associated with the implementation + * as a whole. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int ptw32_processInitialized = PTW32_FALSE; +ptw32_thread_t * ptw32_threadReuseTop = PTW32_THREAD_REUSE_EMPTY; +ptw32_thread_t * ptw32_threadReuseBottom = PTW32_THREAD_REUSE_EMPTY; +pthread_key_t ptw32_selfThreadKey = NULL; +pthread_key_t ptw32_cleanupKey = NULL; +pthread_cond_t ptw32_cond_list_head = NULL; +pthread_cond_t ptw32_cond_list_tail = NULL; + +int ptw32_concurrency = 0; + +/* What features have been auto-detaected */ +int ptw32_features = 0; + +BOOL ptw32_smp_system = PTW32_TRUE; /* Safer if assumed true initially. */ + +#if 0 +/* + * Function pointer to InterlockedCompareExchange if it exists, otherwise + * it will be set at runtime to a substitute local version with the same + * functionality but may be architecture specific. + */ +PTW32_INTERLOCKED_LONG + (WINAPI * ptw32_interlocked_compare_exchange) (PTW32_INTERLOCKED_LPLONG, + PTW32_INTERLOCKED_LONG, + PTW32_INTERLOCKED_LONG) = + NULL; +#endif + +/* + * Function pointer to QueueUserAPCEx if it exists, otherwise + * it will be set at runtime to a substitute routine which cannot unblock + * blocked threads. + */ +DWORD (*ptw32_register_cancelation) (PAPCFUNC, HANDLE, DWORD) = NULL; + +/* + * Global lock for managing pthread_t struct reuse. + */ +CRITICAL_SECTION ptw32_thread_reuse_lock; + +/* + * Global lock for testing internal state of statically declared mutexes. + */ +CRITICAL_SECTION ptw32_mutex_test_init_lock; + +/* + * Global lock for testing internal state of PTHREAD_COND_INITIALIZER + * created condition variables. + */ +CRITICAL_SECTION ptw32_cond_test_init_lock; + +/* + * Global lock for testing internal state of PTHREAD_RWLOCK_INITIALIZER + * created read/write locks. + */ +CRITICAL_SECTION ptw32_rwlock_test_init_lock; + +/* + * Global lock for testing internal state of PTHREAD_SPINLOCK_INITIALIZER + * created spin locks. + */ +CRITICAL_SECTION ptw32_spinlock_test_init_lock; + +/* + * Global lock for condition variable linked list. The list exists + * to wake up CVs when a WM_TIMECHANGE message arrives. See + * w32_TimeChangeHandler.c. + */ +CRITICAL_SECTION ptw32_cond_list_lock; + +#ifdef _UWIN +/* + * Keep a count of the number of threads. + */ +int pthread_count = 0; +#endif diff --git a/pcsx2/windows/pthreads/implement.h b/pcsx2/windows/pthreads/implement.h new file mode 100644 index 0000000000..9652583fe2 --- /dev/null +++ b/pcsx2/windows/pthreads/implement.h @@ -0,0 +1,717 @@ +/* + * implement.h + * + * Definitions that don't need to be public. + * + * Keeps all the internals out of pthread.h + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef _IMPLEMENT_H +#define _IMPLEMENT_H + +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x400 + +#include + +/* + * In case windows.h doesn't define it (e.g. WinCE perhaps) + */ +#ifdef WINCE +typedef VOID (APIENTRY *PAPCFUNC)(DWORD dwParam); +#endif + +/* + * note: ETIMEDOUT is correctly defined in winsock.h + */ +#include + +/* + * In case ETIMEDOUT hasn't been defined above somehow. + */ +#ifndef ETIMEDOUT +# define ETIMEDOUT 10060 /* This is the value in winsock.h. */ +#endif + +#if !defined(malloc) +#include +#endif + +#if !defined(INT_MAX) +#include +#endif + +/* use local include files during development */ +#include "semaphore.h" +#include "sched.h" + +#if defined(HAVE_C_INLINE) || defined(__cplusplus) +#define INLINE __forceinline +#else +#define INLINE +#endif + +#if defined (__MINGW32__) || (_MSC_VER >= 1300) +#define PTW32_INTERLOCKED_LONG long +#define PTW32_INTERLOCKED_LPLONG long* +#else +#define PTW32_INTERLOCKED_LONG PVOID +#define PTW32_INTERLOCKED_LPLONG PVOID* +#endif + +#if defined(__MINGW32__) +#include +#elif defined(__BORLANDC__) +#define int64_t ULONGLONG +#else +#define int64_t _int64 +#endif + +typedef enum +{ + /* + * This enumeration represents the state of the thread; + * The thread is still "alive" if the numeric value of the + * state is greater or equal "PThreadStateRunning". + */ + PThreadStateInitial = 0, /* Thread not running */ + PThreadStateRunning, /* Thread alive & kicking */ + PThreadStateSuspended, /* Thread alive but suspended */ + PThreadStateCancelPending, /* Thread alive but is */ + /* has cancelation pending. */ + PThreadStateCanceling, /* Thread alive but is */ + /* in the process of terminating */ + /* due to a cancellation request */ + PThreadStateException, /* Thread alive but exiting */ + /* due to an exception */ + PThreadStateLast +} +PThreadState; + + +typedef struct ptw32_thread_t_ ptw32_thread_t; + +struct ptw32_thread_t_ +{ +#ifdef _UWIN + DWORD dummy[5]; +#endif + DWORD thread; + HANDLE threadH; /* Win32 thread handle - POSIX thread is invalid if threadH == 0 */ + pthread_t ptHandle; /* This thread's permanent pthread_t handle */ + ptw32_thread_t * prevReuse; /* Links threads on reuse stack */ + volatile PThreadState state; + void *exitStatus; + void *parms; + int ptErrno; + int detachState; + pthread_mutex_t threadLock; /* Used for serialised access to public thread state */ + int sched_priority; /* As set, not as currently is */ + pthread_mutex_t cancelLock; /* Used for async-cancel safety */ + int cancelState; + int cancelType; + HANDLE cancelEvent; +#ifdef __CLEANUP_C + jmp_buf start_mark; +#endif /* __CLEANUP_C */ +#if HAVE_SIGSET_T + sigset_t sigmask; +#endif /* HAVE_SIGSET_T */ + int implicit:1; + void *keys; + void *nextAssoc; +}; + + +/* + * Special value to mark attribute objects as valid. + */ +#define PTW32_ATTR_VALID ((unsigned long) 0xC4C0FFEE) + +struct pthread_attr_t_ +{ + unsigned long valid; + void *stackaddr; + size_t stacksize; + int detachstate; + struct sched_param param; + int inheritsched; + int contentionscope; +#if HAVE_SIGSET_T + sigset_t sigmask; +#endif /* HAVE_SIGSET_T */ +}; + + +/* + * ==================== + * ==================== + * Semaphores, Mutexes and Condition Variables + * ==================== + * ==================== + */ + +struct sem_t_ +{ + int value; + pthread_mutex_t lock; + HANDLE sem; +#ifdef NEED_SEM + int leftToUnblock; +#endif +}; + +#define PTW32_OBJECT_AUTO_INIT ((void *) -1) +#define PTW32_OBJECT_INVALID NULL + +struct pthread_mutex_t_ +{ + LONG lock_idx; /* Provides exclusive access to mutex state + via the Interlocked* mechanism. + 0: unlocked/free. + 1: locked - no other waiters. + -1: locked - with possible other waiters. + */ + int recursive_count; /* Number of unlocks a thread needs to perform + before the lock is released (recursive + mutexes only). */ + int kind; /* Mutex type. */ + pthread_t ownerThread; + HANDLE event; /* Mutex release notification to waiting + threads. */ +}; + +struct pthread_mutexattr_t_ +{ + int pshared; + int kind; +}; + +/* + * Possible values, other than PTW32_OBJECT_INVALID, + * for the "interlock" element in a spinlock. + * + * In this implementation, when a spinlock is initialised, + * the number of cpus available to the process is checked. + * If there is only one cpu then "interlock" is set equal to + * PTW32_SPIN_USE_MUTEX and u.mutex is a initialised mutex. + * If the number of cpus is greater than 1 then "interlock" + * is set equal to PTW32_SPIN_UNLOCKED and the number is + * stored in u.cpus. This arrangement allows the spinlock + * routines to attempt an InterlockedCompareExchange on "interlock" + * immediately and, if that fails, to try the inferior mutex. + * + * "u.cpus" isn't used for anything yet, but could be used at + * some point to optimise spinlock behaviour. + */ +#define PTW32_SPIN_UNLOCKED (1) +#define PTW32_SPIN_LOCKED (2) +#define PTW32_SPIN_USE_MUTEX (3) + +struct pthread_spinlock_t_ +{ + long interlock; /* Locking element for multi-cpus. */ + union + { + int cpus; /* No. of cpus if multi cpus, or */ + pthread_mutex_t mutex; /* mutex if single cpu. */ + } u; +}; + +struct pthread_barrier_t_ +{ + unsigned int nCurrentBarrierHeight; + unsigned int nInitialBarrierHeight; + int iStep; + int pshared; + sem_t semBarrierBreeched[2]; +}; + +struct pthread_barrierattr_t_ +{ + int pshared; +}; + +struct pthread_key_t_ +{ + DWORD key; + void (*destructor) (void *); + pthread_mutex_t keyLock; + void *threads; +}; + + +typedef struct ThreadParms ThreadParms; +typedef struct ThreadKeyAssoc ThreadKeyAssoc; + +struct ThreadParms +{ + pthread_t tid; + void *(*start) (void *); + void *arg; +}; + + +struct pthread_cond_t_ +{ + long nWaitersBlocked; /* Number of threads blocked */ + long nWaitersGone; /* Number of threads timed out */ + long nWaitersToUnblock; /* Number of threads to unblock */ + sem_t semBlockQueue; /* Queue up threads waiting for the */ + /* condition to become signalled */ + sem_t semBlockLock; /* Semaphore that guards access to */ + /* | waiters blocked count/block queue */ + /* +-> Mandatory Sync.LEVEL-1 */ + pthread_mutex_t mtxUnblockLock; /* Mutex that guards access to */ + /* | waiters (to)unblock(ed) counts */ + /* +-> Optional* Sync.LEVEL-2 */ + pthread_cond_t next; /* Doubly linked list */ + pthread_cond_t prev; +}; + + +struct pthread_condattr_t_ +{ + int pshared; +}; + +#define PTW32_RWLOCK_MAGIC 0xfacade2 + +struct pthread_rwlock_t_ +{ + pthread_mutex_t mtxExclusiveAccess; + pthread_mutex_t mtxSharedAccessCompleted; + pthread_cond_t cndSharedAccessCompleted; + int nSharedAccessCount; + int nExclusiveAccessCount; + int nCompletedSharedAccessCount; + int nMagic; +}; + +struct pthread_rwlockattr_t_ +{ + int pshared; +}; + +/* + * MCS lock queue node - see ptw32_MCS_lock.c + */ +struct ptw32_mcs_node_t_ +{ + struct ptw32_mcs_node_t_ **lock; /* ptr to tail of queue */ + struct ptw32_mcs_node_t_ *next; /* ptr to successor in queue */ + LONG readyFlag; /* set after lock is released by + predecessor */ + LONG nextFlag; /* set after 'next' ptr is set by + successor */ +}; + +typedef struct ptw32_mcs_node_t_ ptw32_mcs_local_node_t; +typedef struct ptw32_mcs_node_t_ *ptw32_mcs_lock_t; + + +struct ThreadKeyAssoc +{ + /* + * Purpose: + * This structure creates an association between a thread and a key. + * It is used to implement the implicit invocation of a user defined + * destroy routine for thread specific data registered by a user upon + * exiting a thread. + * + * Graphically, the arrangement is as follows, where: + * + * K - Key with destructor + * (head of chain is key->threads) + * T - Thread that has called pthread_setspecific(Kn) + * (head of chain is thread->keys) + * A - Association. Each association is a node at the + * intersection of two doubly-linked lists. + * + * T1 T2 T3 + * | | | + * | | | + * K1 -----+-----A-----A-----> + * | | | + * | | | + * K2 -----A-----A-----+-----> + * | | | + * | | | + * K3 -----A-----+-----A-----> + * | | | + * | | | + * V V V + * + * Access to the association is guarded by two locks: the key's + * general lock (guarding the row) and the thread's general + * lock (guarding the column). This avoids the need for a + * dedicated lock for each association, which not only consumes + * more handles but requires that: before the lock handle can + * be released - both the key must be deleted and the thread + * must have called the destructor. The two-lock arrangement + * allows the resources to be freed as soon as either thread or + * key is concluded. + * + * To avoid deadlock: whenever both locks are required, the key + * and thread locks are always acquired in the order: key lock + * then thread lock. An exception to this exists when a thread + * calls the destructors, however this is done carefully to + * avoid deadlock. + * + * An association is created when a thread first calls + * pthread_setspecific() on a key that has a specified + * destructor. + * + * An association is destroyed either immediately after the + * thread calls the key destructor function on thread exit, or + * when the key is deleted. + * + * Attributes: + * thread + * reference to the thread that owns the + * association. This is actually the pointer to the + * thread struct itself. Since the association is + * destroyed before the thread exits, this can never + * point to a different logical thread to the one that + * created the assoc, i.e. after thread struct reuse. + * + * key + * reference to the key that owns the association. + * + * nextKey + * The pthread_t->keys attribute is the head of a + * chain of associations that runs through the nextKey + * link. This chain provides the 1 to many relationship + * between a pthread_t and all pthread_key_t on which + * it called pthread_setspecific. + * + * prevKey + * Similarly. + * + * nextThread + * The pthread_key_t->threads attribute is the head of + * a chain of assoctiations that runs through the + * nextThreads link. This chain provides the 1 to many + * relationship between a pthread_key_t and all the + * PThreads that have called pthread_setspecific for + * this pthread_key_t. + * + * prevThread + * Similarly. + * + * Notes: + * 1) As soon as either the key or the thread is no longer + * referencing the association, it can be destroyed. The + * association will be removed from both chains. + * + * 2) Under WIN32, an association is only created by + * pthread_setspecific if the user provided a + * destroyRoutine when they created the key. + * + * + */ + ptw32_thread_t * thread; + pthread_key_t key; + ThreadKeyAssoc *nextKey; + ThreadKeyAssoc *nextThread; + ThreadKeyAssoc *prevKey; + ThreadKeyAssoc *prevThread; +}; + + +#ifdef __CLEANUP_SEH +/* + * -------------------------------------------------------------- + * MAKE_SOFTWARE_EXCEPTION + * This macro constructs a software exception code following + * the same format as the standard Win32 error codes as defined + * in WINERROR.H + * Values are 32 bit values layed out as follows: + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---+-+-+-----------------------+-------------------------------+ + * |Sev|C|R| Facility | Code | + * +---+-+-+-----------------------+-------------------------------+ + * + * Severity Values: + */ +#define SE_SUCCESS 0x00 +#define SE_INFORMATION 0x01 +#define SE_WARNING 0x02 +#define SE_ERROR 0x03 + +#define MAKE_SOFTWARE_EXCEPTION( _severity, _facility, _exception ) \ +( (DWORD) ( ( (_severity) << 30 ) | /* Severity code */ \ + ( 1 << 29 ) | /* MS=0, User=1 */ \ + ( 0 << 28 ) | /* Reserved */ \ + ( (_facility) << 16 ) | /* Facility Code */ \ + ( (_exception) << 0 ) /* Exception Code */ \ + ) ) + +/* + * We choose one specific Facility/Error code combination to + * identify our software exceptions vs. WIN32 exceptions. + * We store our actual component and error code within + * the optional information array. + */ +#define EXCEPTION_PTW32_SERVICES \ + MAKE_SOFTWARE_EXCEPTION( SE_ERROR, \ + PTW32_SERVICES_FACILITY, \ + PTW32_SERVICES_ERROR ) + +#define PTW32_SERVICES_FACILITY 0xBAD +#define PTW32_SERVICES_ERROR 0xDEED + +#endif /* __CLEANUP_SEH */ + +/* + * Services available through EXCEPTION_PTW32_SERVICES + * and also used [as parameters to ptw32_throw()] as + * generic exception selectors. + */ + +#define PTW32_EPS_EXIT (1) +#define PTW32_EPS_CANCEL (2) + + +/* Useful macros */ +#define PTW32_MAX(a,b) ((a)<(b)?(b):(a)) +#define PTW32_MIN(a,b) ((a)>(b)?(b):(a)) + + +#if 0 +/* Declared in global.c */ +extern PTW32_INTERLOCKED_LONG (WINAPI * + ptw32_interlocked_compare_exchange) + (PTW32_INTERLOCKED_LPLONG, PTW32_INTERLOCKED_LONG, PTW32_INTERLOCKED_LONG); +#endif + +/* Declared in pthread_cancel.c */ +extern DWORD (*ptw32_register_cancelation) (PAPCFUNC, HANDLE, DWORD); + +/* Thread Reuse stack bottom marker. Must not be NULL or any valid pointer to memory. */ +#define PTW32_THREAD_REUSE_EMPTY ((ptw32_thread_t *) 1) + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +extern int ptw32_processInitialized; +extern ptw32_thread_t * ptw32_threadReuseTop; +extern ptw32_thread_t * ptw32_threadReuseBottom; +extern pthread_key_t ptw32_selfThreadKey; +extern pthread_key_t ptw32_cleanupKey; +extern pthread_cond_t ptw32_cond_list_head; +extern pthread_cond_t ptw32_cond_list_tail; + +extern int ptw32_mutex_default_kind; + +extern int ptw32_concurrency; + +extern int ptw32_features; + +extern BOOL ptw32_smp_system; /* True: SMP system, False: Uni-processor system */ + +extern CRITICAL_SECTION ptw32_thread_reuse_lock; +extern CRITICAL_SECTION ptw32_mutex_test_init_lock; +extern CRITICAL_SECTION ptw32_cond_list_lock; +extern CRITICAL_SECTION ptw32_cond_test_init_lock; +extern CRITICAL_SECTION ptw32_rwlock_test_init_lock; +extern CRITICAL_SECTION ptw32_spinlock_test_init_lock; + +#ifdef _UWIN +extern int pthread_count; +#endif + + +/* + * ===================== + * ===================== + * Forward Declarations + * ===================== + * ===================== + */ + + int ptw32_is_attr (const pthread_attr_t * attr); + + int ptw32_cond_check_need_init (pthread_cond_t * cond); + int ptw32_mutex_check_need_init (pthread_mutex_t * mutex); + int ptw32_rwlock_check_need_init (pthread_rwlock_t * rwlock); + + PTW32_INTERLOCKED_LONG WINAPI + ptw32_InterlockedCompareExchange (volatile PTW32_INTERLOCKED_LPLONG location, + PTW32_INTERLOCKED_LONG value, + PTW32_INTERLOCKED_LONG comparand); + + LONG WINAPI + ptw32_InterlockedExchange (volatile PTW32_INTERLOCKED_LPLONG location, + LONG value); + + DWORD + ptw32_RegisterCancelation (PAPCFUNC callback, + HANDLE threadH, DWORD callback_arg); + + int ptw32_processInitialize (void); + + void ptw32_processTerminate (void); + + void ptw32_threadDestroy (pthread_t tid); + + void ptw32_pop_cleanup_all (int execute); + + pthread_t ptw32_new (void); + + pthread_t ptw32_threadReusePop (void); + + void ptw32_threadReusePush (pthread_t thread); + + int ptw32_getprocessors (int *count); + + int ptw32_setthreadpriority (pthread_t thread, int policy, int priority); + + void ptw32_rwlock_cancelwrwait (void *arg); + +#if ! defined (__MINGW32__) || defined (__MSVCRT__) + unsigned __stdcall +#else + void +#endif + ptw32_threadStart (void *vthreadParms); + + void ptw32_callUserDestroyRoutines (pthread_t thread); + + int ptw32_tkAssocCreate (ptw32_thread_t * thread, pthread_key_t key); + + void ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc); + + int ptw32_semwait (sem_t * sem); + + DWORD ptw32_relmillisecs (const struct timespec * abstime); + + void ptw32_mcs_lock_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node); + + void ptw32_mcs_lock_release (ptw32_mcs_local_node_t * node); + + // added to remove some warnings (air) + int ptw32_spinlock_check_need_init (pthread_spinlock_t * lock); + +#ifdef NEED_FTIME + void ptw32_timespec_to_filetime (const struct timespec *ts, FILETIME * ft); + void ptw32_filetime_to_timespec (const FILETIME * ft, struct timespec *ts); +#endif + +/* Declared in misc.c */ +#ifdef NEED_CALLOC +#define calloc(n, s) ptw32_calloc(n, s) + void *ptw32_calloc (size_t n, size_t s); +#endif + +/* Declared in private.c */ + void ptw32_throw (DWORD exception); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#ifdef _UWIN_ +# ifdef _MT +# ifdef __cplusplus +extern "C" +{ +# endif + _CRTIMP unsigned long __cdecl _beginthread (void (__cdecl *) (void *), + unsigned, void *); + _CRTIMP void __cdecl _endthread (void); + _CRTIMP unsigned long __cdecl _beginthreadex (void *, unsigned, + unsigned (__stdcall *) (void *), + void *, unsigned, unsigned *); + _CRTIMP void __cdecl _endthreadex (unsigned); +# ifdef __cplusplus +} +# endif +# endif +#else +# include +#endif + + +/* + * Defaults. Could be overridden when building the inlined version of the dll. + * See ptw32_InterlockedCompareExchange.c + */ +// Default to inlining the pthreads versions... (air) +#ifndef PTW32_INTERLOCKED_COMPARE_EXCHANGE +#define PTW32_INTERLOCKED_COMPARE_EXCHANGE ptw32_InterlockedCompareExchange +#endif + +#ifndef PTW32_INTERLOCKED_EXCHANGE +#define PTW32_INTERLOCKED_EXCHANGE ptw32_InterlockedExchange +#endif + + +/* + * Check for old and new versions of cygwin. See the FAQ file: + * + * Question 1 - How do I get pthreads-win32 to link under Cygwin or Mingw32? + * + * Patch by Anders Norlander + */ +#if defined(__CYGWIN32__) || defined(__CYGWIN__) || defined(NEED_CREATETHREAD) + +/* + * Macro uses args so we can cast start_proc to LPTHREAD_START_ROUTINE + * in order to avoid warnings because of return type + */ + +#define _beginthreadex(security, \ + stack_size, \ + start_proc, \ + arg, \ + flags, \ + pid) \ + CreateThread(security, \ + stack_size, \ + (LPTHREAD_START_ROUTINE) start_proc, \ + arg, \ + flags, \ + pid) + +#define _endthreadex ExitThread + +#endif /* __CYGWIN32__ || __CYGWIN__ || NEED_CREATETHREAD */ + + +#endif /* _IMPLEMENT_H */ diff --git a/pcsx2/windows/pthreads/misc.c b/pcsx2/windows/pthreads/misc.c new file mode 100644 index 0000000000..5853569a70 --- /dev/null +++ b/pcsx2/windows/pthreads/misc.c @@ -0,0 +1,50 @@ +/* + * misc.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +#include "pthread_kill.c" +#include "pthread_once.c" +#include "pthread_self.c" +#include "pthread_equal.c" +#include "pthread_setconcurrency.c" +#include "pthread_getconcurrency.c" +#include "ptw32_new.c" +#include "ptw32_calloc.c" +#include "ptw32_reuse.c" +#include "w32_CancelableWait.c" diff --git a/pcsx2/windows/pthreads/mutex.c b/pcsx2/windows/pthreads/mutex.c new file mode 100644 index 0000000000..c199901db5 --- /dev/null +++ b/pcsx2/windows/pthreads/mutex.c @@ -0,0 +1,59 @@ +/* + * mutex.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef _UWIN +# include +#endif +#ifndef NEED_FTIME +#include +#endif +#include "pthread.h" +#include "implement.h" + + +#include "ptw32_mutex_check_need_init.c" +#include "pthread_mutex_init.c" +#include "pthread_mutex_destroy.c" +#include "pthread_mutexattr_init.c" +#include "pthread_mutexattr_destroy.c" +#include "pthread_mutexattr_getpshared.c" +#include "pthread_mutexattr_setpshared.c" +#include "pthread_mutexattr_settype.c" +#include "pthread_mutexattr_gettype.c" +#include "pthread_mutex_lock.c" +#include "pthread_mutex_timedlock.c" +#include "pthread_mutex_unlock.c" +#include "pthread_mutex_trylock.c" diff --git a/pcsx2/windows/pthreads/need_errno.h b/pcsx2/windows/pthreads/need_errno.h new file mode 100644 index 0000000000..d523135756 --- /dev/null +++ b/pcsx2/windows/pthreads/need_errno.h @@ -0,0 +1,132 @@ +/*** +* errno.h - system wide error numbers (set by system calls) +* +* Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved. +* +* Purpose: +* This file defines the system-wide error numbers (set by +* system calls). Conforms to the XENIX standard. Extended +* for compatibility with Uniforum standard. +* [System V] +* +* [Public] +* +****/ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#ifndef _INC_ERRNO +#define _INC_ERRNO + +#if !defined(_WIN32) && !defined(_MAC) +#error ERROR: Only Mac or Win32 targets supported! +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* Define _CRTIMP */ + +#ifndef _CRTIMP +#ifdef _DLL +#define _CRTIMP __declspec(dllimport) +#else /* ndef _DLL */ +#define _CRTIMP +#endif /* _DLL */ +#endif /* _CRTIMP */ + + +/* Define __cdecl for non-Microsoft compilers */ + +#if ( !defined(_MSC_VER) && !defined(__cdecl) ) +#define __cdecl +#endif + +/* Define _CRTAPI1 (for compatibility with the NT SDK) */ + +#ifndef _CRTAPI1 +#if _MSC_VER >= 800 && _M_IX86 >= 300 +#define _CRTAPI1 __cdecl +#else +#define _CRTAPI1 +#endif +#endif + + +/* declare reference to errno */ + +#if (defined(_MT) || defined(_MD) || defined(_DLL)) && !defined(_MAC) +_CRTIMP extern int * __cdecl _errno(void); +#define errno (*_errno()) +#else /* ndef _MT && ndef _MD && ndef _DLL */ +_CRTIMP extern int errno; +#endif /* _MT || _MD || _DLL */ + +/* Error Codes */ + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define EDEADLK 36 + +/* defined differently in winsock.h on WinCE */ +#ifndef ENAMETOOLONG +#define ENAMETOOLONG 38 +#endif + +#define ENOLCK 39 +#define ENOSYS 40 + +/* defined differently in winsock.h on WinCE */ +#ifndef ENOTEMPTY +#define ENOTEMPTY 41 +#endif + +#define EILSEQ 42 + +/* + * Support EDEADLOCK for compatibiity with older MS-C versions. + */ +#define EDEADLOCK EDEADLK + +#ifdef __cplusplus +} +#endif + +#endif /* _INC_ERRNO */ diff --git a/pcsx2/windows/pthreads/nonportable.c b/pcsx2/windows/pthreads/nonportable.c new file mode 100644 index 0000000000..d1b0976507 --- /dev/null +++ b/pcsx2/windows/pthreads/nonportable.c @@ -0,0 +1,46 @@ +/* + * nonportable.c + * + * Description: + * This translation unit implements non-portable thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +#include "pthread_mutexattr_setkind_np.c" +#include "pthread_mutexattr_getkind_np.c" +#include "pthread_getw32threadhandle_np.c" +#include "pthread_delay_np.c" +#include "pthread_num_processors_np.c" +#include "pthread_win32_attach_detach_np.c" +#include "pthread_timechange_handler_np.c" diff --git a/pcsx2/windows/pthreads/private.c b/pcsx2/windows/pthreads/private.c new file mode 100644 index 0000000000..3755ef4d56 --- /dev/null +++ b/pcsx2/windows/pthreads/private.c @@ -0,0 +1,57 @@ +/* + * private.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* Must be first to define HAVE_INLINABLE_INTERLOCKED_CMPXCHG */ +#include "ptw32_InterlockedCompareExchange.c" + +#include "ptw32_MCS_lock.c" +#include "ptw32_is_attr.c" +#include "ptw32_processInitialize.c" +#include "ptw32_processTerminate.c" +#include "ptw32_threadStart.c" +#include "ptw32_threadDestroy.c" +#include "ptw32_tkAssocCreate.c" +#include "ptw32_tkAssocDestroy.c" +#include "ptw32_callUserDestroyRoutines.c" +#include "ptw32_semwait.c" +#include "ptw32_timespec.c" +#include "ptw32_relmillisecs.c" +#include "ptw32_throw.c" +#include "ptw32_getprocessors.c" diff --git a/pcsx2/windows/pthreads/pthread.h b/pcsx2/windows/pthreads/pthread.h new file mode 100644 index 0000000000..7cca67585a --- /dev/null +++ b/pcsx2/windows/pthreads/pthread.h @@ -0,0 +1,1388 @@ +/* This is an implementation of the threads API of POSIX 1003.1-2001. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#if !defined( PTHREAD_H ) +#define PTHREAD_H + +/* + * See the README file for an explanation of the pthreads-win32 version + * numbering scheme and how the DLL is named etc. + */ +#define PTW32_VERSION 2,8,0,0 +#define PTW32_VERSION_STRING "2, 8, 0, 0\0" + +/* There are three implementations of cancel cleanup. + * Note that pthread.h is included in both application + * compilation units and also internally for the library. + * The code here and within the library aims to work + * for all reasonable combinations of environments. + * + * The three implementations are: + * + * WIN32 SEH + * C + * C++ + * + * Please note that exiting a push/pop block via + * "return", "exit", "break", or "continue" will + * lead to different behaviour amongst applications + * depending upon whether the library was built + * using SEH, C++, or C. For example, a library built + * with SEH will call the cleanup routine, while both + * C++ and C built versions will not. + */ + +/* + * Define defaults for cleanup code. + * Note: Unless the build explicitly defines one of the following, then + * we default to standard C style cleanup. This style uses setjmp/longjmp + * in the cancelation and thread exit implementations and therefore won't + * do stack unwinding if linked to applications that have it (e.g. + * C++ apps). This is currently consistent with most/all commercial Unix + * POSIX threads implementations. + */ +#if !defined( __CLEANUP_SEH ) && !defined( __CLEANUP_CXX ) && !defined( __CLEANUP_C ) +# define __CLEANUP_C +#endif + +#if defined( __CLEANUP_SEH ) && ( !defined( _MSC_VER ) && !defined(PTW32_RC_MSC)) +#error ERROR [__FILE__, line __LINE__]: SEH is not supported for this compiler. +#endif + +/* + * Stop here if we are being included by the resource compiler. + */ +#ifndef RC_INVOKED + +#undef PTW32_LEVEL + +#if defined(_POSIX_SOURCE) +#define PTW32_LEVEL 0 +/* Early POSIX */ +#endif + +#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309 +#undef PTW32_LEVEL +#define PTW32_LEVEL 1 +/* Include 1b, 1c and 1d */ +#endif + +#if defined(INCLUDE_NP) +#undef PTW32_LEVEL +#define PTW32_LEVEL 2 +/* Include Non-Portable extensions */ +#endif + +#define PTW32_LEVEL_MAX 3 + +#if !defined(PTW32_LEVEL) +#define PTW32_LEVEL PTW32_LEVEL_MAX +/* Include everything */ +#endif + +#ifdef _UWIN +# define HAVE_STRUCT_TIMESPEC 1 +# define HAVE_SIGNAL_H 1 +# undef HAVE_CONFIG_H +# pragma comment(lib, "pthread") +#endif + +/* + * ------------------------------------------------------------- + * + * + * Module: pthread.h + * + * Purpose: + * Provides an implementation of PThreads based upon the + * standard: + * + * POSIX 1003.1-2001 + * and + * The Single Unix Specification version 3 + * + * (these two are equivalent) + * + * in order to enhance code portability between Windows, + * various commercial Unix implementations, and Linux. + * + * See the ANNOUNCE file for a full list of conforming + * routines and defined constants, and a list of missing + * routines and constants not defined in this implementation. + * + * Authors: + * There have been many contributors to this library. + * The initial implementation was contributed by + * John Bossom, and several others have provided major + * sections or revisions of parts of the implementation. + * Often significant effort has been contributed to + * find and fix important bugs and other problems to + * improve the reliability of the library, which sometimes + * is not reflected in the amount of code which changed as + * result. + * As much as possible, the contributors are acknowledged + * in the ChangeLog file in the source code distribution + * where their changes are noted in detail. + * + * Contributors are listed in the CONTRIBUTORS file. + * + * As usual, all bouquets go to the contributors, and all + * brickbats go to the project maintainer. + * + * Maintainer: + * The code base for this project is coordinated and + * eventually pre-tested, packaged, and made available by + * + * Ross Johnson + * + * QA Testers: + * Ultimately, the library is tested in the real world by + * a host of competent and demanding scientists and + * engineers who report bugs and/or provide solutions + * which are then fixed or incorporated into subsequent + * versions of the library. Each time a bug is fixed, a + * test case is written to prove the fix and ensure + * that later changes to the code don't reintroduce the + * same error. The number of test cases is slowly growing + * and therefore so is the code reliability. + * + * Compliance: + * See the file ANNOUNCE for the list of implemented + * and not-implemented routines and defined options. + * Of course, these are all defined is this file as well. + * + * Web site: + * The source code and other information about this library + * are available from + * + * http://sources.redhat.com/pthreads-win32/ + * + * ------------------------------------------------------------- + */ + +/* Try to avoid including windows.h */ +#if defined(__MINGW32__) && defined(__cplusplus) +#define PTW32_INCLUDE_WINDOWS_H +#endif + +#ifdef PTW32_INCLUDE_WINDOWS_H +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1300 || defined(__DMC__) +/* + * VC++6.0 or early compiler's header has no DWORD_PTR type. + */ +typedef unsigned long DWORD_PTR; +#endif +/* + * ----------------- + * autoconf switches + * ----------------- + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifndef NEED_FTIME +#include +#else /* NEED_FTIME */ +/* use native WIN32 time API */ +#endif /* NEED_FTIME */ + +#if HAVE_SIGNAL_H +#include +#endif /* HAVE_SIGNAL_H */ + +#include +#include + +/* + * Boolean values to make us independent of system includes. + */ +enum { + PTW32_FALSE = 0, + PTW32_TRUE = (! PTW32_FALSE) +}; + +/* + * This is a duplicate of what is in the autoconf config.h, + * which is only used when building the pthread-win32 libraries. + */ + +#ifndef PTW32_CONFIG_H +# if defined(WINCE) +# define NEED_ERRNO +# define NEED_SEM +# endif +# if defined(_UWIN) || defined(__MINGW32__) +# define HAVE_MODE_T +# endif +#endif + +/* + * + */ + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX +#ifdef NEED_ERRNO +#include "need_errno.h" +#else +#include +#endif +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Several systems don't define some error numbers. + */ +#ifndef ENOTSUP +# define ENOTSUP 48 /* This is the value in Solaris. */ +#endif + +#ifndef ETIMEDOUT +# define ETIMEDOUT 10060 /* This is the value in winsock.h. */ +#endif + +#ifndef ENOSYS +# define ENOSYS 140 /* Semi-arbitrary value */ +#endif + +#ifndef EDEADLK +# ifdef EDEADLOCK +# define EDEADLK EDEADLOCK +# else +# define EDEADLK 36 /* This is the value in MSVC. */ +# endif +#endif + +#include + +/* + * To avoid including windows.h we define only those things that we + * actually need from it. + */ +#ifndef PTW32_INCLUDE_WINDOWS_H +#ifndef HANDLE +# define PTW32__HANDLE_DEF +# define HANDLE void * +#endif +#ifndef DWORD +# define PTW32__DWORD_DEF +# define DWORD unsigned long +#endif +#endif + +#ifndef HAVE_STRUCT_TIMESPEC +#define HAVE_STRUCT_TIMESPEC 1 +struct timespec { + long tv_sec; + long tv_nsec; +}; +#endif /* HAVE_STRUCT_TIMESPEC */ + +#ifndef SIG_BLOCK +#define SIG_BLOCK 0 +#endif /* SIG_BLOCK */ + +#ifndef SIG_UNBLOCK +#define SIG_UNBLOCK 1 +#endif /* SIG_UNBLOCK */ + +#ifndef SIG_SETMASK +#define SIG_SETMASK 2 +#endif /* SIG_SETMASK */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * ------------------------------------------------------------- + * + * POSIX 1003.1-2001 Options + * ========================= + * + * Options are normally set in , which is not provided + * with pthreads-win32. + * + * For conformance with the Single Unix Specification (version 3), all of the + * options below are defined, and have a value of either -1 (not supported) + * or 200112L (supported). + * + * These options can neither be left undefined nor have a value of 0, because + * either indicates that sysconf(), which is not implemented, may be used at + * runtime to check the status of the option. + * + * _POSIX_THREADS (== 200112L) + * If == 200112L, you can use threads + * + * _POSIX_THREAD_ATTR_STACKSIZE (== 200112L) + * If == 200112L, you can control the size of a thread's + * stack + * pthread_attr_getstacksize + * pthread_attr_setstacksize + * + * _POSIX_THREAD_ATTR_STACKADDR (== -1) + * If == 200112L, you can allocate and control a thread's + * stack. If not supported, the following functions + * will return ENOSYS, indicating they are not + * supported: + * pthread_attr_getstackaddr + * pthread_attr_setstackaddr + * + * _POSIX_THREAD_PRIORITY_SCHEDULING (== -1) + * If == 200112L, you can use realtime scheduling. + * This option indicates that the behaviour of some + * implemented functions conforms to the additional TPS + * requirements in the standard. E.g. rwlocks favour + * writers over readers when threads have equal priority. + * + * _POSIX_THREAD_PRIO_INHERIT (== -1) + * If == 200112L, you can create priority inheritance + * mutexes. + * pthread_mutexattr_getprotocol + + * pthread_mutexattr_setprotocol + + * + * _POSIX_THREAD_PRIO_PROTECT (== -1) + * If == 200112L, you can create priority ceiling mutexes + * Indicates the availability of: + * pthread_mutex_getprioceiling + * pthread_mutex_setprioceiling + * pthread_mutexattr_getprioceiling + * pthread_mutexattr_getprotocol + + * pthread_mutexattr_setprioceiling + * pthread_mutexattr_setprotocol + + * + * _POSIX_THREAD_PROCESS_SHARED (== -1) + * If set, you can create mutexes and condition + * variables that can be shared with another + * process.If set, indicates the availability + * of: + * pthread_mutexattr_getpshared + * pthread_mutexattr_setpshared + * pthread_condattr_getpshared + * pthread_condattr_setpshared + * + * _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L) + * If == 200112L you can use the special *_r library + * functions that provide thread-safe behaviour + * + * _POSIX_READER_WRITER_LOCKS (== 200112L) + * If == 200112L, you can use read/write locks + * + * _POSIX_SPIN_LOCKS (== 200112L) + * If == 200112L, you can use spin locks + * + * _POSIX_BARRIERS (== 200112L) + * If == 200112L, you can use barriers + * + * + These functions provide both 'inherit' and/or + * 'protect' protocol, based upon these macro + * settings. + * + * ------------------------------------------------------------- + */ + +/* + * POSIX Options + */ +#undef _POSIX_THREADS +#define _POSIX_THREADS 200112L + +#undef _POSIX_READER_WRITER_LOCKS +#define _POSIX_READER_WRITER_LOCKS 200112L + +#undef _POSIX_SPIN_LOCKS +#define _POSIX_SPIN_LOCKS 200112L + +#undef _POSIX_BARRIERS +#define _POSIX_BARRIERS 200112L + +#undef _POSIX_THREAD_SAFE_FUNCTIONS +#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L + +#undef _POSIX_THREAD_ATTR_STACKSIZE +#define _POSIX_THREAD_ATTR_STACKSIZE 200112L + +/* + * The following options are not supported + */ +#undef _POSIX_THREAD_ATTR_STACKADDR +#define _POSIX_THREAD_ATTR_STACKADDR -1 + +#undef _POSIX_THREAD_PRIO_INHERIT +#define _POSIX_THREAD_PRIO_INHERIT -1 + +#undef _POSIX_THREAD_PRIO_PROTECT +#define _POSIX_THREAD_PRIO_PROTECT -1 + +/* TPS is not fully supported. */ +#undef _POSIX_THREAD_PRIORITY_SCHEDULING +#define _POSIX_THREAD_PRIORITY_SCHEDULING -1 + +#undef _POSIX_THREAD_PROCESS_SHARED +#define _POSIX_THREAD_PROCESS_SHARED -1 + + +/* + * POSIX 1003.1-2001 Limits + * =========================== + * + * These limits are normally set in , which is not provided with + * pthreads-win32. + * + * PTHREAD_DESTRUCTOR_ITERATIONS + * Maximum number of attempts to destroy + * a thread's thread-specific data on + * termination (must be at least 4) + * + * PTHREAD_KEYS_MAX + * Maximum number of thread-specific data keys + * available per process (must be at least 128) + * + * PTHREAD_STACK_MIN + * Minimum supported stack size for a thread + * + * PTHREAD_THREADS_MAX + * Maximum number of threads supported per + * process (must be at least 64). + * + * SEM_NSEMS_MAX + * The maximum number of semaphores a process can have. + * (must be at least 256) + * + * SEM_VALUE_MAX + * The maximum value a semaphore can have. + * (must be at least 32767) + * + */ +#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 + +#undef PTHREAD_DESTRUCTOR_ITERATIONS +#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS + +#undef _POSIX_THREAD_KEYS_MAX +#define _POSIX_THREAD_KEYS_MAX 128 + +#undef PTHREAD_KEYS_MAX +#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX + +#undef PTHREAD_STACK_MIN +#define PTHREAD_STACK_MIN 0 + +#undef _POSIX_THREAD_THREADS_MAX +#define _POSIX_THREAD_THREADS_MAX 64 + + /* Arbitrary value */ +#undef PTHREAD_THREADS_MAX +#define PTHREAD_THREADS_MAX 2019 + +#undef _POSIX_SEM_NSEMS_MAX +#define _POSIX_SEM_NSEMS_MAX 256 + + /* Arbitrary value */ +#undef SEM_NSEMS_MAX +#define SEM_NSEMS_MAX 1024 + +#undef _POSIX_SEM_VALUE_MAX +#define _POSIX_SEM_VALUE_MAX 32767 + +#undef SEM_VALUE_MAX +#define SEM_VALUE_MAX INT_MAX + + +#if __GNUC__ && ! defined (__declspec) +# error Please upgrade your GNU compiler to one that supports __declspec. +#endif + +/* + * When building the DLL code, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the DLL, + * do NOT define PTW32_BUILD, and then the variables/functions will + * be imported correctly. + */ +#ifndef PTW32_STATIC_LIB +# ifdef PTW32_BUILD +# define PTW32_DLLPORT __declspec (dllexport) +# else +# define PTW32_DLLPORT __declspec (dllimport) +# endif +#else +# define PTW32_DLLPORT +#endif + +/* + * The Open Watcom C/C++ compiler uses a non-standard calling convention + * that passes function args in registers unless __cdecl is explicitly specified + * in exposed function prototypes. + * + * We force all calls to cdecl even though this could slow Watcom code down + * slightly. If you know that the Watcom compiler will be used to build both + * the DLL and application, then you can probably define this as a null string. + * Remember that pthread.h (this file) is used for both the DLL and application builds. + */ +#define PTW32_CDECL __cdecl + +#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX +# include +#else +/* + * Generic handle type - intended to extend uniqueness beyond + * that available with a simple pointer. It should scale for either + * IA-32 or IA-64. + */ +typedef struct _ptw32_handle_t { + void * p; /* Pointer to actual object */ + unsigned int x; /* Extra information - reuse count etc */ + +#ifdef __cplusplus + // Added support for various operators so that the struct is + // more pthreads-compliant in behavior. (air) + const bool operator ==( const void* rightside ) + { + return p == rightside; + } + + const bool operator !=( const void* rightside ) + { + return p != rightside; + } + + bool operator =( void* rightside ) + { + p = rightside; + } +#endif + +} ptw32_handle_t; + +typedef ptw32_handle_t pthread_t; +typedef struct pthread_attr_t_ * pthread_attr_t; +typedef struct pthread_once_t_ pthread_once_t; +typedef struct pthread_key_t_ * pthread_key_t; +typedef struct pthread_mutex_t_ * pthread_mutex_t; +typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t; +typedef struct pthread_cond_t_ * pthread_cond_t; +typedef struct pthread_condattr_t_ * pthread_condattr_t; +#endif +typedef struct pthread_rwlock_t_ * pthread_rwlock_t; +typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t; +typedef struct pthread_spinlock_t_ * pthread_spinlock_t; +typedef struct pthread_barrier_t_ * pthread_barrier_t; +typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t; + +/* + * ==================== + * ==================== + * POSIX Threads + * ==================== + * ==================== + */ + +enum { +/* + * pthread_attr_{get,set}detachstate + */ + PTHREAD_CREATE_JOINABLE = 0, /* Default */ + PTHREAD_CREATE_DETACHED = 1, + +/* + * pthread_attr_{get,set}inheritsched + */ + PTHREAD_INHERIT_SCHED = 0, + PTHREAD_EXPLICIT_SCHED = 1, /* Default */ + +/* + * pthread_{get,set}scope + */ + PTHREAD_SCOPE_PROCESS = 0, + PTHREAD_SCOPE_SYSTEM = 1, /* Default */ + +/* + * pthread_setcancelstate paramters + */ + PTHREAD_CANCEL_ENABLE = 0, /* Default */ + PTHREAD_CANCEL_DISABLE = 1, + +/* + * pthread_setcanceltype parameters + */ + PTHREAD_CANCEL_ASYNCHRONOUS = 0, + PTHREAD_CANCEL_DEFERRED = 1, /* Default */ + +/* + * pthread_mutexattr_{get,set}pshared + * pthread_condattr_{get,set}pshared + */ + PTHREAD_PROCESS_PRIVATE = 0, + PTHREAD_PROCESS_SHARED = 1, + +/* + * pthread_barrier_wait + */ + PTHREAD_BARRIER_SERIAL_THREAD = -1 +}; + +/* + * ==================== + * ==================== + * Cancelation + * ==================== + * ==================== + */ +#define PTHREAD_CANCELED ((void *) -1) + + +/* + * ==================== + * ==================== + * Once Key + * ==================== + * ==================== + */ +#define PTHREAD_ONCE_INIT { PTW32_FALSE, 0, 0, 0} + +struct pthread_once_t_ +{ + int done; /* indicates if user function has been executed */ + void * lock; + int reserved1; + int reserved2; +}; + + +/* + * ==================== + * ==================== + * Object initialisers + * ==================== + * ==================== + */ +#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t) -1) +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t) -2) +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t) -3) + +/* + * Compatibility with LinuxThreads + */ +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER + +#define PTHREAD_COND_INITIALIZER ((pthread_cond_t) -1) + +#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t) -1) + +#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t) -1) + + +/* + * Mutex types. + */ +enum +{ + /* Compatibility with LinuxThreads */ + PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP, + /* For compatibility with POSIX */ + PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL +}; + + +typedef struct ptw32_cleanup_t ptw32_cleanup_t; + +#if defined(_MSC_VER) +/* Disable MSVC 'anachronism used' warning */ +#pragma warning( disable : 4229 ) +#endif + +typedef void (* PTW32_CDECL ptw32_cleanup_callback_t)(void *); + +#if defined(_MSC_VER) +#pragma warning( default : 4229 ) +#endif + +struct ptw32_cleanup_t +{ + ptw32_cleanup_callback_t routine; + void *arg; + struct ptw32_cleanup_t *prev; +}; + +#ifdef __CLEANUP_SEH + /* + * WIN32 SEH version of cancel cleanup. + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + ptw32_cleanup_t _cleanup; \ + \ + _cleanup.routine = (ptw32_cleanup_callback_t)(_rout); \ + _cleanup.arg = (_arg); \ + __try \ + { \ + +#define pthread_cleanup_pop( _execute ) \ + } \ + __finally \ + { \ + if( _execute || AbnormalTermination()) \ + { \ + (*(_cleanup.routine))( _cleanup.arg ); \ + } \ + } \ + } + +#else /* __CLEANUP_SEH */ + +#ifdef __CLEANUP_C + + /* + * C implementation of PThreads cancel cleanup + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + ptw32_cleanup_t _cleanup; \ + \ + ptw32_push_cleanup( &_cleanup, (ptw32_cleanup_callback_t) (_rout), (_arg) ); \ + +#define pthread_cleanup_pop( _execute ) \ + (void) ptw32_pop_cleanup( _execute ); \ + } + +#else /* __CLEANUP_C */ + +#ifdef __CLEANUP_CXX + + /* + * C++ version of cancel cleanup. + * - John E. Bossom. + */ + + class PThreadCleanup { + /* + * PThreadCleanup + * + * Purpose + * This class is a C++ helper class that is + * used to implement pthread_cleanup_push/ + * pthread_cleanup_pop. + * The destructor of this class automatically + * pops the pushed cleanup routine regardless + * of how the code exits the scope + * (i.e. such as by an exception) + */ + ptw32_cleanup_callback_t cleanUpRout; + void * obj; + int executeIt; + + public: + PThreadCleanup() : + cleanUpRout( 0 ), + obj( 0 ), + executeIt( 0 ) + /* + * No cleanup performed + */ + { + } + + PThreadCleanup( + ptw32_cleanup_callback_t routine, + void * arg ) : + cleanUpRout( routine ), + obj( arg ), + executeIt( 1 ) + /* + * Registers a cleanup routine for 'arg' + */ + { + } + + ~PThreadCleanup() + { + if ( executeIt && ((void *) cleanUpRout != (void *) 0) ) + { + (void) (*cleanUpRout)( obj ); + } + } + + void execute( int exec ) + { + executeIt = exec; + } + }; + + /* + * C++ implementation of PThreads cancel cleanup; + * This implementation takes advantage of a helper + * class who's destructor automatically calls the + * cleanup routine if we exit our scope weirdly + */ +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + PThreadCleanup cleanup((ptw32_cleanup_callback_t)(_rout), \ + (void *) (_arg) ); + +#define pthread_cleanup_pop( _execute ) \ + cleanup.execute( _execute ); \ + } + +#else + +#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined. + +#endif /* __CLEANUP_CXX */ + +#endif /* __CLEANUP_C */ + +#endif /* __CLEANUP_SEH */ + +/* + * =============== + * =============== + * Methods + * =============== + * =============== + */ + +/* + * PThread Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_attr_init (pthread_attr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_destroy (pthread_attr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getdetachstate (const pthread_attr_t * attr, + int *detachstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstackaddr (const pthread_attr_t * attr, + void **stackaddr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstacksize (const pthread_attr_t * attr, + size_t * stacksize); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setdetachstate (pthread_attr_t * attr, + int detachstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstackaddr (pthread_attr_t * attr, + void *stackaddr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstacksize (pthread_attr_t * attr, + size_t stacksize); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedparam (const pthread_attr_t *attr, + struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedparam (pthread_attr_t *attr, + const struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedpolicy (pthread_attr_t *, + int); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedpolicy (pthread_attr_t *, + int *); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setinheritsched(pthread_attr_t * attr, + int inheritsched); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getinheritsched(pthread_attr_t * attr, + int * inheritsched); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setscope (pthread_attr_t *, + int); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getscope (const pthread_attr_t *, + int *); + +/* + * PThread Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid, + const pthread_attr_t * attr, + void *(*start) (void *), + void *arg); + +PTW32_DLLPORT int PTW32_CDECL pthread_detach (pthread_t tid); + +PTW32_DLLPORT int PTW32_CDECL pthread_equal (pthread_t t1, + pthread_t t2); + +PTW32_DLLPORT void PTW32_CDECL pthread_exit (void *value_ptr); + +PTW32_DLLPORT int PTW32_CDECL pthread_join (pthread_t thread, + void **value_ptr); + +PTW32_DLLPORT pthread_t PTW32_CDECL pthread_self (void); + +PTW32_DLLPORT int PTW32_CDECL pthread_cancel (pthread_t thread); + +PTW32_DLLPORT int PTW32_CDECL pthread_setcancelstate (int state, + int *oldstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_setcanceltype (int type, + int *oldtype); + +PTW32_DLLPORT void PTW32_CDECL pthread_testcancel (void); + +PTW32_DLLPORT int PTW32_CDECL pthread_once (pthread_once_t * once_control, + void (*init_routine) (void)); + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX +PTW32_DLLPORT ptw32_cleanup_t * PTW32_CDECL ptw32_pop_cleanup (int execute); + +PTW32_DLLPORT void PTW32_CDECL ptw32_push_cleanup (ptw32_cleanup_t * cleanup, + void (*routine) (void *), + void *arg); +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Thread Specific Data Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_key_create (pthread_key_t * key, + void (*destructor) (void *)); + +PTW32_DLLPORT int PTW32_CDECL pthread_key_delete (pthread_key_t key); + +PTW32_DLLPORT int PTW32_CDECL pthread_setspecific (pthread_key_t key, + const void *value); + +PTW32_DLLPORT void * PTW32_CDECL pthread_getspecific (pthread_key_t key); + + +/* + * Mutex Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_init (pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_destroy (pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getpshared (const pthread_mutexattr_t + * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, + int pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_gettype (pthread_mutexattr_t * attr, int *kind); + +/* + * Barrier Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_init (pthread_barrierattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_destroy (pthread_barrierattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_getpshared (const pthread_barrierattr_t + * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_setpshared (pthread_barrierattr_t * attr, + int pshared); + +/* + * Mutex Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_init (pthread_mutex_t * mutex, + const pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_destroy (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_lock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_timedlock(pthread_mutex_t *mutex, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_trylock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_unlock (pthread_mutex_t * mutex); + +/* + * Spinlock Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_spin_init (pthread_spinlock_t * lock, int pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_destroy (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_lock (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_trylock (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_unlock (pthread_spinlock_t * lock); + +/* + * Barrier Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_init (pthread_barrier_t * barrier, + const pthread_barrierattr_t * attr, + unsigned int count); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_destroy (pthread_barrier_t * barrier); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_wait (pthread_barrier_t * barrier); + +/* + * Condition Variable Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_init (pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_destroy (pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_getpshared (const pthread_condattr_t * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_setpshared (pthread_condattr_t * attr, + int pshared); + +/* + * Condition Variable Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_cond_init (pthread_cond_t * cond, + const pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_destroy (pthread_cond_t * cond); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_wait (pthread_cond_t * cond, + pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_timedwait (pthread_cond_t * cond, + pthread_mutex_t * mutex, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_signal (pthread_cond_t * cond); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_broadcast (pthread_cond_t * cond); + +/* + * Scheduling + */ +PTW32_DLLPORT int PTW32_CDECL pthread_setschedparam (pthread_t thread, + int policy, + const struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_getschedparam (pthread_t thread, + int *policy, + struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_setconcurrency (int); + +PTW32_DLLPORT int PTW32_CDECL pthread_getconcurrency (void); + +/* + * Read-Write Lock Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_init(pthread_rwlock_t *lock, + const pthread_rwlockattr_t *attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_destroy(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_tryrdlock(pthread_rwlock_t *); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_trywrlock(pthread_rwlock_t *); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_rdlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedrdlock(pthread_rwlock_t *lock, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_wrlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedwrlock(pthread_rwlock_t *lock, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_unlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_init (pthread_rwlockattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr, + int pshared); + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 + +/* + * Signal Functions. Should be defined in but MSVC and MinGW32 + * already have signal.h that don't define these. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_kill(pthread_t thread, int sig); + +/* + * Non-portable functions + */ + +/* + * Compatibility with Linux. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr, + int kind); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr, + int *kind); + +/* + * Possibly supported by other POSIX threads implementations + */ +PTW32_DLLPORT int PTW32_CDECL pthread_delay_np (struct timespec * interval); +PTW32_DLLPORT int PTW32_CDECL pthread_num_processors_np(void); + +/* + * Useful if an application wants to statically link + * the lib rather than load the DLL at run-time. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_attach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_detach_np(void); + +/* + * Features that are auto-detected at load/run time. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_win32_test_features_np(int); +enum ptw32_features { + PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE = 0x0001, /* System provides it. */ + PTW32_ALERTABLE_ASYNC_CANCEL = 0x0002 /* Can cancel blocked threads. */ +}; + +/* + * Register a system time change with the library. + * Causes the library to perform various functions + * in response to the change. Should be called whenever + * the application's top level window receives a + * WM_TIMECHANGE message. It can be passed directly to + * pthread_create() as a new thread if desired. + */ +PTW32_DLLPORT void * PTW32_CDECL pthread_timechange_handler_np(void *); + +#endif /*PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 */ + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX + +/* + * Returns the Win32 HANDLE for the POSIX thread. + */ +PTW32_DLLPORT HANDLE PTW32_CDECL pthread_getw32threadhandle_np(pthread_t thread); + + +/* + * Protected Methods + * + * This function blocks until the given WIN32 handle + * is signaled or pthread_cancel had been called. + * This function allows the caller to hook into the + * PThreads cancel mechanism. It is implemented using + * + * WaitForMultipleObjects + * + * on 'waitHandle' and a manually reset WIN32 Event + * used to implement pthread_cancel. The 'timeout' + * argument to TimedWait is simply passed to + * WaitForMultipleObjects. + */ +PTW32_DLLPORT int PTW32_CDECL pthreadCancelableWait (HANDLE waitHandle); +PTW32_DLLPORT int PTW32_CDECL pthreadCancelableTimedWait (HANDLE waitHandle, + DWORD timeout); + +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Thread-Safe C Runtime Library Mappings. + */ +#ifndef _UWIN +# if defined(NEED_ERRNO) + PTW32_DLLPORT int * PTW32_CDECL _errno( void ); +# else +# ifndef errno +# if (defined(_MT) || defined(_DLL)) + __declspec(dllimport) extern int * __cdecl _errno(void); +# define errno (*_errno()) +# endif +# endif +# endif +#endif + +/* + * WIN32 C runtime library had been made thread-safe + * without affecting the user interface. Provide + * mappings from the UNIX thread-safe versions to + * the standard C runtime library calls. + * Only provide function mappings for functions that + * actually exist on WIN32. + */ + +#if !defined(__MINGW32__) +#define strtok_r( _s, _sep, _lasts ) \ + ( *(_lasts) = strtok( (_s), (_sep) ) ) +#endif /* !__MINGW32__ */ + +#define asctime_r( _tm, _buf ) \ + ( strcpy( (_buf), asctime( (_tm) ) ), \ + (_buf) ) + +#define ctime_r( _clock, _buf ) \ + ( strcpy( (_buf), ctime( (_clock) ) ), \ + (_buf) ) + +#define gmtime_r( _clock, _result ) \ + ( *(_result) = *gmtime( (_clock) ), \ + (_result) ) + +#define localtime_r( _clock, _result ) \ + ( *(_result) = *localtime( (_clock) ), \ + (_result) ) + +#define rand_r( _seed ) \ + ( _seed == _seed? rand() : rand() ) + + +/* + * Some compiler environments don't define some things. + */ +#if defined(__BORLANDC__) +# define _ftime ftime +# define _timeb timeb +#endif + +#ifdef __cplusplus + +/* + * Internal exceptions + */ +class ptw32_exception {}; +class ptw32_exception_cancel : public ptw32_exception {}; +class ptw32_exception_exit : public ptw32_exception {}; + +#endif + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX + +/* FIXME: This is only required if the library was built using SEH */ +/* + * Get internal SEH tag + */ +PTW32_DLLPORT DWORD PTW32_CDECL ptw32_get_exception_services_code(void); + +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +#ifndef PTW32_BUILD + +#ifdef __CLEANUP_SEH + +/* + * Redefine the SEH __except keyword to ensure that applications + * propagate our internal exceptions up to the library's internal handlers. + */ +#define __except( E ) \ + __except( ( GetExceptionCode() == ptw32_get_exception_services_code() ) \ + ? EXCEPTION_CONTINUE_SEARCH : ( E ) ) + +#endif /* __CLEANUP_SEH */ + +#ifdef __CLEANUP_CXX + +/* + * Redefine the C++ catch keyword to ensure that applications + * propagate our internal exceptions up to the library's internal handlers. + */ +#ifdef _MSC_VER + /* + * WARNING: Replace any 'catch( ... )' with 'PtW32CatchAll' + * if you want Pthread-Win32 cancelation and pthread_exit to work. + */ + +#ifndef PtW32NoCatchWarn + +#pragma message("Specify \"/DPtW32NoCatchWarn\" compiler flag to skip this message.") +#pragma message("------------------------------------------------------------------") +#pragma message("When compiling applications with MSVC++ and C++ exception handling:") +#pragma message(" Replace any 'catch( ... )' in routines called from POSIX threads") +#pragma message(" with 'PtW32CatchAll' or 'CATCHALL' if you want POSIX thread") +#pragma message(" cancelation and pthread_exit to work. For example:") +#pragma message("") +#pragma message(" #ifdef PtW32CatchAll") +#pragma message(" PtW32CatchAll") +#pragma message(" #else") +#pragma message(" catch(...)") +#pragma message(" #endif") +#pragma message(" {") +#pragma message(" /* Catchall block processing */") +#pragma message(" }") +#pragma message("------------------------------------------------------------------") + +#endif + +#define PtW32CatchAll \ + catch( ptw32_exception & ) { throw; } \ + catch( ... ) + +#else /* _MSC_VER */ + +#define catch( E ) \ + catch( ptw32_exception & ) { throw; } \ + catch( E ) + +#endif /* _MSC_VER */ + +#endif /* __CLEANUP_CXX */ + +#endif /* ! PTW32_BUILD */ + +#ifdef __cplusplus +} /* End of extern "C" */ +#endif /* __cplusplus */ + +#ifdef PTW32__HANDLE_DEF +# undef HANDLE +#endif +#ifdef PTW32__DWORD_DEF +# undef DWORD +#endif + +#undef PTW32_LEVEL +#undef PTW32_LEVEL_MAX + +#endif /* ! RC_INVOKED */ + +#endif /* PTHREAD_H */ diff --git a/pcsx2/windows/pthreads/pthread_attr_destroy.c b/pcsx2/windows/pthreads/pthread_attr_destroy.c new file mode 100644 index 0000000000..0ae40b5552 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_destroy.c @@ -0,0 +1,79 @@ +/* + * pthread_attr_destroy.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_attr_destroy (pthread_attr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Destroys a thread attributes object. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * + * DESCRIPTION + * Destroys a thread attributes object. + * + * NOTES: + * 1) Does not affect threads created with 'attr'. + * + * RESULTS + * 0 successfully destroyed attr, + * EINVAL 'attr' is invalid. + * + * ------------------------------------------------------ + */ +{ + if (ptw32_is_attr (attr) != 0) + { + return EINVAL; + } + + /* + * Set the attribute object to a specific invalid value. + */ + (*attr)->valid = 0; + free (*attr); + *attr = NULL; + + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_attr_getdetachstate.c b/pcsx2/windows/pthreads/pthread_attr_getdetachstate.c new file mode 100644 index 0000000000..45eb4f254c --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_getdetachstate.c @@ -0,0 +1,87 @@ +/* + * pthread_attr_getdetachstate.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_attr_getdetachstate (const pthread_attr_t * attr, int *detachstate) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function determines whether threads created with + * 'attr' will run detached. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * detachstate + * pointer to an integer into which is returned one + * of: + * + * PTHREAD_CREATE_JOINABLE + * Thread ID is valid, must be joined + * + * PTHREAD_CREATE_DETACHED + * Thread ID is invalid, cannot be joined, + * canceled, or modified + * + * + * DESCRIPTION + * This function determines whether threads created with + * 'attr' will run detached. + * + * NOTES: + * 1) You cannot join or cancel detached threads. + * + * RESULTS + * 0 successfully retrieved detach state, + * EINVAL 'attr' is invalid + * + * ------------------------------------------------------ + */ +{ + if (ptw32_is_attr (attr) != 0 || detachstate == NULL) + { + *detachstate = PTHREAD_CREATE_DETACHED; + return EINVAL; + } + + *detachstate = (*attr)->detachstate; + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_attr_getinheritsched.c b/pcsx2/windows/pthreads/pthread_attr_getinheritsched.c new file mode 100644 index 0000000000..e9901f7933 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_getinheritsched.c @@ -0,0 +1,51 @@ +/* + * pthread_attr_getinheritsched.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +pthread_attr_getinheritsched (pthread_attr_t * attr, int *inheritsched) +{ + if (ptw32_is_attr (attr) != 0 || inheritsched == NULL) + { + return EINVAL; + } + + *inheritsched = (*attr)->inheritsched; + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_attr_getschedparam.c b/pcsx2/windows/pthreads/pthread_attr_getschedparam.c new file mode 100644 index 0000000000..7ec68cbcdf --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_getschedparam.c @@ -0,0 +1,52 @@ +/* + * pthread_attr_getschedparam.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +pthread_attr_getschedparam (const pthread_attr_t * attr, + struct sched_param *param) +{ + if (ptw32_is_attr (attr) != 0 || param == NULL) + { + return EINVAL; + } + + memcpy (param, &(*attr)->param, sizeof (*param)); + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_attr_getschedpolicy.c b/pcsx2/windows/pthreads/pthread_attr_getschedpolicy.c new file mode 100644 index 0000000000..ccd91fd39e --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_getschedpolicy.c @@ -0,0 +1,61 @@ +/* + * pthread_attr_getschedpolicy.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +pthread_attr_getschedpolicy (pthread_attr_t * attr, int *policy) +{ + if (ptw32_is_attr (attr) != 0 || policy == NULL) + { + return EINVAL; + } + + /* + * Validate the policy arg. + * Check that a policy constant wasn't passed rather than &policy. + */ + if (policy <= (int *) SCHED_MAX) + { + return EINVAL; + } + + *policy = SCHED_OTHER; + + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_attr_getscope.c b/pcsx2/windows/pthreads/pthread_attr_getscope.c new file mode 100644 index 0000000000..d0ca1cd346 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_getscope.c @@ -0,0 +1,54 @@ +/* + * pthread_attr_getscope.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* ignore warning "unreferenced formal parameter" */ +#ifdef _MSC_VER +#pragma warning( disable : 4100 ) +#endif + +int +pthread_attr_getscope (const pthread_attr_t * attr, int *contentionscope) +{ +#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING + *contentionscope = (*attr)->contentionscope; + return 0; +#else + return ENOSYS; +#endif +} diff --git a/pcsx2/windows/pthreads/pthread_attr_getstackaddr.c b/pcsx2/windows/pthreads/pthread_attr_getstackaddr.c new file mode 100644 index 0000000000..41bf16299a --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_getstackaddr.c @@ -0,0 +1,97 @@ +/* + * pthread_attr_getstackaddr.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* ignore warning "unreferenced formal parameter" */ +#ifdef _MSC_VER +#pragma warning( disable : 4100 ) +#endif + +int +pthread_attr_getstackaddr (const pthread_attr_t * attr, void **stackaddr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function determines the address of the stack + * on which threads created with 'attr' will run. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * stackaddr + * pointer into which is returned the stack address. + * + * + * DESCRIPTION + * This function determines the address of the stack + * on which threads created with 'attr' will run. + * + * NOTES: + * 1) Function supported only if this macro is + * defined: + * + * _POSIX_THREAD_ATTR_STACKADDR + * + * 2) Create only one thread for each stack + * address.. + * + * RESULTS + * 0 successfully retreived stack address, + * EINVAL 'attr' is invalid + * ENOSYS function not supported + * + * ------------------------------------------------------ + */ +{ +#if defined( _POSIX_THREAD_ATTR_STACKADDR ) + + if (ptw32_is_attr (attr) != 0) + { + return EINVAL; + } + + *stackaddr = (*attr)->stackaddr; + return 0; + +#else + + return ENOSYS; + +#endif /* _POSIX_THREAD_ATTR_STACKADDR */ +} diff --git a/pcsx2/windows/pthreads/pthread_attr_getstacksize.c b/pcsx2/windows/pthreads/pthread_attr_getstacksize.c new file mode 100644 index 0000000000..9b4cbedbda --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_getstacksize.c @@ -0,0 +1,100 @@ +/* + * pthread_attr_getstacksize.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* ignore warning "unreferenced formal parameter" */ +#ifdef _MSC_VER +#pragma warning( disable : 4100 ) +#endif + +int +pthread_attr_getstacksize (const pthread_attr_t * attr, size_t * stacksize) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function determines the size of the stack on + * which threads created with 'attr' will run. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * stacksize + * pointer to size_t into which is returned the + * stack size, in bytes. + * + * + * DESCRIPTION + * This function determines the size of the stack on + * which threads created with 'attr' will run. + * + * NOTES: + * 1) Function supported only if this macro is + * defined: + * + * _POSIX_THREAD_ATTR_STACKSIZE + * + * 2) Use on newly created attributes object to + * find the default stack size. + * + * RESULTS + * 0 successfully retrieved stack size, + * EINVAL 'attr' is invalid + * ENOSYS function not supported + * + * ------------------------------------------------------ + */ +{ +#ifdef _POSIX_THREAD_ATTR_STACKSIZE + + if (ptw32_is_attr (attr) != 0) + { + return EINVAL; + } + + /* Everything is okay. */ + *stacksize = (*attr)->stacksize; + return 0; + +#else + + return ENOSYS; + +#endif /* _POSIX_THREAD_ATTR_STACKSIZE */ + +} diff --git a/pcsx2/windows/pthreads/pthread_attr_init.c b/pcsx2/windows/pthreads/pthread_attr_init.c new file mode 100644 index 0000000000..bf13449f70 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_init.c @@ -0,0 +1,117 @@ +/* + * pthread_attr_init.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_attr_init (pthread_attr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Initializes a thread attributes object with default + * attributes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * + * DESCRIPTION + * Initializes a thread attributes object with default + * attributes. + * + * NOTES: + * 1) Used to define thread attributes + * + * RESULTS + * 0 successfully initialized attr, + * ENOMEM insufficient memory for attr. + * + * ------------------------------------------------------ + */ +{ + pthread_attr_t attr_result; + + if (attr == NULL) + { + /* This is disallowed. */ + return EINVAL; + } + + attr_result = (pthread_attr_t) malloc (sizeof (*attr_result)); + + if (attr_result == NULL) + { + return ENOMEM; + } + +#ifdef _POSIX_THREAD_ATTR_STACKSIZE + /* + * Default to zero size. Unless changed explicitly this + * will allow Win32 to set the size to that of the + * main thread. + */ + attr_result->stacksize = 0; +#endif + +#ifdef _POSIX_THREAD_ATTR_STACKADDR + /* FIXME: Set this to something sensible when we support it. */ + attr_result->stackaddr = NULL; +#endif + + attr_result->detachstate = PTHREAD_CREATE_JOINABLE; + +#if HAVE_SIGSET_T + memset (&(attr_result->sigmask), 0, sizeof (sigset_t)); +#endif /* HAVE_SIGSET_T */ + + /* + * Win32 sets new threads to THREAD_PRIORITY_NORMAL and + * not to that of the parent thread. We choose to default to + * this arrangement. + */ + attr_result->param.sched_priority = THREAD_PRIORITY_NORMAL; + attr_result->inheritsched = PTHREAD_EXPLICIT_SCHED; + attr_result->contentionscope = PTHREAD_SCOPE_SYSTEM; + + attr_result->valid = PTW32_ATTR_VALID; + + *attr = attr_result; + + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_attr_setdetachstate.c b/pcsx2/windows/pthreads/pthread_attr_setdetachstate.c new file mode 100644 index 0000000000..d96c20c92a --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_setdetachstate.c @@ -0,0 +1,91 @@ +/* + * pthread_attr_setdetachstate.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_attr_setdetachstate (pthread_attr_t * attr, int detachstate) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function specifies whether threads created with + * 'attr' will run detached. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * detachstate + * an integer containing one of: + * + * PTHREAD_CREATE_JOINABLE + * Thread ID is valid, must be joined + * + * PTHREAD_CREATE_DETACHED + * Thread ID is invalid, cannot be joined, + * canceled, or modified + * + * + * DESCRIPTION + * This function specifies whether threads created with + * 'attr' will run detached. + * + * NOTES: + * 1) You cannot join or cancel detached threads. + * + * RESULTS + * 0 successfully set detach state, + * EINVAL 'attr' or 'detachstate' is invalid + * + * ------------------------------------------------------ + */ +{ + if (ptw32_is_attr (attr) != 0) + { + return EINVAL; + } + + if (detachstate != PTHREAD_CREATE_JOINABLE && + detachstate != PTHREAD_CREATE_DETACHED) + { + return EINVAL; + } + + (*attr)->detachstate = detachstate; + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_attr_setinheritsched.c b/pcsx2/windows/pthreads/pthread_attr_setinheritsched.c new file mode 100644 index 0000000000..8f92559a45 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_setinheritsched.c @@ -0,0 +1,57 @@ +/* + * pthread_attr_setinheritsched.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +pthread_attr_setinheritsched (pthread_attr_t * attr, int inheritsched) +{ + if (ptw32_is_attr (attr) != 0) + { + return EINVAL; + } + + if (PTHREAD_INHERIT_SCHED != inheritsched + && PTHREAD_EXPLICIT_SCHED != inheritsched) + { + return EINVAL; + } + + (*attr)->inheritsched = inheritsched; + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_attr_setschedparam.c b/pcsx2/windows/pthreads/pthread_attr_setschedparam.c new file mode 100644 index 0000000000..cf380577db --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_setschedparam.c @@ -0,0 +1,63 @@ +/* + * pthread_attr_setschedparam.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +pthread_attr_setschedparam (pthread_attr_t * attr, + const struct sched_param *param) +{ + int priority; + + if (ptw32_is_attr (attr) != 0 || param == NULL) + { + return EINVAL; + } + + priority = param->sched_priority; + + /* Validate priority level. */ + if (priority < sched_get_priority_min (SCHED_OTHER) || + priority > sched_get_priority_max (SCHED_OTHER)) + { + return EINVAL; + } + + memcpy (&(*attr)->param, param, sizeof (*param)); + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_attr_setschedpolicy.c b/pcsx2/windows/pthreads/pthread_attr_setschedpolicy.c new file mode 100644 index 0000000000..3a1ed6679b --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_setschedpolicy.c @@ -0,0 +1,55 @@ +/* + * pthread_attr_setschedpolicy.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +pthread_attr_setschedpolicy (pthread_attr_t * attr, int policy) +{ + if (ptw32_is_attr (attr) != 0) + { + return EINVAL; + } + + if (policy != SCHED_OTHER) + { + return ENOTSUP; + } + + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_attr_setscope.c b/pcsx2/windows/pthreads/pthread_attr_setscope.c new file mode 100644 index 0000000000..9892034f89 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_setscope.c @@ -0,0 +1,62 @@ +/* + * pthread_attr_setscope.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* ignore warning "unreferenced formal parameter" */ +#ifdef _MSC_VER +#pragma warning( disable : 4100 ) +#endif + +int +pthread_attr_setscope (pthread_attr_t * attr, int contentionscope) +{ +#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING + switch (contentionscope) + { + case PTHREAD_SCOPE_SYSTEM: + (*attr)->contentionscope = contentionscope; + return 0; + case PTHREAD_SCOPE_PROCESS: + return ENOTSUP; + default: + return EINVAL; + } +#else + return ENOSYS; +#endif +} diff --git a/pcsx2/windows/pthreads/pthread_attr_setstackaddr.c b/pcsx2/windows/pthreads/pthread_attr_setstackaddr.c new file mode 100644 index 0000000000..ce35f31cf7 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_setstackaddr.c @@ -0,0 +1,97 @@ +/* + * pthread_attr_setstackaddr.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_attr_setstackaddr (pthread_attr_t * attr, void *stackaddr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Threads created with 'attr' will run on the stack + * starting at 'stackaddr'. + * Stack must be at least PTHREAD_STACK_MIN bytes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * stacksize + * stack size, in bytes. + * + * + * DESCRIPTION + * Threads created with 'attr' will run on the stack + * starting at 'stackaddr'. + * Stack must be at least PTHREAD_STACK_MIN bytes. + * + * NOTES: + * 1) Function supported only if this macro is + * defined: + * + * _POSIX_THREAD_ATTR_STACKADDR + * + * 2) Create only one thread for each stack + * address.. + * + * 3) Ensure that stackaddr is aligned. + * + * RESULTS + * 0 successfully set stack address, + * EINVAL 'attr' is invalid + * ENOSYS function not supported + * + * ------------------------------------------------------ + */ +{ +#if defined( _POSIX_THREAD_ATTR_STACKADDR ) + + if (ptw32_is_attr (attr) != 0) + { + return EINVAL; + } + + (*attr)->stackaddr = stackaddr; + return 0; + +#else + + return ENOSYS; + +#endif /* _POSIX_THREAD_ATTR_STACKADDR */ +} diff --git a/pcsx2/windows/pthreads/pthread_attr_setstacksize.c b/pcsx2/windows/pthreads/pthread_attr_setstacksize.c new file mode 100644 index 0000000000..f1bc263692 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_attr_setstacksize.c @@ -0,0 +1,110 @@ +/* + * pthread_attr_setstacksize.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_attr_setstacksize (pthread_attr_t * attr, size_t stacksize) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function specifies the size of the stack on + * which threads created with 'attr' will run. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * stacksize + * stack size, in bytes. + * + * + * DESCRIPTION + * This function specifies the size of the stack on + * which threads created with 'attr' will run. + * + * NOTES: + * 1) Function supported only if this macro is + * defined: + * + * _POSIX_THREAD_ATTR_STACKSIZE + * + * 2) Find the default first (using + * pthread_attr_getstacksize), then increase + * by multiplying. + * + * 3) Only use if thread needs more than the + * default. + * + * RESULTS + * 0 successfully set stack size, + * EINVAL 'attr' is invalid or stacksize too + * small or too big. + * ENOSYS function not supported + * + * ------------------------------------------------------ + */ +{ +#ifdef _POSIX_THREAD_ATTR_STACKSIZE + +#if PTHREAD_STACK_MIN > 0 + + /* Verify that the stack size is within range. */ + if (stacksize < PTHREAD_STACK_MIN) + { + return EINVAL; + } + +#endif + + if (ptw32_is_attr (attr) != 0) + { + return EINVAL; + } + + /* Everything is okay. */ + (*attr)->stacksize = stacksize; + return 0; + +#else + + return ENOSYS; + +#endif /* _POSIX_THREAD_ATTR_STACKSIZE */ + +} diff --git a/pcsx2/windows/pthreads/pthread_barrier_destroy.c b/pcsx2/windows/pthreads/pthread_barrier_destroy.c new file mode 100644 index 0000000000..bfff3181d6 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_barrier_destroy.c @@ -0,0 +1,67 @@ +/* + * pthread_barrier_destroy.c + * + * Description: + * This translation unit implements barrier primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_barrier_destroy (pthread_barrier_t * barrier) +{ + int result = 0; + pthread_barrier_t b; + + if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID) + { + return EINVAL; + } + + b = *barrier; + *barrier = NULL; + + if (0 == (result = sem_destroy (&(b->semBarrierBreeched[0])))) + { + if (0 == (result = sem_destroy (&(b->semBarrierBreeched[1])))) + { + (void) free (b); + return 0; + } + (void) sem_init (&(b->semBarrierBreeched[0]), b->pshared, 0); + } + + *barrier = b; + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_barrier_init.c b/pcsx2/windows/pthreads/pthread_barrier_init.c new file mode 100644 index 0000000000..b588e73b31 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_barrier_init.c @@ -0,0 +1,81 @@ +/* + * pthread_barrier_init.c + * + * Description: + * This translation unit implements barrier primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_barrier_init (pthread_barrier_t * barrier, + const pthread_barrierattr_t * attr, unsigned int count) +{ + pthread_barrier_t b; + + if (barrier == NULL || count == 0) + { + return EINVAL; + } + + if (NULL != (b = (pthread_barrier_t) calloc (1, sizeof (*b)))) + { + b->pshared = (attr != NULL && *attr != NULL + ? (*attr)->pshared : PTHREAD_PROCESS_PRIVATE); + + b->nCurrentBarrierHeight = b->nInitialBarrierHeight = count; + b->iStep = 0; + + /* + * Two semaphores are used in the same way as two stepping + * stones might be used in crossing a stream. Once all + * threads are safely on one stone, the other stone can + * be moved ahead, and the threads can start moving to it. + * If some threads decide to eat their lunch before moving + * then the other threads have to wait. + */ + if (0 == sem_init (&(b->semBarrierBreeched[0]), b->pshared, 0)) + { + if (0 == sem_init (&(b->semBarrierBreeched[1]), b->pshared, 0)) + { + *barrier = b; + return 0; + } + (void) sem_destroy (&(b->semBarrierBreeched[0])); + } + (void) free (b); + } + + return ENOMEM; +} diff --git a/pcsx2/windows/pthreads/pthread_barrier_wait.c b/pcsx2/windows/pthreads/pthread_barrier_wait.c new file mode 100644 index 0000000000..cb9a08ee07 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_barrier_wait.c @@ -0,0 +1,99 @@ +/* + * pthread_barrier_wait.c + * + * Description: + * This translation unit implements barrier primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_barrier_wait (pthread_barrier_t * barrier) +{ + int result; + int step; + pthread_barrier_t b; + + if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID) + { + return EINVAL; + } + + b = *barrier; + step = b->iStep; + + if (0 == InterlockedDecrement ((long *) &(b->nCurrentBarrierHeight))) + { + /* Must be done before posting the semaphore. */ + b->nCurrentBarrierHeight = b->nInitialBarrierHeight; + + /* + * There is no race condition between the semaphore wait and post + * because we are using two alternating semas and all threads have + * entered barrier_wait and checked nCurrentBarrierHeight before this + * barrier's sema can be posted. Any threads that have not quite + * entered sem_wait below when the multiple_post has completed + * will nevertheless continue through the semaphore (barrier) + * and will not be left stranded. + */ + result = (b->nInitialBarrierHeight > 1 + ? sem_post_multiple (&(b->semBarrierBreeched[step]), + b->nInitialBarrierHeight - 1) : 0); + } + else + { + /* + * Use the non-cancelable version of sem_wait(). + */ + result = ptw32_semwait (&(b->semBarrierBreeched[step])); + } + + /* + * The first thread across will be the PTHREAD_BARRIER_SERIAL_THREAD. + * This also sets up the alternate semaphore as the next barrier. + */ + if (0 == result) + { + result = ((PTW32_INTERLOCKED_LONG) step == + PTW32_INTERLOCKED_COMPARE_EXCHANGE ((PTW32_INTERLOCKED_LPLONG) + & (b->iStep), + (PTW32_INTERLOCKED_LONG) + (1L - step), + (PTW32_INTERLOCKED_LONG) + step) ? + PTHREAD_BARRIER_SERIAL_THREAD : 0); + } + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_barrierattr_destroy.c b/pcsx2/windows/pthreads/pthread_barrierattr_destroy.c new file mode 100644 index 0000000000..aabfc5fca1 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_barrierattr_destroy.c @@ -0,0 +1,83 @@ +/* + * pthread_barrier_attr_destroy.c + * + * Description: + * This translation unit implements barrier primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_barrierattr_destroy (pthread_barrierattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Destroys a barrier attributes object. The object can + * no longer be used. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_barrierattr_t + * + * + * DESCRIPTION + * Destroys a barrier attributes object. The object can + * no longer be used. + * + * NOTES: + * 1) Does not affect barrieres created using 'attr' + * + * RESULTS + * 0 successfully released attr, + * EINVAL 'attr' is invalid. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + + if (attr == NULL || *attr == NULL) + { + result = EINVAL; + } + else + { + pthread_barrierattr_t ba = *attr; + + *attr = NULL; + free (ba); + } + + return (result); +} /* pthread_barrierattr_destroy */ diff --git a/pcsx2/windows/pthreads/pthread_barrierattr_getpshared.c b/pcsx2/windows/pthreads/pthread_barrierattr_getpshared.c new file mode 100644 index 0000000000..93f7d41815 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_barrierattr_getpshared.c @@ -0,0 +1,95 @@ +/* + * pthread_barrier_attr_getpshared.c + * + * Description: + * This translation unit implements barrier primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_barrierattr_getpshared (const pthread_barrierattr_t * attr, + int *pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Determine whether barriers created with 'attr' can be + * shared between processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_barrierattr_t + * + * pshared + * will be set to one of: + * + * PTHREAD_PROCESS_SHARED + * May be shared if in shared memory + * + * PTHREAD_PROCESS_PRIVATE + * Cannot be shared. + * + * + * DESCRIPTION + * Mutexes creatd with 'attr' can be shared between + * processes if pthread_barrier_t variable is allocated + * in memory shared by these processes. + * NOTES: + * 1) pshared barriers MUST be allocated in shared + * memory. + * 2) The following macro is defined if shared barriers + * are supported: + * _POSIX_THREAD_PROCESS_SHARED + * + * RESULTS + * 0 successfully retrieved attribute, + * EINVAL 'attr' is invalid, + * + * ------------------------------------------------------ + */ +{ + int result; + + if ((attr != NULL && *attr != NULL) && (pshared != NULL)) + { + *pshared = (*attr)->pshared; + result = 0; + } + else + { + result = EINVAL; + } + + return (result); +} /* pthread_barrierattr_getpshared */ diff --git a/pcsx2/windows/pthreads/pthread_barrierattr_init.c b/pcsx2/windows/pthreads/pthread_barrierattr_init.c new file mode 100644 index 0000000000..1b880f1b2b --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_barrierattr_init.c @@ -0,0 +1,85 @@ +/* + * pthread_barrier_attr_init.c + * + * Description: + * This translation unit implements barrier primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_barrierattr_init (pthread_barrierattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Initializes a barrier attributes object with default + * attributes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_barrierattr_t + * + * + * DESCRIPTION + * Initializes a barrier attributes object with default + * attributes. + * + * NOTES: + * 1) Used to define barrier types + * + * RESULTS + * 0 successfully initialized attr, + * ENOMEM insufficient memory for attr. + * + * ------------------------------------------------------ + */ +{ + pthread_barrierattr_t ba; + int result = 0; + + ba = (pthread_barrierattr_t) calloc (1, sizeof (*ba)); + + if (ba == NULL) + { + result = ENOMEM; + } + else + { + ba->pshared = PTHREAD_PROCESS_PRIVATE; + } + + *attr = ba; + + return (result); +} /* pthread_barrierattr_init */ diff --git a/pcsx2/windows/pthreads/pthread_barrierattr_setpshared.c b/pcsx2/windows/pthreads/pthread_barrierattr_setpshared.c new file mode 100644 index 0000000000..0ab999227d --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_barrierattr_setpshared.c @@ -0,0 +1,119 @@ +/* + * pthread_barrier_attr_setpshared.c + * + * Description: + * This translation unit implements barrier primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_barrierattr_setpshared (pthread_barrierattr_t * attr, int pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Barriers created with 'attr' can be shared between + * processes if pthread_barrier_t variable is allocated + * in memory shared by these processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_barrierattr_t + * + * pshared + * must be one of: + * + * PTHREAD_PROCESS_SHARED + * May be shared if in shared memory + * + * PTHREAD_PROCESS_PRIVATE + * Cannot be shared. + * + * DESCRIPTION + * Mutexes creatd with 'attr' can be shared between + * processes if pthread_barrier_t variable is allocated + * in memory shared by these processes. + * + * NOTES: + * 1) pshared barriers MUST be allocated in shared + * memory. + * + * 2) The following macro is defined if shared barriers + * are supported: + * _POSIX_THREAD_PROCESS_SHARED + * + * RESULTS + * 0 successfully set attribute, + * EINVAL 'attr' or pshared is invalid, + * ENOSYS PTHREAD_PROCESS_SHARED not supported, + * + * ------------------------------------------------------ + */ +{ + int result; + + if ((attr != NULL && *attr != NULL) && + ((pshared == PTHREAD_PROCESS_SHARED) || + (pshared == PTHREAD_PROCESS_PRIVATE))) + { + if (pshared == PTHREAD_PROCESS_SHARED) + { + +#if !defined( _POSIX_THREAD_PROCESS_SHARED ) + + result = ENOSYS; + pshared = PTHREAD_PROCESS_PRIVATE; + +#else + + result = 0; + +#endif /* _POSIX_THREAD_PROCESS_SHARED */ + + } + else + { + result = 0; + } + + (*attr)->pshared = pshared; + } + else + { + result = EINVAL; + } + + return (result); + +} /* pthread_barrierattr_setpshared */ diff --git a/pcsx2/windows/pthreads/pthread_cancel.c b/pcsx2/windows/pthreads/pthread_cancel.c new file mode 100644 index 0000000000..d0b79879a9 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_cancel.c @@ -0,0 +1,223 @@ +/* + * pthread_cancel.c + * + * Description: + * POSIX thread functions related to thread cancellation. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +#if defined(_M_IX86) || defined(_X86_) +#define PTW32_PROGCTR(Context) ((Context).Eip) +#endif + +#if defined (_M_IA64) +#define PTW32_PROGCTR(Context) ((Context).StIIP) +#endif + +#if defined(_MIPS_) +#define PTW32_PROGCTR(Context) ((Context).Fir) +#endif + +#if defined(_ALPHA_) +#define PTW32_PROGCTR(Context) ((Context).Fir) +#endif + +#if defined(_PPC_) +#define PTW32_PROGCTR(Context) ((Context).Iar) +#endif + +#if defined(_AMD64_) +#define PTW32_PROGCTR(Context) ((Context).Rip) +#endif + +#if !defined(PTW32_PROGCTR) +#error Module contains CPU-specific code; modify and recompile. +#endif + +static void +ptw32_cancel_self (void) +{ + ptw32_throw (PTW32_EPS_CANCEL); + + /* Never reached */ +} + +static void CALLBACK +ptw32_cancel_callback (DWORD unused) +{ + ptw32_throw (PTW32_EPS_CANCEL); + + /* Never reached */ +} + +/* + * ptw32_RegisterCancelation() - + * Must have args of same type as QueueUserAPCEx because this function + * is a substitute for QueueUserAPCEx if it's not available. + */ +DWORD +ptw32_RegisterCancelation (PAPCFUNC unused1, HANDLE threadH, DWORD unused2) +{ + CONTEXT context; + + context.ContextFlags = CONTEXT_CONTROL; + GetThreadContext (threadH, &context); + PTW32_PROGCTR (context) = (DWORD_PTR) ptw32_cancel_self; + SetThreadContext (threadH, &context); + return 0; +} + +int +pthread_cancel (pthread_t thread) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function requests cancellation of 'thread'. + * + * PARAMETERS + * thread + * reference to an instance of pthread_t + * + * + * DESCRIPTION + * This function requests cancellation of 'thread'. + * NOTE: cancellation is asynchronous; use pthread_join to + * wait for termination of 'thread' if necessary. + * + * RESULTS + * 0 successfully requested cancellation, + * ESRCH no thread found corresponding to 'thread', + * ENOMEM implicit self thread create failed. + * ------------------------------------------------------ + */ +{ + int result; + int cancel_self; + pthread_t self; + ptw32_thread_t * tp; + + result = pthread_kill (thread, 0); + + if (0 != result) + { + return result; + } + + if ((self = pthread_self ()).p == NULL) + { + return ENOMEM; + }; + + /* + * FIXME!! + * + * Can a thread cancel itself? + * + * The standard doesn't + * specify an error to be returned if the target + * thread is itself. + * + * If it may, then we need to ensure that a thread can't + * deadlock itself trying to cancel itself asyncronously + * (pthread_cancel is required to be an async-cancel + * safe function). + */ + cancel_self = pthread_equal (thread, self); + + tp = (ptw32_thread_t *) thread.p; + + /* + * Lock for async-cancel safety. + */ + (void) pthread_mutex_lock (&tp->cancelLock); + + if (tp->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS + && tp->cancelState == PTHREAD_CANCEL_ENABLE + && tp->state < PThreadStateCanceling) + { + if (cancel_self) + { + tp->state = PThreadStateCanceling; + tp->cancelState = PTHREAD_CANCEL_DISABLE; + + (void) pthread_mutex_unlock (&tp->cancelLock); + ptw32_throw (PTW32_EPS_CANCEL); + + /* Never reached */ + } + else + { + HANDLE threadH = tp->threadH; + + SuspendThread (threadH); + + if (WaitForSingleObject (threadH, 0) == WAIT_TIMEOUT) + { + tp->state = PThreadStateCanceling; + tp->cancelState = PTHREAD_CANCEL_DISABLE; + /* + * If alertdrv and QueueUserAPCEx is available then the following + * will result in a call to QueueUserAPCEx with the args given, otherwise + * this will result in a call to ptw32_RegisterCancelation and only + * the threadH arg will be used. + */ + ptw32_register_cancelation (ptw32_cancel_callback, threadH, 0); + (void) pthread_mutex_unlock (&tp->cancelLock); + ResumeThread (threadH); + } + } + } + else + { + /* + * Set for deferred cancellation. + */ + if (tp->state < PThreadStateCancelPending) + { + tp->state = PThreadStateCancelPending; + if (!SetEvent (tp->cancelEvent)) + { + result = ESRCH; + } + } + else if (tp->state >= PThreadStateCanceling) + { + result = ESRCH; + } + + (void) pthread_mutex_unlock (&tp->cancelLock); + } + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_cond_destroy.c b/pcsx2/windows/pthreads/pthread_cond_destroy.c new file mode 100644 index 0000000000..8d016af1ea --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_cond_destroy.c @@ -0,0 +1,244 @@ +/* + * pthread_cond_destroy.c + * + * Description: + * This translation unit implements condition variables and their primitives. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +int +pthread_cond_destroy (pthread_cond_t * cond) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function destroys a condition variable + * + * + * PARAMETERS + * cond + * pointer to an instance of pthread_cond_t + * + * + * DESCRIPTION + * This function destroys a condition variable. + * + * NOTES: + * 1) A condition variable can be destroyed + * immediately after all the threads that + * are blocked on it are awakened. e.g. + * + * struct list { + * pthread_mutex_t lm; + * ... + * } + * + * struct elt { + * key k; + * int busy; + * pthread_cond_t notbusy; + * ... + * } + * + * + * struct elt * + * list_find(struct list *lp, key k) + * { + * struct elt *ep; + * + * pthread_mutex_lock(&lp->lm); + * while ((ep = find_elt(l,k) != NULL) && ep->busy) + * pthread_cond_wait(&ep->notbusy, &lp->lm); + * if (ep != NULL) + * ep->busy = 1; + * pthread_mutex_unlock(&lp->lm); + * return(ep); + * } + * + * delete_elt(struct list *lp, struct elt *ep) + * { + * pthread_mutex_lock(&lp->lm); + * assert(ep->busy); + * ... remove ep from list ... + * ep->busy = 0; + * (A) pthread_cond_broadcast(&ep->notbusy); + * pthread_mutex_unlock(&lp->lm); + * (B) pthread_cond_destroy(&rp->notbusy); + * free(ep); + * } + * + * In this example, the condition variable + * and its list element may be freed (line B) + * immediately after all threads waiting for + * it are awakened (line A), since the mutex + * and the code ensure that no other thread + * can touch the element to be deleted. + * + * RESULTS + * 0 successfully released condition variable, + * EINVAL 'cond' is invalid, + * EBUSY 'cond' is in use, + * + * ------------------------------------------------------ + */ +{ + pthread_cond_t cv; + int result = 0, result1 = 0, result2 = 0; + + /* + * Assuming any race condition here is harmless. + */ + if (cond == NULL || *cond == NULL) + { + return EINVAL; + } + + if (*cond != PTHREAD_COND_INITIALIZER) + { + EnterCriticalSection (&ptw32_cond_list_lock); + + cv = *cond; + + /* + * Close the gate; this will synchronize this thread with + * all already signaled waiters to let them retract their + * waiter status - SEE NOTE 1 ABOVE!!! + */ + if (sem_wait (&(cv->semBlockLock)) != 0) + { + return errno; + } + + /* + * !TRY! lock mtxUnblockLock; try will detect busy condition + * and will not cause a deadlock with respect to concurrent + * signal/broadcast. + */ + if ((result = pthread_mutex_trylock (&(cv->mtxUnblockLock))) != 0) + { + (void) sem_post (&(cv->semBlockLock)); + return result; + } + + /* + * Check whether cv is still busy (still has waiters) + */ + if (cv->nWaitersBlocked > cv->nWaitersGone) + { + if (sem_post (&(cv->semBlockLock)) != 0) + { + result = errno; + } + result1 = pthread_mutex_unlock (&(cv->mtxUnblockLock)); + result2 = EBUSY; + } + else + { + /* + * Now it is safe to destroy + */ + *cond = NULL; + + if (sem_destroy (&(cv->semBlockLock)) != 0) + { + result = errno; + } + if (sem_destroy (&(cv->semBlockQueue)) != 0) + { + result1 = errno; + } + if ((result2 = pthread_mutex_unlock (&(cv->mtxUnblockLock))) == 0) + { + result2 = pthread_mutex_destroy (&(cv->mtxUnblockLock)); + } + + /* Unlink the CV from the list */ + + if (ptw32_cond_list_head == cv) + { + ptw32_cond_list_head = cv->next; + } + else + { + cv->prev->next = cv->next; + } + + if (ptw32_cond_list_tail == cv) + { + ptw32_cond_list_tail = cv->prev; + } + else + { + cv->next->prev = cv->prev; + } + + (void) free (cv); + } + + LeaveCriticalSection (&ptw32_cond_list_lock); + } + else + { + /* + * See notes in ptw32_cond_check_need_init() above also. + */ + EnterCriticalSection (&ptw32_cond_test_init_lock); + + /* + * Check again. + */ + if (*cond == PTHREAD_COND_INITIALIZER) + { + /* + * This is all we need to do to destroy a statically + * initialised cond that has not yet been used (initialised). + * If we get to here, another thread waiting to initialise + * this cond will get an EINVAL. That's OK. + */ + *cond = NULL; + } + else + { + /* + * The cv has been initialised while we were waiting + * so assume it's in use. + */ + result = EBUSY; + } + + LeaveCriticalSection (&ptw32_cond_test_init_lock); + } + + return ((result != 0) ? result : ((result1 != 0) ? result1 : result2)); +} diff --git a/pcsx2/windows/pthreads/pthread_cond_init.c b/pcsx2/windows/pthreads/pthread_cond_init.c new file mode 100644 index 0000000000..e78c5f6f79 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_cond_init.c @@ -0,0 +1,170 @@ +/* + * pthread_cond_init.c + * + * Description: + * This translation unit implements condition variables and their primitives. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function initializes a condition variable. + * + * PARAMETERS + * cond + * pointer to an instance of pthread_cond_t + * + * attr + * specifies optional creation attributes. + * + * + * DESCRIPTION + * This function initializes a condition variable. + * + * RESULTS + * 0 successfully created condition variable, + * EINVAL 'attr' is invalid, + * EAGAIN insufficient resources (other than + * memory, + * ENOMEM insufficient memory, + * EBUSY 'cond' is already initialized, + * + * ------------------------------------------------------ + */ +{ + int result; + pthread_cond_t cv = NULL; + + if (cond == NULL) + { + return EINVAL; + } + +#ifdef PTW32_STATIC_LIB + // This allos for C++ static initializers to function without crashes. (air) + pthread_win32_process_attach_np(); +#endif + + if ((attr != NULL && *attr != NULL) && + ((*attr)->pshared == PTHREAD_PROCESS_SHARED)) + { + /* + * Creating condition variable that can be shared between + * processes. + */ + result = ENOSYS; + goto DONE; + } + + cv = (pthread_cond_t) calloc (1, sizeof (*cv)); + + if (cv == NULL) + { + result = ENOMEM; + goto DONE; + } + + cv->nWaitersBlocked = 0; + cv->nWaitersToUnblock = 0; + cv->nWaitersGone = 0; + + if (sem_init (&(cv->semBlockLock), 0, 1) != 0) + { + result = errno; + goto FAIL0; + } + + if (sem_init (&(cv->semBlockQueue), 0, 0) != 0) + { + result = errno; + goto FAIL1; + } + + if ((result = pthread_mutex_init (&(cv->mtxUnblockLock), 0)) != 0) + { + goto FAIL2; + } + + result = 0; + + goto DONE; + + /* + * ------------- + * Failed... + * ------------- + */ +FAIL2: + (void) sem_destroy (&(cv->semBlockQueue)); + +FAIL1: + (void) sem_destroy (&(cv->semBlockLock)); + +FAIL0: + (void) free (cv); + cv = NULL; + +DONE: + if (0 == result) + { + EnterCriticalSection (&ptw32_cond_list_lock); + + cv->next = NULL; + cv->prev = ptw32_cond_list_tail; + + if (ptw32_cond_list_tail != NULL) + { + ptw32_cond_list_tail->next = cv; + } + + ptw32_cond_list_tail = cv; + + if (ptw32_cond_list_head == NULL) + { + ptw32_cond_list_head = cv; + } + + LeaveCriticalSection (&ptw32_cond_list_lock); + } + + *cond = cv; + + return result; + +} /* pthread_cond_init */ diff --git a/pcsx2/windows/pthreads/pthread_cond_signal.c b/pcsx2/windows/pthreads/pthread_cond_signal.c new file mode 100644 index 0000000000..e147831d41 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_cond_signal.c @@ -0,0 +1,231 @@ +/* + * pthread_cond_signal.c + * + * Description: + * This translation unit implements condition variables and their primitives. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * ------------------------------------------------------------- + * Algorithm: + * See the comments at the top of pthread_cond_wait.c. + */ + +#include "pthread.h" +#include "implement.h" + +static INLINE int +ptw32_cond_unblock (pthread_cond_t * cond, int unblockAll) + /* + * Notes. + * + * Does not use the external mutex for synchronisation, + * therefore semBlockLock is needed. + * mtxUnblockLock is for LEVEL-2 synch. LEVEL-2 is the + * state where the external mutex is not necessarily locked by + * any thread, ie. between cond_wait unlocking and re-acquiring + * the lock after having been signaled or a timeout or + * cancellation. + * + * Uses the following CV elements: + * nWaitersBlocked + * nWaitersToUnblock + * nWaitersGone + * mtxUnblockLock + * semBlockLock + * semBlockQueue + */ +{ + int result; + pthread_cond_t cv; + int nSignalsToIssue; + + if (cond == NULL || *cond == NULL) + { + return EINVAL; + } + + cv = *cond; + + /* + * No-op if the CV is static and hasn't been initialised yet. + * Assuming that any race condition is harmless. + */ + if (cv == PTHREAD_COND_INITIALIZER) + { + return 0; + } + + if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0) + { + return result; + } + + if (0 != cv->nWaitersToUnblock) + { + if (0 == cv->nWaitersBlocked) + { + return pthread_mutex_unlock (&(cv->mtxUnblockLock)); + } + if (unblockAll) + { + cv->nWaitersToUnblock += (nSignalsToIssue = cv->nWaitersBlocked); + cv->nWaitersBlocked = 0; + } + else + { + nSignalsToIssue = 1; + cv->nWaitersToUnblock++; + cv->nWaitersBlocked--; + } + } + else if (cv->nWaitersBlocked > cv->nWaitersGone) + { + /* Use the non-cancellable version of sem_wait() */ + if (ptw32_semwait (&(cv->semBlockLock)) != 0) + { + result = errno; + (void) pthread_mutex_unlock (&(cv->mtxUnblockLock)); + return result; + } + if (0 != cv->nWaitersGone) + { + cv->nWaitersBlocked -= cv->nWaitersGone; + cv->nWaitersGone = 0; + } + if (unblockAll) + { + nSignalsToIssue = cv->nWaitersToUnblock = cv->nWaitersBlocked; + cv->nWaitersBlocked = 0; + } + else + { + nSignalsToIssue = cv->nWaitersToUnblock = 1; + cv->nWaitersBlocked--; + } + } + else + { + return pthread_mutex_unlock (&(cv->mtxUnblockLock)); + } + + if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) == 0) + { + if (sem_post_multiple (&(cv->semBlockQueue), nSignalsToIssue) != 0) + { + result = errno; + } + } + + return result; + +} /* ptw32_cond_unblock */ + +int +pthread_cond_signal (pthread_cond_t * cond) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function signals a condition variable, waking + * one waiting thread. + * If SCHED_FIFO or SCHED_RR policy threads are waiting + * the highest priority waiter is awakened; otherwise, + * an unspecified waiter is awakened. + * + * PARAMETERS + * cond + * pointer to an instance of pthread_cond_t + * + * + * DESCRIPTION + * This function signals a condition variable, waking + * one waiting thread. + * If SCHED_FIFO or SCHED_RR policy threads are waiting + * the highest priority waiter is awakened; otherwise, + * an unspecified waiter is awakened. + * + * NOTES: + * + * 1) Use when any waiter can respond and only one need + * respond (all waiters being equal). + * + * RESULTS + * 0 successfully signaled condition, + * EINVAL 'cond' is invalid, + * + * ------------------------------------------------------ + */ +{ + /* + * The '0'(FALSE) unblockAll arg means unblock ONE waiter. + */ + return (ptw32_cond_unblock (cond, 0)); + +} /* pthread_cond_signal */ + +int +pthread_cond_broadcast (pthread_cond_t * cond) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function broadcasts the condition variable, + * waking all current waiters. + * + * PARAMETERS + * cond + * pointer to an instance of pthread_cond_t + * + * + * DESCRIPTION + * This function signals a condition variable, waking + * all waiting threads. + * + * NOTES: + * + * 1) Use when more than one waiter may respond to + * predicate change or if any waiting thread may + * not be able to respond + * + * RESULTS + * 0 successfully signalled condition to all + * waiting threads, + * EINVAL 'cond' is invalid + * ENOSPC a required resource has been exhausted, + * + * ------------------------------------------------------ + */ +{ + /* + * The TRUE unblockAll arg means unblock ALL waiters. + */ + return (ptw32_cond_unblock (cond, PTW32_TRUE)); + +} /* pthread_cond_broadcast */ diff --git a/pcsx2/windows/pthreads/pthread_cond_wait.c b/pcsx2/windows/pthreads/pthread_cond_wait.c new file mode 100644 index 0000000000..e33713bb46 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_cond_wait.c @@ -0,0 +1,567 @@ +/* + * pthread_cond_wait.c + * + * Description: + * This translation unit implements condition variables and their primitives. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * ------------------------------------------------------------- + * Algorithm: + * The algorithm used in this implementation is that developed by + * Alexander Terekhov in colaboration with Louis Thomas. The bulk + * of the discussion is recorded in the file README.CV, which contains + * several generations of both colaborators original algorithms. The final + * algorithm used here is the one referred to as + * + * Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL + * + * presented below in pseudo-code as it appeared: + * + * + * given: + * semBlockLock - bin.semaphore + * semBlockQueue - semaphore + * mtxExternal - mutex or CS + * mtxUnblockLock - mutex or CS + * nWaitersGone - int + * nWaitersBlocked - int + * nWaitersToUnblock - int + * + * wait( timeout ) { + * + * [auto: register int result ] // error checking omitted + * [auto: register int nSignalsWasLeft ] + * [auto: register int nWaitersWasGone ] + * + * sem_wait( semBlockLock ); + * nWaitersBlocked++; + * sem_post( semBlockLock ); + * + * unlock( mtxExternal ); + * bTimedOut = sem_wait( semBlockQueue,timeout ); + * + * lock( mtxUnblockLock ); + * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) { + * if ( bTimeout ) { // timeout (or canceled) + * if ( 0 != nWaitersBlocked ) { + * nWaitersBlocked--; + * } + * else { + * nWaitersGone++; // count spurious wakeups. + * } + * } + * if ( 0 == --nWaitersToUnblock ) { + * if ( 0 != nWaitersBlocked ) { + * sem_post( semBlockLock ); // open the gate. + * nSignalsWasLeft = 0; // do not open the gate + * // below again. + * } + * else if ( 0 != (nWaitersWasGone = nWaitersGone) ) { + * nWaitersGone = 0; + * } + * } + * } + * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or + * // spurious semaphore :-) + * sem_wait( semBlockLock ); + * nWaitersBlocked -= nWaitersGone; // something is going on here + * // - test of timeouts? :-) + * sem_post( semBlockLock ); + * nWaitersGone = 0; + * } + * unlock( mtxUnblockLock ); + * + * if ( 1 == nSignalsWasLeft ) { + * if ( 0 != nWaitersWasGone ) { + * // sem_adjust( semBlockQueue,-nWaitersWasGone ); + * while ( nWaitersWasGone-- ) { + * sem_wait( semBlockQueue ); // better now than spurious later + * } + * } sem_post( semBlockLock ); // open the gate + * } + * + * lock( mtxExternal ); + * + * return ( bTimedOut ) ? ETIMEOUT : 0; + * } + * + * signal(bAll) { + * + * [auto: register int result ] + * [auto: register int nSignalsToIssue] + * + * lock( mtxUnblockLock ); + * + * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!! + * if ( 0 == nWaitersBlocked ) { // NO-OP + * return unlock( mtxUnblockLock ); + * } + * if (bAll) { + * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked; + * nWaitersBlocked = 0; + * } + * else { + * nSignalsToIssue = 1; + * nWaitersToUnblock++; + * nWaitersBlocked--; + * } + * } + * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION! + * sem_wait( semBlockLock ); // close the gate + * if ( 0 != nWaitersGone ) { + * nWaitersBlocked -= nWaitersGone; + * nWaitersGone = 0; + * } + * if (bAll) { + * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked; + * nWaitersBlocked = 0; + * } + * else { + * nSignalsToIssue = nWaitersToUnblock = 1; + * nWaitersBlocked--; + * } + * } + * else { // NO-OP + * return unlock( mtxUnblockLock ); + * } + * + * unlock( mtxUnblockLock ); + * sem_post( semBlockQueue,nSignalsToIssue ); + * return result; + * } + * ------------------------------------------------------------- + * + * Algorithm 9 / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL + * + * presented below in pseudo-code; basically 8a... + * ...BUT W/O "spurious wakes" prevention: + * + * + * given: + * semBlockLock - bin.semaphore + * semBlockQueue - semaphore + * mtxExternal - mutex or CS + * mtxUnblockLock - mutex or CS + * nWaitersGone - int + * nWaitersBlocked - int + * nWaitersToUnblock - int + * + * wait( timeout ) { + * + * [auto: register int result ] // error checking omitted + * [auto: register int nSignalsWasLeft ] + * + * sem_wait( semBlockLock ); + * ++nWaitersBlocked; + * sem_post( semBlockLock ); + * + * unlock( mtxExternal ); + * bTimedOut = sem_wait( semBlockQueue,timeout ); + * + * lock( mtxUnblockLock ); + * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) { + * --nWaitersToUnblock; + * } + * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or + * // spurious semaphore :-) + * sem_wait( semBlockLock ); + * nWaitersBlocked -= nWaitersGone; // something is going on here + * // - test of timeouts? :-) + * sem_post( semBlockLock ); + * nWaitersGone = 0; + * } + * unlock( mtxUnblockLock ); + * + * if ( 1 == nSignalsWasLeft ) { + * sem_post( semBlockLock ); // open the gate + * } + * + * lock( mtxExternal ); + * + * return ( bTimedOut ) ? ETIMEOUT : 0; + * } + * + * signal(bAll) { + * + * [auto: register int result ] + * [auto: register int nSignalsToIssue] + * + * lock( mtxUnblockLock ); + * + * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!! + * if ( 0 == nWaitersBlocked ) { // NO-OP + * return unlock( mtxUnblockLock ); + * } + * if (bAll) { + * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked; + * nWaitersBlocked = 0; + * } + * else { + * nSignalsToIssue = 1; + * ++nWaitersToUnblock; + * --nWaitersBlocked; + * } + * } + * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION! + * sem_wait( semBlockLock ); // close the gate + * if ( 0 != nWaitersGone ) { + * nWaitersBlocked -= nWaitersGone; + * nWaitersGone = 0; + * } + * if (bAll) { + * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked; + * nWaitersBlocked = 0; + * } + * else { + * nSignalsToIssue = nWaitersToUnblock = 1; + * --nWaitersBlocked; + * } + * } + * else { // NO-OP + * return unlock( mtxUnblockLock ); + * } + * + * unlock( mtxUnblockLock ); + * sem_post( semBlockQueue,nSignalsToIssue ); + * return result; + * } + * ------------------------------------------------------------- + * + */ + +#include "pthread.h" +#include "implement.h" + +/* + * Arguments for cond_wait_cleanup, since we can only pass a + * single void * to it. + */ +typedef struct +{ + pthread_mutex_t *mutexPtr; + pthread_cond_t cv; + int *resultPtr; +} ptw32_cond_wait_cleanup_args_t; + +static void PTW32_CDECL +ptw32_cond_wait_cleanup (void *args) +{ + ptw32_cond_wait_cleanup_args_t *cleanup_args = + (ptw32_cond_wait_cleanup_args_t *) args; + pthread_cond_t cv = cleanup_args->cv; + int *resultPtr = cleanup_args->resultPtr; + int nSignalsWasLeft; + int result; + + /* + * Whether we got here as a result of signal/broadcast or because of + * timeout on wait or thread cancellation we indicate that we are no + * longer waiting. The waiter is responsible for adjusting waiters + * (to)unblock(ed) counts (protected by unblock lock). + */ + if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0) + { + *resultPtr = result; + return; + } + + if (0 != (nSignalsWasLeft = cv->nWaitersToUnblock)) + { + --(cv->nWaitersToUnblock); + } + else if (INT_MAX / 2 == ++(cv->nWaitersGone)) + { + /* Use the non-cancellable version of sem_wait() */ + if (ptw32_semwait (&(cv->semBlockLock)) != 0) + { + *resultPtr = errno; + /* + * This is a fatal error for this CV, + * so we deliberately don't unlock + * cv->mtxUnblockLock before returning. + */ + return; + } + cv->nWaitersBlocked -= cv->nWaitersGone; + if (sem_post (&(cv->semBlockLock)) != 0) + { + *resultPtr = errno; + /* + * This is a fatal error for this CV, + * so we deliberately don't unlock + * cv->mtxUnblockLock before returning. + */ + return; + } + cv->nWaitersGone = 0; + } + + if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) != 0) + { + *resultPtr = result; + return; + } + + if (1 == nSignalsWasLeft) + { + if (sem_post (&(cv->semBlockLock)) != 0) + { + *resultPtr = errno; + return; + } + } + + /* + * XSH: Upon successful return, the mutex has been locked and is owned + * by the calling thread. + */ + if ((result = pthread_mutex_lock (cleanup_args->mutexPtr)) != 0) + { + *resultPtr = result; + } +} /* ptw32_cond_wait_cleanup */ + +static INLINE int +ptw32_cond_timedwait (pthread_cond_t * cond, + pthread_mutex_t * mutex, const struct timespec *abstime) +{ + int result = 0; + pthread_cond_t cv; + ptw32_cond_wait_cleanup_args_t cleanup_args; + + if (cond == NULL || *cond == NULL) + { + return EINVAL; + } + + /* + * We do a quick check to see if we need to do more work + * to initialise a static condition variable. We check + * again inside the guarded section of ptw32_cond_check_need_init() + * to avoid race conditions. + */ + if (*cond == PTHREAD_COND_INITIALIZER) + { + result = ptw32_cond_check_need_init (cond); + } + + if (result != 0 && result != EBUSY) + { + return result; + } + + cv = *cond; + + /* Thread can be cancelled in sem_wait() but this is OK */ + if (sem_wait (&(cv->semBlockLock)) != 0) + { + return errno; + } + + ++(cv->nWaitersBlocked); + + if (sem_post (&(cv->semBlockLock)) != 0) + { + return errno; + } + + /* + * Setup this waiter cleanup handler + */ + cleanup_args.mutexPtr = mutex; + cleanup_args.cv = cv; + cleanup_args.resultPtr = &result; + +#ifdef _MSC_VER +#pragma inline_depth(0) +#endif + pthread_cleanup_push (ptw32_cond_wait_cleanup, (void *) &cleanup_args); + + /* + * Now we can release 'mutex' and... + */ + if ((result = pthread_mutex_unlock (mutex)) == 0) + { + + /* + * ...wait to be awakened by + * pthread_cond_signal, or + * pthread_cond_broadcast, or + * timeout, or + * thread cancellation + * + * Note: + * + * sem_timedwait is a cancellation point, + * hence providing the mechanism for making + * pthread_cond_wait a cancellation point. + * We use the cleanup mechanism to ensure we + * re-lock the mutex and adjust (to)unblock(ed) waiters + * counts if we are cancelled, timed out or signalled. + */ + if (sem_timedwait (&(cv->semBlockQueue), abstime) != 0) + { + result = errno; + } + } + + /* + * Always cleanup + */ + pthread_cleanup_pop (1); +#ifdef _MSC_VER +#pragma inline_depth() +#endif + + /* + * "result" can be modified by the cleanup handler. + */ + return result; + +} /* ptw32_cond_timedwait */ + + +int +pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function waits on a condition variable until + * awakened by a signal or broadcast. + * + * Caller MUST be holding the mutex lock; the + * lock is released and the caller is blocked waiting + * on 'cond'. When 'cond' is signaled, the mutex + * is re-acquired before returning to the caller. + * + * PARAMETERS + * cond + * pointer to an instance of pthread_cond_t + * + * mutex + * pointer to an instance of pthread_mutex_t + * + * + * DESCRIPTION + * This function waits on a condition variable until + * awakened by a signal or broadcast. + * + * NOTES: + * + * 1) The function must be called with 'mutex' LOCKED + * by the calling thread, or undefined behaviour + * will result. + * + * 2) This routine atomically releases 'mutex' and causes + * the calling thread to block on the condition variable. + * The blocked thread may be awakened by + * pthread_cond_signal or + * pthread_cond_broadcast. + * + * Upon successful completion, the 'mutex' has been locked and + * is owned by the calling thread. + * + * + * RESULTS + * 0 caught condition; mutex released, + * EINVAL 'cond' or 'mutex' is invalid, + * EINVAL different mutexes for concurrent waits, + * EINVAL mutex is not held by the calling thread, + * + * ------------------------------------------------------ + */ +{ + /* + * The NULL abstime arg means INFINITE waiting. + */ + return (ptw32_cond_timedwait (cond, mutex, NULL)); + +} /* pthread_cond_wait */ + + +int +pthread_cond_timedwait (pthread_cond_t * cond, + pthread_mutex_t * mutex, + const struct timespec *abstime) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function waits on a condition variable either until + * awakened by a signal or broadcast; or until the time + * specified by abstime passes. + * + * PARAMETERS + * cond + * pointer to an instance of pthread_cond_t + * + * mutex + * pointer to an instance of pthread_mutex_t + * + * abstime + * pointer to an instance of (const struct timespec) + * + * + * DESCRIPTION + * This function waits on a condition variable either until + * awakened by a signal or broadcast; or until the time + * specified by abstime passes. + * + * NOTES: + * 1) The function must be called with 'mutex' LOCKED + * by the calling thread, or undefined behaviour + * will result. + * + * 2) This routine atomically releases 'mutex' and causes + * the calling thread to block on the condition variable. + * The blocked thread may be awakened by + * pthread_cond_signal or + * pthread_cond_broadcast. + * + * + * RESULTS + * 0 caught condition; mutex released, + * EINVAL 'cond', 'mutex', or abstime is invalid, + * EINVAL different mutexes for concurrent waits, + * EINVAL mutex is not held by the calling thread, + * ETIMEDOUT abstime ellapsed before cond was signaled. + * + * ------------------------------------------------------ + */ +{ + if (abstime == NULL) + { + return EINVAL; + } + + return (ptw32_cond_timedwait (cond, mutex, abstime)); + +} /* pthread_cond_timedwait */ diff --git a/pcsx2/windows/pthreads/pthread_condattr_destroy.c b/pcsx2/windows/pthreads/pthread_condattr_destroy.c new file mode 100644 index 0000000000..2ca11b4edf --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_condattr_destroy.c @@ -0,0 +1,86 @@ +/* + * condvar_attr_destroy.c + * + * Description: + * This translation unit implements condition variables and their primitives. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_condattr_destroy (pthread_condattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Destroys a condition variable attributes object. + * The object can no longer be used. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_condattr_t + * + * + * DESCRIPTION + * Destroys a condition variable attributes object. + * The object can no longer be used. + * + * NOTES: + * 1) Does not affect condition variables created + * using 'attr' + * + * RESULTS + * 0 successfully released attr, + * EINVAL 'attr' is invalid. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + + if (attr == NULL || *attr == NULL) + { + result = EINVAL; + } + else + { + (void) free (*attr); + + *attr = NULL; + result = 0; + } + + return result; + +} /* pthread_condattr_destroy */ diff --git a/pcsx2/windows/pthreads/pthread_condattr_getpshared.c b/pcsx2/windows/pthreads/pthread_condattr_getpshared.c new file mode 100644 index 0000000000..b7b854fb44 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_condattr_getpshared.c @@ -0,0 +1,97 @@ +/* + * pthread_condattr_getpshared.c + * + * Description: + * This translation unit implements condition variables and their primitives. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_condattr_getpshared (const pthread_condattr_t * attr, int *pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Determine whether condition variables created with 'attr' + * can be shared between processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_condattr_t + * + * pshared + * will be set to one of: + * + * PTHREAD_PROCESS_SHARED + * May be shared if in shared memory + * + * PTHREAD_PROCESS_PRIVATE + * Cannot be shared. + * + * + * DESCRIPTION + * Condition Variables created with 'attr' can be shared + * between processes if pthread_cond_t variable is allocated + * in memory shared by these processes. + * NOTES: + * 1) pshared condition variables MUST be allocated in + * shared memory. + * + * 2) The following macro is defined if shared mutexes + * are supported: + * _POSIX_THREAD_PROCESS_SHARED + * + * RESULTS + * 0 successfully retrieved attribute, + * EINVAL 'attr' or 'pshared' is invalid, + * + * ------------------------------------------------------ + */ +{ + int result; + + if ((attr != NULL && *attr != NULL) && (pshared != NULL)) + { + *pshared = (*attr)->pshared; + result = 0; + } + else + { + result = EINVAL; + } + + return result; + +} /* pthread_condattr_getpshared */ diff --git a/pcsx2/windows/pthreads/pthread_condattr_init.c b/pcsx2/windows/pthreads/pthread_condattr_init.c new file mode 100644 index 0000000000..da79e21958 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_condattr_init.c @@ -0,0 +1,87 @@ +/* + * pthread_condattr_init.c + * + * Description: + * This translation unit implements condition variables and their primitives. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_condattr_init (pthread_condattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Initializes a condition variable attributes object + * with default attributes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_condattr_t + * + * + * DESCRIPTION + * Initializes a condition variable attributes object + * with default attributes. + * + * NOTES: + * 1) Use to define condition variable types + * 2) It is up to the application to ensure + * that it doesn't re-init an attribute + * without destroying it first. Otherwise + * a memory leak is created. + * + * RESULTS + * 0 successfully initialized attr, + * ENOMEM insufficient memory for attr. + * + * ------------------------------------------------------ + */ +{ + pthread_condattr_t attr_result; + int result = 0; + + attr_result = (pthread_condattr_t) calloc (1, sizeof (*attr_result)); + + if (attr_result == NULL) + { + result = ENOMEM; + } + + *attr = attr_result; + + return result; + +} /* pthread_condattr_init */ diff --git a/pcsx2/windows/pthreads/pthread_condattr_setpshared.c b/pcsx2/windows/pthreads/pthread_condattr_setpshared.c new file mode 100644 index 0000000000..b8b2213266 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_condattr_setpshared.c @@ -0,0 +1,117 @@ +/* + * pthread_condattr_setpshared.c + * + * Description: + * This translation unit implements condition variables and their primitives. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_condattr_setpshared (pthread_condattr_t * attr, int pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Mutexes created with 'attr' can be shared between + * processes if pthread_mutex_t variable is allocated + * in memory shared by these processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * pshared + * must be one of: + * + * PTHREAD_PROCESS_SHARED + * May be shared if in shared memory + * + * PTHREAD_PROCESS_PRIVATE + * Cannot be shared. + * + * DESCRIPTION + * Mutexes creatd with 'attr' can be shared between + * processes if pthread_mutex_t variable is allocated + * in memory shared by these processes. + * + * NOTES: + * 1) pshared mutexes MUST be allocated in shared + * memory. + * + * 2) The following macro is defined if shared mutexes + * are supported: + * _POSIX_THREAD_PROCESS_SHARED + * + * RESULTS + * 0 successfully set attribute, + * EINVAL 'attr' or pshared is invalid, + * ENOSYS PTHREAD_PROCESS_SHARED not supported, + * + * ------------------------------------------------------ + */ +{ + int result; + + if ((attr != NULL && *attr != NULL) + && ((pshared == PTHREAD_PROCESS_SHARED) + || (pshared == PTHREAD_PROCESS_PRIVATE))) + { + if (pshared == PTHREAD_PROCESS_SHARED) + { + +#if !defined( _POSIX_THREAD_PROCESS_SHARED ) + result = ENOSYS; + pshared = PTHREAD_PROCESS_PRIVATE; +#else + result = 0; + +#endif /* _POSIX_THREAD_PROCESS_SHARED */ + + } + else + { + result = 0; + } + + (*attr)->pshared = pshared; + } + else + { + result = EINVAL; + } + + return result; + +} /* pthread_condattr_setpshared */ diff --git a/pcsx2/windows/pthreads/pthread_delay_np.c b/pcsx2/windows/pthreads/pthread_delay_np.c new file mode 100644 index 0000000000..628cb780a1 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_delay_np.c @@ -0,0 +1,171 @@ +/* + * pthreads_delay_np.c + * + * Description: + * This translation unit implements non-portable thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* + * pthread_delay_np + * + * DESCRIPTION + * + * This routine causes a thread to delay execution for a specific period of time. + * This period ends at the current time plus the specified interval. The routine + * will not return before the end of the period is reached, but may return an + * arbitrary amount of time after the period has gone by. This can be due to + * system load, thread priorities, and system timer granularity. + * + * Specifying an interval of zero (0) seconds and zero (0) nanoseconds is + * allowed and can be used to force the thread to give up the processor or to + * deliver a pending cancelation request. + * + * The timespec structure contains the following two fields: + * + * tv_sec is an integer number of seconds. + * tv_nsec is an integer number of nanoseconds. + * + * Return Values + * + * If an error condition occurs, this routine returns an integer value indicating + * the type of error. Possible return values are as follows: + * + * 0 + * Successful completion. + * [EINVAL] + * The value specified by interval is invalid. + * + * Example + * + * The following code segment would wait for 5 and 1/2 seconds + * + * struct timespec tsWait; + * int intRC; + * + * tsWait.tv_sec = 5; + * tsWait.tv_nsec = 500000000L; + * intRC = pthread_delay_np(&tsWait); + */ +int +pthread_delay_np (struct timespec *interval) +{ + DWORD wait_time; + DWORD secs_in_millisecs; + DWORD millisecs; + DWORD status; + pthread_t self; + ptw32_thread_t * sp; + + if (interval == NULL) + { + return EINVAL; + } + + if (interval->tv_sec == 0L && interval->tv_nsec == 0L) + { + pthread_testcancel (); + Sleep (0); + pthread_testcancel (); + return (0); + } + + /* convert secs to millisecs */ + secs_in_millisecs = interval->tv_sec * 1000L; + + /* convert nanosecs to millisecs (rounding up) */ + millisecs = (interval->tv_nsec + 999999L) / 1000000L; + +#if defined(__WATCOMC__) +#pragma disable_message (124) +#endif + + /* + * Most compilers will issue a warning 'comparison always 0' + * because the variable type is unsigned, but we need to keep this + * for some reason I can't recall now. + */ + if (0 > (wait_time = secs_in_millisecs + millisecs)) + { + return EINVAL; + } + +#if defined(__WATCOMC__) +#pragma enable_message (124) +#endif + + if (NULL == (self = pthread_self ()).p) + { + return ENOMEM; + } + + sp = (ptw32_thread_t *) self.p; + + if (sp->cancelState == PTHREAD_CANCEL_ENABLE) + { + /* + * Async cancelation won't catch us until wait_time is up. + * Deferred cancelation will cancel us immediately. + */ + if (WAIT_OBJECT_0 == + (status = WaitForSingleObject (sp->cancelEvent, wait_time))) + { + /* + * Canceling! + */ + (void) pthread_mutex_lock (&sp->cancelLock); + if (sp->state < PThreadStateCanceling) + { + sp->state = PThreadStateCanceling; + sp->cancelState = PTHREAD_CANCEL_DISABLE; + (void) pthread_mutex_unlock (&sp->cancelLock); + + ptw32_throw (PTW32_EPS_CANCEL); + } + + (void) pthread_mutex_unlock (&sp->cancelLock); + return ESRCH; + } + else if (status != WAIT_TIMEOUT) + { + return EINVAL; + } + } + else + { + Sleep (wait_time); + } + + return (0); +} diff --git a/pcsx2/windows/pthreads/pthread_detach.c b/pcsx2/windows/pthreads/pthread_detach.c new file mode 100644 index 0000000000..3dbe2bb26a --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_detach.c @@ -0,0 +1,139 @@ +/* + * pthread_detach.c + * + * Description: + * This translation unit implements functions related to thread + * synchronisation. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* + * Not needed yet, but defining it should indicate clashes with build target + * environment that should be fixed. + */ +#include + + +int +pthread_detach (pthread_t thread) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function detaches the given thread. + * + * PARAMETERS + * thread + * an instance of a pthread_t + * + * + * DESCRIPTION + * This function detaches the given thread. You may use it to + * detach the main thread or to detach a joinable thread. + * NOTE: detached threads cannot be joined; + * storage is freed immediately on termination. + * + * RESULTS + * 0 successfully detached the thread, + * EINVAL thread is not a joinable thread, + * ENOSPC a required resource has been exhausted, + * ESRCH no thread could be found for 'thread', + * + * ------------------------------------------------------ + */ +{ + int result; + BOOL destroyIt = PTW32_FALSE; + ptw32_thread_t * tp = (ptw32_thread_t *) thread.p; + + EnterCriticalSection (&ptw32_thread_reuse_lock); + + if (NULL == tp + || thread.x != tp->ptHandle.x) + { + result = ESRCH; + } + else if (PTHREAD_CREATE_DETACHED == tp->detachState) + { + result = EINVAL; + } + else + { + /* + * Joinable ptw32_thread_t structs are not scavenged until + * a join or detach is done. The thread may have exited already, + * but all of the state and locks etc are still there. + */ + result = 0; + + if (pthread_mutex_lock (&tp->cancelLock) == 0) + { + if (tp->state != PThreadStateLast) + { + tp->detachState = PTHREAD_CREATE_DETACHED; + } + else if (tp->detachState != PTHREAD_CREATE_DETACHED) + { + /* + * Thread is joinable and has exited or is exiting. + */ + destroyIt = PTW32_TRUE; + } + (void) pthread_mutex_unlock (&tp->cancelLock); + } + else + { + /* cancelLock shouldn't fail, but if it does ... */ + result = ESRCH; + } + } + + LeaveCriticalSection (&ptw32_thread_reuse_lock); + + if (result == 0) + { + /* Thread is joinable */ + + if (destroyIt) + { + /* The thread has exited or is exiting but has not been joined or + * detached. Need to wait in case it's still exiting. + */ + (void) WaitForSingleObject(tp->threadH, INFINITE); + ptw32_threadDestroy (thread); + } + } + + return (result); + +} /* pthread_detach */ diff --git a/pcsx2/windows/pthreads/pthread_equal.c b/pcsx2/windows/pthreads/pthread_equal.c new file mode 100644 index 0000000000..129bbb48ba --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_equal.c @@ -0,0 +1,76 @@ +/* + * pthread_equal.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_equal (pthread_t t1, pthread_t t2) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function returns nonzero if t1 and t2 are equal, else + * returns nonzero + * + * PARAMETERS + * t1, + * t2 + * thread IDs + * + * + * DESCRIPTION + * This function returns nonzero if t1 and t2 are equal, else + * returns zero. + * + * RESULTS + * non-zero if t1 and t2 refer to the same thread, + * 0 t1 and t2 do not refer to the same thread + * + * ------------------------------------------------------ + */ +{ + int result; + + /* + * We also accept NULL == NULL - treating NULL as a thread + * for this special case, because there is no error that we can return. + */ + result = ( t1.p == t2.p && t1.x == t2.x ); + + return (result); + +} /* pthread_equal */ diff --git a/pcsx2/windows/pthreads/pthread_exit.c b/pcsx2/windows/pthreads/pthread_exit.c new file mode 100644 index 0000000000..b401fb2e85 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_exit.c @@ -0,0 +1,106 @@ +/* + * pthread_exit.c + * + * Description: + * This translation unit implements routines associated with exiting from + * a thread. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#ifndef _UWIN +//# include +#endif + +void +pthread_exit (void *value_ptr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function terminates the calling thread, returning + * the value 'value_ptr' to any joining thread. + * + * PARAMETERS + * value_ptr + * a generic data value (i.e. not the address of a value) + * + * + * DESCRIPTION + * This function terminates the calling thread, returning + * the value 'value_ptr' to any joining thread. + * NOTE: thread should be joinable. + * + * RESULTS + * N/A + * + * ------------------------------------------------------ + */ +{ + ptw32_thread_t * sp; + + /* + * Don't use pthread_self() to avoid creating an implicit POSIX thread handle + * unnecessarily. + */ + sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); + +#ifdef _UWIN + if (--pthread_count <= 0) + exit ((int) value_ptr); +#endif + + if (NULL == sp) + { + /* + * A POSIX thread handle was never created. I.e. this is a + * Win32 thread that has never called a pthreads-win32 routine that + * required a POSIX handle. + * + * Implicit POSIX handles are cleaned up in ptw32_throw() now. + */ + +#if ! defined (__MINGW32__) || defined (__MSVCRT__) || defined (__DMC__) + _endthreadex ((unsigned) value_ptr); +#else + _endthread (); +#endif + + /* Never reached */ + } + + sp->exitStatus = value_ptr; + + ptw32_throw (PTW32_EPS_EXIT); + + /* Never reached. */ + +} diff --git a/pcsx2/windows/pthreads/pthread_getconcurrency.c b/pcsx2/windows/pthreads/pthread_getconcurrency.c new file mode 100644 index 0000000000..52430c6f8a --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_getconcurrency.c @@ -0,0 +1,45 @@ +/* + * pthread_getconcurrency.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_getconcurrency (void) +{ + return ptw32_concurrency; +} diff --git a/pcsx2/windows/pthreads/pthread_getschedparam.c b/pcsx2/windows/pthreads/pthread_getschedparam.c new file mode 100644 index 0000000000..997978f07c --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_getschedparam.c @@ -0,0 +1,75 @@ +/* + * sched_getschedparam.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +pthread_getschedparam (pthread_t thread, int *policy, + struct sched_param *param) +{ + int result; + + /* Validate the thread id. */ + result = pthread_kill (thread, 0); + if (0 != result) + { + return result; + } + + /* + * Validate the policy and param args. + * Check that a policy constant wasn't passed rather than &policy. + */ + if (policy <= (int *) SCHED_MAX || param == NULL) + { + return EINVAL; + } + + /* Fill out the policy. */ + *policy = SCHED_OTHER; + + /* + * This function must return the priority value set by + * the most recent pthread_setschedparam() or pthread_create() + * for the target thread. It must not return the actual thread + * priority as altered by any system priority adjustments etc. + */ + param->sched_priority = ((ptw32_thread_t *)thread.p)->sched_priority; + + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_getspecific.c b/pcsx2/windows/pthreads/pthread_getspecific.c new file mode 100644 index 0000000000..13e3a69011 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_getspecific.c @@ -0,0 +1,84 @@ +/* + * pthread_getspecific.c + * + * Description: + * POSIX thread functions which implement thread-specific data (TSD). + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +void * +pthread_getspecific (pthread_key_t key) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function returns the current value of key in the + * calling thread. If no value has been set for 'key' in + * the thread, NULL is returned. + * + * PARAMETERS + * key + * an instance of pthread_key_t + * + * + * DESCRIPTION + * This function returns the current value of key in the + * calling thread. If no value has been set for 'key' in + * the thread, NULL is returned. + * + * RESULTS + * key value or NULL on failure + * + * ------------------------------------------------------ + */ +{ + void * ptr; + + if (key == NULL) + { + ptr = NULL; + } + else + { + int lasterror = GetLastError (); + int lastWSAerror = WSAGetLastError (); + + ptr = TlsGetValue (key->key); + + SetLastError (lasterror); + WSASetLastError (lastWSAerror); + } + + return ptr; +} diff --git a/pcsx2/windows/pthreads/pthread_getw32threadhandle_np.c b/pcsx2/windows/pthreads/pthread_getw32threadhandle_np.c new file mode 100644 index 0000000000..66bcff3256 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_getw32threadhandle_np.c @@ -0,0 +1,53 @@ +/* + * pthread_getw32threadhandle_np.c + * + * Description: + * This translation unit implements non-portable thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* + * pthread_getw32threadhandle_np() + * + * Returns the win32 thread handle that the POSIX + * thread "thread" is running as. + * + * Applications can use the win32 handle to set + * win32 specific attributes of the thread. + */ +HANDLE +pthread_getw32threadhandle_np (pthread_t thread) +{ + return ((ptw32_thread_t *)thread.p)->threadH; +} diff --git a/pcsx2/windows/pthreads/pthread_join.c b/pcsx2/windows/pthreads/pthread_join.c new file mode 100644 index 0000000000..be6640b8c3 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_join.c @@ -0,0 +1,154 @@ +/* + * pthread_join.c + * + * Description: + * This translation unit implements functions related to thread + * synchronisation. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* + * Not needed yet, but defining it should indicate clashes with build target + * environment that should be fixed. + */ +#include + + +int +pthread_join (pthread_t thread, void **value_ptr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function waits for 'thread' to terminate and + * returns the thread's exit value if 'value_ptr' is not + * NULL. This also detaches the thread on successful + * completion. + * + * PARAMETERS + * thread + * an instance of pthread_t + * + * value_ptr + * pointer to an instance of pointer to void + * + * + * DESCRIPTION + * This function waits for 'thread' to terminate and + * returns the thread's exit value if 'value_ptr' is not + * NULL. This also detaches the thread on successful + * completion. + * NOTE: detached threads cannot be joined or canceled + * + * RESULTS + * 0 'thread' has completed + * EINVAL thread is not a joinable thread, + * ESRCH no thread could be found with ID 'thread', + * ENOENT thread couldn't find it's own valid handle, + * EDEADLK attempt to join thread with self + * + * ------------------------------------------------------ + */ +{ + int result; + pthread_t self; + ptw32_thread_t * tp = (ptw32_thread_t *) thread.p; + + EnterCriticalSection (&ptw32_thread_reuse_lock); + + if (NULL == tp + || thread.x != tp->ptHandle.x) + { + result = ESRCH; + } + else if (PTHREAD_CREATE_DETACHED == tp->detachState) + { + result = EINVAL; + } + else + { + result = 0; + } + + LeaveCriticalSection (&ptw32_thread_reuse_lock); + + if (result == 0) + { + /* + * The target thread is joinable and can't be reused before we join it. + */ + self = pthread_self(); + + if (NULL == self.p) + { + result = ENOENT; + } + else if (pthread_equal (self, thread)) + { + result = EDEADLK; + } + else + { + /* + * Pthread_join is a cancelation point. + * If we are canceled then our target thread must not be + * detached (destroyed). This is guarranteed because + * pthreadCancelableWait will not return if we + * are canceled. + */ + result = pthreadCancelableWait (tp->threadH); + + if (0 == result) + { + if (value_ptr != NULL) + { + *value_ptr = tp->exitStatus; + } + + /* + * The result of making multiple simultaneous calls to + * pthread_join() or pthread_detach() specifying the same + * target is undefined. + */ + result = pthread_detach (thread); + } + else + { + result = ESRCH; + } + } + } + + return (result); + +} /* pthread_join */ diff --git a/pcsx2/windows/pthreads/pthread_key_create.c b/pcsx2/windows/pthreads/pthread_key_create.c new file mode 100644 index 0000000000..b3b410ac9f --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_key_create.c @@ -0,0 +1,108 @@ +/* + * pthread_key_create.c + * + * Description: + * POSIX thread functions which implement thread-specific data (TSD). + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +/* TLS_OUT_OF_INDEXES not defined on WinCE */ +#ifndef TLS_OUT_OF_INDEXES +#define TLS_OUT_OF_INDEXES 0xffffffff +#endif + +int +pthread_key_create (pthread_key_t * key, void (*destructor) (void *)) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function creates a thread-specific data key visible + * to all threads. All existing and new threads have a value + * NULL for key until set using pthread_setspecific. When any + * thread with a non-NULL value for key terminates, 'destructor' + * is called with key's current value for that thread. + * + * PARAMETERS + * key + * pointer to an instance of pthread_key_t + * + * + * DESCRIPTION + * This function creates a thread-specific data key visible + * to all threads. All existing and new threads have a value + * NULL for key until set using pthread_setspecific. When any + * thread with a non-NULL value for key terminates, 'destructor' + * is called with key's current value for that thread. + * + * RESULTS + * 0 successfully created semaphore, + * EAGAIN insufficient resources or PTHREAD_KEYS_MAX + * exceeded, + * ENOMEM insufficient memory to create the key, + * + * ------------------------------------------------------ + */ +{ + int result = 0; + pthread_key_t newkey; + + if ((newkey = (pthread_key_t) calloc (1, sizeof (*newkey))) == NULL) + { + result = ENOMEM; + } + else if ((newkey->key = TlsAlloc ()) == TLS_OUT_OF_INDEXES) + { + result = EAGAIN; + + free (newkey); + newkey = NULL; + } + else if (destructor != NULL) + { + /* + * Have to manage associations between thread and key; + * Therefore, need a lock that allows multiple threads + * to gain exclusive access to the key->threads list. + * + * The mutex will only be created when it is first locked. + */ + newkey->keyLock = PTHREAD_MUTEX_INITIALIZER; + newkey->destructor = destructor; + } + + *key = newkey; + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_key_delete.c b/pcsx2/windows/pthreads/pthread_key_delete.c new file mode 100644 index 0000000000..c6acc11576 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_key_delete.c @@ -0,0 +1,133 @@ +/* + * pthread_key_delete.c + * + * Description: + * POSIX thread functions which implement thread-specific data (TSD). + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_key_delete (pthread_key_t key) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function deletes a thread-specific data key. This + * does not change the value of the thread specific data key + * for any thread and does not run the key's destructor + * in any thread so it should be used with caution. + * + * PARAMETERS + * key + * pointer to an instance of pthread_key_t + * + * + * DESCRIPTION + * This function deletes a thread-specific data key. This + * does not change the value of the thread specific data key + * for any thread and does not run the key's destructor + * in any thread so it should be used with caution. + * + * RESULTS + * 0 successfully deleted the key, + * EINVAL key is invalid, + * + * ------------------------------------------------------ + */ +{ + int result = 0; + + if (key != NULL) + { + if (key->threads != NULL && + key->destructor != NULL && + pthread_mutex_lock (&(key->keyLock)) == 0) + { + ThreadKeyAssoc *assoc; + /* + * Run through all Thread<-->Key associations + * for this key. + * + * While we hold at least one of the locks guarding + * the assoc, we know that the assoc pointed to by + * key->threads is valid. + */ + while ((assoc = (ThreadKeyAssoc *) key->threads) != NULL) + { + ptw32_thread_t * thread = assoc->thread; + + if (assoc == NULL) + { + /* Finished */ + break; + } + + if (pthread_mutex_lock (&(thread->threadLock)) == 0) + { + /* + * Since we are starting at the head of the key's threads + * chain, this will also point key->threads at the next assoc. + * While we hold key->keyLock, no other thread can insert + * a new assoc via pthread_setspecific. + */ + ptw32_tkAssocDestroy (assoc); + (void) pthread_mutex_unlock (&(thread->threadLock)); + } + else + { + /* Thread or lock is no longer valid? */ + ptw32_tkAssocDestroy (assoc); + } + } + pthread_mutex_unlock (&(key->keyLock)); + } + + TlsFree (key->key); + if (key->destructor != NULL) + { + /* A thread could be holding the keyLock */ + while (EBUSY == pthread_mutex_destroy (&(key->keyLock))) + { + Sleep(1); // Ugly. + } + } + +#if defined( _DEBUG ) + memset ((char *) key, 0, sizeof (*key)); +#endif + free (key); + } + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_kill.c b/pcsx2/windows/pthreads/pthread_kill.c new file mode 100644 index 0000000000..89d73fa85e --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_kill.c @@ -0,0 +1,102 @@ +/* + * pthread_kill.c + * + * Description: + * This translation unit implements the pthread_kill routine. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* + * Not needed yet, but defining it should indicate clashes with build target + * environment that should be fixed. + */ +#include + +int +pthread_kill (pthread_t thread, int sig) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function requests that a signal be delivered to the + * specified thread. If sig is zero, error checking is + * performed but no signal is actually sent such that this + * function can be used to check for a valid thread ID. + * + * PARAMETERS + * thread reference to an instances of pthread_t + * sig signal. Currently only a value of 0 is supported. + * + * + * DESCRIPTION + * This function requests that a signal be delivered to the + * specified thread. If sig is zero, error checking is + * performed but no signal is actually sent such that this + * function can be used to check for a valid thread ID. + * + * RESULTS + * ESRCH the thread is not a valid thread ID, + * EINVAL the value of the signal is invalid + * or unsupported. + * 0 the signal was successfully sent. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + ptw32_thread_t * tp; + + EnterCriticalSection (&ptw32_thread_reuse_lock); + + tp = (ptw32_thread_t *) thread.p; + + if (NULL == tp + || thread.x != tp->ptHandle.x + || NULL == tp->threadH) + { + result = ESRCH; + } + + LeaveCriticalSection (&ptw32_thread_reuse_lock); + + if (0 == result && 0 != sig) + { + /* + * Currently does not support any signals. + */ + result = EINVAL; + } + + return result; + +} /* pthread_kill */ diff --git a/pcsx2/windows/pthreads/pthread_mutex_destroy.c b/pcsx2/windows/pthreads/pthread_mutex_destroy.c new file mode 100644 index 0000000000..105f1109f6 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutex_destroy.c @@ -0,0 +1,146 @@ +/* + * pthread_mutex_destroy.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_mutex_destroy (pthread_mutex_t * mutex) +{ + int result = 0; + pthread_mutex_t mx; + + /* + * Let the system deal with invalid pointers. + */ + + /* + * Check to see if we have something to delete. + */ + if (*mutex < PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) + { + mx = *mutex; + + result = pthread_mutex_trylock (&mx); + + /* + * If trylock succeeded and the mutex is not recursively locked it + * can be destroyed. + */ + if (result == 0) + { + if (mx->kind != PTHREAD_MUTEX_RECURSIVE || 1 == mx->recursive_count) + { + /* + * FIXME!!! + * The mutex isn't held by another thread but we could still + * be too late invalidating the mutex below since another thread + * may already have entered mutex_lock and the check for a valid + * *mutex != NULL. + * + * Note that this would be an unusual situation because it is not + * common that mutexes are destroyed while they are still in + * use by other threads. + */ + *mutex = NULL; + + result = pthread_mutex_unlock (&mx); + + if (result == 0) + { + if (!CloseHandle (mx->event)) + { + *mutex = mx; + result = EINVAL; + } + else + { + free (mx); + } + } + else + { + /* + * Restore the mutex before we return the error. + */ + *mutex = mx; + } + } + else /* mx->recursive_count > 1 */ + { + /* + * The mutex must be recursive and already locked by us (this thread). + */ + mx->recursive_count--; /* Undo effect of pthread_mutex_trylock() above */ + result = EBUSY; + } + } + } + else + { + /* + * See notes in ptw32_mutex_check_need_init() above also. + */ + EnterCriticalSection (&ptw32_mutex_test_init_lock); + + /* + * Check again. + */ + if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) + { + /* + * This is all we need to do to destroy a statically + * initialised mutex that has not yet been used (initialised). + * If we get to here, another thread + * waiting to initialise this mutex will get an EINVAL. + */ + *mutex = NULL; + } + else + { + /* + * The mutex has been initialised while we were waiting + * so assume it's in use. + */ + result = EBUSY; + } + + LeaveCriticalSection (&ptw32_mutex_test_init_lock); + } + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_mutex_init.c b/pcsx2/windows/pthreads/pthread_mutex_init.c new file mode 100644 index 0000000000..57119c76eb --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutex_init.c @@ -0,0 +1,109 @@ +/* + * pthread_mutex_init.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_mutex_init (pthread_mutex_t * mutex, const pthread_mutexattr_t * attr) +{ + int result = 0; + pthread_mutex_t mx; + + if (mutex == NULL) + { + return EINVAL; + } + +#ifdef PTW32_STATIC_LIB + // This allos for C++ static initializers to function without crashes. (air) + pthread_win32_process_attach_np(); +#endif + + if (attr != NULL + && *attr != NULL && (*attr)->pshared == PTHREAD_PROCESS_SHARED) + { + /* + * Creating mutex that can be shared between + * processes. + */ +#if _POSIX_THREAD_PROCESS_SHARED >= 0 + + /* + * Not implemented yet. + */ + +#error ERROR [__FILE__, line __LINE__]: Process shared mutexes are not supported yet. + +#else + + return ENOSYS; + +#endif /* _POSIX_THREAD_PROCESS_SHARED */ + + } + + mx = (pthread_mutex_t) calloc (1, sizeof (*mx)); + + if (mx == NULL) + { + result = ENOMEM; + } + else + { + mx->lock_idx = 0; + mx->recursive_count = 0; + mx->kind = (attr == NULL || *attr == NULL + ? PTHREAD_MUTEX_DEFAULT : (*attr)->kind); + mx->ownerThread.p = NULL; + + mx->event = CreateEvent (NULL, PTW32_FALSE, /* manual reset = No */ + PTW32_FALSE, /* initial state = not signaled */ + NULL); /* event name */ + + if (0 == mx->event) + { + result = ENOSPC; + free (mx); + mx = NULL; + } + } + + *mutex = mx; + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_mutex_lock.c b/pcsx2/windows/pthreads/pthread_mutex_lock.c new file mode 100644 index 0000000000..2e4da24dd7 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutex_lock.c @@ -0,0 +1,139 @@ +/* + * pthread_mutex_lock.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef _UWIN +//# include +#endif +#include "pthread.h" +#include "implement.h" + +int +pthread_mutex_lock (pthread_mutex_t * mutex) +{ + int result = 0; + pthread_mutex_t mx; + + /* + * Let the system deal with invalid pointers. + */ + if (*mutex == NULL) + { + return EINVAL; + } + + /* + * We do a quick check to see if we need to do more work + * to initialise a static mutex. We check + * again inside the guarded section of ptw32_mutex_check_need_init() + * to avoid race conditions. + */ + if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) + { + if ((result = ptw32_mutex_check_need_init (mutex)) != 0) + { + return (result); + } + } + + mx = *mutex; + + if (mx->kind == PTHREAD_MUTEX_NORMAL) + { + if ((LONG) PTW32_INTERLOCKED_EXCHANGE( + (LPLONG) &mx->lock_idx, + (LONG) 1) != 0) + { + while ((LONG) PTW32_INTERLOCKED_EXCHANGE( + (LPLONG) &mx->lock_idx, + (LONG) -1) != 0) + { + if (WAIT_OBJECT_0 != WaitForSingleObject (mx->event, INFINITE)) + { + result = EINVAL; + break; + } + } + } + } + else + { + pthread_t self = pthread_self(); + + if ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_COMPARE_EXCHANGE( + (PTW32_INTERLOCKED_LPLONG) &mx->lock_idx, + (PTW32_INTERLOCKED_LONG) 1, + (PTW32_INTERLOCKED_LONG) 0) == 0) + { + mx->recursive_count = 1; + mx->ownerThread = self; + } + else + { + if (pthread_equal (mx->ownerThread, self)) + { + if (mx->kind == PTHREAD_MUTEX_RECURSIVE) + { + mx->recursive_count++; + } + else + { + result = EDEADLK; + } + } + else + { + while ((LONG) PTW32_INTERLOCKED_EXCHANGE( + (LPLONG) &mx->lock_idx, + (LONG) -1) != 0) + { + if (WAIT_OBJECT_0 != WaitForSingleObject (mx->event, INFINITE)) + { + result = EINVAL; + break; + } + } + + if (0 == result) + { + mx->recursive_count = 1; + mx->ownerThread = self; + } + } + } + } + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_mutex_timedlock.c b/pcsx2/windows/pthreads/pthread_mutex_timedlock.c new file mode 100644 index 0000000000..ad373ba419 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutex_timedlock.c @@ -0,0 +1,196 @@ +/* + * pthread_mutex_timedlock.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +static INLINE int +ptw32_timed_eventwait (HANDLE event, const struct timespec *abstime) + /* + * ------------------------------------------------------ + * DESCRIPTION + * This function waits on an event until signaled or until + * abstime passes. + * If abstime has passed when this routine is called then + * it returns a result to indicate this. + * + * If 'abstime' is a NULL pointer then this function will + * block until it can successfully decrease the value or + * until interrupted by a signal. + * + * This routine is not a cancelation point. + * + * RESULTS + * 0 successfully signaled, + * ETIMEDOUT abstime passed + * EINVAL 'event' is not a valid event, + * + * ------------------------------------------------------ + */ +{ + + DWORD milliseconds; + DWORD status; + + if (event == NULL) + { + return EINVAL; + } + else + { + if (abstime == NULL) + { + milliseconds = INFINITE; + } + else + { + /* + * Calculate timeout as milliseconds from current system time. + */ + milliseconds = ptw32_relmillisecs (abstime); + } + + status = WaitForSingleObject (event, milliseconds); + + if (status == WAIT_OBJECT_0) + { + return 0; + } + else if (status == WAIT_TIMEOUT) + { + return ETIMEDOUT; + } + else + { + return EINVAL; + } + } + + return 0; + +} /* ptw32_timed_semwait */ + + +int +pthread_mutex_timedlock (pthread_mutex_t * mutex, + const struct timespec *abstime) +{ + int result; + pthread_mutex_t mx; + + /* + * Let the system deal with invalid pointers. + */ + + /* + * We do a quick check to see if we need to do more work + * to initialise a static mutex. We check + * again inside the guarded section of ptw32_mutex_check_need_init() + * to avoid race conditions. + */ + if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) + { + if ((result = ptw32_mutex_check_need_init (mutex)) != 0) + { + return (result); + } + } + + mx = *mutex; + + if (mx->kind == PTHREAD_MUTEX_NORMAL) + { + if ((LONG) PTW32_INTERLOCKED_EXCHANGE( + (LPLONG) &mx->lock_idx, + (LONG) 1) != 0) + { + while ((LONG) PTW32_INTERLOCKED_EXCHANGE( + (LPLONG) &mx->lock_idx, + (LONG) -1) != 0) + { + if (0 != (result = ptw32_timed_eventwait (mx->event, abstime))) + { + return result; + } + } + } + } + else + { + pthread_t self = pthread_self(); + + if ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_COMPARE_EXCHANGE( + (PTW32_INTERLOCKED_LPLONG) &mx->lock_idx, + (PTW32_INTERLOCKED_LONG) 1, + (PTW32_INTERLOCKED_LONG) 0) == 0) + { + mx->recursive_count = 1; + mx->ownerThread = self; + } + else + { + if (pthread_equal (mx->ownerThread, self)) + { + if (mx->kind == PTHREAD_MUTEX_RECURSIVE) + { + mx->recursive_count++; + } + else + { + return EDEADLK; + } + } + else + { + while ((LONG) PTW32_INTERLOCKED_EXCHANGE( + (LPLONG) &mx->lock_idx, + (LONG) -1) != 0) + { + if (0 != (result = ptw32_timed_eventwait (mx->event, abstime))) + { + return result; + } + } + + mx->recursive_count = 1; + mx->ownerThread = self; + } + } + } + + return 0; +} diff --git a/pcsx2/windows/pthreads/pthread_mutex_trylock.c b/pcsx2/windows/pthreads/pthread_mutex_trylock.c new file mode 100644 index 0000000000..048869c966 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutex_trylock.c @@ -0,0 +1,92 @@ +/* + * pthread_mutex_trylock.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_mutex_trylock (pthread_mutex_t * mutex) +{ + int result = 0; + pthread_mutex_t mx; + + /* + * Let the system deal with invalid pointers. + */ + + /* + * We do a quick check to see if we need to do more work + * to initialise a static mutex. We check + * again inside the guarded section of ptw32_mutex_check_need_init() + * to avoid race conditions. + */ + if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) + { + if ((result = ptw32_mutex_check_need_init (mutex)) != 0) + { + return (result); + } + } + + mx = *mutex; + + if (0 == (LONG) PTW32_INTERLOCKED_COMPARE_EXCHANGE ( + (PTW32_INTERLOCKED_LPLONG) &mx->lock_idx, + (PTW32_INTERLOCKED_LONG) 1, + (PTW32_INTERLOCKED_LONG) 0)) + { + if (mx->kind != PTHREAD_MUTEX_NORMAL) + { + mx->recursive_count = 1; + mx->ownerThread = pthread_self (); + } + } + else + { + if (mx->kind == PTHREAD_MUTEX_RECURSIVE && + pthread_equal (mx->ownerThread, pthread_self ())) + { + mx->recursive_count++; + } + else + { + result = EBUSY; + } + } + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_mutex_unlock.c b/pcsx2/windows/pthreads/pthread_mutex_unlock.c new file mode 100644 index 0000000000..ab8ec6500a --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutex_unlock.c @@ -0,0 +1,119 @@ +/* + * pthread_mutex_unlock.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_mutex_unlock (pthread_mutex_t * mutex) +{ + int result = 0; + pthread_mutex_t mx; + + /* + * Let the system deal with invalid pointers. + */ + + mx = *mutex; + + /* + * If the thread calling us holds the mutex then there is no + * race condition. If another thread holds the + * lock then we shouldn't be in here. + */ + if (mx < PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) + { + if (mx->kind == PTHREAD_MUTEX_NORMAL) + { + LONG idx; + + idx = (LONG) PTW32_INTERLOCKED_EXCHANGE ((LPLONG) &mx->lock_idx, + (LONG) 0); + if (idx != 0) + { + if (idx < 0) + { + /* + * Someone may be waiting on that mutex. + */ + if (SetEvent (mx->event) == 0) + { + result = EINVAL; + } + } + } + else + { + /* + * Was not locked (so can't be owned by us). + */ + result = EPERM; + } + } + else + { + if (pthread_equal (mx->ownerThread, pthread_self ())) + { + if (mx->kind != PTHREAD_MUTEX_RECURSIVE + || 0 == --mx->recursive_count) + { + mx->ownerThread.p = NULL; + + if ((LONG) PTW32_INTERLOCKED_EXCHANGE ((LPLONG) &mx->lock_idx, + (LONG) 0) < 0) + { + /* Someone may be waiting on that mutex */ + if (SetEvent (mx->event) == 0) + { + result = EINVAL; + } + } + } + } + else + { + result = EPERM; + } + } + } + else + { + result = EINVAL; + } + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_mutexattr_destroy.c b/pcsx2/windows/pthreads/pthread_mutexattr_destroy.c new file mode 100644 index 0000000000..5821f41396 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutexattr_destroy.c @@ -0,0 +1,83 @@ +/* + * pthread_mutexattr_destroy.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_mutexattr_destroy (pthread_mutexattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Destroys a mutex attributes object. The object can + * no longer be used. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * + * DESCRIPTION + * Destroys a mutex attributes object. The object can + * no longer be used. + * + * NOTES: + * 1) Does not affect mutexes created using 'attr' + * + * RESULTS + * 0 successfully released attr, + * EINVAL 'attr' is invalid. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + + if (attr == NULL || *attr == NULL) + { + result = EINVAL; + } + else + { + pthread_mutexattr_t ma = *attr; + + *attr = NULL; + free (ma); + } + + return (result); +} /* pthread_mutexattr_destroy */ diff --git a/pcsx2/windows/pthreads/pthread_mutexattr_getkind_np.c b/pcsx2/windows/pthreads/pthread_mutexattr_getkind_np.c new file mode 100644 index 0000000000..51508fe753 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutexattr_getkind_np.c @@ -0,0 +1,44 @@ +/* + * pthread_mutexattr_getkind_np.c + * + * Description: + * This translation unit implements non-portable thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +int +pthread_mutexattr_getkind_np (pthread_mutexattr_t * attr, int *kind) +{ + return pthread_mutexattr_gettype (attr, kind); +} diff --git a/pcsx2/windows/pthreads/pthread_mutexattr_getpshared.c b/pcsx2/windows/pthreads/pthread_mutexattr_getpshared.c new file mode 100644 index 0000000000..64d195254e --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutexattr_getpshared.c @@ -0,0 +1,95 @@ +/* + * pthread_mutexattr_getpshared.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_mutexattr_getpshared (const pthread_mutexattr_t * attr, int *pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Determine whether mutexes created with 'attr' can be + * shared between processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * pshared + * will be set to one of: + * + * PTHREAD_PROCESS_SHARED + * May be shared if in shared memory + * + * PTHREAD_PROCESS_PRIVATE + * Cannot be shared. + * + * + * DESCRIPTION + * Mutexes creatd with 'attr' can be shared between + * processes if pthread_mutex_t variable is allocated + * in memory shared by these processes. + * NOTES: + * 1) pshared mutexes MUST be allocated in shared + * memory. + * 2) The following macro is defined if shared mutexes + * are supported: + * _POSIX_THREAD_PROCESS_SHARED + * + * RESULTS + * 0 successfully retrieved attribute, + * EINVAL 'attr' is invalid, + * + * ------------------------------------------------------ + */ +{ + int result; + + if ((attr != NULL && *attr != NULL) && (pshared != NULL)) + { + *pshared = (*attr)->pshared; + result = 0; + } + else + { + result = EINVAL; + } + + return (result); + +} /* pthread_mutexattr_getpshared */ diff --git a/pcsx2/windows/pthreads/pthread_mutexattr_gettype.c b/pcsx2/windows/pthreads/pthread_mutexattr_gettype.c new file mode 100644 index 0000000000..f05bb68fc0 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutexattr_gettype.c @@ -0,0 +1,56 @@ +/* + * pthread_mutexattr_gettype.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_mutexattr_gettype (pthread_mutexattr_t * attr, int *kind) +{ + int result = 0; + + if (attr != NULL && *attr != NULL && kind != NULL) + { + *kind = (*attr)->kind; + } + else + { + result = EINVAL; + } + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_mutexattr_init.c b/pcsx2/windows/pthreads/pthread_mutexattr_init.c new file mode 100644 index 0000000000..3e7a1a27e9 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutexattr_init.c @@ -0,0 +1,86 @@ +/* + * pthread_mutexattr_init.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_mutexattr_init (pthread_mutexattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Initializes a mutex attributes object with default + * attributes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * + * DESCRIPTION + * Initializes a mutex attributes object with default + * attributes. + * + * NOTES: + * 1) Used to define mutex types + * + * RESULTS + * 0 successfully initialized attr, + * ENOMEM insufficient memory for attr. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + pthread_mutexattr_t ma; + + ma = (pthread_mutexattr_t) calloc (1, sizeof (*ma)); + + if (ma == NULL) + { + result = ENOMEM; + } + else + { + ma->pshared = PTHREAD_PROCESS_PRIVATE; + ma->kind = PTHREAD_MUTEX_DEFAULT; + } + + *attr = ma; + + return (result); +} /* pthread_mutexattr_init */ diff --git a/pcsx2/windows/pthreads/pthread_mutexattr_setkind_np.c b/pcsx2/windows/pthreads/pthread_mutexattr_setkind_np.c new file mode 100644 index 0000000000..25549ddea9 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutexattr_setkind_np.c @@ -0,0 +1,44 @@ +/* + * pthread_mutexattr_setkind_np.c + * + * Description: + * This translation unit implements non-portable thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +int +pthread_mutexattr_setkind_np (pthread_mutexattr_t * attr, int kind) +{ + return pthread_mutexattr_settype (attr, kind); +} diff --git a/pcsx2/windows/pthreads/pthread_mutexattr_setpshared.c b/pcsx2/windows/pthreads/pthread_mutexattr_setpshared.c new file mode 100644 index 0000000000..7ca78d10b4 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutexattr_setpshared.c @@ -0,0 +1,119 @@ +/* + * pthread_mutexattr_setpshared.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, int pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Mutexes created with 'attr' can be shared between + * processes if pthread_mutex_t variable is allocated + * in memory shared by these processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * pshared + * must be one of: + * + * PTHREAD_PROCESS_SHARED + * May be shared if in shared memory + * + * PTHREAD_PROCESS_PRIVATE + * Cannot be shared. + * + * DESCRIPTION + * Mutexes creatd with 'attr' can be shared between + * processes if pthread_mutex_t variable is allocated + * in memory shared by these processes. + * + * NOTES: + * 1) pshared mutexes MUST be allocated in shared + * memory. + * + * 2) The following macro is defined if shared mutexes + * are supported: + * _POSIX_THREAD_PROCESS_SHARED + * + * RESULTS + * 0 successfully set attribute, + * EINVAL 'attr' or pshared is invalid, + * ENOSYS PTHREAD_PROCESS_SHARED not supported, + * + * ------------------------------------------------------ + */ +{ + int result; + + if ((attr != NULL && *attr != NULL) && + ((pshared == PTHREAD_PROCESS_SHARED) || + (pshared == PTHREAD_PROCESS_PRIVATE))) + { + if (pshared == PTHREAD_PROCESS_SHARED) + { + +#if !defined( _POSIX_THREAD_PROCESS_SHARED ) + + result = ENOSYS; + pshared = PTHREAD_PROCESS_PRIVATE; + +#else + + result = 0; + +#endif /* _POSIX_THREAD_PROCESS_SHARED */ + + } + else + { + result = 0; + } + + (*attr)->pshared = pshared; + } + else + { + result = EINVAL; + } + + return (result); + +} /* pthread_mutexattr_setpshared */ diff --git a/pcsx2/windows/pthreads/pthread_mutexattr_settype.c b/pcsx2/windows/pthreads/pthread_mutexattr_settype.c new file mode 100644 index 0000000000..62f77ba2ae --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_mutexattr_settype.c @@ -0,0 +1,143 @@ +/* + * pthread_mutexattr_settype.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind) + /* + * ------------------------------------------------------ + * + * DOCPUBLIC + * The pthread_mutexattr_settype() and + * pthread_mutexattr_gettype() functions respectively set and + * get the mutex type attribute. This attribute is set in the + * type parameter to these functions. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * type + * must be one of: + * + * PTHREAD_MUTEX_DEFAULT + * + * PTHREAD_MUTEX_NORMAL + * + * PTHREAD_MUTEX_ERRORCHECK + * + * PTHREAD_MUTEX_RECURSIVE + * + * DESCRIPTION + * The pthread_mutexattr_settype() and + * pthread_mutexattr_gettype() functions respectively set and + * get the mutex type attribute. This attribute is set in the + * type parameter to these functions. The default value of the + * type attribute is PTHREAD_MUTEX_DEFAULT. + * + * The type of mutex is contained in the type attribute of the + * mutex attributes. Valid mutex types include: + * + * PTHREAD_MUTEX_NORMAL + * This type of mutex does not detect deadlock. A + * thread attempting to relock this mutex without + * first unlocking it will deadlock. Attempting to + * unlock a mutex locked by a different thread + * results in undefined behavior. Attempting to + * unlock an unlocked mutex results in undefined + * behavior. + * + * PTHREAD_MUTEX_ERRORCHECK + * This type of mutex provides error checking. A + * thread attempting to relock this mutex without + * first unlocking it will return with an error. A + * thread attempting to unlock a mutex which another + * thread has locked will return with an error. A + * thread attempting to unlock an unlocked mutex will + * return with an error. + * + * PTHREAD_MUTEX_DEFAULT + * Same as PTHREAD_MUTEX_NORMAL. + * + * PTHREAD_MUTEX_RECURSIVE + * A thread attempting to relock this mutex without + * first unlocking it will succeed in locking the + * mutex. The relocking deadlock which can occur with + * mutexes of type PTHREAD_MUTEX_NORMAL cannot occur + * with this type of mutex. Multiple locks of this + * mutex require the same number of unlocks to + * release the mutex before another thread can + * acquire the mutex. A thread attempting to unlock a + * mutex which another thread has locked will return + * with an error. A thread attempting to unlock an + * unlocked mutex will return with an error. This + * type of mutex is only supported for mutexes whose + * process shared attribute is + * PTHREAD_PROCESS_PRIVATE. + * + * RESULTS + * 0 successfully set attribute, + * EINVAL 'attr' or 'type' is invalid, + * + * ------------------------------------------------------ + */ +{ + int result = 0; + + if ((attr != NULL && *attr != NULL)) + { + switch (kind) + { + case PTHREAD_MUTEX_FAST_NP: + case PTHREAD_MUTEX_RECURSIVE_NP: + case PTHREAD_MUTEX_ERRORCHECK_NP: + (*attr)->kind = kind; + break; + default: + result = EINVAL; + break; + } + } + else + { + result = EINVAL; + } + + return (result); +} /* pthread_mutexattr_settype */ diff --git a/pcsx2/windows/pthreads/pthread_num_processors_np.c b/pcsx2/windows/pthreads/pthread_num_processors_np.c new file mode 100644 index 0000000000..897a1b260a --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_num_processors_np.c @@ -0,0 +1,56 @@ +/* + * pthread_num_processors_np.c + * + * Description: + * This translation unit implements non-portable thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* + * pthread_num_processors_np() + * + * Get the number of CPUs available to the process. + */ +int +pthread_num_processors_np (void) +{ + int count; + + if (ptw32_getprocessors (&count) != 0) + { + count = 1; + } + + return (count); +} diff --git a/pcsx2/windows/pthreads/pthread_once.c b/pcsx2/windows/pthreads/pthread_once.c new file mode 100644 index 0000000000..19e63b4124 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_once.c @@ -0,0 +1,86 @@ +/* + * pthread_once.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +static void PTW32_CDECL +ptw32_once_on_init_cancel (void * arg) +{ + /* when the initting thread is cancelled we have to release the lock */ + ptw32_mcs_local_node_t *node = (ptw32_mcs_local_node_t *)arg; + ptw32_mcs_lock_release(node); +} + +int +pthread_once (pthread_once_t * once_control, void (*init_routine) (void)) +{ + if (once_control == NULL || init_routine == NULL) + { + return EINVAL; + } + + if (!InterlockedExchangeAdd((LPLONG)&once_control->done, 0)) /* MBR fence */ + { + ptw32_mcs_local_node_t node; + + ptw32_mcs_lock_acquire((ptw32_mcs_lock_t *)&once_control->lock, &node); + + if (!once_control->done) + { + +#ifdef _MSC_VER +#pragma inline_depth(0) +#endif + + pthread_cleanup_push(ptw32_once_on_init_cancel, (void *)&node); + (*init_routine)(); + pthread_cleanup_pop(0); + +#ifdef _MSC_VER +#pragma inline_depth() +#endif + + once_control->done = PTW32_TRUE; + } + + ptw32_mcs_lock_release(&node); + } + + return 0; + +} /* pthread_once */ diff --git a/pcsx2/windows/pthreads/pthread_rwlock_destroy.c b/pcsx2/windows/pthreads/pthread_rwlock_destroy.c new file mode 100644 index 0000000000..7b6a6deb68 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlock_destroy.c @@ -0,0 +1,143 @@ +/* + * pthread_rwlock_destroy.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlock_destroy (pthread_rwlock_t * rwlock) +{ + pthread_rwlock_t rwl; + int result = 0, result1 = 0, result2 = 0; + + if (rwlock == NULL || *rwlock == NULL) + { + return EINVAL; + } + + if (*rwlock != PTHREAD_RWLOCK_INITIALIZER) + { + rwl = *rwlock; + + if (rwl->nMagic != PTW32_RWLOCK_MAGIC) + { + return EINVAL; + } + + if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0) + { + return result; + } + + if ((result = + pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return result; + } + + /* + * Check whether any threads own/wait for the lock (wait for ex.access); + * report "BUSY" if so. + */ + if (rwl->nExclusiveAccessCount > 0 + || rwl->nSharedAccessCount > rwl->nCompletedSharedAccessCount) + { + result = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted)); + result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + result2 = EBUSY; + } + else + { + rwl->nMagic = 0; + + if ((result = + pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + pthread_mutex_unlock (&rwl->mtxExclusiveAccess); + return result; + } + + if ((result = + pthread_mutex_unlock (&(rwl->mtxExclusiveAccess))) != 0) + { + return result; + } + + *rwlock = NULL; /* Invalidate rwlock before anything else */ + result = pthread_cond_destroy (&(rwl->cndSharedAccessCompleted)); + result1 = pthread_mutex_destroy (&(rwl->mtxSharedAccessCompleted)); + result2 = pthread_mutex_destroy (&(rwl->mtxExclusiveAccess)); + (void) free (rwl); + } + } + else + { + /* + * See notes in ptw32_rwlock_check_need_init() above also. + */ + EnterCriticalSection (&ptw32_rwlock_test_init_lock); + + /* + * Check again. + */ + if (*rwlock == PTHREAD_RWLOCK_INITIALIZER) + { + /* + * This is all we need to do to destroy a statically + * initialised rwlock that has not yet been used (initialised). + * If we get to here, another thread + * waiting to initialise this rwlock will get an EINVAL. + */ + *rwlock = NULL; + } + else + { + /* + * The rwlock has been initialised while we were waiting + * so assume it's in use. + */ + result = EBUSY; + } + + LeaveCriticalSection (&ptw32_rwlock_test_init_lock); + } + + return ((result != 0) ? result : ((result1 != 0) ? result1 : result2)); +} diff --git a/pcsx2/windows/pthreads/pthread_rwlock_init.c b/pcsx2/windows/pthreads/pthread_rwlock_init.c new file mode 100644 index 0000000000..476fe25ac7 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlock_init.c @@ -0,0 +1,115 @@ +/* + * pthread_rwlock_init.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlock_init (pthread_rwlock_t * rwlock, + const pthread_rwlockattr_t * attr) +{ + int result; + pthread_rwlock_t rwl = 0; + + if (rwlock == NULL) + { + return EINVAL; + } + +#ifdef PTW32_STATIC_LIB + // This allos for C++ static initializers to function without crashes. (air) + pthread_win32_process_attach_np(); +#endif + + if (attr != NULL && *attr != NULL) + { + result = EINVAL; /* Not supported */ + goto DONE; + } + + rwl = (pthread_rwlock_t) calloc (1, sizeof (*rwl)); + + if (rwl == NULL) + { + result = ENOMEM; + goto DONE; + } + + rwl->nSharedAccessCount = 0; + rwl->nExclusiveAccessCount = 0; + rwl->nCompletedSharedAccessCount = 0; + + result = pthread_mutex_init (&rwl->mtxExclusiveAccess, NULL); + if (result != 0) + { + goto FAIL0; + } + + result = pthread_mutex_init (&rwl->mtxSharedAccessCompleted, NULL); + if (result != 0) + { + goto FAIL1; + } + + result = pthread_cond_init (&rwl->cndSharedAccessCompleted, NULL); + if (result != 0) + { + goto FAIL2; + } + + rwl->nMagic = PTW32_RWLOCK_MAGIC; + + result = 0; + goto DONE; + +FAIL2: + (void) pthread_mutex_destroy (&(rwl->mtxSharedAccessCompleted)); + +FAIL1: + (void) pthread_mutex_destroy (&(rwl->mtxExclusiveAccess)); + +FAIL0: + (void) free (rwl); + rwl = NULL; + +DONE: + *rwlock = rwl; + + return result; +} diff --git a/pcsx2/windows/pthreads/pthread_rwlock_rdlock.c b/pcsx2/windows/pthreads/pthread_rwlock_rdlock.c new file mode 100644 index 0000000000..3c493ea975 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlock_rdlock.c @@ -0,0 +1,103 @@ +/* + * pthread_rwlock_rdlock.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlock_rdlock (pthread_rwlock_t * rwlock) +{ + int result; + pthread_rwlock_t rwl; + + if (rwlock == NULL || *rwlock == NULL) + { + return EINVAL; + } + + /* + * We do a quick check to see if we need to do more work + * to initialise a static rwlock. We check + * again inside the guarded section of ptw32_rwlock_check_need_init() + * to avoid race conditions. + */ + if (*rwlock == PTHREAD_RWLOCK_INITIALIZER) + { + result = ptw32_rwlock_check_need_init (rwlock); + + if (result != 0 && result != EBUSY) + { + return result; + } + } + + rwl = *rwlock; + + if (rwl->nMagic != PTW32_RWLOCK_MAGIC) + { + return EINVAL; + } + + if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0) + { + return result; + } + + if (++rwl->nSharedAccessCount == INT_MAX) + { + if ((result = + pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return result; + } + + rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount; + rwl->nCompletedSharedAccessCount = 0; + + if ((result = + pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return result; + } + } + + return (pthread_mutex_unlock (&(rwl->mtxExclusiveAccess))); +} diff --git a/pcsx2/windows/pthreads/pthread_rwlock_timedrdlock.c b/pcsx2/windows/pthreads/pthread_rwlock_timedrdlock.c new file mode 100644 index 0000000000..92373eaf8f --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlock_timedrdlock.c @@ -0,0 +1,110 @@ +/* + * pthread_rwlock_timedrdlock.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlock_timedrdlock (pthread_rwlock_t * rwlock, + const struct timespec *abstime) +{ + int result; + pthread_rwlock_t rwl; + + if (rwlock == NULL || *rwlock == NULL) + { + return EINVAL; + } + + /* + * We do a quick check to see if we need to do more work + * to initialise a static rwlock. We check + * again inside the guarded section of ptw32_rwlock_check_need_init() + * to avoid race conditions. + */ + if (*rwlock == PTHREAD_RWLOCK_INITIALIZER) + { + result = ptw32_rwlock_check_need_init (rwlock); + + if (result != 0 && result != EBUSY) + { + return result; + } + } + + rwl = *rwlock; + + if (rwl->nMagic != PTW32_RWLOCK_MAGIC) + { + return EINVAL; + } + + if ((result = + pthread_mutex_timedlock (&(rwl->mtxExclusiveAccess), abstime)) != 0) + { + return result; + } + + if (++rwl->nSharedAccessCount == INT_MAX) + { + if ((result = + pthread_mutex_timedlock (&(rwl->mtxSharedAccessCompleted), + abstime)) != 0) + { + if (result == ETIMEDOUT) + { + ++rwl->nCompletedSharedAccessCount; + } + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return result; + } + + rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount; + rwl->nCompletedSharedAccessCount = 0; + + if ((result = + pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return result; + } + } + + return (pthread_mutex_unlock (&(rwl->mtxExclusiveAccess))); +} diff --git a/pcsx2/windows/pthreads/pthread_rwlock_timedwrlock.c b/pcsx2/windows/pthreads/pthread_rwlock_timedwrlock.c new file mode 100644 index 0000000000..ca5e53728d --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlock_timedwrlock.c @@ -0,0 +1,140 @@ +/* + * pthread_rwlock_timedwrlock.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlock_timedwrlock (pthread_rwlock_t * rwlock, + const struct timespec *abstime) +{ + int result; + pthread_rwlock_t rwl; + + if (rwlock == NULL || *rwlock == NULL) + { + return EINVAL; + } + + /* + * We do a quick check to see if we need to do more work + * to initialise a static rwlock. We check + * again inside the guarded section of ptw32_rwlock_check_need_init() + * to avoid race conditions. + */ + if (*rwlock == PTHREAD_RWLOCK_INITIALIZER) + { + result = ptw32_rwlock_check_need_init (rwlock); + + if (result != 0 && result != EBUSY) + { + return result; + } + } + + rwl = *rwlock; + + if (rwl->nMagic != PTW32_RWLOCK_MAGIC) + { + return EINVAL; + } + + if ((result = + pthread_mutex_timedlock (&(rwl->mtxExclusiveAccess), abstime)) != 0) + { + return result; + } + + if ((result = + pthread_mutex_timedlock (&(rwl->mtxSharedAccessCompleted), + abstime)) != 0) + { + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return result; + } + + if (rwl->nExclusiveAccessCount == 0) + { + if (rwl->nCompletedSharedAccessCount > 0) + { + rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount; + rwl->nCompletedSharedAccessCount = 0; + } + + if (rwl->nSharedAccessCount > 0) + { + rwl->nCompletedSharedAccessCount = -rwl->nSharedAccessCount; + + /* + * This routine may be a cancelation point + * according to POSIX 1003.1j section 18.1.2. + */ +#ifdef _MSC_VER +#pragma inline_depth(0) +#endif + pthread_cleanup_push (ptw32_rwlock_cancelwrwait, (void *) rwl); + + do + { + result = + pthread_cond_timedwait (&(rwl->cndSharedAccessCompleted), + &(rwl->mtxSharedAccessCompleted), + abstime); + } + while (result == 0 && rwl->nCompletedSharedAccessCount < 0); + + pthread_cleanup_pop ((result != 0) ? 1 : 0); +#ifdef _MSC_VER +#pragma inline_depth() +#endif + + if (result == 0) + { + rwl->nSharedAccessCount = 0; + } + } + } + + if (result == 0) + { + rwl->nExclusiveAccessCount++; + } + + return result; +} diff --git a/pcsx2/windows/pthreads/pthread_rwlock_tryrdlock.c b/pcsx2/windows/pthreads/pthread_rwlock_tryrdlock.c new file mode 100644 index 0000000000..fbca91043d --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlock_tryrdlock.c @@ -0,0 +1,103 @@ +/* + * pthread_rwlock_tryrdlock.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlock_tryrdlock (pthread_rwlock_t * rwlock) +{ + int result; + pthread_rwlock_t rwl; + + if (rwlock == NULL || *rwlock == NULL) + { + return EINVAL; + } + + /* + * We do a quick check to see if we need to do more work + * to initialise a static rwlock. We check + * again inside the guarded section of ptw32_rwlock_check_need_init() + * to avoid race conditions. + */ + if (*rwlock == PTHREAD_RWLOCK_INITIALIZER) + { + result = ptw32_rwlock_check_need_init (rwlock); + + if (result != 0 && result != EBUSY) + { + return result; + } + } + + rwl = *rwlock; + + if (rwl->nMagic != PTW32_RWLOCK_MAGIC) + { + return EINVAL; + } + + if ((result = pthread_mutex_trylock (&(rwl->mtxExclusiveAccess))) != 0) + { + return result; + } + + if (++rwl->nSharedAccessCount == INT_MAX) + { + if ((result = + pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return result; + } + + rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount; + rwl->nCompletedSharedAccessCount = 0; + + if ((result = + pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return result; + } + } + + return (pthread_mutex_unlock (&rwl->mtxExclusiveAccess)); +} diff --git a/pcsx2/windows/pthreads/pthread_rwlock_trywrlock.c b/pcsx2/windows/pthreads/pthread_rwlock_trywrlock.c new file mode 100644 index 0000000000..e17adb38c3 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlock_trywrlock.c @@ -0,0 +1,123 @@ +/* + * pthread_rwlock_trywrlock.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlock_trywrlock (pthread_rwlock_t * rwlock) +{ + int result, result1; + pthread_rwlock_t rwl; + + if (rwlock == NULL || *rwlock == NULL) + { + return EINVAL; + } + + /* + * We do a quick check to see if we need to do more work + * to initialise a static rwlock. We check + * again inside the guarded section of ptw32_rwlock_check_need_init() + * to avoid race conditions. + */ + if (*rwlock == PTHREAD_RWLOCK_INITIALIZER) + { + result = ptw32_rwlock_check_need_init (rwlock); + + if (result != 0 && result != EBUSY) + { + return result; + } + } + + rwl = *rwlock; + + if (rwl->nMagic != PTW32_RWLOCK_MAGIC) + { + return EINVAL; + } + + if ((result = pthread_mutex_trylock (&(rwl->mtxExclusiveAccess))) != 0) + { + return result; + } + + if ((result = + pthread_mutex_trylock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return ((result1 != 0) ? result1 : result); + } + + if (rwl->nExclusiveAccessCount == 0) + { + if (rwl->nCompletedSharedAccessCount > 0) + { + rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount; + rwl->nCompletedSharedAccessCount = 0; + } + + if (rwl->nSharedAccessCount > 0) + { + if ((result = + pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return result; + } + + if ((result = + pthread_mutex_unlock (&(rwl->mtxExclusiveAccess))) == 0) + { + result = EBUSY; + } + } + else + { + rwl->nExclusiveAccessCount = 1; + } + } + else + { + result = EBUSY; + } + + return result; +} diff --git a/pcsx2/windows/pthreads/pthread_rwlock_unlock.c b/pcsx2/windows/pthreads/pthread_rwlock_unlock.c new file mode 100644 index 0000000000..2a5c84b892 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlock_unlock.c @@ -0,0 +1,94 @@ +/* + * pthread_rwlock_unlock.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlock_unlock (pthread_rwlock_t * rwlock) +{ + int result, result1; + pthread_rwlock_t rwl; + + if (rwlock == NULL || *rwlock == NULL) + { + return (EINVAL); + } + + if (*rwlock == PTHREAD_RWLOCK_INITIALIZER) + { + /* + * Assume any race condition here is harmless. + */ + return 0; + } + + rwl = *rwlock; + + if (rwl->nMagic != PTW32_RWLOCK_MAGIC) + { + return EINVAL; + } + + if (rwl->nExclusiveAccessCount == 0) + { + if ((result = + pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + return result; + } + + if (++rwl->nCompletedSharedAccessCount == 0) + { + result = pthread_cond_signal (&(rwl->cndSharedAccessCompleted)); + } + + result1 = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted)); + } + else + { + rwl->nExclusiveAccessCount--; + + result = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted)); + result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + + } + + return ((result != 0) ? result : result1); +} diff --git a/pcsx2/windows/pthreads/pthread_rwlock_wrlock.c b/pcsx2/windows/pthreads/pthread_rwlock_wrlock.c new file mode 100644 index 0000000000..789c5aa00e --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlock_wrlock.c @@ -0,0 +1,134 @@ +/* + * pthread_rwlock_wrlock.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlock_wrlock (pthread_rwlock_t * rwlock) +{ + int result; + pthread_rwlock_t rwl; + + if (rwlock == NULL || *rwlock == NULL) + { + return EINVAL; + } + + /* + * We do a quick check to see if we need to do more work + * to initialise a static rwlock. We check + * again inside the guarded section of ptw32_rwlock_check_need_init() + * to avoid race conditions. + */ + if (*rwlock == PTHREAD_RWLOCK_INITIALIZER) + { + result = ptw32_rwlock_check_need_init (rwlock); + + if (result != 0 && result != EBUSY) + { + return result; + } + } + + rwl = *rwlock; + + if (rwl->nMagic != PTW32_RWLOCK_MAGIC) + { + return EINVAL; + } + + if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0) + { + return result; + } + + if ((result = pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0) + { + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); + return result; + } + + if (rwl->nExclusiveAccessCount == 0) + { + if (rwl->nCompletedSharedAccessCount > 0) + { + rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount; + rwl->nCompletedSharedAccessCount = 0; + } + + if (rwl->nSharedAccessCount > 0) + { + rwl->nCompletedSharedAccessCount = -rwl->nSharedAccessCount; + + /* + * This routine may be a cancelation point + * according to POSIX 1003.1j section 18.1.2. + */ +#ifdef _MSC_VER +#pragma inline_depth(0) +#endif + pthread_cleanup_push (ptw32_rwlock_cancelwrwait, (void *) rwl); + + do + { + result = pthread_cond_wait (&(rwl->cndSharedAccessCompleted), + &(rwl->mtxSharedAccessCompleted)); + } + while (result == 0 && rwl->nCompletedSharedAccessCount < 0); + + pthread_cleanup_pop ((result != 0) ? 1 : 0); +#ifdef _MSC_VER +#pragma inline_depth() +#endif + + if (result == 0) + { + rwl->nSharedAccessCount = 0; + } + } + } + + if (result == 0) + { + rwl->nExclusiveAccessCount++; + } + + return result; +} diff --git a/pcsx2/windows/pthreads/pthread_rwlockattr_destroy.c b/pcsx2/windows/pthreads/pthread_rwlockattr_destroy.c new file mode 100644 index 0000000000..c1971b5a15 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlockattr_destroy.c @@ -0,0 +1,85 @@ +/* + * pthread_rwlockattr_destroy.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Destroys a rwlock attributes object. The object can + * no longer be used. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_rwlockattr_t + * + * + * DESCRIPTION + * Destroys a rwlock attributes object. The object can + * no longer be used. + * + * NOTES: + * 1) Does not affect rwlockss created using 'attr' + * + * RESULTS + * 0 successfully released attr, + * EINVAL 'attr' is invalid. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + + if (attr == NULL || *attr == NULL) + { + result = EINVAL; + } + else + { + pthread_rwlockattr_t rwa = *attr; + + *attr = NULL; + free (rwa); + } + + return (result); +} /* pthread_rwlockattr_destroy */ diff --git a/pcsx2/windows/pthreads/pthread_rwlockattr_getpshared.c b/pcsx2/windows/pthreads/pthread_rwlockattr_getpshared.c new file mode 100644 index 0000000000..098e925436 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlockattr_getpshared.c @@ -0,0 +1,98 @@ +/* + * pthread_rwlockattr_getpshared.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr, + int *pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Determine whether rwlocks created with 'attr' can be + * shared between processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_rwlockattr_t + * + * pshared + * will be set to one of: + * + * PTHREAD_PROCESS_SHARED + * May be shared if in shared memory + * + * PTHREAD_PROCESS_PRIVATE + * Cannot be shared. + * + * + * DESCRIPTION + * Rwlocks creatd with 'attr' can be shared between + * processes if pthread_rwlock_t variable is allocated + * in memory shared by these processes. + * NOTES: + * 1) pshared rwlocks MUST be allocated in shared + * memory. + * 2) The following macro is defined if shared rwlocks + * are supported: + * _POSIX_THREAD_PROCESS_SHARED + * + * RESULTS + * 0 successfully retrieved attribute, + * EINVAL 'attr' is invalid, + * + * ------------------------------------------------------ + */ +{ + int result; + + if ((attr != NULL && *attr != NULL) && (pshared != NULL)) + { + *pshared = (*attr)->pshared; + result = 0; + } + else + { + result = EINVAL; + } + + return (result); + +} /* pthread_rwlockattr_getpshared */ diff --git a/pcsx2/windows/pthreads/pthread_rwlockattr_init.c b/pcsx2/windows/pthreads/pthread_rwlockattr_init.c new file mode 100644 index 0000000000..31261185c5 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlockattr_init.c @@ -0,0 +1,84 @@ +/* + * pthread_rwlockattr_init.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlockattr_init (pthread_rwlockattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Initializes a rwlock attributes object with default + * attributes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_rwlockattr_t + * + * + * DESCRIPTION + * Initializes a rwlock attributes object with default + * attributes. + * + * RESULTS + * 0 successfully initialized attr, + * ENOMEM insufficient memory for attr. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + pthread_rwlockattr_t rwa; + + rwa = (pthread_rwlockattr_t) calloc (1, sizeof (*rwa)); + + if (rwa == NULL) + { + result = ENOMEM; + } + else + { + rwa->pshared = PTHREAD_PROCESS_PRIVATE; + } + + *attr = rwa; + + return (result); +} /* pthread_rwlockattr_init */ diff --git a/pcsx2/windows/pthreads/pthread_rwlockattr_setpshared.c b/pcsx2/windows/pthreads/pthread_rwlockattr_setpshared.c new file mode 100644 index 0000000000..b0c8160b13 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_rwlockattr_setpshared.c @@ -0,0 +1,121 @@ +/* + * pthread_rwlockattr_setpshared.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include + +#include "pthread.h" +#include "implement.h" + +int +pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr, int pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Rwlocks created with 'attr' can be shared between + * processes if pthread_rwlock_t variable is allocated + * in memory shared by these processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_rwlockattr_t + * + * pshared + * must be one of: + * + * PTHREAD_PROCESS_SHARED + * May be shared if in shared memory + * + * PTHREAD_PROCESS_PRIVATE + * Cannot be shared. + * + * DESCRIPTION + * Rwlocks creatd with 'attr' can be shared between + * processes if pthread_rwlock_t variable is allocated + * in memory shared by these processes. + * + * NOTES: + * 1) pshared rwlocks MUST be allocated in shared + * memory. + * + * 2) The following macro is defined if shared rwlocks + * are supported: + * _POSIX_THREAD_PROCESS_SHARED + * + * RESULTS + * 0 successfully set attribute, + * EINVAL 'attr' or pshared is invalid, + * ENOSYS PTHREAD_PROCESS_SHARED not supported, + * + * ------------------------------------------------------ + */ +{ + int result; + + if ((attr != NULL && *attr != NULL) && + ((pshared == PTHREAD_PROCESS_SHARED) || + (pshared == PTHREAD_PROCESS_PRIVATE))) + { + if (pshared == PTHREAD_PROCESS_SHARED) + { + +#if !defined( _POSIX_THREAD_PROCESS_SHARED ) + + result = ENOSYS; + pshared = PTHREAD_PROCESS_PRIVATE; + +#else + + result = 0; + +#endif /* _POSIX_THREAD_PROCESS_SHARED */ + + } + else + { + result = 0; + } + + (*attr)->pshared = pshared; + } + else + { + result = EINVAL; + } + + return (result); + +} /* pthread_rwlockattr_setpshared */ diff --git a/pcsx2/windows/pthreads/pthread_self.c b/pcsx2/windows/pthreads/pthread_self.c new file mode 100644 index 0000000000..fb2899cdac --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_self.c @@ -0,0 +1,138 @@ +/* + * pthread_self.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +pthread_t +pthread_self (void) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function returns a reference to the current running + * thread. + * + * PARAMETERS + * N/A + * + * + * DESCRIPTION + * This function returns a reference to the current running + * thread. + * + * RESULTS + * pthread_t reference to the current thread + * + * ------------------------------------------------------ + */ +{ + pthread_t self; + pthread_t nil = {NULL, 0}; + ptw32_thread_t * sp; + +#ifdef _UWIN + if (!ptw32_selfThreadKey) + return nil; +#endif + + sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); + + if (sp != NULL) + { + self = sp->ptHandle; + } + else + { + /* + * Need to create an implicit 'self' for the currently + * executing thread. + */ + self = ptw32_new (); + sp = (ptw32_thread_t *) self.p; + + if (sp != NULL) + { + /* + * This is a non-POSIX thread which has chosen to call + * a POSIX threads function for some reason. We assume that + * it isn't joinable, but we do assume that it's + * (deferred) cancelable. + */ + sp->implicit = 1; + sp->detachState = PTHREAD_CREATE_DETACHED; + sp->thread = GetCurrentThreadId (); + +#ifdef NEED_DUPLICATEHANDLE + /* + * DuplicateHandle does not exist on WinCE. + * + * NOTE: + * GetCurrentThread only returns a pseudo-handle + * which is only valid in the current thread context. + * Therefore, you should not pass the handle to + * other threads for whatever purpose. + */ + sp->threadH = GetCurrentThread (); +#else + if (!DuplicateHandle (GetCurrentProcess (), + GetCurrentThread (), + GetCurrentProcess (), + &sp->threadH, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + /* + * Should not do this, but we have no alternative if + * we can't get a Win32 thread handle. + * Thread structs are never freed. + */ + ptw32_threadReusePush (self); + return nil; + } +#endif + + /* + * No need to explicitly serialise access to sched_priority + * because the new handle is not yet public. + */ + sp->sched_priority = GetThreadPriority (sp->threadH); + + pthread_setspecific (ptw32_selfThreadKey, (void *) sp); + } + } + + return (self); + +} /* pthread_self */ diff --git a/pcsx2/windows/pthreads/pthread_setcancelstate.c b/pcsx2/windows/pthreads/pthread_setcancelstate.c new file mode 100644 index 0000000000..d3efaae082 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_setcancelstate.c @@ -0,0 +1,124 @@ +/* + * pthread_setcancelstate.c + * + * Description: + * POSIX thread functions related to thread cancellation. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_setcancelstate (int state, int *oldstate) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function atomically sets the calling thread's + * cancelability state to 'state' and returns the previous + * cancelability state at the location referenced by + * 'oldstate' + * + * PARAMETERS + * state, + * oldstate + * PTHREAD_CANCEL_ENABLE + * cancellation is enabled, + * + * PTHREAD_CANCEL_DISABLE + * cancellation is disabled + * + * + * DESCRIPTION + * This function atomically sets the calling thread's + * cancelability state to 'state' and returns the previous + * cancelability state at the location referenced by + * 'oldstate'. + * + * NOTES: + * 1) Use to disable cancellation around 'atomic' code that + * includes cancellation points + * + * COMPATIBILITY ADDITIONS + * If 'oldstate' is NULL then the previous state is not returned + * but the function still succeeds. (Solaris) + * + * RESULTS + * 0 successfully set cancelability type, + * EINVAL 'state' is invalid + * + * ------------------------------------------------------ + */ +{ + int result = 0; + pthread_t self = pthread_self (); + ptw32_thread_t * sp = (ptw32_thread_t *) self.p; + + if (sp == NULL + || (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE)) + { + return EINVAL; + } + + /* + * Lock for async-cancel safety. + */ + (void) pthread_mutex_lock (&sp->cancelLock); + + if (oldstate != NULL) + { + *oldstate = sp->cancelState; + } + + sp->cancelState = state; + + /* + * Check if there is a pending asynchronous cancel + */ + if (state == PTHREAD_CANCEL_ENABLE + && sp->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS + && WaitForSingleObject (sp->cancelEvent, 0) == WAIT_OBJECT_0) + { + sp->state = PThreadStateCanceling; + sp->cancelState = PTHREAD_CANCEL_DISABLE; + ResetEvent (sp->cancelEvent); + (void) pthread_mutex_unlock (&sp->cancelLock); + ptw32_throw (PTW32_EPS_CANCEL); + + /* Never reached */ + } + + (void) pthread_mutex_unlock (&sp->cancelLock); + + return (result); + +} /* pthread_setcancelstate */ diff --git a/pcsx2/windows/pthreads/pthread_setcanceltype.c b/pcsx2/windows/pthreads/pthread_setcanceltype.c new file mode 100644 index 0000000000..558ddc43e5 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_setcanceltype.c @@ -0,0 +1,125 @@ +/* + * pthread_setcanceltype.c + * + * Description: + * POSIX thread functions related to thread cancellation. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_setcanceltype (int type, int *oldtype) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function atomically sets the calling thread's + * cancelability type to 'type' and returns the previous + * cancelability type at the location referenced by + * 'oldtype' + * + * PARAMETERS + * type, + * oldtype + * PTHREAD_CANCEL_DEFERRED + * only deferred cancelation is allowed, + * + * PTHREAD_CANCEL_ASYNCHRONOUS + * Asynchronous cancellation is allowed + * + * + * DESCRIPTION + * This function atomically sets the calling thread's + * cancelability type to 'type' and returns the previous + * cancelability type at the location referenced by + * 'oldtype' + * + * NOTES: + * 1) Use with caution; most code is not safe for use + * with asynchronous cancelability. + * + * COMPATIBILITY ADDITIONS + * If 'oldtype' is NULL then the previous type is not returned + * but the function still succeeds. (Solaris) + * + * RESULTS + * 0 successfully set cancelability type, + * EINVAL 'type' is invalid + * + * ------------------------------------------------------ + */ +{ + int result = 0; + pthread_t self = pthread_self (); + ptw32_thread_t * sp = (ptw32_thread_t *) self.p; + + if (sp == NULL + || (type != PTHREAD_CANCEL_DEFERRED + && type != PTHREAD_CANCEL_ASYNCHRONOUS)) + { + return EINVAL; + } + + /* + * Lock for async-cancel safety. + */ + (void) pthread_mutex_lock (&sp->cancelLock); + + if (oldtype != NULL) + { + *oldtype = sp->cancelType; + } + + sp->cancelType = type; + + /* + * Check if there is a pending asynchronous cancel + */ + if (sp->cancelState == PTHREAD_CANCEL_ENABLE + && type == PTHREAD_CANCEL_ASYNCHRONOUS + && WaitForSingleObject (sp->cancelEvent, 0) == WAIT_OBJECT_0) + { + sp->state = PThreadStateCanceling; + sp->cancelState = PTHREAD_CANCEL_DISABLE; + ResetEvent (sp->cancelEvent); + (void) pthread_mutex_unlock (&sp->cancelLock); + ptw32_throw (PTW32_EPS_CANCEL); + + /* Never reached */ + } + + (void) pthread_mutex_unlock (&sp->cancelLock); + + return (result); + +} /* pthread_setcanceltype */ diff --git a/pcsx2/windows/pthreads/pthread_setconcurrency.c b/pcsx2/windows/pthreads/pthread_setconcurrency.c new file mode 100644 index 0000000000..b1693da1ff --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_setconcurrency.c @@ -0,0 +1,53 @@ +/* + * pthread_setconcurrency.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_setconcurrency (int level) +{ + if (level < 0) + { + return EINVAL; + } + else + { + ptw32_concurrency = level; + return 0; + } +} diff --git a/pcsx2/windows/pthreads/pthread_setschedparam.c b/pcsx2/windows/pthreads/pthread_setschedparam.c new file mode 100644 index 0000000000..0e705bb6c1 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_setschedparam.c @@ -0,0 +1,125 @@ +/* + * sched_setschedparam.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +pthread_setschedparam (pthread_t thread, int policy, + const struct sched_param *param) +{ + int result; + + /* Validate the thread id. */ + result = pthread_kill (thread, 0); + if (0 != result) + { + return result; + } + + /* Validate the scheduling policy. */ + if (policy < SCHED_MIN || policy > SCHED_MAX) + { + return EINVAL; + } + + /* Ensure the policy is SCHED_OTHER. */ + if (policy != SCHED_OTHER) + { + return ENOTSUP; + } + + return (ptw32_setthreadpriority (thread, policy, param->sched_priority)); +} + + +int +ptw32_setthreadpriority (pthread_t thread, int policy, int priority) +{ + int prio; + int result; + ptw32_thread_t * tp = (ptw32_thread_t *) thread.p; + + prio = priority; + + /* Validate priority level. */ + if (prio < sched_get_priority_min (policy) || + prio > sched_get_priority_max (policy)) + { + return EINVAL; + } + +#if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL) +/* WinCE */ +#else +/* Everything else */ + + if (THREAD_PRIORITY_IDLE < prio && THREAD_PRIORITY_LOWEST > prio) + { + prio = THREAD_PRIORITY_LOWEST; + } + else if (THREAD_PRIORITY_TIME_CRITICAL > prio + && THREAD_PRIORITY_HIGHEST < prio) + { + prio = THREAD_PRIORITY_HIGHEST; + } + +#endif + + result = pthread_mutex_lock (&tp->threadLock); + + if (0 == result) + { + /* If this fails, the current priority is unchanged. */ + if (0 == SetThreadPriority (tp->threadH, prio)) + { + result = EINVAL; + } + else + { + /* + * Must record the thread's sched_priority as given, + * not as finally adjusted. + */ + tp->sched_priority = priority; + } + + (void) pthread_mutex_unlock (&tp->threadLock); + } + + return result; +} diff --git a/pcsx2/windows/pthreads/pthread_setspecific.c b/pcsx2/windows/pthreads/pthread_setspecific.c new file mode 100644 index 0000000000..c3fcc299a1 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_setspecific.c @@ -0,0 +1,168 @@ +/* + * pthread_setspecific.c + * + * Description: + * POSIX thread functions which implement thread-specific data (TSD). + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_setspecific (pthread_key_t key, const void *value) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function sets the value of the thread specific + * key in the calling thread. + * + * PARAMETERS + * key + * an instance of pthread_key_t + * value + * the value to set key to + * + * + * DESCRIPTION + * This function sets the value of the thread specific + * key in the calling thread. + * + * RESULTS + * 0 successfully set value + * EAGAIN could not set value + * ENOENT SERIOUS!! + * + * ------------------------------------------------------ + */ +{ + pthread_t self; + int result = 0; + + if (key != ptw32_selfThreadKey) + { + /* + * Using pthread_self will implicitly create + * an instance of pthread_t for the current + * thread if one wasn't explicitly created + */ + self = pthread_self (); + if (self.p == NULL) + { + return ENOENT; + } + } + else + { + /* + * Resolve catch-22 of registering thread with selfThread + * key + */ + ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); + + if (sp == NULL) + { + if (value == NULL) + { + return ENOENT; + } + self = *((pthread_t *) value); + } + else + { + self = sp->ptHandle; + } + } + + result = 0; + + if (key != NULL) + { + if (self.p != NULL && key->destructor != NULL && value != NULL) + { + /* + * Only require associations if we have to + * call user destroy routine. + * Don't need to locate an existing association + * when setting data to NULL for WIN32 since the + * data is stored with the operating system; not + * on the association; setting assoc to NULL short + * circuits the search. + */ + ThreadKeyAssoc *assoc; + + if (pthread_mutex_lock(&(key->keyLock)) == 0) + { + ptw32_thread_t * sp = (ptw32_thread_t *) self.p; + + (void) pthread_mutex_lock(&(sp->threadLock)); + + assoc = (ThreadKeyAssoc *) sp->keys; + /* + * Locate existing association + */ + while (assoc != NULL) + { + if (assoc->key == key) + { + /* + * Association already exists + */ + break; + } + assoc = assoc->nextKey; + } + + /* + * create an association if not found + */ + if (assoc == NULL) + { + result = ptw32_tkAssocCreate (sp, key); + } + + (void) pthread_mutex_unlock(&(sp->threadLock)); + } + (void) pthread_mutex_unlock(&(key->keyLock)); + } + + if (result == 0) + { + if (!TlsSetValue (key->key, (LPVOID) value)) + { + result = EAGAIN; + } + } + } + + return (result); +} /* pthread_setspecific */ diff --git a/pcsx2/windows/pthreads/pthread_spin_destroy.c b/pcsx2/windows/pthreads/pthread_spin_destroy.c new file mode 100644 index 0000000000..80f248258a --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_spin_destroy.c @@ -0,0 +1,112 @@ +/* + * pthread_spin_destroy.c + * + * Description: + * This translation unit implements spin lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_spin_destroy (pthread_spinlock_t * lock) +{ + register pthread_spinlock_t s; + int result = 0; + + if (lock == NULL || *lock == NULL) + { + return EINVAL; + } + + if ((s = *lock) != PTHREAD_SPINLOCK_INITIALIZER) + { + if (s->interlock == PTW32_SPIN_USE_MUTEX) + { + result = pthread_mutex_destroy (&(s->u.mutex)); + } + else if ((PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED != + PTW32_INTERLOCKED_COMPARE_EXCHANGE ((PTW32_INTERLOCKED_LPLONG) + & (s->interlock), + (PTW32_INTERLOCKED_LONG) + PTW32_OBJECT_INVALID, + (PTW32_INTERLOCKED_LONG) + PTW32_SPIN_UNLOCKED)) + { + result = EINVAL; + } + + if (0 == result) + { + /* + * We are relying on the application to ensure that all other threads + * have finished with the spinlock before destroying it. + */ + *lock = NULL; + (void) free (s); + } + } + else + { + /* + * See notes in ptw32_spinlock_check_need_init() above also. + */ + EnterCriticalSection (&ptw32_spinlock_test_init_lock); + + /* + * Check again. + */ + if (*lock == PTHREAD_SPINLOCK_INITIALIZER) + { + /* + * This is all we need to do to destroy a statically + * initialised spinlock that has not yet been used (initialised). + * If we get to here, another thread + * waiting to initialise this mutex will get an EINVAL. + */ + *lock = NULL; + } + else + { + /* + * The spinlock has been initialised while we were waiting + * so assume it's in use. + */ + result = EBUSY; + } + + LeaveCriticalSection (&ptw32_spinlock_test_init_lock); + } + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_spin_init.c b/pcsx2/windows/pthreads/pthread_spin_init.c new file mode 100644 index 0000000000..949d77409a --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_spin_init.c @@ -0,0 +1,131 @@ +/* + * pthread_spin_init.c + * + * Description: + * This translation unit implements spin lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_spin_init (pthread_spinlock_t * lock, int pshared) +{ + pthread_spinlock_t s; + int cpus = 0; + int result = 0; + + if (lock == NULL) + { + return EINVAL; + } + +#ifdef PTW32_STATIC_LIB + // This allos for C++ static initializers to function without crashes. (air) + pthread_win32_process_attach_np(); +#endif + +// Optimized this so that it doesn't do cpu count checks needlessly. (air) +#if _POSIX_THREAD_PROCESS_SHARED >= 0 + /* + * Not implemented yet. + */ + + if (0 != ptw32_getprocessors (&cpus)) + { + cpus = 1; + } + + if (cpus > 1) + { + if (pshared == PTHREAD_PROCESS_SHARED) + { + /* + * Creating spinlock that can be shared between + * processes. + */ + +#error ERROR [__FILE__, line __LINE__]: Process shared spin locks are not supported yet. + + + } + } + +#else + + if (pshared == PTHREAD_PROCESS_SHARED) + return ENOSYS; + +#endif /* _POSIX_THREAD_PROCESS_SHARED */ + + s = (pthread_spinlock_t) calloc (1, sizeof (*s)); + + if (s == NULL) + { + return ENOMEM; + } + + if (cpus > 1) + { + s->u.cpus = cpus; + s->interlock = PTW32_SPIN_UNLOCKED; + } + else + { + pthread_mutexattr_t ma; + result = pthread_mutexattr_init (&ma); + + if (0 == result) + { + ma->pshared = pshared; + result = pthread_mutex_init (&(s->u.mutex), &ma); + if (0 == result) + { + s->interlock = PTW32_SPIN_USE_MUTEX; + } + } + (void) pthread_mutexattr_destroy (&ma); + } + + if (0 == result) + { + *lock = s; + } + else + { + (void) free (s); + *lock = NULL; + } + + return (result); +} diff --git a/pcsx2/windows/pthreads/pthread_spin_lock.c b/pcsx2/windows/pthreads/pthread_spin_lock.c new file mode 100644 index 0000000000..7e9bd2edae --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_spin_lock.c @@ -0,0 +1,83 @@ +/* + * pthread_spin_lock.c + * + * Description: + * This translation unit implements spin lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_spin_lock (pthread_spinlock_t * lock) +{ + register pthread_spinlock_t s; + + if (NULL == lock || NULL == *lock) + { + return (EINVAL); + } + + if (*lock == PTHREAD_SPINLOCK_INITIALIZER) + { + int result; + + if ((result = ptw32_spinlock_check_need_init (lock)) != 0) + { + return (result); + } + } + + s = *lock; + + while ((PTW32_INTERLOCKED_LONG) PTW32_SPIN_LOCKED == + PTW32_INTERLOCKED_COMPARE_EXCHANGE ((PTW32_INTERLOCKED_LPLONG) & + (s->interlock), + (PTW32_INTERLOCKED_LONG) + PTW32_SPIN_LOCKED, + (PTW32_INTERLOCKED_LONG) + PTW32_SPIN_UNLOCKED)) + { + } + + if (s->interlock == PTW32_SPIN_LOCKED) + { + return 0; + } + else if (s->interlock == PTW32_SPIN_USE_MUTEX) + { + return pthread_mutex_lock (&(s->u.mutex)); + } + + return EINVAL; +} diff --git a/pcsx2/windows/pthreads/pthread_spin_trylock.c b/pcsx2/windows/pthreads/pthread_spin_trylock.c new file mode 100644 index 0000000000..caba165fb3 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_spin_trylock.c @@ -0,0 +1,80 @@ +/* + * pthread_spin_trylock.c + * + * Description: + * This translation unit implements spin lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_spin_trylock (pthread_spinlock_t * lock) +{ + register pthread_spinlock_t s; + + if (NULL == lock || NULL == *lock) + { + return (EINVAL); + } + + if (*lock == PTHREAD_SPINLOCK_INITIALIZER) + { + int result; + + if ((result = ptw32_spinlock_check_need_init (lock)) != 0) + { + return (result); + } + } + + s = *lock; + + switch ((long) + PTW32_INTERLOCKED_COMPARE_EXCHANGE ((PTW32_INTERLOCKED_LPLONG) & + (s->interlock), + (PTW32_INTERLOCKED_LONG) + PTW32_SPIN_LOCKED, + (PTW32_INTERLOCKED_LONG) + PTW32_SPIN_UNLOCKED)) + { + case PTW32_SPIN_UNLOCKED: + return 0; + case PTW32_SPIN_LOCKED: + return EBUSY; + case PTW32_SPIN_USE_MUTEX: + return pthread_mutex_trylock (&(s->u.mutex)); + } + + return EINVAL; +} diff --git a/pcsx2/windows/pthreads/pthread_spin_unlock.c b/pcsx2/windows/pthreads/pthread_spin_unlock.c new file mode 100644 index 0000000000..c14781669d --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_spin_unlock.c @@ -0,0 +1,75 @@ +/* + * pthread_spin_unlock.c + * + * Description: + * This translation unit implements spin lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +pthread_spin_unlock (pthread_spinlock_t * lock) +{ + register pthread_spinlock_t s; + + if (NULL == lock || NULL == *lock) + { + return (EINVAL); + } + + s = *lock; + + if (s == PTHREAD_SPINLOCK_INITIALIZER) + { + return EPERM; + } + + switch ((long) + PTW32_INTERLOCKED_COMPARE_EXCHANGE ((PTW32_INTERLOCKED_LPLONG) & + (s->interlock), + (PTW32_INTERLOCKED_LONG) + PTW32_SPIN_UNLOCKED, + (PTW32_INTERLOCKED_LONG) + PTW32_SPIN_LOCKED)) + { + case PTW32_SPIN_LOCKED: + return 0; + case PTW32_SPIN_UNLOCKED: + return EPERM; + case PTW32_SPIN_USE_MUTEX: + return pthread_mutex_unlock (&(s->u.mutex)); + } + + return EINVAL; +} diff --git a/pcsx2/windows/pthreads/pthread_testcancel.c b/pcsx2/windows/pthreads/pthread_testcancel.c new file mode 100644 index 0000000000..10a846e15a --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_testcancel.c @@ -0,0 +1,102 @@ +/* + * pthread_testcancel.c + * + * Description: + * POSIX thread functions related to thread cancellation. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +void +pthread_testcancel (void) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function creates a deferred cancellation point + * in the calling thread. The call has no effect if the + * current cancelability state is + * PTHREAD_CANCEL_DISABLE + * + * PARAMETERS + * N/A + * + * + * DESCRIPTION + * This function creates a deferred cancellation point + * in the calling thread. The call has no effect if the + * current cancelability state is + * PTHREAD_CANCEL_DISABLE + * + * NOTES: + * 1) Cancellation is asynchronous. Use pthread_join + * to wait for termination of thread if necessary + * + * RESULTS + * N/A + * + * ------------------------------------------------------ + */ +{ + pthread_t self = pthread_self (); + ptw32_thread_t * sp = (ptw32_thread_t *) self.p; + + if (sp == NULL) + { + return; + } + + /* + * Pthread_cancel() will have set sp->state to PThreadStateCancelPending + * and set an event, so no need to enter kernel space if + * sp->state != PThreadStateCancelPending - that only slows us down. + */ + if (sp->state != PThreadStateCancelPending) + { + return; + } + + (void) pthread_mutex_lock (&sp->cancelLock); + + if (sp->cancelState != PTHREAD_CANCEL_DISABLE) + { + ResetEvent(sp->cancelEvent); + sp->state = PThreadStateCanceling; + (void) pthread_mutex_unlock (&sp->cancelLock); + sp->cancelState = PTHREAD_CANCEL_DISABLE; + (void) pthread_mutex_unlock (&sp->cancelLock); + ptw32_throw (PTW32_EPS_CANCEL); + } + + (void) pthread_mutex_unlock (&sp->cancelLock); +} /* pthread_testcancel */ diff --git a/pcsx2/windows/pthreads/pthread_timechange_handler_np.c b/pcsx2/windows/pthreads/pthread_timechange_handler_np.c new file mode 100644 index 0000000000..99a4d61d59 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_timechange_handler_np.c @@ -0,0 +1,107 @@ +/* + * pthread_timechange_handler_np.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* + * Notes on handling system time adjustments (especially negative ones). + * --------------------------------------------------------------------- + * + * This solution was suggested by Alexander Terekhov, but any errors + * in the implementation are mine - [Ross Johnson] + * + * 1) The problem: threads doing a timedwait on a CV may expect to timeout + * at a specific absolute time according to a system timer. If the + * system clock is adjusted backwards then those threads sleep longer than + * expected. Also, pthreads-win32 converts absolute times to intervals in + * order to make use of the underlying Win32, and so waiting threads may + * awake before their proper abstimes. + * + * 2) We aren't able to distinquish between threads on timed or untimed waits, + * so we wake them all at the time of the adjustment so that they can + * re-evaluate their conditions and re-compute their timeouts. + * + * 3) We rely on correctly written applications for this to work. Specifically, + * they must be able to deal properly with spurious wakeups. That is, + * they must re-test their condition upon wakeup and wait again if + * the condition is not satisfied. + */ + +void * +pthread_timechange_handler_np (void *arg) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Broadcasts all CVs to force re-evaluation and + * new timeouts if required. + * + * PARAMETERS + * NONE + * + * + * DESCRIPTION + * Broadcasts all CVs to force re-evaluation and + * new timeouts if required. + * + * This routine may be passed directly to pthread_create() + * as a new thread in order to run asynchronously. + * + * + * RESULTS + * 0 successfully broadcast all CVs + * EAGAIN Not all CVs were broadcast + * + * ------------------------------------------------------ + */ +{ + int result = 0; + pthread_cond_t cv; + + EnterCriticalSection (&ptw32_cond_list_lock); + + cv = ptw32_cond_list_head; + + while (cv != NULL && 0 == result) + { + result = pthread_cond_broadcast (&cv); + cv = cv->next; + } + + LeaveCriticalSection (&ptw32_cond_list_lock); + + return (void *) (result != 0 ? EAGAIN : 0); +} diff --git a/pcsx2/windows/pthreads/pthread_win32_attach_detach_np.c b/pcsx2/windows/pthreads/pthread_win32_attach_detach_np.c new file mode 100644 index 0000000000..0e25b07d80 --- /dev/null +++ b/pcsx2/windows/pthreads/pthread_win32_attach_detach_np.c @@ -0,0 +1,310 @@ +/* + * pthread_win32_attach_detach_np.c + * + * Description: + * This translation unit implements non-portable thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* + * Handle to kernel32.dll + */ +static HINSTANCE ptw32_h_kernel32; + +/* + * Handle to quserex.dll + */ +static HINSTANCE ptw32_h_quserex; + +BOOL +pthread_win32_process_attach_np () +{ + BOOL result = TRUE; + DWORD_PTR vProcessCPUs; + DWORD_PTR vSystemCPUs; + + if( ptw32_processInitialized ) return TRUE; + + result = ptw32_processInitialize (); + +#ifdef _UWIN + pthread_count++; +#endif + + ptw32_features = 0; + + +#if defined(NEED_PROCESS_AFFINITY_MASK) + + ptw32_smp_system = PTW32_FALSE; + +#else + + if (GetProcessAffinityMask (GetCurrentProcess (), + &vProcessCPUs, &vSystemCPUs)) + { + int CPUs = 0; + DWORD_PTR bit; + + for (bit = 1; bit != 0; bit <<= 1) + { + if (vSystemCPUs & bit) + { + CPUs++; + } + } + ptw32_smp_system = (CPUs > 1); + } + else + { + ptw32_smp_system = PTW32_FALSE; + } + +#endif + +#if 0 + +#ifdef WINCE + + /* + * Load COREDLL and try to get address of InterlockedCompareExchange + */ + ptw32_h_kernel32 = LoadLibrary (TEXT ("COREDLL.DLL")); + +#else + + /* + * Load KERNEL32 and try to get address of InterlockedCompareExchange + */ + ptw32_h_kernel32 = LoadLibrary (TEXT ("KERNEL32.DLL")); + +#endif + +// We're only using pthreads' inline version of InterlockedExchange + ptw32_interlocked_compare_exchange = + (PTW32_INTERLOCKED_LONG (WINAPI *) + (PTW32_INTERLOCKED_LPLONG, PTW32_INTERLOCKED_LONG, + PTW32_INTERLOCKED_LONG)) +#if defined(NEED_UNICODE_CONSTS) + GetProcAddress (ptw32_h_kernel32, + (const TCHAR *) TEXT ("InterlockedCompareExchange")); +#else + GetProcAddress (ptw32_h_kernel32, (LPCSTR) "InterlockedCompareExchange"); +#endif + + if (ptw32_interlocked_compare_exchange == NULL) + { + ptw32_interlocked_compare_exchange = ptw32_InterlockedCompareExchange; + + /* + * If InterlockedCompareExchange is not being used, then free + * the kernel32.dll handle now, rather than leaving it until + * DLL_PROCESS_DETACH. + * + * Note: this is not a pedantic exercise in freeing unused + * resources! It is a work-around for a bug in Windows 95 + * (see microsoft knowledge base article, Q187684) which + * does Bad Things when FreeLibrary is called within + * the DLL_PROCESS_DETACH code, in certain situations. + * Since w95 just happens to be a platform which does not + * provide InterlockedCompareExchange, the bug will be + * effortlessly avoided. + */ + (void) FreeLibrary (ptw32_h_kernel32); + ptw32_h_kernel32 = 0; + } + else + { + ptw32_features |= PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE; + } + +#endif + + /* + * Load QUSEREX.DLL and try to get address of QueueUserAPCEx + */ + ptw32_h_quserex = LoadLibrary (TEXT ("QUSEREX.DLL")); + + if (ptw32_h_quserex != NULL) + { + ptw32_register_cancelation = (DWORD (*)(PAPCFUNC, HANDLE, DWORD)) +#if defined(NEED_UNICODE_CONSTS) + GetProcAddress (ptw32_h_quserex, + (const TCHAR *) TEXT ("QueueUserAPCEx")); +#else + GetProcAddress (ptw32_h_quserex, (LPCSTR) "QueueUserAPCEx"); +#endif + } + + if (NULL == ptw32_register_cancelation) + { + ptw32_register_cancelation = ptw32_RegisterCancelation; + + if (ptw32_h_quserex != NULL) + { + (void) FreeLibrary (ptw32_h_quserex); + } + ptw32_h_quserex = 0; + } + else + { + /* Initialise QueueUserAPCEx */ + BOOL (*queue_user_apc_ex_init) (VOID); + + queue_user_apc_ex_init = (BOOL (*)(VOID)) +#if defined(NEED_UNICODE_CONSTS) + GetProcAddress (ptw32_h_quserex, + (const TCHAR *) TEXT ("QueueUserAPCEx_Init")); +#else + GetProcAddress (ptw32_h_quserex, (LPCSTR) "QueueUserAPCEx_Init"); +#endif + + if (queue_user_apc_ex_init == NULL || !queue_user_apc_ex_init ()) + { + ptw32_register_cancelation = ptw32_RegisterCancelation; + + (void) FreeLibrary (ptw32_h_quserex); + ptw32_h_quserex = 0; + } + } + + if (ptw32_h_quserex) + { + ptw32_features |= PTW32_ALERTABLE_ASYNC_CANCEL; + } + + return result; +} + + +BOOL +pthread_win32_process_detach_np () +{ + if (ptw32_processInitialized) + { + ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); + + if (sp != NULL) + { + /* + * Detached threads have their resources automatically + * cleaned up upon exit (others must be 'joined'). + */ + if (sp->detachState == PTHREAD_CREATE_DETACHED) + { + ptw32_threadDestroy (sp->ptHandle); + TlsSetValue (ptw32_selfThreadKey->key, NULL); + } + } + + /* + * The DLL is being unmapped from the process's address space + */ + ptw32_processTerminate (); + + if (ptw32_h_quserex) + { + /* Close QueueUserAPCEx */ + BOOL (*queue_user_apc_ex_fini) (VOID); + + queue_user_apc_ex_fini = (BOOL (*)(VOID)) +#if defined(NEED_UNICODE_CONSTS) + GetProcAddress (ptw32_h_quserex, + (const TCHAR *) TEXT ("QueueUserAPCEx_Fini")); +#else + GetProcAddress (ptw32_h_quserex, (LPCSTR) "QueueUserAPCEx_Fini"); +#endif + + if (queue_user_apc_ex_fini != NULL) + { + (void) queue_user_apc_ex_fini (); + } + (void) FreeLibrary (ptw32_h_quserex); + } + + if (ptw32_h_kernel32) + { + (void) FreeLibrary (ptw32_h_kernel32); + } + } + + return TRUE; +} + +BOOL +pthread_win32_thread_attach_np () +{ + return TRUE; +} + +BOOL +pthread_win32_thread_detach_np () +{ + if (ptw32_processInitialized) + { + /* + * Don't use pthread_self() - to avoid creating an implicit POSIX thread handle + * unnecessarily. + */ + ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); + + if (sp != NULL) // otherwise Win32 thread with no implicit POSIX handle. + { + ptw32_callUserDestroyRoutines (sp->ptHandle); + + (void) pthread_mutex_lock (&sp->cancelLock); + sp->state = PThreadStateLast; + /* + * If the thread is joinable at this point then it MUST be joined + * or detached explicitly by the application. + */ + (void) pthread_mutex_unlock (&sp->cancelLock); + + if (sp->detachState == PTHREAD_CREATE_DETACHED) + { + ptw32_threadDestroy (sp->ptHandle); + + TlsSetValue (ptw32_selfThreadKey->key, NULL); + } + } + } + + return TRUE; +} + +BOOL +pthread_win32_test_features_np (int feature_mask) +{ + return ((ptw32_features & feature_mask) == feature_mask); +} diff --git a/pcsx2/windows/pthreads/ptw32_InterlockedCompareExchange.c b/pcsx2/windows/pthreads/ptw32_InterlockedCompareExchange.c new file mode 100644 index 0000000000..0ddabc952c --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_InterlockedCompareExchange.c @@ -0,0 +1,303 @@ +/* + * ptw32_InterlockedCompareExchange.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +/* + * ptw32_InterlockedCompareExchange -- + * + * Originally needed because W9x doesn't support InterlockedCompareExchange. + * We now use this version wherever possible so we can inline it. + */ + +INLINE PTW32_INTERLOCKED_LONG WINAPI +ptw32_InterlockedCompareExchange (volatile PTW32_INTERLOCKED_LPLONG location, + PTW32_INTERLOCKED_LONG value, + PTW32_INTERLOCKED_LONG comparand) +{ + +#if defined(__WATCOMC__) +/* Don't report that result is not assigned a value before being referenced */ +#pragma disable_message (200) +#endif + + PTW32_INTERLOCKED_LONG result; + + /* + * Using the LOCK prefix on uni-processor machines is significantly slower + * and it is not necessary. The overhead of the conditional below is + * negligible in comparison. Since an optimised DLL will inline this + * routine, this will be faster than calling the system supplied + * Interlocked routine, which appears to avoid the LOCK prefix on + * uniprocessor systems. So one DLL works for all systems. + */ + if (1) //ptw32_smp_system) + +/* *INDENT-OFF* */ + +#if defined(_M_IX86) || defined(_X86_) + +#if defined(_MSC_VER) || defined(__WATCOMC__) || (defined(__BORLANDC__) && defined(HAVE_TASM32)) +#define HAVE_INLINABLE_INTERLOCKED_CMPXCHG + { + _asm { + //PUSH ecx + //PUSH edx + MOV ecx,dword ptr [location] + MOV edx,dword ptr [value] + MOV eax,dword ptr [comparand] + LOCK CMPXCHG dword ptr [ecx],edx + MOV dword ptr [result], eax + //POP edx + //POP ecx + } + } + else + { + _asm { + //PUSH ecx + //PUSH edx + MOV ecx,dword ptr [location] + MOV edx,dword ptr [value] + MOV eax,dword ptr [comparand] + CMPXCHG dword ptr [ecx],edx + MOV dword ptr [result], eax + //POP edx + //POP ecx + } + } + +#elif defined(__GNUC__) +#define HAVE_INLINABLE_INTERLOCKED_CMPXCHG + + { + __asm__ __volatile__ + ( + "lock\n\t" + "cmpxchgl %2,%1" /* if (EAX == [location]) */ + /* [location] = value */ + /* else */ + /* EAX = [location] */ + :"=a" (result) + :"m" (*location), "r" (value), "a" (comparand)); + } + else + { + __asm__ __volatile__ + ( + "cmpxchgl %2,%1" /* if (EAX == [location]) */ + /* [location] = value */ + /* else */ + /* EAX = [location] */ + :"=a" (result) + :"m" (*location), "r" (value), "a" (comparand)); + } + +#endif + +#else + + /* + * If execution gets to here then we're running on a currently + * unsupported processor or compiler. + */ + + result = 0; + +#endif + +/* *INDENT-ON* */ + + return result; + +#if defined(__WATCOMC__) +#pragma enable_message (200) +#endif + +} + +/* + * ptw32_InterlockedExchange -- + * + * We now use this version wherever possible so we can inline it. + */ + +INLINE LONG WINAPI +ptw32_InterlockedExchange (volatile PTW32_INTERLOCKED_LPLONG location, + LONG value) +{ + +#if defined(__WATCOMC__) +/* Don't report that result is not assigned a value before being referenced */ +#pragma disable_message (200) +#endif + + LONG result; + + /* + * The XCHG instruction always locks the bus with or without the + * LOCKED prefix. This makes it significantly slower than CMPXCHG on + * uni-processor machines. The Windows InterlockedExchange function + * is nearly 3 times faster than the XCHG instruction, so this routine + * is not yet very useful for speeding up pthreads. + */ + if (1) //ptw32_smp_system) + +/* *INDENT-OFF* */ + +#if defined(_M_IX86) || defined(_X86_) + +#if defined(_MSC_VER) || defined(__WATCOMC__) || (defined(__BORLANDC__) && defined(HAVE_TASM32)) +#define HAVE_INLINABLE_INTERLOCKED_XCHG + + { + _asm { + //PUSH ecx + MOV ecx,dword ptr [location] + MOV eax,dword ptr [value] + XCHG dword ptr [ecx],eax + MOV dword ptr [result], eax + //POP ecx + } + } + else + { + /* + * Faster version of XCHG for uni-processor systems because + * it doesn't lock the bus. If an interrupt or context switch + * occurs between the MOV and the CMPXCHG then the value in + * 'location' may have changed, in which case we will loop + * back to do the MOV again. + * + * FIXME! Need memory barriers for the MOV+CMPXCHG combo? + * + * Tests show that this routine has almost identical timing + * to Win32's InterlockedExchange(), which is much faster than + * using the inlined 'xchg' instruction above, so it's probably + * doing something similar to this (on UP systems). + * + * Can we do without the PUSH/POP instructions? + */ + _asm { + //PUSH ecx + //PUSH edx + MOV ecx,dword ptr [location] + MOV edx,dword ptr [value] +L1: MOV eax,dword ptr [ecx] + CMPXCHG dword ptr [ecx],edx + JNZ L1 + MOV dword ptr [result], eax + //POP edx + //POP ecx + } + } + +#elif defined(__GNUC__) +#define HAVE_INLINABLE_INTERLOCKED_XCHG + + { + __asm__ __volatile__ + ( + "xchgl %2,%1" + :"=r" (result) + :"m" (*location), "0" (value)); + } + else + { + /* + * Faster version of XCHG for uni-processor systems because + * it doesn't lock the bus. If an interrupt or context switch + * occurs between the movl and the cmpxchgl then the value in + * 'location' may have changed, in which case we will loop + * back to do the movl again. + * + * FIXME! Need memory barriers for the MOV+CMPXCHG combo? + * + * Tests show that this routine has almost identical timing + * to Win32's InterlockedExchange(), which is much faster than + * using the an inlined 'xchg' instruction, so it's probably + * doing something similar to this (on UP systems). + */ + __asm__ __volatile__ + ( + "0:\n\t" + "movl %1,%%eax\n\t" + "cmpxchgl %2,%1\n\t" + "jnz 0b" + :"=&a" (result) + :"m" (*location), "r" (value)); + } + +#endif + +#else + + /* + * If execution gets to here then we're running on a currently + * unsupported processor or compiler. + */ + + result = 0; + +#endif + +/* *INDENT-ON* */ + + return result; + +#if defined(__WATCOMC__) +#pragma enable_message (200) +#endif + +} + + +#if 1 + +#if defined(PTW32_BUILD_INLINED) && defined(HAVE_INLINABLE_INTERLOCKED_CMPXCHG) +#undef PTW32_INTERLOCKED_COMPARE_EXCHANGE +#define PTW32_INTERLOCKED_COMPARE_EXCHANGE ptw32_InterlockedCompareExchange +#endif + +#if defined(PTW32_BUILD_INLINED) && defined(HAVE_INLINABLE_INTERLOCKED_XCHG) +#undef PTW32_INTERLOCKED_EXCHANGE +#define PTW32_INTERLOCKED_EXCHANGE ptw32_InterlockedExchange +#endif + +#endif diff --git a/pcsx2/windows/pthreads/ptw32_MCS_lock.c b/pcsx2/windows/pthreads/ptw32_MCS_lock.c new file mode 100644 index 0000000000..414e04bdc6 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_MCS_lock.c @@ -0,0 +1,210 @@ +/* + * ptw32_MCS_lock.c + * + * Description: + * This translation unit implements queue-based locks. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +/* + * About MCS locks: + * + * MCS locks are queue-based locks, where the queue nodes are local to the + * thread. The 'lock' is nothing more than a global pointer that points to + * the last node in the queue, or is NULL if the queue is empty. + * + * Originally designed for use as spin locks requiring no kernel resources + * for synchronisation or blocking, the implementation below has adapted + * the MCS spin lock for use as a general mutex that will suspend threads + * when there is lock contention. + * + * Because the queue nodes are thread-local, most of the memory read/write + * operations required to add or remove nodes from the queue do not trigger + * cache-coherence updates. + * + * Like 'named' mutexes, MCS locks consume system resources transiently - + * they are able to acquire and free resources automatically - but MCS + * locks do not require any unique 'name' to identify the lock to all + * threads using it. + * + * Usage of MCS locks: + * + * - you need a global ptw32_mcs_lock_t instance initialised to 0 or NULL. + * - you need a local thread-scope ptw32_mcs_local_node_t instance, which + * may serve several different locks but you need at least one node for + * every lock held concurrently by a thread. + * + * E.g.: + * + * ptw32_mcs_lock_t lock1 = 0; + * ptw32_mcs_lock_t lock2 = 0; + * + * void *mythread(void *arg) + * { + * ptw32_mcs_local_node_t node; + * + * ptw32_mcs_acquire (&lock1, &node); + * ptw32_mcs_release (&node); + * + * ptw32_mcs_acquire (&lock2, &node); + * ptw32_mcs_release (&node); + * { + * ptw32_mcs_local_node_t nodex; + * + * ptw32_mcs_acquire (&lock1, &node); + * ptw32_mcs_acquire (&lock2, &nodex); + * + * ptw32_mcs_release (&nodex); + * ptw32_mcs_release (&node); + * } + * return (void *)0; + * } + */ + +#include "pthread.h" +#include "implement.h" + +/* + * ptw32_mcs_flag_set -- notify another thread about an event. + * + * Set event if an event handle has been stored in the flag, and + * set flag to -1 otherwise. Note that -1 cannot be a valid handle value. + */ +INLINE void +ptw32_mcs_flag_set (LONG * flag) +{ + HANDLE e = (HANDLE)PTW32_INTERLOCKED_COMPARE_EXCHANGE( + (PTW32_INTERLOCKED_LPLONG)flag, + (PTW32_INTERLOCKED_LONG)-1, + (PTW32_INTERLOCKED_LONG)0); + if ((HANDLE)0 != e) + { + /* another thread has already stored an event handle in the flag */ + SetEvent(e); + } +} + +/* + * ptw32_mcs_flag_set -- wait for notification from another. + * + * Store an event handle in the flag and wait on it if the flag has not been + * set, and proceed without creating an event otherwise. + */ +INLINE void +ptw32_mcs_flag_wait (LONG * flag) +{ + if (0 == InterlockedExchangeAdd((LPLONG)flag, 0)) /* MBR fence */ + { + /* the flag is not set. create event. */ + + HANDLE e = CreateEvent(NULL, PTW32_FALSE, PTW32_FALSE, NULL); + + if (0 == PTW32_INTERLOCKED_COMPARE_EXCHANGE( + (PTW32_INTERLOCKED_LPLONG)flag, + (PTW32_INTERLOCKED_LONG)e, + (PTW32_INTERLOCKED_LONG)0)) + { + /* stored handle in the flag. wait on it now. */ + WaitForSingleObject(e, INFINITE); + } + + CloseHandle(e); + } +} + +/* + * ptw32_mcs_lock_acquire -- acquire an MCS lock. + * + * See: + * J. M. Mellor-Crummey and M. L. Scott. + * Algorithms for Scalable Synchronization on Shared-Memory Multiprocessors. + * ACM Transactions on Computer Systems, 9(1):21-65, Feb. 1991. + */ +INLINE void +ptw32_mcs_lock_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node) +{ + ptw32_mcs_local_node_t *pred; + + node->lock = lock; + node->nextFlag = 0; + node->readyFlag = 0; + node->next = 0; /* initially, no successor */ + + /* queue for the lock */ + pred = (ptw32_mcs_local_node_t *)PTW32_INTERLOCKED_EXCHANGE((LPLONG)lock, + (LONG)node); + + if (0 != pred) + { + /* the lock was not free. link behind predecessor. */ + pred->next = node; + ptw32_mcs_flag_set(&pred->nextFlag); + ptw32_mcs_flag_wait(&node->readyFlag); + } +} + +/* + * ptw32_mcs_lock_release -- release an MCS lock. + * + * See: + * J. M. Mellor-Crummey and M. L. Scott. + * Algorithms for Scalable Synchronization on Shared-Memory Multiprocessors. + * ACM Transactions on Computer Systems, 9(1):21-65, Feb. 1991. + */ +INLINE void +ptw32_mcs_lock_release (ptw32_mcs_local_node_t * node) +{ + ptw32_mcs_lock_t *lock = node->lock; + ptw32_mcs_local_node_t *next = (ptw32_mcs_local_node_t *) + InterlockedExchangeAdd((LPLONG)&node->next, 0); /* MBR fence */ + + if (0 == next) + { + /* no known successor */ + + if (node == (ptw32_mcs_local_node_t *) + PTW32_INTERLOCKED_COMPARE_EXCHANGE((PTW32_INTERLOCKED_LPLONG)lock, + (PTW32_INTERLOCKED_LONG)0, + (PTW32_INTERLOCKED_LONG)node)) + { + /* no successor, lock is free now */ + return; + } + + /* wait for successor */ + ptw32_mcs_flag_wait(&node->nextFlag); + next = (ptw32_mcs_local_node_t *) + InterlockedExchangeAdd((LPLONG)&node->next, 0); /* MBR fence */ + } + + /* pass the lock */ + ptw32_mcs_flag_set(&next->readyFlag); +} diff --git a/pcsx2/windows/pthreads/ptw32_callUserDestroyRoutines.c b/pcsx2/windows/pthreads/ptw32_callUserDestroyRoutines.c new file mode 100644 index 0000000000..48ea23cf31 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_callUserDestroyRoutines.c @@ -0,0 +1,220 @@ +/* + * ptw32_callUserDestroyRoutines.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +#ifdef __cplusplus +# if ! defined (_MSC_VER) && ! (defined(__GNUC__) && __GNUC__ < 3) && ! defined(__WATCOMC__) +using + std::terminate; +# endif +#endif + +void +ptw32_callUserDestroyRoutines (pthread_t thread) + /* + * ------------------------------------------------------------------- + * DOCPRIVATE + * + * This the routine runs through all thread keys and calls + * the destroy routines on the user's data for the current thread. + * It simulates the behaviour of POSIX Threads. + * + * PARAMETERS + * thread + * an instance of pthread_t + * + * RETURNS + * N/A + * ------------------------------------------------------------------- + */ +{ + ThreadKeyAssoc * assoc; + + if (thread.p != NULL) + { + int assocsRemaining; + int iterations = 0; + ptw32_thread_t * sp = (ptw32_thread_t *) thread.p; + + /* + * Run through all Thread<-->Key associations + * for the current thread. + * + * Do this process at most PTHREAD_DESTRUCTOR_ITERATIONS times. + */ + do + { + assocsRemaining = 0; + iterations++; + + (void) pthread_mutex_lock(&(sp->threadLock)); + /* + * The pointer to the next assoc is stored in the thread struct so that + * the assoc destructor in pthread_key_delete can adjust it + * if it deletes this assoc. This can happen if we fail to acquire + * both locks below, and are forced to release all of our locks, + * leaving open the opportunity for pthread_key_delete to get in + * before us. + */ + sp->nextAssoc = sp->keys; + (void) pthread_mutex_unlock(&(sp->threadLock)); + + for (;;) + { + void * value; + pthread_key_t k; + void (*destructor) (void *); + + /* + * First we need to serialise with pthread_key_delete by locking + * both assoc guards, but in the reverse order to our convention, + * so we must be careful to avoid deadlock. + */ + (void) pthread_mutex_lock(&(sp->threadLock)); + + if ((assoc = (ThreadKeyAssoc *)sp->nextAssoc) == NULL) + { + /* Finished */ + pthread_mutex_unlock(&(sp->threadLock)); + break; + } + else + { + /* + * assoc->key must be valid because assoc can't change or be + * removed from our chain while we hold at least one lock. If + * the assoc was on our key chain then the key has not been + * deleted yet. + * + * Now try to acquire the second lock without deadlocking. + * If we fail, we need to relinquish the first lock and the + * processor and then try to acquire them all again. + */ + if (pthread_mutex_trylock(&(assoc->key->keyLock)) == EBUSY) + { + pthread_mutex_unlock(&(sp->threadLock)); + Sleep(1); // Ugly but necessary to avoid priority effects. + /* + * Go around again. + * If pthread_key_delete has removed this assoc in the meantime, + * sp->nextAssoc will point to a new assoc. + */ + continue; + } + } + + /* We now hold both locks */ + + sp->nextAssoc = assoc->nextKey; + + /* + * Key still active; pthread_key_delete + * will block on these same mutexes before + * it can release actual key; therefore, + * key is valid and we can call the destroy + * routine; + */ + k = assoc->key; + destructor = k->destructor; + value = TlsGetValue(k->key); + TlsSetValue (k->key, NULL); + + // Every assoc->key exists and has a destructor + if (value != NULL && iterations <= PTHREAD_DESTRUCTOR_ITERATIONS) + { + /* + * Unlock both locks before the destructor runs. + * POSIX says pthread_key_delete can be run from destructors, + * and that probably includes with this key as target. + * pthread_setspecific can also be run from destructors and + * also needs to be able to access the assocs. + */ + (void) pthread_mutex_unlock(&(sp->threadLock)); + (void) pthread_mutex_unlock(&(k->keyLock)); + + assocsRemaining++; + +#ifdef __cplusplus + + try + { + /* + * Run the caller's cleanup routine. + */ + destructor (value); + } + catch (...) + { + /* + * A system unexpected exception has occurred + * running the user's destructor. + * We get control back within this block in case + * the application has set up it's own terminate + * handler. Since we are leaving the thread we + * should not get any internal pthreads + * exceptions. + */ + terminate (); + } + +#else /* __cplusplus */ + + /* + * Run the caller's cleanup routine. + */ + destructor (value); + +#endif /* __cplusplus */ + + } + else + { + /* + * Remove association from both the key and thread chains + * and reclaim it's memory resources. + */ + ptw32_tkAssocDestroy (assoc); + (void) pthread_mutex_unlock(&(sp->threadLock)); + (void) pthread_mutex_unlock(&(k->keyLock)); + } + } + } + while (assocsRemaining); + } +} /* ptw32_callUserDestroyRoutines */ diff --git a/pcsx2/windows/pthreads/ptw32_calloc.c b/pcsx2/windows/pthreads/ptw32_calloc.c new file mode 100644 index 0000000000..5389335c1e --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_calloc.c @@ -0,0 +1,56 @@ +/* + * ptw32_calloc.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +#ifdef NEED_CALLOC +void * +ptw32_calloc (size_t n, size_t s) +{ + unsigned int m = n * s; + void *p; + + p = malloc (m); + if (p == NULL) + return NULL; + + memset (p, 0, m); + + return p; +} +#endif diff --git a/pcsx2/windows/pthreads/ptw32_cond_check_need_init.c b/pcsx2/windows/pthreads/ptw32_cond_check_need_init.c new file mode 100644 index 0000000000..d4e1634fba --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_cond_check_need_init.c @@ -0,0 +1,94 @@ +/* + * ptw32_cond_check_need_init.c + * + * Description: + * This translation unit implements condition variables and their primitives. + * + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +INLINE int +ptw32_cond_check_need_init (pthread_cond_t * cond) +{ + int result = 0; + + /* + * The following guarded test is specifically for statically + * initialised condition variables (via PTHREAD_OBJECT_INITIALIZER). + * + * Note that by not providing this synchronisation we risk + * introducing race conditions into applications which are + * correctly written. + * + * Approach + * -------- + * We know that static condition variables will not be PROCESS_SHARED + * so we can serialise access to internal state using + * Win32 Critical Sections rather than Win32 Mutexes. + * + * If using a single global lock slows applications down too much, + * multiple global locks could be created and hashed on some random + * value associated with each mutex, the pointer perhaps. At a guess, + * a good value for the optimal number of global locks might be + * the number of processors + 1. + * + */ + EnterCriticalSection (&ptw32_cond_test_init_lock); + + /* + * We got here possibly under race + * conditions. Check again inside the critical section. + * If a static cv has been destroyed, the application can + * re-initialise it only by calling pthread_cond_init() + * explicitly. + */ + if (*cond == PTHREAD_COND_INITIALIZER) + { + result = pthread_cond_init (cond, NULL); + } + else if (*cond == NULL) + { + /* + * The cv has been destroyed while we were waiting to + * initialise it, so the operation that caused the + * auto-initialisation should fail. + */ + result = EINVAL; + } + + LeaveCriticalSection (&ptw32_cond_test_init_lock); + + return result; +} diff --git a/pcsx2/windows/pthreads/ptw32_getprocessors.c b/pcsx2/windows/pthreads/ptw32_getprocessors.c new file mode 100644 index 0000000000..7772ec7fbf --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_getprocessors.c @@ -0,0 +1,91 @@ +/* + * ptw32_getprocessors.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +/* + * ptw32_getprocessors() + * + * Get the number of CPUs available to the process. + * + * If the available number of CPUs is 1 then pthread_spin_lock() + * will block rather than spin if the lock is already owned. + * + * pthread_spin_init() calls this routine when initialising + * a spinlock. If the number of available processors changes + * (after a call to SetProcessAffinityMask()) then only + * newly initialised spinlocks will notice. + */ +int +ptw32_getprocessors (int *count) +{ + DWORD_PTR vProcessCPUs; + DWORD_PTR vSystemCPUs; + int result = 0; + +#if defined(NEED_PROCESS_AFFINITY_MASK) + + *count = 1; + +#else + + if (GetProcessAffinityMask (GetCurrentProcess (), + &vProcessCPUs, &vSystemCPUs)) + { + DWORD_PTR bit; + int CPUs = 0; + + for (bit = 1; bit != 0; bit <<= 1) + { + if (vProcessCPUs & bit) + { + CPUs++; + } + } + *count = CPUs; + } + else + { + result = EAGAIN; + } + +#endif + + return (result); +} diff --git a/pcsx2/windows/pthreads/ptw32_is_attr.c b/pcsx2/windows/pthreads/ptw32_is_attr.c new file mode 100644 index 0000000000..fc6fd6b8a6 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_is_attr.c @@ -0,0 +1,47 @@ +/* + * ptw32_is_attr.c + * + * Description: + * This translation unit implements operations on thread attribute objects. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +int +ptw32_is_attr (const pthread_attr_t * attr) +{ + /* Return 0 if the attr object is valid, non-zero otherwise. */ + + return (attr == NULL || + *attr == NULL || (*attr)->valid != PTW32_ATTR_VALID); +} diff --git a/pcsx2/windows/pthreads/ptw32_mutex_check_need_init.c b/pcsx2/windows/pthreads/ptw32_mutex_check_need_init.c new file mode 100644 index 0000000000..4ab3e7e3c9 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_mutex_check_need_init.c @@ -0,0 +1,112 @@ +/* + * ptw32_mutex_check_need_init.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +static struct pthread_mutexattr_t_ ptw32_recursive_mutexattr_s = + {PTHREAD_PROCESS_PRIVATE, PTHREAD_MUTEX_RECURSIVE}; +static struct pthread_mutexattr_t_ ptw32_errorcheck_mutexattr_s = + {PTHREAD_PROCESS_PRIVATE, PTHREAD_MUTEX_ERRORCHECK}; +static pthread_mutexattr_t ptw32_recursive_mutexattr = &ptw32_recursive_mutexattr_s; +static pthread_mutexattr_t ptw32_errorcheck_mutexattr = &ptw32_errorcheck_mutexattr_s; + + +INLINE int +ptw32_mutex_check_need_init (pthread_mutex_t * mutex) +{ + register int result = 0; + register pthread_mutex_t mtx; + + /* + * The following guarded test is specifically for statically + * initialised mutexes (via PTHREAD_MUTEX_INITIALIZER). + * + * Note that by not providing this synchronisation we risk + * introducing race conditions into applications which are + * correctly written. + * + * Approach + * -------- + * We know that static mutexes will not be PROCESS_SHARED + * so we can serialise access to internal state using + * Win32 Critical Sections rather than Win32 Mutexes. + * + * If using a single global lock slows applications down too much, + * multiple global locks could be created and hashed on some random + * value associated with each mutex, the pointer perhaps. At a guess, + * a good value for the optimal number of global locks might be + * the number of processors + 1. + * + */ + EnterCriticalSection (&ptw32_mutex_test_init_lock); + + /* + * We got here possibly under race + * conditions. Check again inside the critical section + * and only initialise if the mutex is valid (not been destroyed). + * If a static mutex has been destroyed, the application can + * re-initialise it only by calling pthread_mutex_init() + * explicitly. + */ + mtx = *mutex; + + if (mtx == PTHREAD_MUTEX_INITIALIZER) + { + result = pthread_mutex_init (mutex, NULL); + } + else if (mtx == PTHREAD_RECURSIVE_MUTEX_INITIALIZER) + { + result = pthread_mutex_init (mutex, &ptw32_recursive_mutexattr); + } + else if (mtx == PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) + { + result = pthread_mutex_init (mutex, &ptw32_errorcheck_mutexattr); + } + else if (mtx == NULL) + { + /* + * The mutex has been destroyed while we were waiting to + * initialise it, so the operation that caused the + * auto-initialisation should fail. + */ + result = EINVAL; + } + + LeaveCriticalSection (&ptw32_mutex_test_init_lock); + + return (result); +} diff --git a/pcsx2/windows/pthreads/ptw32_new.c b/pcsx2/windows/pthreads/ptw32_new.c new file mode 100644 index 0000000000..fa4c15dc88 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_new.c @@ -0,0 +1,91 @@ +/* + * ptw32_new.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +pthread_t +ptw32_new (void) +{ + pthread_t t; + pthread_t nil = {NULL, 0}; + ptw32_thread_t * tp; + + /* + * If there's a reusable pthread_t then use it. + */ + t = ptw32_threadReusePop (); + + if (NULL != t.p) + { + tp = (ptw32_thread_t *) t.p; + } + else + { + /* No reuse threads available */ + tp = (ptw32_thread_t *) calloc (1, sizeof(ptw32_thread_t)); + + if (tp == NULL) + { + return nil; + } + + /* ptHandle.p needs to point to it's parent ptw32_thread_t. */ + t.p = tp->ptHandle.p = tp; + t.x = tp->ptHandle.x = 0; + } + + /* Set default state. */ + tp->sched_priority = THREAD_PRIORITY_NORMAL; + tp->detachState = PTHREAD_CREATE_JOINABLE; + tp->cancelState = PTHREAD_CANCEL_ENABLE; + tp->cancelType = PTHREAD_CANCEL_DEFERRED; + tp->cancelLock = PTHREAD_MUTEX_INITIALIZER; + tp->threadLock = PTHREAD_MUTEX_INITIALIZER; + tp->cancelEvent = CreateEvent (0, (int) PTW32_TRUE, /* manualReset */ + (int) PTW32_FALSE, /* setSignaled */ + NULL); + + if (tp->cancelEvent == NULL) + { + ptw32_threadReusePush (tp->ptHandle); + return nil; + } + + return t; + +} diff --git a/pcsx2/windows/pthreads/ptw32_processInitialize.c b/pcsx2/windows/pthreads/ptw32_processInitialize.c new file mode 100644 index 0000000000..075e03a4da --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_processInitialize.c @@ -0,0 +1,102 @@ +/* + * ptw32_processInitialize.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +ptw32_processInitialize (void) + /* + * ------------------------------------------------------ + * DOCPRIVATE + * This function performs process wide initialization for + * the pthread library. + * + * PARAMETERS + * N/A + * + * DESCRIPTION + * This function performs process wide initialization for + * the pthread library. + * If successful, this routine sets the global variable + * ptw32_processInitialized to TRUE. + * + * RESULTS + * TRUE if successful, + * FALSE otherwise + * + * ------------------------------------------------------ + */ +{ + if (ptw32_processInitialized) + { + /* + * Ignore if already initialized. this is useful for + * programs that uses a non-dll pthread + * library. Such programs must call ptw32_processInitialize() explicitly, + * since this initialization routine is automatically called only when + * the dll is loaded. + */ + return PTW32_TRUE; + } + + ptw32_processInitialized = PTW32_TRUE; + + /* + * Initialize Keys + */ + if ((pthread_key_create (&ptw32_selfThreadKey, NULL) != 0) || + (pthread_key_create (&ptw32_cleanupKey, NULL) != 0)) + { + + ptw32_processTerminate (); + } + + /* + * Set up the global locks. + */ + InitializeCriticalSection (&ptw32_thread_reuse_lock); + InitializeCriticalSection (&ptw32_mutex_test_init_lock); + InitializeCriticalSection (&ptw32_cond_list_lock); + InitializeCriticalSection (&ptw32_cond_test_init_lock); + InitializeCriticalSection (&ptw32_rwlock_test_init_lock); + InitializeCriticalSection (&ptw32_spinlock_test_init_lock); + + return (ptw32_processInitialized); + +} /* processInitialize */ diff --git a/pcsx2/windows/pthreads/ptw32_processTerminate.c b/pcsx2/windows/pthreads/ptw32_processTerminate.c new file mode 100644 index 0000000000..dd641bc409 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_processTerminate.c @@ -0,0 +1,114 @@ +/* + * ptw32_processTerminate.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +void +ptw32_processTerminate (void) + /* + * ------------------------------------------------------ + * DOCPRIVATE + * This function performs process wide termination for + * the pthread library. + * + * PARAMETERS + * N/A + * + * DESCRIPTION + * This function performs process wide termination for + * the pthread library. + * This routine sets the global variable + * ptw32_processInitialized to FALSE + * + * RESULTS + * N/A + * + * ------------------------------------------------------ + */ +{ + if (ptw32_processInitialized) + { + ptw32_thread_t * tp, * tpNext; + + if (ptw32_selfThreadKey != NULL) + { + /* + * Release ptw32_selfThreadKey + */ + pthread_key_delete (ptw32_selfThreadKey); + + ptw32_selfThreadKey = NULL; + } + + if (ptw32_cleanupKey != NULL) + { + /* + * Release ptw32_cleanupKey + */ + pthread_key_delete (ptw32_cleanupKey); + + ptw32_cleanupKey = NULL; + } + + EnterCriticalSection (&ptw32_thread_reuse_lock); + + tp = ptw32_threadReuseTop; + while (tp != PTW32_THREAD_REUSE_EMPTY) + { + tpNext = tp->prevReuse; + free (tp); + tp = tpNext; + } + + LeaveCriticalSection (&ptw32_thread_reuse_lock); + + /* + * Destroy the global locks and other objects. + */ + DeleteCriticalSection (&ptw32_spinlock_test_init_lock); + DeleteCriticalSection (&ptw32_rwlock_test_init_lock); + DeleteCriticalSection (&ptw32_cond_test_init_lock); + DeleteCriticalSection (&ptw32_cond_list_lock); + DeleteCriticalSection (&ptw32_mutex_test_init_lock); + DeleteCriticalSection (&ptw32_thread_reuse_lock); + + ptw32_processInitialized = PTW32_FALSE; + } + +} /* processTerminate */ diff --git a/pcsx2/windows/pthreads/ptw32_relmillisecs.c b/pcsx2/windows/pthreads/ptw32_relmillisecs.c new file mode 100644 index 0000000000..b6dc2dad82 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_relmillisecs.c @@ -0,0 +1,120 @@ +/* + * ptw32_relmillisecs.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef _UWIN +//#include +#endif +#include "pthread.h" +#include "implement.h" +#ifndef NEED_FTIME +#include +#endif + + +INLINE DWORD +ptw32_relmillisecs (const struct timespec * abstime) +{ + const int64_t NANOSEC_PER_MILLISEC = 1000000; + const int64_t MILLISEC_PER_SEC = 1000; + DWORD milliseconds; + int64_t tmpAbsMilliseconds; + int64_t tmpCurrMilliseconds; +#ifdef NEED_FTIME + struct timespec currSysTime; + FILETIME ft; + SYSTEMTIME st; +#else /* ! NEED_FTIME */ + struct _timeb currSysTime; +#endif /* NEED_FTIME */ + + + /* + * Calculate timeout as milliseconds from current system time. + */ + + /* + * subtract current system time from abstime in a way that checks + * that abstime is never in the past, or is never equivalent to the + * defined INFINITE value (0xFFFFFFFF). + * + * Assume all integers are unsigned, i.e. cannot test if less than 0. + */ + tmpAbsMilliseconds = (int64_t)abstime->tv_sec * MILLISEC_PER_SEC; + tmpAbsMilliseconds += ((int64_t)abstime->tv_nsec + (NANOSEC_PER_MILLISEC/2)) / NANOSEC_PER_MILLISEC; + + /* get current system time */ + +#ifdef NEED_FTIME + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + /* + * GetSystemTimeAsFileTime(&ft); would be faster, + * but it does not exist on WinCE + */ + + ptw32_filetime_to_timespec(&ft, &currSysTime); + + tmpCurrMilliseconds = (int64_t)currSysTime.tv_sec * MILLISEC_PER_SEC; + tmpCurrMilliseconds += ((int64_t)currSysTime.tv_nsec + (NANOSEC_PER_MILLISEC/2)) + / NANOSEC_PER_MILLISEC; + +#else /* ! NEED_FTIME */ + + _ftime64_s(&currSysTime); + + tmpCurrMilliseconds = (int64_t) currSysTime.time * MILLISEC_PER_SEC; + tmpCurrMilliseconds += (int64_t) currSysTime.millitm; + +#endif /* NEED_FTIME */ + + if (tmpAbsMilliseconds > tmpCurrMilliseconds) + { + milliseconds = (DWORD) (tmpAbsMilliseconds - tmpCurrMilliseconds); + if (milliseconds == INFINITE) + { + /* Timeouts must be finite */ + milliseconds--; + } + } + else + { + /* The abstime given is in the past */ + milliseconds = 0; + } + + return milliseconds; +} diff --git a/pcsx2/windows/pthreads/ptw32_reuse.c b/pcsx2/windows/pthreads/ptw32_reuse.c new file mode 100644 index 0000000000..950759be37 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_reuse.c @@ -0,0 +1,147 @@ +/* + * ptw32_threadReuse.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +/* + * How it works: + * A pthread_t is a struct (2x32 bit scalar types on IA-32, 2x64 bit on IA-64) + * which is normally passed/returned by value to/from pthreads routines. + * Applications are therefore storing a copy of the struct as it is at that + * time. + * + * The original pthread_t struct plus all copies of it contain the address of + * the thread state struct ptw32_thread_t_ (p), plus a reuse counter (x). Each + * ptw32_thread_t contains the original copy of it's pthread_t. + * Once malloced, a ptw32_thread_t_ struct is not freed until the process exits. + * + * The thread reuse stack is a simple LILO stack managed through a singly + * linked list element in the ptw32_thread_t. + * + * Each time a thread is destroyed, the ptw32_thread_t address is pushed onto the + * reuse stack after it's ptHandle's reuse counter has been incremented. + * + * The following can now be said from this: + * - two pthread_t's are identical if their ptw32_thread_t reference pointers + * are equal and their reuse counters are equal. That is, + * + * equal = (a.p == b.p && a.x == b.x) + * + * - a pthread_t copy refers to a destroyed thread if the reuse counter in + * the copy is not equal to the reuse counter in the original. + * + * threadDestroyed = (copy.x != ((ptw32_thread_t *)copy.p)->ptHandle.x) + * + */ + +/* + * Pop a clean pthread_t struct off the reuse stack. + */ +pthread_t +ptw32_threadReusePop (void) +{ + pthread_t t = {NULL, 0}; + + EnterCriticalSection (&ptw32_thread_reuse_lock); + + if (PTW32_THREAD_REUSE_EMPTY != ptw32_threadReuseTop) + { + ptw32_thread_t * tp; + + tp = ptw32_threadReuseTop; + + ptw32_threadReuseTop = tp->prevReuse; + + if (PTW32_THREAD_REUSE_EMPTY == ptw32_threadReuseTop) + { + ptw32_threadReuseBottom = PTW32_THREAD_REUSE_EMPTY; + } + + tp->prevReuse = NULL; + + t = tp->ptHandle; + } + + LeaveCriticalSection (&ptw32_thread_reuse_lock); + + return t; + +} + +/* + * Push a clean pthread_t struct onto the reuse stack. + * Must be re-initialised when reused. + * All object elements (mutexes, events etc) must have been either + * detroyed before this, or never initialised. + */ +void +ptw32_threadReusePush (pthread_t thread) +{ + ptw32_thread_t * tp = (ptw32_thread_t *) thread.p; + pthread_t t; + + EnterCriticalSection (&ptw32_thread_reuse_lock); + + t = tp->ptHandle; + memset(tp, 0, sizeof(ptw32_thread_t)); + + /* Must restore the original POSIX handle that we just wiped. */ + tp->ptHandle = t; + + /* Bump the reuse counter now */ +#ifdef PTW32_THREAD_ID_REUSE_INCREMENT + tp->ptHandle.x += PTW32_THREAD_ID_REUSE_INCREMENT; +#else + tp->ptHandle.x++; +#endif + + tp->prevReuse = PTW32_THREAD_REUSE_EMPTY; + + if (PTW32_THREAD_REUSE_EMPTY != ptw32_threadReuseBottom) + { + ptw32_threadReuseBottom->prevReuse = tp; + } + else + { + ptw32_threadReuseTop = tp; + } + + ptw32_threadReuseBottom = tp; + + LeaveCriticalSection (&ptw32_thread_reuse_lock); +} diff --git a/pcsx2/windows/pthreads/ptw32_rwlock_cancelwrwait.c b/pcsx2/windows/pthreads/ptw32_rwlock_cancelwrwait.c new file mode 100644 index 0000000000..7649a3eb1e --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_rwlock_cancelwrwait.c @@ -0,0 +1,50 @@ +/* + * ptw32_rwlock_cancelwrwait.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +void +ptw32_rwlock_cancelwrwait (void *arg) +{ + pthread_rwlock_t rwl = (pthread_rwlock_t) arg; + + rwl->nSharedAccessCount = -rwl->nCompletedSharedAccessCount; + rwl->nCompletedSharedAccessCount = 0; + + (void) pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted)); + (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)); +} diff --git a/pcsx2/windows/pthreads/ptw32_rwlock_check_need_init.c b/pcsx2/windows/pthreads/ptw32_rwlock_check_need_init.c new file mode 100644 index 0000000000..f615e7c766 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_rwlock_check_need_init.c @@ -0,0 +1,93 @@ +/* + * pthread_rwlock_check_need_init.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +INLINE int +ptw32_rwlock_check_need_init (pthread_rwlock_t * rwlock) +{ + int result = 0; + + /* + * The following guarded test is specifically for statically + * initialised rwlocks (via PTHREAD_RWLOCK_INITIALIZER). + * + * Note that by not providing this synchronisation we risk + * introducing race conditions into applications which are + * correctly written. + * + * Approach + * -------- + * We know that static rwlocks will not be PROCESS_SHARED + * so we can serialise access to internal state using + * Win32 Critical Sections rather than Win32 Mutexes. + * + * If using a single global lock slows applications down too much, + * multiple global locks could be created and hashed on some random + * value associated with each mutex, the pointer perhaps. At a guess, + * a good value for the optimal number of global locks might be + * the number of processors + 1. + * + */ + EnterCriticalSection (&ptw32_rwlock_test_init_lock); + + /* + * We got here possibly under race + * conditions. Check again inside the critical section + * and only initialise if the rwlock is valid (not been destroyed). + * If a static rwlock has been destroyed, the application can + * re-initialise it only by calling pthread_rwlock_init() + * explicitly. + */ + if (*rwlock == PTHREAD_RWLOCK_INITIALIZER) + { + result = pthread_rwlock_init (rwlock, NULL); + } + else if (*rwlock == NULL) + { + /* + * The rwlock has been destroyed while we were waiting to + * initialise it, so the operation that caused the + * auto-initialisation should fail. + */ + result = EINVAL; + } + + LeaveCriticalSection (&ptw32_rwlock_test_init_lock); + + return result; +} diff --git a/pcsx2/windows/pthreads/ptw32_semwait.c b/pcsx2/windows/pthreads/ptw32_semwait.c new file mode 100644 index 0000000000..209c8a6e75 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_semwait.c @@ -0,0 +1,118 @@ +/* + * ptw32_semwait.c + * + * Description: + * This translation unit implements mutual exclusion (mutex) primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef _UWIN +//# include +#endif +#include "pthread.h" +#include "implement.h" + + +int +ptw32_semwait (sem_t * sem) + /* + * ------------------------------------------------------ + * DESCRIPTION + * This function waits on a POSIX semaphore. If the + * semaphore value is greater than zero, it decreases + * its value by one. If the semaphore value is zero, then + * the calling thread (or process) is blocked until it can + * successfully decrease the value. + * + * Unlike sem_wait(), this routine is non-cancelable. + * + * RESULTS + * 0 successfully decreased semaphore, + * -1 failed, error in errno. + * ERRNO + * EINVAL 'sem' is not a valid semaphore, + * ENOSYS semaphores are not supported, + * EINTR the function was interrupted by a signal, + * EDEADLK a deadlock condition was detected. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + sem_t s = *sem; + + if (s == NULL) + { + result = EINVAL; + } + else + { + if ((result = pthread_mutex_lock (&s->lock)) == 0) + { + int v = --s->value; + + (void) pthread_mutex_unlock (&s->lock); + + if (v < 0) + { + /* Must wait */ + if (WaitForSingleObject (s->sem, INFINITE) == WAIT_OBJECT_0) + { +#ifdef NEED_SEM + if (pthread_mutex_lock (&s->lock) == 0) + { + if (s->leftToUnblock > 0) + { + --s->leftToUnblock; + SetEvent(s->sem); + } + (void) pthread_mutex_unlock (&s->lock); + } +#endif + return 0; + } + } + else + { + return 0; + } + } + } + + if (result != 0) + { + errno = result; + return -1; + } + + return 0; + +} /* ptw32_semwait */ diff --git a/pcsx2/windows/pthreads/ptw32_spinlock_check_need_init.c b/pcsx2/windows/pthreads/ptw32_spinlock_check_need_init.c new file mode 100644 index 0000000000..dcc8b98d5f --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_spinlock_check_need_init.c @@ -0,0 +1,81 @@ +/* + * ptw32_spinlock_check_need_init.c + * + * Description: + * This translation unit implements spin lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +INLINE int +ptw32_spinlock_check_need_init (pthread_spinlock_t * lock) +{ + int result = 0; + + /* + * The following guarded test is specifically for statically + * initialised spinlocks (via PTHREAD_SPINLOCK_INITIALIZER). + * + * Note that by not providing this synchronisation we risk + * introducing race conditions into applications which are + * correctly written. + */ + EnterCriticalSection (&ptw32_spinlock_test_init_lock); + + /* + * We got here possibly under race + * conditions. Check again inside the critical section + * and only initialise if the spinlock is valid (not been destroyed). + * If a static spinlock has been destroyed, the application can + * re-initialise it only by calling pthread_spin_init() + * explicitly. + */ + if (*lock == PTHREAD_SPINLOCK_INITIALIZER) + { + result = pthread_spin_init (lock, PTHREAD_PROCESS_PRIVATE); + } + else if (*lock == NULL) + { + /* + * The spinlock has been destroyed while we were waiting to + * initialise it, so the operation that caused the + * auto-initialisation should fail. + */ + result = EINVAL; + } + + LeaveCriticalSection (&ptw32_spinlock_test_init_lock); + + return (result); +} diff --git a/pcsx2/windows/pthreads/ptw32_threadDestroy.c b/pcsx2/windows/pthreads/ptw32_threadDestroy.c new file mode 100644 index 0000000000..a88a2b0b81 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_threadDestroy.c @@ -0,0 +1,82 @@ +/* + * ptw32_threadDestroy.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +void +ptw32_threadDestroy (pthread_t thread) +{ + ptw32_thread_t * tp = (ptw32_thread_t *) thread.p; + ptw32_thread_t threadCopy; + + if (tp != NULL) + { + /* + * Copy thread state so that the thread can be atomically NULLed. + */ + memcpy (&threadCopy, tp, sizeof (threadCopy)); + + /* + * Thread ID structs are never freed. They're NULLed and reused. + * This also sets the thread to PThreadStateInitial (invalid). + */ + ptw32_threadReusePush (thread); + + /* Now work on the copy. */ + if (threadCopy.cancelEvent != NULL) + { + CloseHandle (threadCopy.cancelEvent); + } + + (void) pthread_mutex_destroy(&threadCopy.cancelLock); + (void) pthread_mutex_destroy(&threadCopy.threadLock); + +#if ! defined (__MINGW32__) || defined (__MSVCRT__) || defined (__DMC__) + /* + * See documentation for endthread vs endthreadex. + */ + if (threadCopy.threadH != 0) + { + CloseHandle (threadCopy.threadH); + } +#endif + + } +} /* ptw32_threadDestroy */ + diff --git a/pcsx2/windows/pthreads/ptw32_threadStart.c b/pcsx2/windows/pthreads/ptw32_threadStart.c new file mode 100644 index 0000000000..fbee09dcf2 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_threadStart.c @@ -0,0 +1,360 @@ +/* + * ptw32_threadStart.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +#ifdef __CLEANUP_SEH + +static DWORD +ExceptionFilter (EXCEPTION_POINTERS * ep, DWORD * ei) +{ + switch (ep->ExceptionRecord->ExceptionCode) + { + case EXCEPTION_PTW32_SERVICES: + { + DWORD param; + DWORD numParams = ep->ExceptionRecord->NumberParameters; + + numParams = (numParams > 3) ? 3 : numParams; + + for (param = 0; param < numParams; param++) + { + ei[param] = ep->ExceptionRecord->ExceptionInformation[param]; + } + + return EXCEPTION_EXECUTE_HANDLER; + break; + } + default: + { + /* + * A system unexpected exception has occurred running the user's + * routine. We need to cleanup before letting the exception + * out of thread scope. + */ + pthread_t self = pthread_self (); + + (void) pthread_mutex_destroy (&((ptw32_thread_t *)self.p)->cancelLock); + ptw32_callUserDestroyRoutines (self); + + return EXCEPTION_CONTINUE_SEARCH; + break; + } + } +} + +#elif defined(__CLEANUP_CXX) + +#if defined(_MSC_VER) +# include +#elif defined(__WATCOMC__) +# include +# include +typedef terminate_handler + terminate_function; +#else +# if defined(__GNUC__) && __GNUC__ < 3 +# include +# else +# include +using + std::terminate_handler; +using + std::terminate; +using + std::set_terminate; +# endif +typedef terminate_handler + terminate_function; +#endif + +static terminate_function + ptw32_oldTerminate; + +void +ptw32_terminate () +{ + set_terminate (ptw32_oldTerminate); + (void) pthread_win32_thread_detach_np (); + terminate (); +} + +#endif + +#if ! defined (__MINGW32__) || (defined (__MSVCRT__) && ! defined (__DMC__)) +unsigned + __stdcall +#else +void +#endif +ptw32_threadStart (void *vthreadParms) +{ + ThreadParms * threadParms = (ThreadParms *) vthreadParms; + pthread_t self; + ptw32_thread_t * sp; + void *(*start) (void *); + void * arg; + +#ifdef __CLEANUP_SEH + DWORD + ei[] = { 0, 0, 0 }; +#endif + +#ifdef __CLEANUP_C + int setjmp_rc; +#endif + + void * status = (void *) 0; + + self = threadParms->tid; + sp = (ptw32_thread_t *) self.p; + start = threadParms->start; + arg = threadParms->arg; + + free (threadParms); + +#if defined (__MINGW32__) && ! defined (__MSVCRT__) + /* + * beginthread does not return the thread id and is running + * before it returns us the thread handle, and so we do it here. + */ + sp->thread = GetCurrentThreadId (); + /* + * Here we're using cancelLock as a general-purpose lock + * to make the new thread wait until the creating thread + * has the new handle. + */ + if (pthread_mutex_lock (&sp->cancelLock) == 0) + { + (void) pthread_mutex_unlock (&sp->cancelLock); + } +#endif + + pthread_setspecific (ptw32_selfThreadKey, sp); + + sp->state = PThreadStateRunning; + +#ifdef __CLEANUP_SEH + + __try + { + /* + * Run the caller's routine; + */ + status = sp->exitStatus = (*start) (arg); + +#ifdef _UWIN + if (--pthread_count <= 0) + exit (0); +#endif + + } + __except (ExceptionFilter (GetExceptionInformation (), ei)) + { + switch (ei[0]) + { + case PTW32_EPS_CANCEL: + status = sp->exitStatus = PTHREAD_CANCELED; +#ifdef _UWIN + if (--pthread_count <= 0) + exit (0); +#endif + break; + case PTW32_EPS_EXIT: + status = sp->exitStatus; + break; + default: + status = sp->exitStatus = PTHREAD_CANCELED; + break; + } + } + +#else /* __CLEANUP_SEH */ + +#ifdef __CLEANUP_C + + setjmp_rc = setjmp (sp->start_mark); + + if (0 == setjmp_rc) + { + + /* + * Run the caller's routine; + */ + status = sp->exitStatus = (*start) (arg); + } + else + { + switch (setjmp_rc) + { + case PTW32_EPS_CANCEL: + status = sp->exitStatus = PTHREAD_CANCELED; + break; + case PTW32_EPS_EXIT: + status = sp->exitStatus; + break; + default: + status = sp->exitStatus = PTHREAD_CANCELED; + break; + } + } + +#else /* __CLEANUP_C */ + +#ifdef __CLEANUP_CXX + + ptw32_oldTerminate = set_terminate (&ptw32_terminate); + + try + { + /* + * Run the caller's routine in a nested try block so that we + * can run the user's terminate function, which may call + * pthread_exit() or be canceled. + */ + try + { + status = sp->exitStatus = (*start) (arg); + } + catch (ptw32_exception &) + { + /* + * Pass these through to the outer block. + */ + throw; + } + catch (...) + { + /* + * We want to run the user's terminate function if supplied. + * That function may call pthread_exit() or be canceled, which will + * be handled by the outer try block. + * + * ptw32_terminate() will be called if there is no user + * supplied function. + */ + + terminate_function + term_func = set_terminate (0); + set_terminate (term_func); + + if (term_func != 0) + { + term_func (); + } + + throw; + } + } + catch (ptw32_exception_cancel &) + { + /* + * Thread was canceled. + */ + status = sp->exitStatus = PTHREAD_CANCELED; + } + catch (ptw32_exception_exit &) + { + /* + * Thread was exited via pthread_exit(). + */ + status = sp->exitStatus; + } + catch (...) + { + /* + * A system unexpected exception has occurred running the user's + * terminate routine. We get control back within this block - cleanup + * and release the exception out of thread scope. + */ + status = sp->exitStatus = PTHREAD_CANCELED; + (void) pthread_mutex_lock (&sp->cancelLock); + sp->state = PThreadStateException; + (void) pthread_mutex_unlock (&sp->cancelLock); + (void) pthread_win32_thread_detach_np (); + (void) set_terminate (ptw32_oldTerminate); + throw; + + /* + * Never reached. + */ + } + + (void) set_terminate (ptw32_oldTerminate); + +#else + +#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined. + +#endif /* __CLEANUP_CXX */ +#endif /* __CLEANUP_C */ +#endif /* __CLEANUP_SEH */ + +#if defined(PTW32_STATIC_LIB) + /* + * We need to cleanup the pthread now if we have + * been statically linked, in which case the cleanup + * in dllMain won't get done. Joinable threads will + * only be partially cleaned up and must be fully cleaned + * up by pthread_join() or pthread_detach(). + * + * Note: if this library has been statically linked, + * implicitly created pthreads (those created + * for Win32 threads which have called pthreads routines) + * must be cleaned up explicitly by the application + * (by calling pthread_win32_thread_detach_np()). + * For the dll, dllMain will do the cleanup automatically. + */ + (void) pthread_win32_thread_detach_np (); +#endif + +#if ! defined (__MINGW32__) || defined (__MSVCRT__) || defined (__DMC__) + _endthreadex ((unsigned) status); +#else + _endthread (); +#endif + + /* + * Never reached. + */ + +#if ! defined (__MINGW32__) || defined (__MSVCRT__) || defined (__DMC__) + return (unsigned) status; +#endif + +} /* ptw32_threadStart */ diff --git a/pcsx2/windows/pthreads/ptw32_throw.c b/pcsx2/windows/pthreads/ptw32_throw.c new file mode 100644 index 0000000000..cc9ee86639 --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_throw.c @@ -0,0 +1,167 @@ +/* + * ptw32_throw.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + +/* + * ptw32_throw + * + * All canceled and explicitly exited POSIX threads go through + * here. This routine knows how to exit both POSIX initiated threads and + * 'implicit' POSIX threads for each of the possible language modes (C, + * C++, and SEH). + */ +void +ptw32_throw (DWORD exception) +{ + /* + * Don't use pthread_self() to avoid creating an implicit POSIX thread handle + * unnecessarily. + */ + ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); + +#ifdef __CLEANUP_SEH + DWORD exceptionInformation[3]; +#endif + + if (exception != PTW32_EPS_CANCEL && exception != PTW32_EPS_EXIT) + { + /* Should never enter here */ + exit (1); + } + + if (NULL == sp || sp->implicit) + { + /* + * We're inside a non-POSIX initialised Win32 thread + * so there is no point to jump or throw back to. Just do an + * explicit thread exit here after cleaning up POSIX + * residue (i.e. cleanup handlers, POSIX thread handle etc). + */ + unsigned exitCode = 0; + + switch (exception) + { + case PTW32_EPS_CANCEL: + exitCode = (unsigned) PTHREAD_CANCELED; + break; + case PTW32_EPS_EXIT: + exitCode = (unsigned) sp->exitStatus;; + break; + } + +#if defined(PTW32_STATIC_LIB) + + pthread_win32_thread_detach_np (); + +#endif + +#if ! defined (__MINGW32__) || defined (__MSVCRT__) || defined (__DMC__) + _endthreadex (exitCode); +#else + _endthread (); +#endif + + } + +#ifdef __CLEANUP_SEH + + + exceptionInformation[0] = (DWORD) (exception); + exceptionInformation[1] = (DWORD) (0); + exceptionInformation[2] = (DWORD) (0); + + RaiseException (EXCEPTION_PTW32_SERVICES, 0, 3, exceptionInformation); + +#else /* __CLEANUP_SEH */ + +#ifdef __CLEANUP_C + + ptw32_pop_cleanup_all (1); + longjmp (sp->start_mark, exception); + +#else /* __CLEANUP_C */ + +#ifdef __CLEANUP_CXX + + switch (exception) + { + case PTW32_EPS_CANCEL: + throw ptw32_exception_cancel (); + break; + case PTW32_EPS_EXIT: + throw ptw32_exception_exit (); + break; + } + +#else + +#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined. + +#endif /* __CLEANUP_CXX */ + +#endif /* __CLEANUP_C */ + +#endif /* __CLEANUP_SEH */ + + /* Never reached */ +} + + +void +ptw32_pop_cleanup_all (int execute) +{ + while (NULL != ptw32_pop_cleanup (execute)) + { + } +} + + +DWORD +ptw32_get_exception_services_code (void) +{ +#ifdef __CLEANUP_SEH + + return EXCEPTION_PTW32_SERVICES; + +#else + + return (DWORD) NULL; + +#endif +} diff --git a/pcsx2/windows/pthreads/ptw32_timespec.c b/pcsx2/windows/pthreads/ptw32_timespec.c new file mode 100644 index 0000000000..629be1572f --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_timespec.c @@ -0,0 +1,83 @@ +/* + * ptw32_timespec.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +#ifdef NEED_FTIME + +/* + * time between jan 1, 1601 and jan 1, 1970 in units of 100 nanoseconds + */ +#define PTW32_TIMESPEC_TO_FILETIME_OFFSET \ + ( ((LONGLONG) 27111902 << 32) + (LONGLONG) 3577643008 ) + +INLINE void +ptw32_timespec_to_filetime (const struct timespec *ts, FILETIME * ft) + /* + * ------------------------------------------------------------------- + * converts struct timespec + * where the time is expressed in seconds and nanoseconds from Jan 1, 1970. + * into FILETIME (as set by GetSystemTimeAsFileTime), where the time is + * expressed in 100 nanoseconds from Jan 1, 1601, + * ------------------------------------------------------------------- + */ +{ + *(LONGLONG *) ft = ts->tv_sec * 10000000 + + (ts->tv_nsec + 50) / 100 + PTW32_TIMESPEC_TO_FILETIME_OFFSET; +} + +INLINE void +ptw32_filetime_to_timespec (const FILETIME * ft, struct timespec *ts) + /* + * ------------------------------------------------------------------- + * converts FILETIME (as set by GetSystemTimeAsFileTime), where the time is + * expressed in 100 nanoseconds from Jan 1, 1601, + * into struct timespec + * where the time is expressed in seconds and nanoseconds from Jan 1, 1970. + * ------------------------------------------------------------------- + */ +{ + ts->tv_sec = + (int) ((*(LONGLONG *) ft - PTW32_TIMESPEC_TO_FILETIME_OFFSET) / 10000000); + ts->tv_nsec = + (int) ((*(LONGLONG *) ft - PTW32_TIMESPEC_TO_FILETIME_OFFSET - + ((LONGLONG) ts->tv_sec * (LONGLONG) 10000000)) * 100); +} + +#endif /* NEED_FTIME */ diff --git a/pcsx2/windows/pthreads/ptw32_tkAssocCreate.c b/pcsx2/windows/pthreads/ptw32_tkAssocCreate.c new file mode 100644 index 0000000000..411c9cd88e --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_tkAssocCreate.c @@ -0,0 +1,118 @@ +/* + * ptw32_tkAssocCreate.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +int +ptw32_tkAssocCreate (ptw32_thread_t * sp, pthread_key_t key) + /* + * ------------------------------------------------------------------- + * This routine creates an association that + * is unique for the given (thread,key) combination.The association + * is referenced by both the thread and the key. + * This association allows us to determine what keys the + * current thread references and what threads a given key + * references. + * See the detailed description + * at the beginning of this file for further details. + * + * Notes: + * 1) New associations are pushed to the beginning of the + * chain so that the internal ptw32_selfThreadKey association + * is always last, thus allowing selfThreadExit to + * be implicitly called last by pthread_exit. + * 2) + * + * Parameters: + * thread + * current running thread. + * key + * key on which to create an association. + * Returns: + * 0 - if successful, + * ENOMEM - not enough memory to create assoc or other object + * EINVAL - an internal error occurred + * ENOSYS - an internal error occurred + * ------------------------------------------------------------------- + */ +{ + ThreadKeyAssoc *assoc; + + /* + * Have to create an association and add it + * to both the key and the thread. + * + * Both key->keyLock and thread->threadLock are locked on + * entry to this routine. + */ + assoc = (ThreadKeyAssoc *) calloc (1, sizeof (*assoc)); + + if (assoc == NULL) + { + return ENOMEM; + } + + assoc->thread = sp; + assoc->key = key; + + /* + * Register assoc with key + */ + assoc->prevThread = NULL; + assoc->nextThread = (ThreadKeyAssoc *) key->threads; + if (assoc->nextThread != NULL) + { + assoc->nextThread->prevThread = assoc; + } + key->threads = (void *) assoc; + + /* + * Register assoc with thread + */ + assoc->prevKey = NULL; + assoc->nextKey = (ThreadKeyAssoc *) sp->keys; + if (assoc->nextKey != NULL) + { + assoc->nextKey->prevKey = assoc; + } + sp->keys = (void *) assoc; + + return (0); + +} /* ptw32_tkAssocCreate */ diff --git a/pcsx2/windows/pthreads/ptw32_tkAssocDestroy.c b/pcsx2/windows/pthreads/ptw32_tkAssocDestroy.c new file mode 100644 index 0000000000..d50178fede --- /dev/null +++ b/pcsx2/windows/pthreads/ptw32_tkAssocDestroy.c @@ -0,0 +1,114 @@ +/* + * ptw32_tkAssocDestroy.c + * + * Description: + * This translation unit implements routines which are private to + * the implementation and may be used throughout it. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +void +ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc) + /* + * ------------------------------------------------------------------- + * This routine releases all resources for the given ThreadKeyAssoc + * once it is no longer being referenced + * ie) either the key or thread has stopped referencing it. + * + * Parameters: + * assoc + * an instance of ThreadKeyAssoc. + * Returns: + * N/A + * ------------------------------------------------------------------- + */ +{ + + /* + * Both key->keyLock and thread->threadLock are locked on + * entry to this routine. + */ + if (assoc != NULL) + { + ThreadKeyAssoc * prev, * next; + + /* Remove assoc from thread's keys chain */ + prev = assoc->prevKey; + next = assoc->nextKey; + if (prev != NULL) + { + prev->nextKey = next; + } + if (next != NULL) + { + next->prevKey = prev; + } + + if (assoc->thread->keys == assoc) + { + /* We're at the head of the thread's keys chain */ + assoc->thread->keys = next; + } + if (assoc->thread->nextAssoc == assoc) + { + /* + * Thread is exiting and we're deleting the assoc to be processed next. + * Hand thread the assoc after this one. + */ + assoc->thread->nextAssoc = next; + } + + /* Remove assoc from key's threads chain */ + prev = assoc->prevThread; + next = assoc->nextThread; + if (prev != NULL) + { + prev->nextThread = next; + } + if (next != NULL) + { + next->prevThread = prev; + } + + if (assoc->key->threads == assoc) + { + /* We're at the head of the key's threads chain */ + assoc->key->threads = next; + } + + free (assoc); + } + +} /* ptw32_tkAssocDestroy */ diff --git a/pcsx2/windows/pthreads/rwlock.c b/pcsx2/windows/pthreads/rwlock.c new file mode 100644 index 0000000000..85d39c75b4 --- /dev/null +++ b/pcsx2/windows/pthreads/rwlock.c @@ -0,0 +1,51 @@ +/* + * rwlock.c + * + * Description: + * This translation unit implements read/write lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "ptw32_rwlock_check_need_init.c" +#include "ptw32_rwlock_cancelwrwait.c" +#include "pthread_rwlock_init.c" +#include "pthread_rwlock_destroy.c" +#include "pthread_rwlockattr_init.c" +#include "pthread_rwlockattr_destroy.c" +#include "pthread_rwlockattr_getpshared.c" +#include "pthread_rwlockattr_setpshared.c" +#include "pthread_rwlock_rdlock.c" +#include "pthread_rwlock_timedrdlock.c" +#include "pthread_rwlock_wrlock.c" +#include "pthread_rwlock_timedwrlock.c" +#include "pthread_rwlock_unlock.c" +#include "pthread_rwlock_tryrdlock.c" +#include "pthread_rwlock_trywrlock.c" diff --git a/pcsx2/windows/pthreads/sched.c b/pcsx2/windows/pthreads/sched.c new file mode 100644 index 0000000000..ec90e85f38 --- /dev/null +++ b/pcsx2/windows/pthreads/sched.c @@ -0,0 +1,53 @@ +/* + * sched.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +#include "pthread_attr_setschedpolicy.c" +#include "pthread_attr_getschedpolicy.c" +#include "pthread_attr_setschedparam.c" +#include "pthread_attr_getschedparam.c" +#include "pthread_attr_setinheritsched.c" +#include "pthread_attr_getinheritsched.c" +#include "pthread_setschedparam.c" +#include "pthread_getschedparam.c" +#include "sched_get_priority_max.c" +#include "sched_get_priority_min.c" +#include "sched_setscheduler.c" +#include "sched_getscheduler.c" +#include "sched_yield.c" diff --git a/pcsx2/windows/pthreads/sched.h b/pcsx2/windows/pthreads/sched.h new file mode 100644 index 0000000000..10ecb5d7ea --- /dev/null +++ b/pcsx2/windows/pthreads/sched.h @@ -0,0 +1,178 @@ +/* + * Module: sched.h + * + * Purpose: + * Provides an implementation of POSIX realtime extensions + * as defined in + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#ifndef _SCHED_H +#define _SCHED_H + +#undef PTW32_LEVEL + +#if defined(_POSIX_SOURCE) +#define PTW32_LEVEL 0 +/* Early POSIX */ +#endif + +#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309 +#undef PTW32_LEVEL +#define PTW32_LEVEL 1 +/* Include 1b, 1c and 1d */ +#endif + +#if defined(INCLUDE_NP) +#undef PTW32_LEVEL +#define PTW32_LEVEL 2 +/* Include Non-Portable extensions */ +#endif + +#define PTW32_LEVEL_MAX 3 + +#if !defined(PTW32_LEVEL) +#define PTW32_LEVEL PTW32_LEVEL_MAX +/* Include everything */ +#endif + + +#if __GNUC__ && ! defined (__declspec) +# error Please upgrade your GNU compiler to one that supports __declspec. +#endif + +/* + * When building the DLL code, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the DLL, + * do NOT define PTW32_BUILD, and then the variables/functions will + * be imported correctly. + */ +#ifndef PTW32_STATIC_LIB +# ifdef PTW32_BUILD +# define PTW32_DLLPORT __declspec (dllexport) +# else +# define PTW32_DLLPORT __declspec (dllimport) +# endif +#else +# define PTW32_DLLPORT +#endif + +/* + * This is a duplicate of what is in the autoconf config.h, + * which is only used when building the pthread-win32 libraries. + */ + +#ifndef PTW32_CONFIG_H +# if defined(WINCE) +# define NEED_ERRNO +# define NEED_SEM +# endif +# if defined(_UWIN) || defined(__MINGW32__) +# define HAVE_MODE_T +# endif +#endif + +/* + * + */ + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX +#ifdef NEED_ERRNO +#include "need_errno.h" +#else +#include +#endif +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +#if defined(__MINGW32__) || defined(_UWIN) +#if PTW32_LEVEL >= PTW32_LEVEL_MAX +/* For pid_t */ +# include +/* Required by Unix 98 */ +# include +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ +#else +typedef int pid_t; +#endif + +/* Thread scheduling policies */ + +enum { + SCHED_OTHER = 0, + SCHED_FIFO, + SCHED_RR, + SCHED_MIN = SCHED_OTHER, + SCHED_MAX = SCHED_RR +}; + +struct sched_param { + int sched_priority; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +PTW32_DLLPORT int __cdecl sched_yield (void); + +PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy); + +PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy); + +PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy); + +PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid); + +/* + * Note that this macro returns ENOTSUP rather than + * ENOSYS as might be expected. However, returning ENOSYS + * should mean that sched_get_priority_{min,max} are + * not implemented as well as sched_rr_get_interval. + * This is not the case, since we just don't support + * round-robin scheduling. Therefore I have chosen to + * return the same value as sched_setscheduler when + * SCHED_RR is passed to it. + */ +#define sched_rr_get_interval(_pid, _interval) \ + ( errno = ENOTSUP, (int) -1 ) + + +#ifdef __cplusplus +} /* End of extern "C" */ +#endif /* __cplusplus */ + +#undef PTW32_LEVEL +#undef PTW32_LEVEL_MAX + +#endif /* !_SCHED_H */ + diff --git a/pcsx2/windows/pthreads/sched_get_priority_max.c b/pcsx2/windows/pthreads/sched_get_priority_max.c new file mode 100644 index 0000000000..b69f82e21d --- /dev/null +++ b/pcsx2/windows/pthreads/sched_get_priority_max.c @@ -0,0 +1,134 @@ +/* + * sched_get_priority_max.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +/* + * On Windows98, THREAD_PRIORITY_LOWEST is (-2) and + * THREAD_PRIORITY_HIGHEST is 2, and everything works just fine. + * + * On WinCE 3.0, it so happen that THREAD_PRIORITY_LOWEST is 5 + * and THREAD_PRIORITY_HIGHEST is 1 (yes, I know, it is funny: + * highest priority use smaller numbers) and the following happens: + * + * sched_get_priority_min() returns 5 + * sched_get_priority_max() returns 1 + * + * The following table shows the base priority levels for combinations + * of priority class and priority value in Win32. + * + * Process Priority Class Thread Priority Level + * ----------------------------------------------------------------- + * 1 IDLE_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 1 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 1 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 1 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 1 HIGH_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 2 IDLE_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 3 IDLE_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 4 IDLE_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 4 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 5 IDLE_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 5 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 5 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 6 IDLE_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 6 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 6 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 7 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 7 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 7 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 8 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 8 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 8 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 8 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 9 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 9 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 9 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 10 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 10 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 11 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 11 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 11 HIGH_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 12 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 12 HIGH_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 13 HIGH_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 14 HIGH_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 15 HIGH_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 15 HIGH_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * 15 IDLE_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * 15 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * 15 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * 15 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * 16 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 17 REALTIME_PRIORITY_CLASS -7 + * 18 REALTIME_PRIORITY_CLASS -6 + * 19 REALTIME_PRIORITY_CLASS -5 + * 20 REALTIME_PRIORITY_CLASS -4 + * 21 REALTIME_PRIORITY_CLASS -3 + * 22 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 23 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 24 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 25 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 26 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 27 REALTIME_PRIORITY_CLASS 3 + * 28 REALTIME_PRIORITY_CLASS 4 + * 29 REALTIME_PRIORITY_CLASS 5 + * 30 REALTIME_PRIORITY_CLASS 6 + * 31 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * + * Windows NT: Values -7, -6, -5, -4, -3, 3, 4, 5, and 6 are not supported. + */ + + +int +sched_get_priority_max (int policy) +{ + if (policy < SCHED_MIN || policy > SCHED_MAX) + { + errno = EINVAL; + return -1; + } + +#if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL) + /* WinCE? */ + return PTW32_MAX (THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL); +#else + /* This is independent of scheduling policy in Win32. */ + return PTW32_MAX (THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL); +#endif +} diff --git a/pcsx2/windows/pthreads/sched_get_priority_min.c b/pcsx2/windows/pthreads/sched_get_priority_min.c new file mode 100644 index 0000000000..3c6d24aa4c --- /dev/null +++ b/pcsx2/windows/pthreads/sched_get_priority_min.c @@ -0,0 +1,135 @@ +/* + * sched_get_priority_min.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +/* + * On Windows98, THREAD_PRIORITY_LOWEST is (-2) and + * THREAD_PRIORITY_HIGHEST is 2, and everything works just fine. + * + * On WinCE 3.0, it so happen that THREAD_PRIORITY_LOWEST is 5 + * and THREAD_PRIORITY_HIGHEST is 1 (yes, I know, it is funny: + * highest priority use smaller numbers) and the following happens: + * + * sched_get_priority_min() returns 5 + * sched_get_priority_max() returns 1 + * + * The following table shows the base priority levels for combinations + * of priority class and priority value in Win32. + * + * Process Priority Class Thread Priority Level + * ----------------------------------------------------------------- + * 1 IDLE_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 1 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 1 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 1 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 1 HIGH_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 2 IDLE_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 3 IDLE_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 4 IDLE_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 4 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 5 IDLE_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 5 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 5 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 6 IDLE_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 6 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 6 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 7 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 7 Background NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 7 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 8 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 8 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 8 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 8 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 9 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 9 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 9 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 10 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 10 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 11 Foreground NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 11 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 11 HIGH_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 12 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 12 HIGH_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 13 HIGH_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 14 HIGH_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 15 HIGH_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 15 HIGH_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * 15 IDLE_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * 15 BELOW_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * 15 NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * 15 ABOVE_NORMAL_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * 16 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_IDLE + * 17 REALTIME_PRIORITY_CLASS -7 + * 18 REALTIME_PRIORITY_CLASS -6 + * 19 REALTIME_PRIORITY_CLASS -5 + * 20 REALTIME_PRIORITY_CLASS -4 + * 21 REALTIME_PRIORITY_CLASS -3 + * 22 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_LOWEST + * 23 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_BELOW_NORMAL + * 24 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_NORMAL + * 25 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_ABOVE_NORMAL + * 26 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_HIGHEST + * 27 REALTIME_PRIORITY_CLASS 3 + * 28 REALTIME_PRIORITY_CLASS 4 + * 29 REALTIME_PRIORITY_CLASS 5 + * 30 REALTIME_PRIORITY_CLASS 6 + * 31 REALTIME_PRIORITY_CLASS THREAD_PRIORITY_TIME_CRITICAL + * + * Windows NT: Values -7, -6, -5, -4, -3, 3, 4, 5, and 6 are not supported. + * + */ + + +int +sched_get_priority_min (int policy) +{ + if (policy < SCHED_MIN || policy > SCHED_MAX) + { + errno = EINVAL; + return -1; + } + +#if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL) + /* WinCE? */ + return PTW32_MIN (THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL); +#else + /* This is independent of scheduling policy in Win32. */ + return PTW32_MIN (THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL); +#endif +} diff --git a/pcsx2/windows/pthreads/sched_getscheduler.c b/pcsx2/windows/pthreads/sched_getscheduler.c new file mode 100644 index 0000000000..1f0957cbf4 --- /dev/null +++ b/pcsx2/windows/pthreads/sched_getscheduler.c @@ -0,0 +1,69 @@ +/* + * sched_getscheduler.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +sched_getscheduler (pid_t pid) +{ + /* + * Win32 only has one policy which we call SCHED_OTHER. + * However, we try to provide other valid side-effects + * such as EPERM and ESRCH errors. + */ + if (0 != pid) + { + int selfPid = (int) GetCurrentProcessId (); + + if (pid != selfPid) + { + HANDLE h = + OpenProcess (PROCESS_QUERY_INFORMATION, PTW32_FALSE, (DWORD) pid); + + if (NULL == h) + { + errno = + (GetLastError () == + (0xFF & ERROR_ACCESS_DENIED)) ? EPERM : ESRCH; + return -1; + } + } + } + + return SCHED_OTHER; +} diff --git a/pcsx2/windows/pthreads/sched_setscheduler.c b/pcsx2/windows/pthreads/sched_setscheduler.c new file mode 100644 index 0000000000..06f77901af --- /dev/null +++ b/pcsx2/windows/pthreads/sched_setscheduler.c @@ -0,0 +1,81 @@ +/* + * sched_setscheduler.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +sched_setscheduler (pid_t pid, int policy) +{ + /* + * Win32 only has one policy which we call SCHED_OTHER. + * However, we try to provide other valid side-effects + * such as EPERM and ESRCH errors. Choosing to check + * for a valid policy last allows us to get the most value out + * of this function. + */ + if (0 != pid) + { + int selfPid = (int) GetCurrentProcessId (); + + if (pid != selfPid) + { + HANDLE h = + OpenProcess (PROCESS_SET_INFORMATION, PTW32_FALSE, (DWORD) pid); + + if (NULL == h) + { + errno = + (GetLastError () == + (0xFF & ERROR_ACCESS_DENIED)) ? EPERM : ESRCH; + return -1; + } + } + } + + if (SCHED_OTHER != policy) + { + errno = ENOSYS; + return -1; + } + + /* + * Don't set anything because there is nothing to set. + * Just return the current (the only possible) value. + */ + return SCHED_OTHER; +} diff --git a/pcsx2/windows/pthreads/sched_yield.c b/pcsx2/windows/pthreads/sched_yield.c new file mode 100644 index 0000000000..4220cc67fc --- /dev/null +++ b/pcsx2/windows/pthreads/sched_yield.c @@ -0,0 +1,71 @@ +/* + * sched_yield.c + * + * Description: + * POSIX thread functions that deal with thread scheduling. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" +#include "sched.h" + +int +sched_yield (void) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function indicates that the calling thread is + * willing to give up some time slices to other threads. + * + * PARAMETERS + * N/A + * + * + * DESCRIPTION + * This function indicates that the calling thread is + * willing to give up some time slices to other threads. + * NOTE: Since this is part of POSIX 1003.1b + * (realtime extensions), it is defined as returning + * -1 if an error occurs and sets errno to the actual + * error. + * + * RESULTS + * 0 successfully created semaphore, + * ENOSYS sched_yield not supported, + * + * ------------------------------------------------------ + */ +{ + Sleep (0); + + return 0; +} diff --git a/pcsx2/windows/pthreads/sem_close.c b/pcsx2/windows/pthreads/sem_close.c new file mode 100644 index 0000000000..c778befac1 --- /dev/null +++ b/pcsx2/windows/pthreads/sem_close.c @@ -0,0 +1,58 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_close.c + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + +/* ignore warning "unreferenced formal parameter" */ +#ifdef _MSC_VER +#pragma warning( disable : 4100 ) +#endif + +int +sem_close (sem_t * sem) +{ + errno = ENOSYS; + return -1; +} /* sem_close */ diff --git a/pcsx2/windows/pthreads/sem_destroy.c b/pcsx2/windows/pthreads/sem_destroy.c new file mode 100644 index 0000000000..e2bdf86ca8 --- /dev/null +++ b/pcsx2/windows/pthreads/sem_destroy.c @@ -0,0 +1,144 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_destroy.c + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + + +int +sem_destroy (sem_t * sem) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function destroys an unnamed semaphore. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * DESCRIPTION + * This function destroys an unnamed semaphore. + * + * RESULTS + * 0 successfully destroyed semaphore, + * -1 failed, error in errno + * ERRNO + * EINVAL 'sem' is not a valid semaphore, + * ENOSYS semaphores are not supported, + * EBUSY threads (or processes) are currently + * blocked on 'sem' + * + * ------------------------------------------------------ + */ +{ + int result = 0; + sem_t s = NULL; + + if (sem == NULL || *sem == NULL) + { + result = EINVAL; + } + else + { + s = *sem; + + if ((result = pthread_mutex_lock (&s->lock)) == 0) + { + if (s->value < 0) + { + (void) pthread_mutex_unlock (&s->lock); + result = EBUSY; + } + else + { + /* There are no threads currently blocked on this semaphore. */ + + if (!CloseHandle (s->sem)) + { + (void) pthread_mutex_unlock (&s->lock); + result = EINVAL; + } + else + { + /* + * Invalidate the semaphore handle when we have the lock. + * Other sema operations should test this after acquiring the lock + * to check that the sema is still valid, i.e. before performing any + * operations. This may only be necessary before the sema op routine + * returns so that the routine can return EINVAL - e.g. if setting + * s->value to SEM_VALUE_MAX below does force a fall-through. + */ + *sem = NULL; + + /* Prevent anyone else actually waiting on or posting this sema. + */ + s->value = SEM_VALUE_MAX; + + (void) pthread_mutex_unlock (&s->lock); + + do + { + /* Give other threads a chance to run and exit any sema op + * routines. Due to the SEM_VALUE_MAX value, if sem_post or + * sem_wait were blocked by us they should fall through. + */ + Sleep(0); + } + while (pthread_mutex_destroy (&s->lock) == EBUSY); + } + } + } + } + + if (result != 0) + { + errno = result; + return -1; + } + + free (s); + + return 0; + +} /* sem_destroy */ diff --git a/pcsx2/windows/pthreads/sem_getvalue.c b/pcsx2/windows/pthreads/sem_getvalue.c new file mode 100644 index 0000000000..233635b139 --- /dev/null +++ b/pcsx2/windows/pthreads/sem_getvalue.c @@ -0,0 +1,110 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_getvalue.c + * + * Purpose: + * Semaphores aren't actually part of PThreads. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1-2001 + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + + +int +sem_getvalue (sem_t * sem, int *sval) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function stores the current count value of the + * semaphore. + * RESULTS + * + * Return value + * + * 0 sval has been set. + * -1 failed, error in errno + * + * in global errno + * + * EINVAL 'sem' is not a valid semaphore, + * ENOSYS this function is not supported, + * + * + * PARAMETERS + * + * sem pointer to an instance of sem_t + * + * sval pointer to int. + * + * DESCRIPTION + * This function stores the current count value of the semaphore + * pointed to by sem in the int pointed to by sval. + */ +{ + if (sem == NULL || *sem == NULL || sval == NULL) + { + errno = EINVAL; + return -1; + } + else + { + long value; + register sem_t s = *sem; + int result = 0; + + if ((result = pthread_mutex_lock(&s->lock)) == 0) + { + /* See sem_destroy.c + */ + if (*sem == NULL) + { + (void) pthread_mutex_unlock (&s->lock); + errno = EINVAL; + return -1; + } + + value = s->value; + (void) pthread_mutex_unlock(&s->lock); + *sval = value; + } + + return result; + } + +} /* sem_getvalue */ diff --git a/pcsx2/windows/pthreads/sem_init.c b/pcsx2/windows/pthreads/sem_init.c new file mode 100644 index 0000000000..8e5e3f3902 --- /dev/null +++ b/pcsx2/windows/pthreads/sem_init.c @@ -0,0 +1,169 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_init.c + * + * Purpose: + * Semaphores aren't actually part of PThreads. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1-2001 + * + * ------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + +int +sem_init (sem_t * sem, int pshared, unsigned int value) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function initializes a semaphore. The + * initial value of the semaphore is 'value' + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * pshared + * if zero, this semaphore may only be shared between + * threads in the same process. + * if nonzero, the semaphore can be shared between + * processes + * + * value + * initial value of the semaphore counter + * + * DESCRIPTION + * This function initializes a semaphore. The + * initial value of the semaphore is set to 'value'. + * + * RESULTS + * 0 successfully created semaphore, + * -1 failed, error in errno + * ERRNO + * EINVAL 'sem' is not a valid semaphore, or + * 'value' >= SEM_VALUE_MAX + * ENOMEM out of memory, + * ENOSPC a required resource has been exhausted, + * ENOSYS semaphores are not supported, + * EPERM the process lacks appropriate privilege + * + * ------------------------------------------------------ + */ +{ + int result = 0; + sem_t s = NULL; + + if (pshared != 0) + { + /* + * Creating a semaphore that can be shared between + * processes + */ + result = EPERM; + } + else if (value > (unsigned int)SEM_VALUE_MAX) + { + result = EINVAL; + } + else + { + s = (sem_t) calloc (1, sizeof (*s)); + + if (NULL == s) + { + result = ENOMEM; + } + else + { + + s->value = value; + if (pthread_mutex_init(&s->lock, NULL) == 0) + { + +#ifdef NEED_SEM + + s->sem = CreateEvent (NULL, + PTW32_FALSE, /* auto (not manual) reset */ + PTW32_FALSE, /* initial state is unset */ + NULL); + + if (0 == s->sem) + { + free (s); + (void) pthread_mutex_destroy(&s->lock); + result = ENOSPC; + } + else + { + s->leftToUnblock = 0; + } + +#else /* NEED_SEM */ + + if ((s->sem = CreateSemaphore (NULL, /* Always NULL */ + (long) 0, /* Force threads to wait */ + (long) SEM_VALUE_MAX, /* Maximum value */ + NULL)) == 0) /* Name */ + { + (void) pthread_mutex_destroy(&s->lock); + result = ENOSPC; + } + +#endif /* NEED_SEM */ + + } + else + { + result = ENOSPC; + } + + if (result != 0) + { + free(s); + } + } + } + + if (result != 0) + { + errno = result; + return -1; + } + + *sem = s; + + return 0; + +} /* sem_init */ diff --git a/pcsx2/windows/pthreads/sem_open.c b/pcsx2/windows/pthreads/sem_open.c new file mode 100644 index 0000000000..3e2d6688f7 --- /dev/null +++ b/pcsx2/windows/pthreads/sem_open.c @@ -0,0 +1,58 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_open.c + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + +/* ignore warning "unreferenced formal parameter" */ +#ifdef _MSC_VER +#pragma warning( disable : 4100 ) +#endif + +int +sem_open (const char *name, int oflag, mode_t mode, unsigned int value) +{ + errno = ENOSYS; + return -1; +} /* sem_open */ diff --git a/pcsx2/windows/pthreads/sem_post.c b/pcsx2/windows/pthreads/sem_post.c new file mode 100644 index 0000000000..9449cb2b7d --- /dev/null +++ b/pcsx2/windows/pthreads/sem_post.c @@ -0,0 +1,128 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_post.c + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + + +int +sem_post (sem_t * sem) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function posts a wakeup to a semaphore. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * DESCRIPTION + * This function posts a wakeup to a semaphore. If there + * are waiting threads (or processes), one is awakened; + * otherwise, the semaphore value is incremented by one. + * + * RESULTS + * 0 successfully posted semaphore, + * -1 failed, error in errno + * ERRNO + * EINVAL 'sem' is not a valid semaphore, + * ENOSYS semaphores are not supported, + * ERANGE semaphore count is too big + * + * ------------------------------------------------------ + */ +{ + int result = 0; + sem_t s = *sem; + + if (s == NULL) + { + result = EINVAL; + } + else if ((result = pthread_mutex_lock (&s->lock)) == 0) + { + /* See sem_destroy.c + */ + if (*sem == NULL) + { + (void) pthread_mutex_unlock (&s->lock); + result = EINVAL; + return -1; + } + + if (s->value < SEM_VALUE_MAX) + { +#ifdef NEED_SEM + if (++s->value <= 0 + && !SetEvent(s->sem)) + { + s->value--; + result = EINVAL; + } +#else + if (++s->value <= 0 + && !ReleaseSemaphore (s->sem, 1, NULL)) + { + s->value--; + result = EINVAL; + } +#endif /* NEED_SEM */ + } + else + { + result = ERANGE; + } + + (void) pthread_mutex_unlock (&s->lock); + } + + if (result != 0) + { + errno = result; + return -1; + } + + return 0; + +} /* sem_post */ diff --git a/pcsx2/windows/pthreads/sem_post_multiple.c b/pcsx2/windows/pthreads/sem_post_multiple.c new file mode 100644 index 0000000000..0784bc46bf --- /dev/null +++ b/pcsx2/windows/pthreads/sem_post_multiple.c @@ -0,0 +1,142 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_post_multiple.c + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + + +int +sem_post_multiple (sem_t * sem, int count) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function posts multiple wakeups to a semaphore. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * count + * counter, must be greater than zero. + * + * DESCRIPTION + * This function posts multiple wakeups to a semaphore. If there + * are waiting threads (or processes), n <= count are awakened; + * the semaphore value is incremented by count - n. + * + * RESULTS + * 0 successfully posted semaphore, + * -1 failed, error in errno + * ERRNO + * EINVAL 'sem' is not a valid semaphore + * or count is less than or equal to zero. + * ERANGE semaphore count is too big + * + * ------------------------------------------------------ + */ +{ + int result = 0; + long waiters; + sem_t s = *sem; + + if (s == NULL || count <= 0) + { + result = EINVAL; + } + else if ((result = pthread_mutex_lock (&s->lock)) == 0) + { + /* See sem_destroy.c + */ + if (*sem == NULL) + { + (void) pthread_mutex_unlock (&s->lock); + result = EINVAL; + return -1; + } + + if (s->value <= (SEM_VALUE_MAX - count)) + { + waiters = -s->value; + s->value += count; + if (waiters > 0) + { +#ifdef NEED_SEM + if (SetEvent(s->sem)) + { + waiters--; + s->leftToUnblock += count - 1; + if (s->leftToUnblock > waiters) + { + s->leftToUnblock = waiters; + } + } +#else + if (ReleaseSemaphore (s->sem, (waiters<=count)?waiters:count, 0)) + { + /* No action */ + } +#endif + else + { + s->value -= count; + result = EINVAL; + } + } + } + else + { + result = ERANGE; + } + (void) pthread_mutex_unlock (&s->lock); + } + + if (result != 0) + { + errno = result; + return -1; + } + + return 0; + +} /* sem_post_multiple */ diff --git a/pcsx2/windows/pthreads/sem_timedwait.c b/pcsx2/windows/pthreads/sem_timedwait.c new file mode 100644 index 0000000000..684ecd10d0 --- /dev/null +++ b/pcsx2/windows/pthreads/sem_timedwait.c @@ -0,0 +1,238 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_timedwait.c + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + + +typedef struct { + sem_t sem; + int * resultPtr; +} sem_timedwait_cleanup_args_t; + + +static void PTW32_CDECL +ptw32_sem_timedwait_cleanup (void * args) +{ + sem_timedwait_cleanup_args_t * a = (sem_timedwait_cleanup_args_t *)args; + sem_t s = a->sem; + + if (pthread_mutex_lock (&s->lock) == 0) + { + /* + * We either timed out or were cancelled. + * If someone has posted between then and now we try to take the semaphore. + * Otherwise the semaphore count may be wrong after we + * return. In the case of a cancellation, it is as if we + * were cancelled just before we return (after taking the semaphore) + * which is ok. + */ + if (WaitForSingleObject(s->sem, 0) == WAIT_OBJECT_0) + { + /* We got the semaphore on the second attempt */ + *(a->resultPtr) = 0; + } + else + { + /* Indicate we're no longer waiting */ + s->value++; +#ifdef NEED_SEM + if (s->value > 0) + { + s->leftToUnblock = 0; + } +#else + /* + * Don't release the W32 sema, it doesn't need adjustment + * because it doesn't record the number of waiters. + */ +#endif + } + (void) pthread_mutex_unlock (&s->lock); + } +} + + +int +sem_timedwait (sem_t * sem, const struct timespec *abstime) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function waits on a semaphore possibly until + * 'abstime' time. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * abstime + * pointer to an instance of struct timespec + * + * DESCRIPTION + * This function waits on a semaphore. If the + * semaphore value is greater than zero, it decreases + * its value by one. If the semaphore value is zero, then + * the calling thread (or process) is blocked until it can + * successfully decrease the value or until interrupted by + * a signal. + * + * If 'abstime' is a NULL pointer then this function will + * block until it can successfully decrease the value or + * until interrupted by a signal. + * + * RESULTS + * 0 successfully decreased semaphore, + * -1 failed, error in errno + * ERRNO + * EINVAL 'sem' is not a valid semaphore, + * ENOSYS semaphores are not supported, + * EINTR the function was interrupted by a signal, + * EDEADLK a deadlock condition was detected. + * ETIMEDOUT abstime elapsed before success. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + sem_t s = *sem; + + pthread_testcancel(); + + if (sem == NULL) + { + result = EINVAL; + } + else + { + DWORD milliseconds; + + if (abstime == NULL) + { + milliseconds = INFINITE; + } + else + { + /* + * Calculate timeout as milliseconds from current system time. + */ + milliseconds = ptw32_relmillisecs (abstime); + } + + if ((result = pthread_mutex_lock (&s->lock)) == 0) + { + int v; + + /* See sem_destroy.c + */ + if (*sem == NULL) + { + (void) pthread_mutex_unlock (&s->lock); + errno = EINVAL; + return -1; + } + + v = --s->value; + (void) pthread_mutex_unlock (&s->lock); + + if (v < 0) + { +#ifdef NEED_SEM + int timedout; +#endif + sem_timedwait_cleanup_args_t cleanup_args; + + cleanup_args.sem = s; + cleanup_args.resultPtr = &result; + +#ifdef _MSC_VER +#pragma inline_depth(0) +#endif + /* Must wait */ + pthread_cleanup_push(ptw32_sem_timedwait_cleanup, (void *) &cleanup_args); +#ifdef NEED_SEM + timedout = +#endif + result = pthreadCancelableTimedWait (s->sem, milliseconds); + pthread_cleanup_pop(result); +#ifdef _MSC_VER +#pragma inline_depth() +#endif + +#ifdef NEED_SEM + + if (!timedout && pthread_mutex_lock (&s->lock) == 0) + { + if (*sem == NULL) + { + (void) pthread_mutex_unlock (&s->lock); + errno = EINVAL; + return -1; + } + + if (s->leftToUnblock > 0) + { + --s->leftToUnblock; + SetEvent(s->sem); + } + (void) pthread_mutex_unlock (&s->lock); + } + +#endif /* NEED_SEM */ + + } + } + + } + + if (result != 0) + { + + errno = result; + return -1; + + } + + return 0; + +} /* sem_timedwait */ diff --git a/pcsx2/windows/pthreads/sem_trywait.c b/pcsx2/windows/pthreads/sem_trywait.c new file mode 100644 index 0000000000..a9169c00b4 --- /dev/null +++ b/pcsx2/windows/pthreads/sem_trywait.c @@ -0,0 +1,117 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_trywait.c + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + + +int +sem_trywait (sem_t * sem) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function tries to wait on a semaphore. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * DESCRIPTION + * This function tries to wait on a semaphore. If the + * semaphore value is greater than zero, it decreases + * its value by one. If the semaphore value is zero, then + * this function returns immediately with the error EAGAIN + * + * RESULTS + * 0 successfully decreased semaphore, + * -1 failed, error in errno + * ERRNO + * EAGAIN the semaphore was already locked, + * EINVAL 'sem' is not a valid semaphore, + * ENOTSUP sem_trywait is not supported, + * EINTR the function was interrupted by a signal, + * EDEADLK a deadlock condition was detected. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + sem_t s = *sem; + + if (s == NULL) + { + result = EINVAL; + } + else if ((result = pthread_mutex_lock (&s->lock)) == 0) + { + /* See sem_destroy.c + */ + if (*sem == NULL) + { + (void) pthread_mutex_unlock (&s->lock); + errno = EINVAL; + return -1; + } + + if (s->value > 0) + { + s->value--; + } + else + { + result = EAGAIN; + } + + (void) pthread_mutex_unlock (&s->lock); + } + + if (result != 0) + { + errno = result; + return -1; + } + + return 0; + +} /* sem_trywait */ diff --git a/pcsx2/windows/pthreads/sem_unlink.c b/pcsx2/windows/pthreads/sem_unlink.c new file mode 100644 index 0000000000..27aed87794 --- /dev/null +++ b/pcsx2/windows/pthreads/sem_unlink.c @@ -0,0 +1,58 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_unlink.c + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + +/* ignore warning "unreferenced formal parameter" */ +#ifdef _MSC_VER +#pragma warning( disable : 4100 ) +#endif + +int +sem_unlink (const char *name) +{ + errno = ENOSYS; + return -1; +} /* sem_unlink */ diff --git a/pcsx2/windows/pthreads/sem_wait.c b/pcsx2/windows/pthreads/sem_wait.c new file mode 100644 index 0000000000..a501504fbd --- /dev/null +++ b/pcsx2/windows/pthreads/sem_wait.c @@ -0,0 +1,187 @@ +/* + * ------------------------------------------------------------- + * + * Module: sem_wait.c + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + + +static void PTW32_CDECL +ptw32_sem_wait_cleanup(void * sem) +{ + sem_t s = (sem_t) sem; + + if (pthread_mutex_lock (&s->lock) == 0) + { + /* + * If sema is destroyed do nothing, otherwise:- + * If the sema is posted between us being cancelled and us locking + * the sema again above then we need to consume that post but cancel + * anyway. If we don't get the semaphore we indicate that we're no + * longer waiting. + */ + if (*((sem_t *)sem) != NULL && !(WaitForSingleObject(s->sem, 0) == WAIT_OBJECT_0)) + { + ++s->value; +#ifdef NEED_SEM + if (s->value > 0) + { + s->leftToUnblock = 0; + } +#else + /* + * Don't release the W32 sema, it doesn't need adjustment + * because it doesn't record the number of waiters. + */ +#endif /* NEED_SEM */ + } + (void) pthread_mutex_unlock (&s->lock); + } +} + +int +sem_wait (sem_t * sem) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function waits on a semaphore. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * DESCRIPTION + * This function waits on a semaphore. If the + * semaphore value is greater than zero, it decreases + * its value by one. If the semaphore value is zero, then + * the calling thread (or process) is blocked until it can + * successfully decrease the value or until interrupted by + * a signal. + * + * RESULTS + * 0 successfully decreased semaphore, + * -1 failed, error in errno + * ERRNO + * EINVAL 'sem' is not a valid semaphore, + * ENOSYS semaphores are not supported, + * EINTR the function was interrupted by a signal, + * EDEADLK a deadlock condition was detected. + * + * ------------------------------------------------------ + */ +{ + int result = 0; + sem_t s = *sem; + + pthread_testcancel(); + + if (s == NULL) + { + result = EINVAL; + } + else + { + if ((result = pthread_mutex_lock (&s->lock)) == 0) + { + int v; + + /* See sem_destroy.c + */ + if (*sem == NULL) + { + (void) pthread_mutex_unlock (&s->lock); + errno = EINVAL; + return -1; + } + + v = --s->value; + (void) pthread_mutex_unlock (&s->lock); + + if (v < 0) + { +#ifdef _MSC_VER +#pragma inline_depth(0) +#endif + /* Must wait */ + pthread_cleanup_push(ptw32_sem_wait_cleanup, (void *) s); + result = pthreadCancelableWait (s->sem); + /* Cleanup if we're canceled or on any other error */ + pthread_cleanup_pop(result); +#ifdef _MSC_VER +#pragma inline_depth() +#endif + } +#ifdef NEED_SEM + + if (!result && pthread_mutex_lock (&s->lock) == 0) + { + if (*sem == NULL) + { + (void) pthread_mutex_unlock (&s->lock); + errno = EINVAL; + return -1; + } + + if (s->leftToUnblock > 0) + { + --s->leftToUnblock; + SetEvent(s->sem); + } + (void) pthread_mutex_unlock (&s->lock); + } + +#endif /* NEED_SEM */ + + } + + } + + if (result != 0) + { + errno = result; + return -1; + } + + return 0; + +} /* sem_wait */ diff --git a/pcsx2/windows/pthreads/semaphore.c b/pcsx2/windows/pthreads/semaphore.c new file mode 100644 index 0000000000..0a5131f41d --- /dev/null +++ b/pcsx2/windows/pthreads/semaphore.c @@ -0,0 +1,69 @@ +/* + * ------------------------------------------------------------- + * + * Module: semaphore.c + * + * Purpose: + * Concatenated version of separate modules to allow + * inlining optimisation, which it is assumed can only + * be effective within a single module. + * + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * ------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef NEED_FTIME +# include +#endif + +#include + +#include "pthread.h" +#include "semaphore.h" +#include "implement.h" + + +#include "sem_init.c" +#include "sem_destroy.c" +#include "sem_trywait.c" +#include "sem_wait.c" +#include "sem_timedwait.c" +#include "sem_post.c" +#include "sem_post_multiple.c" +#include "sem_getvalue.c" +#include "sem_open.c" +#include "sem_close.c" +#include "sem_unlink.c" diff --git a/pcsx2/windows/pthreads/semaphore.h b/pcsx2/windows/pthreads/semaphore.h new file mode 100644 index 0000000000..ea42ce3703 --- /dev/null +++ b/pcsx2/windows/pthreads/semaphore.h @@ -0,0 +1,166 @@ +/* + * Module: semaphore.h + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined( SEMAPHORE_H ) +#define SEMAPHORE_H + +#undef PTW32_LEVEL + +#if defined(_POSIX_SOURCE) +#define PTW32_LEVEL 0 +/* Early POSIX */ +#endif + +#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309 +#undef PTW32_LEVEL +#define PTW32_LEVEL 1 +/* Include 1b, 1c and 1d */ +#endif + +#if defined(INCLUDE_NP) +#undef PTW32_LEVEL +#define PTW32_LEVEL 2 +/* Include Non-Portable extensions */ +#endif + +#define PTW32_LEVEL_MAX 3 + +#if !defined(PTW32_LEVEL) +#define PTW32_LEVEL PTW32_LEVEL_MAX +/* Include everything */ +#endif + +#if __GNUC__ && ! defined (__declspec) +# error Please upgrade your GNU compiler to one that supports __declspec. +#endif + +/* + * When building the DLL code, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the DLL, + * do NOT define PTW32_BUILD, and then the variables/functions will + * be imported correctly. + */ +#ifndef PTW32_STATIC_LIB +# ifdef PTW32_BUILD +# define PTW32_DLLPORT __declspec (dllexport) +# else +# define PTW32_DLLPORT __declspec (dllimport) +# endif +#else +# define PTW32_DLLPORT +#endif + +/* + * This is a duplicate of what is in the autoconf config.h, + * which is only used when building the pthread-win32 libraries. + */ + +#ifndef PTW32_CONFIG_H +# if defined(WINCE) +# define NEED_ERRNO +# define NEED_SEM +# endif +# if defined(_UWIN) || defined(__MINGW32__) +# define HAVE_MODE_T +# endif +#endif + +/* + * + */ + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX +#ifdef NEED_ERRNO +#include "need_errno.h" +#else +#include +#endif +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +#define _POSIX_SEMAPHORES + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#ifndef HAVE_MODE_T +typedef unsigned int mode_t; +#endif + + +typedef struct sem_t_ * sem_t; + +PTW32_DLLPORT int __cdecl sem_init (sem_t * sem, + int pshared, + unsigned int value); + +PTW32_DLLPORT int __cdecl sem_destroy (sem_t * sem); + +PTW32_DLLPORT int __cdecl sem_trywait (sem_t * sem); + +PTW32_DLLPORT int __cdecl sem_wait (sem_t * sem); + +PTW32_DLLPORT int __cdecl sem_timedwait (sem_t * sem, + const struct timespec * abstime); + +PTW32_DLLPORT int __cdecl sem_post (sem_t * sem); + +PTW32_DLLPORT int __cdecl sem_post_multiple (sem_t * sem, + int count); + +PTW32_DLLPORT int __cdecl sem_open (const char * name, + int oflag, + mode_t mode, + unsigned int value); + +PTW32_DLLPORT int __cdecl sem_close (sem_t * sem); + +PTW32_DLLPORT int __cdecl sem_unlink (const char * name); + +PTW32_DLLPORT int __cdecl sem_getvalue (sem_t * sem, + int * sval); + +#ifdef __cplusplus +} /* End of extern "C" */ +#endif /* __cplusplus */ + +#undef PTW32_LEVEL +#undef PTW32_LEVEL_MAX + +#endif /* !SEMAPHORE_H */ diff --git a/pcsx2/windows/pthreads/signal.c b/pcsx2/windows/pthreads/signal.c new file mode 100644 index 0000000000..b95b3eac3c --- /dev/null +++ b/pcsx2/windows/pthreads/signal.c @@ -0,0 +1,179 @@ +/* + * signal.c + * + * Description: + * Thread-aware signal functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +/* + * Possible future strategy for implementing pthread_kill() + * ======================================================== + * + * Win32 does not implement signals. + * Signals are simply software interrupts. + * pthread_kill() asks the system to deliver a specified + * signal (interrupt) to a specified thread in the same + * process. + * Signals are always asynchronous (no deferred signals). + * Pthread-win32 has an async cancelation mechanism. + * A similar system can be written to deliver signals + * within the same process (on ix86 processors at least). + * + * Each thread maintains information about which + * signals it will respond to. Handler routines + * are set on a per-process basis - not per-thread. + * When signalled, a thread will check it's sigmask + * and, if the signal is not being ignored, call the + * handler routine associated with the signal. The + * thread must then (except for some signals) return to + * the point where it was interrupted. + * + * Ideally the system itself would check the target thread's + * mask before possibly needlessly bothering the thread + * itself. This could be done by pthread_kill(), that is, + * in the signaling thread since it has access to + * all pthread_t structures. It could also retrieve + * the handler routine address to minimise the target + * threads response overhead. This may also simplify + * serialisation of the access to the per-thread signal + * structures. + * + * pthread_kill() eventually calls a routine similar to + * ptw32_cancel_thread() which manipulates the target + * threads processor context to cause the thread to + * run the handler launcher routine. pthread_kill() must + * save the target threads current context so that the + * handler launcher routine can restore the context after + * the signal handler has returned. Some handlers will not + * return, eg. the default SIGKILL handler may simply + * call pthread_exit(). + * + * The current context is saved in the target threads + * pthread_t structure. + */ + +#include "pthread.h" +#include "implement.h" + +#if HAVE_SIGSET_T + +static void +ptw32_signal_thread () +{ +} + +static void +ptw32_signal_callhandler () +{ +} + +int +pthread_sigmask (int how, sigset_t const *set, sigset_t * oset) +{ + pthread_t thread = pthread_self (); + + if (thread.p == NULL) + { + return ENOENT; + } + + /* Validate the `how' argument. */ + if (set != NULL) + { + switch (how) + { + case SIG_BLOCK: + break; + case SIG_UNBLOCK: + break; + case SIG_SETMASK: + break; + default: + /* Invalid `how' argument. */ + return EINVAL; + } + } + + /* Copy the old mask before modifying it. */ + if (oset != NULL) + { + memcpy (oset, &(thread.p->sigmask), sizeof (sigset_t)); + } + + if (set != NULL) + { + unsigned int i; + + /* FIXME: this code assumes that sigmask is an even multiple of + the size of a long integer. */ + + unsigned long *src = (unsigned long const *) set; + unsigned long *dest = (unsigned long *) &(thread.p->sigmask); + + switch (how) + { + case SIG_BLOCK: + for (i = 0; i < (sizeof (sigset_t) / sizeof (unsigned long)); i++) + { + /* OR the bit field longword-wise. */ + *dest++ |= *src++; + } + break; + case SIG_UNBLOCK: + for (i = 0; i < (sizeof (sigset_t) / sizeof (unsigned long)); i++) + { + /* XOR the bitfield longword-wise. */ + *dest++ ^= *src++; + } + case SIG_SETMASK: + /* Replace the whole sigmask. */ + memcpy (&(thread.p->sigmask), set, sizeof (sigset_t)); + break; + } + } + + return 0; +} + +int +sigwait (const sigset_t * set, int *sig) +{ + /* This routine is a cancellation point */ + pthread_test_cancel(); +} + +int +sigaction (int signum, const struct sigaction *act, struct sigaction *oldact) +{ +} + +#endif /* HAVE_SIGSET_T */ diff --git a/pcsx2/windows/pthreads/spin.c b/pcsx2/windows/pthreads/spin.c new file mode 100644 index 0000000000..92b9bed9d4 --- /dev/null +++ b/pcsx2/windows/pthreads/spin.c @@ -0,0 +1,46 @@ +/* + * spin.c + * + * Description: + * This translation unit implements spin lock primitives. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +#include "ptw32_spinlock_check_need_init.c" +#include "pthread_spin_init.c" +#include "pthread_spin_destroy.c" +#include "pthread_spin_lock.c" +#include "pthread_spin_unlock.c" +#include "pthread_spin_trylock.c" diff --git a/pcsx2/windows/pthreads/sync.c b/pcsx2/windows/pthreads/sync.c new file mode 100644 index 0000000000..03c9881910 --- /dev/null +++ b/pcsx2/windows/pthreads/sync.c @@ -0,0 +1,43 @@ +/* + * sync.c + * + * Description: + * This translation unit implements functions related to thread + * synchronisation. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +#include "pthread_detach.c" +#include "pthread_join.c" diff --git a/pcsx2/windows/pthreads/tsd.c b/pcsx2/windows/pthreads/tsd.c new file mode 100644 index 0000000000..ef49d4e475 --- /dev/null +++ b/pcsx2/windows/pthreads/tsd.c @@ -0,0 +1,44 @@ +/* + * tsd.c + * + * Description: + * POSIX thread functions which implement thread-specific data (TSD). + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +#include "pthread_key_create.c" +#include "pthread_key_delete.c" +#include "pthread_setspecific.c" +#include "pthread_getspecific.c" diff --git a/pcsx2/windows/pthreads/w32_CancelableWait.c b/pcsx2/windows/pthreads/w32_CancelableWait.c new file mode 100644 index 0000000000..7fa54ecdd6 --- /dev/null +++ b/pcsx2/windows/pthreads/w32_CancelableWait.c @@ -0,0 +1,160 @@ +/* + * w32_CancelableWait.c + * + * Description: + * This translation unit implements miscellaneous thread functions. + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "pthread.h" +#include "implement.h" + + +static INLINE int +ptw32_cancelable_wait (HANDLE waitHandle, DWORD timeout) + /* + * ------------------------------------------------------------------- + * This provides an extra hook into the pthread_cancel + * mechanism that will allow you to wait on a Windows handle and make it a + * cancellation point. This function blocks until the given WIN32 handle is + * signaled or pthread_cancel has been called. It is implemented using + * WaitForMultipleObjects on 'waitHandle' and a manually reset WIN32 + * event used to implement pthread_cancel. + * + * Given this hook it would be possible to implement more of the cancellation + * points. + * ------------------------------------------------------------------- + */ +{ + int result; + pthread_t self; + ptw32_thread_t * sp; + HANDLE handles[2]; + DWORD nHandles = 1; + DWORD status; + + handles[0] = waitHandle; + + self = pthread_self(); + sp = (ptw32_thread_t *) self.p; + + if (sp != NULL) + { + /* + * Get cancelEvent handle + */ + if (sp->cancelState == PTHREAD_CANCEL_ENABLE) + { + + if ((handles[1] = sp->cancelEvent) != NULL) + { + nHandles++; + } + } + } + else + { + handles[1] = NULL; + } + + status = WaitForMultipleObjects (nHandles, handles, PTW32_FALSE, timeout); + + switch (status - WAIT_OBJECT_0) + { + case 0: + /* + * Got the handle. + * In the event that both handles are signalled, the smallest index + * value (us) is returned. As it has been arranged, this ensures that + * we don't drop a signal that we should act on (i.e. semaphore, + * mutex, or condition variable etc). + */ + result = 0; + break; + + case 1: + /* + * Got cancel request. + * In the event that both handles are signaled, the cancel will + * be ignored (see case 0 comment). + */ + ResetEvent (handles[1]); + + if (sp != NULL) + { + /* + * Should handle POSIX and implicit POSIX threads.. + * Make sure we haven't been async-canceled in the meantime. + */ + (void) pthread_mutex_lock (&sp->cancelLock); + if (sp->state < PThreadStateCanceling) + { + sp->state = PThreadStateCanceling; + sp->cancelState = PTHREAD_CANCEL_DISABLE; + (void) pthread_mutex_unlock (&sp->cancelLock); + ptw32_throw (PTW32_EPS_CANCEL); + + /* Never reached */ + } + (void) pthread_mutex_unlock (&sp->cancelLock); + } + + /* Should never get to here. */ + result = EINVAL; + break; + + default: + if (status == WAIT_TIMEOUT) + { + result = ETIMEDOUT; + } + else + { + result = EINVAL; + } + break; + } + + return (result); + +} /* CancelableWait */ + +int +pthreadCancelableWait (HANDLE waitHandle) +{ + return (ptw32_cancelable_wait (waitHandle, INFINITE)); +} + +int +pthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout) +{ + return (ptw32_cancelable_wait (waitHandle, timeout)); +} diff --git a/pcsx2/windows/resource.h b/pcsx2/windows/resource.h new file mode 100644 index 0000000000..04acc2f078 --- /dev/null +++ b/pcsx2/windows/resource.h @@ -0,0 +1,736 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by pcsx2.rc +// +#define IDDEFAULT 3 +#define IDD_CONFIG 101 +#define IDD_MCDCONF 102 +#define IDD_BPEXEC 103 +#define ABOUT_DIALOG 104 +#define IDD_BPCNT 105 +#define IDD_DEBUG 108 +#define IDI_ICON 108 +#define IDD_MEMORY 110 +#define IDD_VU0REGS 111 +#define IDD_JUMP 112 +#define SPLASH_LOGO 113 +#define IDD_VU0INTEGER 113 +#define IDD_GPREGS 114 +#define IDD_CP0REGS 115 +#define IDD_CP1REGS 116 +#define IDD_VU1INTEGER 117 +#define IDD_VU1REGS 118 +#define IDD_LOGGING 119 +#define IDD_CMDLINE 120 +#define IDD_RDEBUGPARAMS 121 +#define ID_DEBUG_REMOTEDEBUGGING 122 +#define IDD_RDEBUG 123 +#define IDD_DIALOGBAR 124 +#define IDD_IOP_DEBUG 125 +#define IDD_CPUDLG 126 +#define IDD_ADVANCED 127 +#define IDD_IOPREGS 128 +#define IDD_USERNAME 129 +#define IDB_PS2SILVER 132 +#define IDD_CHEATS 133 +#define IDD_GAMEFIXES 134 +#define IDD_HACKS 135 +#define IDD_DUMP 136 +#define IDD_DUMPMEM 137 +#define IDD_PATCHBROWSER 138 +#define IDD_ADVANCED_OPTIONS 140 +#define IDD_FINDER 174 +#define IDD_ADD 175 +#define IDD_ADDGS 176 +#define IDD_EDITPATCH 177 +#define IDD_ADDRAW 178 +#define IDD_PNACHWRITER 179 +#define IDC_MEM_SCROLL 1001 +#define IDC_EXECBP 1001 +#define IDC_CNTBP 1002 +#define IDC_MCD2 1004 +#define IDC_VU0_VF00 1005 +#define IDC_MCD1 1005 +#define IDC_VU0_VF01 1006 +#define IDC_MCDSEL1 1006 +#define IDC_VU0_VF02 1007 +#define IDC_MCDSEL2 1007 +#define IDC_VU0_VF03 1008 +#define IDC_LOADICO1 1008 +#define IDC_VU0_VF04 1009 +#define IDC_SAVE1 1009 +#define IDC_DEBUG_DISASM 1010 +#define IDC_VU0_VF05 1010 +#define IDC_LOADICO2 1010 +#define IDC_VU0_VF06 1011 +#define IDC_SAVE2 1011 +#define IDC_VU0_VF07 1012 +#define IDC_VU0_VF08 1013 +#define IDC_PCSX_ABOUT_TEXT 1014 +#define IDC_VU0_VF09 1014 +#define IDC_CPU 1015 +#define IDC_VU0_VF10 1015 +#define IDC_VU0_VF11 1016 +#define IDC_ENABLED 1016 +#define IDC_VU0_VF12 1017 +#define IDC_PLACETOPATCH 1017 +#define IDC_VU0_VF13 1018 +#define IDC_TYPE 1018 +#define IDC_VU0_VF14 1019 +#define IDC_CP07 1020 +#define IDC_VU0_VF15 1020 +#define IDC_VU0_VF16 1021 +#define IDC_VU0_VF17 1022 +#define IDC_VU0_VF18 1023 +#define IDC_VU0_VF19 1024 +#define IDC_VU0_VF20 1025 +#define IDC_VU0_VF21 1026 +#define IDC_VU0_VF22 1027 +#define IDC_VU0_VF23 1028 +#define IDC_DEBUG_CLOSE 1029 +#define IDC_VU0_VF24 1029 +#define IDC_DEBUG_STEP 1030 +#define IDC_VU0_VF25 1030 +#define IDC_DEBUG_SKIP 1031 +#define IDC_VU0_VF26 1031 +#define IDC_DEBUG_GO 1032 +#define IDC_VU0_VF27 1032 +#define IDC_DEBUG_BP_EXEC 1033 +#define IDC_VU0_VF28 1033 +#define IDC_CP021 1034 +#define IDC_DEBUG_SCROLL 1034 +#define IDC_VU0_VF29 1034 +#define IDC_CP022 1035 +#define IDC_DEBUG_RESETTOPC 1035 +#define IDC_VU0_VF30 1035 +#define IDC_CP023 1036 +#define IDC_FPU_ACC 1036 +#define IDC_DEBUG_JUMP 1036 +#define IDC_MEMORY_CLOSE 1036 +#define IDC_VU0_VF31 1036 +#define IDC_CP024 1037 +#define IDC_MEMORY_ADDR 1037 +#define IDC_DEBUG_LOG 1037 +#define IDC_CP025 1038 +#define IDC_MEMORY_DUMP 1038 +#define IDC_DEBUG_BP_COUNT 1038 +#define IDC_VU0_VI00 1038 +#define IDC_DEBUG_L1 1039 +#define IDC_VU0_VI01 1039 +#define IDC_ADDRESS_PATCH 1039 +#define IDC_DEBUG_L2 1040 +#define IDC_VU0_VI02 1040 +#define IDC_DATA_PATCH 1040 +#define IDC_DEBUG_L3 1041 +#define IDC_VU0_VI03 1041 +#define IDC_FRAMEMCD1 1041 +#define IDC_DEBUG_L4 1042 +#define IDC_VU0_VI04 1042 +#define IDC_FRAMEMCD2 1042 +#define IDC_DEBUG_L5 1043 +#define IDC_VU0_VI05 1043 +#define IDC_CP031 1044 +#define IDC_DEBUG_L6 1044 +#define IDC_VU0_VI06 1044 +#define IDC_DEBUG_L7 1045 +#define IDC_VU0_VI07 1045 +#define IDC_DEBUG_L8 1046 +#define IDC_VU0_VI08 1046 +#define IDC_DEBUG_L9 1047 +#define IDC_GPR0 1047 +#define IDC_VU0_VI09 1047 +#define IDC_DEBUG_L10 1048 +#define IDC_GPR1 1048 +#define IDC_VU0_VI10 1048 +#define IDC_DEBUG_L11 1049 +#define IDC_GPR2 1049 +#define IDC_VU0_VI11 1049 +#define IDC_DEBUG_L12 1050 +#define IDC_GPR3 1050 +#define IDC_VU0_VI12 1050 +#define IDC_DEBUG_L13 1051 +#define IDC_GPR4 1051 +#define IDC_VU0_VI13 1051 +#define IDC_DEBUG_L14 1052 +#define IDC_GPR5 1052 +#define IDC_LISTGS 1052 +#define IDC_VU0_VI14 1052 +#define IDC_DEBUG_L15 1053 +#define IDC_GPR6 1053 +#define IDC_VU0_VI15 1053 +#define IDC_LISTSPU2 1053 +#define IDC_DEBUG_L16 1054 +#define IDC_GPR7 1054 +#define IDC_VU0_VI16 1054 +#define IDC_LISTCDVD 1054 +#define IDC_DEBUG_L17 1055 +#define IDC_GPR8 1055 +#define IDC_LISTBIOS 1055 +#define IDC_VU0_VI17 1055 +#define IDC_DEBUG_L18 1056 +#define IDC_GPR9 1056 +#define IDC_CONFIGGS 1056 +#define IDC_VU0_VI18 1056 +#define IDC_DEBUG_L19 1057 +#define IDC_GPR10 1057 +#define IDC_TESTGS 1057 +#define IDC_VU0_VI19 1057 +#define IDC_DEBUG_L20 1058 +#define IDC_ABOUTGS 1058 +#define IDC_VU0_VI20 1058 +#define IDC_DEBUG_L21 1059 +#define IDC_VU0_VI21 1059 +#define IDC_CONFIGSPU2 1059 +#define IDC_DEBUG_L22 1060 +#define IDC_VU0_VI22 1060 +#define IDC_TESTSPU2 1060 +#define IDC_DEBUG_L23 1061 +#define IDC_VU0_VI23 1061 +#define IDC_ABOUTSPU2 1061 +#define IDC_DEBUG_L24 1062 +#define IDC_VU0_VI24 1062 +#define IDC_CONFIGCDVD 1062 +#define IDC_DEBUG_L25 1063 +#define IDC_GPR11 1063 +#define IDC_VU0_VI25 1063 +#define IDC_TESTCDVD 1063 +#define IDC_DEBUG_L26 1064 +#define IDC_VU0_VI26 1064 +#define IDC_ABOUTCDVD 1064 +#define IDC_DEBUG_L27 1065 +#define IDC_VU0_VI27 1065 +#define IDC_LISTDEV9 1065 +#define IDC_DEBUG_L28 1066 +#define IDC_LISTPAD1 1066 +#define IDC_VU0_VI28 1066 +#define IDC_DEBUG_L29 1067 +#define IDC_CONFIGPAD1 1067 +#define IDC_VU0_VI29 1067 +#define IDC_DEBUG_R1 1068 +#define IDC_GPR12 1068 +#define IDC_TESTPAD1 1068 +#define IDC_VU0_VI30 1068 +#define IDC_DEBUG_R2 1069 +#define IDC_GPR13 1069 +#define IDC_ABOUTPAD1 1069 +#define IDC_VU0_VI31 1069 +#define IDC_DEBUG_R3 1070 +#define IDC_GPR14 1070 +#define IDC_LISTPAD2 1070 +#define IDC_VU0_ACC 1070 +#define IDC_DEBUG_R4 1071 +#define IDC_GPR15 1071 +#define IDC_CONFIGPAD2 1071 +#define IDC_VU1_VF00 1071 +#define IDC_DEBUG_R5 1072 +#define IDC_GPR16 1072 +#define IDC_TESTPAD2 1072 +#define IDC_VU1_VF01 1072 +#define IDC_DEBUG_R6 1073 +#define IDC_GPR17 1073 +#define IDC_ABOUTPAD2 1073 +#define IDC_VU1_VF02 1073 +#define IDC_DEBUG_R7 1074 +#define IDC_GPR18 1074 +#define IDC_VU1_VF03 1074 +#define IDC_CONFIGDEV9 1074 +#define IDC_DEBUG_R8 1075 +#define IDC_GPR19 1075 +#define IDC_VU1_VF04 1075 +#define IDC_TESTDEV9 1075 +#define IDC_DEBUG_R9 1076 +#define IDC_GPR20 1076 +#define IDC_VU1_VF05 1076 +#define IDC_ABOUTDEV9 1076 +#define IDC_DEBUG_R10 1077 +#define IDC_GPR21 1077 +#define IDC_VU1_VF06 1077 +#define IDC_LISTUSB 1077 +#define IDC_DEBUG_R11 1078 +#define IDC_GPR22 1078 +#define IDC_VU1_VF07 1078 +#define IDC_CONFIGUSB 1078 +#define IDC_DEBUG_R12 1079 +#define IDC_GPR23 1079 +#define IDC_VU1_VF08 1079 +#define IDC_TESTUSB 1079 +#define IDC_DEBUG_R13 1080 +#define IDC_GPR24 1080 +#define IDC_VU1_VF09 1080 +#define IDC_ABOUTUSB 1080 +#define IDC_DEBUG_R14 1081 +#define IDC_GPR25 1081 +#define IDC_VU1_VF10 1081 +#define IDC_LISTFW 1081 +#define IDC_DEBUG_R15 1082 +#define IDC_GPR26 1082 +#define IDC_VU1_VF11 1082 +#define IDC_CONFIGFW 1082 +#define IDC_DEBUG_R16 1083 +#define IDC_GPR27 1083 +#define IDC_VU1_VF12 1083 +#define IDC_TESTFW 1083 +#define IDC_DEBUG_R17 1084 +#define IDC_GPR28 1084 +#define IDC_VU1_VF13 1084 +#define IDC_ABOUTFW 1084 +#define IDC_DEBUG_R18 1085 +#define IDC_GPR29 1085 +#define IDC_VU1_VF14 1085 +#define IDC_DEBUG_R19 1086 +#define IDC_GPR30 1086 +#define IDC_VU1_VF15 1086 +#define IDC_DEBUG_R20 1087 +#define IDC_GPR31 1087 +#define IDC_VU1_VF16 1087 +#define IDC_DEBUG_R21 1088 +#define IDC_VU1_VF17 1088 +#define IDC_DEBUG_R22 1089 +#define IDC_GPR_PC 1089 +#define IDC_VU1_VF18 1089 +#define IDC_DEBUG_R23 1090 +#define IDC_VU1_VF19 1090 +#define IDC_DEBUG_R24 1091 +#define IDC_GPR_HI 1091 +#define IDC_VU1_VF20 1091 +#define IDC_DEBUG_R25 1092 +#define IDC_GPR_LO 1092 +#define IDC_VU1_VF21 1092 +#define IDC_CP00 1093 +#define IDC_DEBUG_R26 1093 +#define IDC_VU1_VF22 1093 +#define IDC_CP01 1094 +#define IDC_DEBUG_R27 1094 +#define IDC_VU1_VF23 1094 +#define IDC_CP02 1095 +#define IDC_DEBUG_R28 1095 +#define IDC_VU1_VF24 1095 +#define IDC_CP03 1096 +#define IDC_DEBUG_R29 1096 +#define IDC_VU1_VF25 1096 +#define IDC_CP04 1097 +#define IDC_DEBUG_DUMP 1097 +#define IDC_JUMP_PC 1097 +#define IDC_VU1_VF26 1097 +#define IDC_CP05 1098 +#define IDC_DUMP_END 1098 +#define IDC_VU1_VF27 1098 +#define IDC_DEBUG_LOGGING 1098 +#define IDC_DEBUG_MEMORY 1098 +#define IDC_CP06 1099 +#define IDC_DUMP_FNAME 1099 +#define IDC_DEBUG_BP_CLEAR 1099 +#define IDC_VU1_VF28 1099 +#define IDC_CP08 1100 +#define IDC_VU1_VF29 1100 +#define IDC_DUMP_ENDIOP 1100 +#define IDC_CP09 1101 +#define IDC_VU1_VF30 1101 +#define IDC_DUMP_FNAMEIOP 1101 +#define IDC_CP010 1102 +#define IDC_VU1_VF31 1102 +#define IDC_CP011 1103 +#define IDC_VU1_VI00 1103 +#define IDC_CP012 1104 +#define IDC_VU1_VI01 1104 +#define IDC_CP013 1105 +#define IDC_VU1_VI02 1105 +#define IDC_CP014 1106 +#define IDC_VU1_VI03 1106 +#define IDC_CP015 1107 +#define IDC_VU1_VI04 1107 +#define IDC_CP016 1108 +#define IDC_VU1_VI05 1108 +#define IDC_CP017 1109 +#define IDC_VU1_VI06 1109 +#define IDC_CP018 1110 +#define IDC_VU1_VI07 1110 +#define IDC_CP019 1111 +#define IDC_VU1_VI08 1111 +#define IDC_CP020 1112 +#define IDC_VU1_VI09 1112 +#define IDC_CP026 1113 +#define IDC_VU1_VI10 1113 +#define IDC_CP027 1114 +#define IDC_VU1_VI11 1114 +#define IDC_CP028 1115 +#define IDC_VU1_VI12 1115 +#define IDC_CP029 1116 +#define IDC_VU1_VI13 1116 +#define IDC_CP030 1117 +#define IDC_VU1_VI14 1117 +#define IDC_FP0 1118 +#define IDC_VU1_VI15 1118 +#define IDC_FP1 1119 +#define IDC_VU1_VI16 1119 +#define IDC_FP2 1120 +#define IDC_VU1_VI17 1120 +#define IDC_FP3 1121 +#define IDC_VU1_VI18 1121 +#define IDC_FP4 1122 +#define IDC_VU1_VI19 1122 +#define IDC_FP5 1123 +#define IDC_VU1_VI20 1123 +#define IDC_FP6 1124 +#define IDC_VU1_VI21 1124 +#define IDC_FP7 1125 +#define IDC_VU1_VI22 1125 +#define IDC_FP8 1126 +#define IDC_VU1_VI23 1126 +#define IDC_FP9 1127 +#define IDC_VU1_VI24 1127 +#define IDC_FP10 1128 +#define IDC_VU1_VI25 1128 +#define IDC_FP11 1129 +#define IDC_VU1_VI26 1129 +#define IDC_FP12 1130 +#define IDC_VU1_VI27 1130 +#define IDC_FP13 1131 +#define IDC_VU1_VI28 1131 +#define IDC_FP14 1132 +#define IDC_VU1_VI29 1132 +#define IDC_FP15 1133 +#define IDC_VU1_VI30 1133 +#define IDC_FP16 1134 +#define IDC_VU1_VI31 1134 +#define IDC_FP17 1135 +#define IDC_VU1_ACC 1135 +#define IDC_FP18 1136 +#define IDC_FP19 1137 +#define IDC_FP20 1138 +#define IDC_FP21 1139 +#define IDC_FP22 1140 +#define IDC_FP23 1141 +#define IDC_FP24 1142 +#define IDC_FP25 1143 +#define IDC_FP26 1144 +#define IDC_FP27 1145 +#define IDC_FP28 1146 +#define IDC_FP29 1147 +#define IDC_FP30 1148 +#define IDC_FP31 1149 +#define IDC_FCR0 1150 +#define IDC_FCR31 1151 +#define IDC_CMDLINE 1155 +#define IDC_PORT 1157 +#define IDC_COMMUNICATION 1158 +#define IDC_CLEAR 1160 +#define IDC_DEBUGBIOS 1161 +#define IDC_EEPC 1162 +#define IDC_IOPPC 1163 +#define IDC_PCSX_ABOUT_GREETS 1163 +#define IDC_PCSX_ABOUT_AUTHORS 1164 +#define IDC_EECYCLE 1164 +#define IDC_GRAPHICS 1165 +#define IDC_IOPCYCLE 1165 +#define IDC_SOUND 1166 +#define IDC_FIRSTCONTROLLER 1167 +#define IDC_SECONDCONTROLLER 1168 +#define IDC_CDVDROM 1169 +#define IDC_BIOS 1170 +#define IDC_DEV9 1171 +#define IDC_DUMP_START 1172 +#define IDC_TIP 1172 +#define IDC_USB 1172 +#define IDC_TEXT 1173 +#define IDC_DUMP_STARTIOP 1173 +#define IDC_CPUOP 1175 +#define IDC_DEBUGEE 1176 +#define IDC_DEBUGIOP 1177 +#define IDC_STOPAT 1178 +#define IDC_STOPAFTER 1179 +#define IDC_PATCH 1180 +#define IDC_LOGS 1186 +#define IDC_DUMPMEM_FNAME 1188 +#define IDC_DUMPMEM_END 1189 +#define IDC_DUMPMEM_START 1190 +#define IDC_DUMPRAW 1191 +#define IDC_DEBUG_DISASM_IOP 1193 +#define IDC_DEBUG_SCROLL_IOP 1194 +#define IDC_EXITPB 1196 +#define IDC_VENDORNAME 1197 +#define IDC_FAMILYNAME 1198 +#define IDC_CPUSPEEDNAME 1199 +#define IDC_FEATURESNAME 1200 +#define IDC_VENDORINPUT 1201 +#define IDC_FAMILYINPUT 1202 +#define IDC_CPUSPEEDINPUT 1203 +#define IDC_FEATURESINPUT 1204 +#define IDC_REGCACHING 1208 +#define IDC_ADVRESET 1214 +#define IDC_SYNCHACK 1217 +#define IDC_SPU2HACK 1218 +#define IDC_SYNCHACK2 1218 +#define IDC_SYNCHACK3 1219 +#define IDC_IOPGPR0 1220 +#define IDC_IOPGPR1 1221 +#define IDC_IOPGPR2 1222 +#define IDC_IOPGPR16 1223 +#define IDC_IOPGPR17 1224 +#define IDC_IOPGPR18 1225 +#define IDC_IOPGPR19 1226 +#define IDC_IOPGPR20 1227 +#define IDC_IOPGPR21 1228 +#define IDC_IOPGPR22 1229 +#define IDC_IOPGPR23 1230 +#define IDC_IOPGPR24 1231 +#define IDC_IOPGPR25 1232 +#define IDC_IOPGPR26 1233 +#define IDC_IOPGPR27 1234 +#define IDC_IOPGPR28 1235 +#define IDC_IOPGPR29 1236 +#define IDC_IOPGPR30 1237 +#define IDC_IOPGPR31 1238 +#define IDC_IOPGPR_LO 1239 +#define IDC_IOPGPR_HI 1240 +#define IDC_IOPGPR_PC 1241 +#define IDC_IOPGPR15 1242 +#define IDC_IOPGPR14 1243 +#define IDC_IOPGPR13 1244 +#define IDC_IOPGPR12 1245 +#define IDC_IOPGPR11 1246 +#define IDC_IOPGPR10 1247 +#define IDC_IOPGPR9 1248 +#define IDC_IOPGPR8 1249 +#define IDC_IOPGPR7 1250 +#define IDC_IOPGPR6 1251 +#define IDC_IOPGPR5 1252 +#define IDC_IOPGPR4 1253 +#define IDC_BIOSDIR 1254 +#define IDC_IOPGPR3 1254 +#define IDC_PLUGINSDIR 1255 +#define IDC_USER_NAME 1257 +#define IDC_CPU_GSMULTI 1259 +#define IDC_PS2SILVER_RECT 1259 +#define IDC_CPU_EEREC 1262 +#define IDC_CPU_VU1REC 1263 +#define IDC_CPU_VU0REC 1264 +#define IDC_CPU_FL_NORMAL 1265 +#define IDC_CPU_FL_LIMIT 1266 +#define IDC_CPU_FL_SKIP 1267 +#define IDC_CPU_FL_SKIPVU 1268 +#define IDC_CPU_VUGROUP 1269 +#define IDC_GROUPS 1272 +#define IDC_PATCHES 1273 +#define IDC_DEBUG_STEP_EE 1274 +#define IDC_DEBUG_STEP_OVER 1275 +#define IDC_CUSTOMFPS 1275 +#define IDC_DEBUG_RUN_TO_CURSOR 1276 +#define IDC_CUSTOM_FPS 1276 +#define IDC_DEBUG_STEP_TO_CURSOR 1277 +#define IDC_ENABLEBUTTON 1277 +#define IDC_CUSTOM_FRAMESKIP 1277 +#define IDC_DEBUG_BREAK 1278 +#define IDC_ADDGS 1278 +#define IDC_CONVERTEDCODE 1278 +#define IDC_CUSTOM_CONSECUTIVE_FRAMES 1278 +#define IDC_VU_OVERFLOWHACK 1278 +#define IDC_HACKDESC 1279 +#define IDC_CONVERT 1279 +#define IDC_EDITPATCH 1279 +#define IDC_FRAMESKIP_LABEL1 1279 +#define IDC_READY 1280 +#define IDC_ADDPATCH 1280 +#define IDC_FRAMESKIP_LABEL2 1280 +#define IDC_GROUP 1281 +#define IDC_ADDRAW 1281 +#define IDC_FRAMESKIP_LABEL3 1281 +#define IDC_DATA 1282 +#define IDC_PNACHWRITER 1282 +#define IDC_CUSTOM_CONSECUTIVE_SKIP 1282 +#define IDC_PNACHWRITER2 1283 +#define IDC_SKIPMPEG 1283 +#define IDC_FRAMESKIP_LABEL4 1283 +#define IDC_SPIN1 1284 +#define IDC_FRAMESKIP_LABEL5 1284 +#define IDC_FRAMESKIP_LABEL6 1285 +#define IDC_SAVE 1286 +#define IDC_CRC 1287 +#define IDC_COMMENT 1288 +#define IDC_GAMETITLE 1289 +#define IDC_FASTMEMORY 1290 +#define IDC_ZEROGS 1291 +#define IDC_ROUND1 1292 +#define IDC_ROUND2 1293 +#define IDC_PATH3HACK 1294 +#define IDC_VUNANMODE 1295 +#define IDC_TREE1 1297 +#define IDC_TREE2 1298 +#define IDC_ICON1 1299 +#define IDC_ICON2 1300 +#define IDC_EE_CHECK1 1300 +#define IDC_EE_CHECK2 1301 +#define IDC_GAMEFIX2 1301 +#define IDC_VU_CHECK1 1302 +#define IDC_VU_FLAGS 1302 +#define IDC_GAMEFIX3 1302 +#define IDC_FRAMELIMIT_OPTIONS 1303 +#define IDC_VU_CHECK2 1303 +#define IDC_GAMEFIX4 1303 +#define IDC_SOUNDHACK2 1304 +#define IDC_ESCHACK 1304 +#define IDC_VU_CHECK3 1304 +#define IDC_GAMEFIX5 1304 +#define IDC_GAMEFIX1 1304 +#define IDC_EE_ROUNDMODE0 1305 +#define IDC_EE_ROUNDMODE1 1306 +#define IDC_EE_ROUNDMODE2 1307 +#define IDC_EE_ROUNDMODE3 1308 +#define IDC_EESYNC_DEFAULT 1308 +#define IDC_VU_CHECK4 1309 +#define IDC_EESYNC1 1309 +#define IDC_EESYNC2 1310 +#define IDC_VU_ROUNDMODE0 1311 +#define IDC_EESYNC3 1311 +#define IDC_VU_ROUNDMODE1 1312 +#define IDC_IOPSYNC 1312 +#define IDC_VU_ROUNDMODE2 1313 +#define IDC_CHECK2 1313 +#define IDC_WAITCYCLES 1313 +#define IDC_VU_ROUNDMODE3 1314 +#define IDC_VU_CLAMPMODE0 1315 +#define IDC_VU_CLAMPMODE1 1316 +#define IDC_VU_CLAMPMODE2 1317 +#define IDC_VU_CLAMPMODE3 1318 +#define IDC_EE_CLAMPMODE0 1319 +#define IDC_EE_CLAMPMODE1 1320 +#define IDC_EE_CLAMPMODE2 1321 +#define IDC_CPULOG 1500 +#define IDC_MEMLOG 1501 +#define IDC_HWLOG 1502 +#define IDC_DMALOG 1503 +#define IDC_BIOSLOG 1504 +#define IDC_ELFLOG 1505 +#define IDC_FPULOG 1506 +#define IDC_MMILOG 1507 +#define IDC_VU0LOG 1508 +#define IDC_COP0LOG 1509 +#define IDC_VIFLOG 1510 +#define IDC_SPRLOG 1511 +#define IDC_GIFLOG 1512 +#define IDC_SIFLOG 1513 +#define IDC_IPULOG 1514 +#define IDC_VUMICROLOG 1515 +#define IDC_RPCSERVICES 1516 +#define IDC_IOPLOG 1520 +#define IDC_IOPMEMLOG 1521 +#define IDC_IOPHWLOG 1522 +#define IDC_IOPBIOSLOG 1523 +#define IDC_IOPDMALOG 1524 +#define IDC_IOPPADLOG 1525 +#define IDC_IOPCDRLOG 1527 +#define IDC_IOPCNTLOG 1529 +#define IDC_EECNTLOG 1530 +#define IDC_SYMLOG 1531 +#define IDC_STDOUTPUTLOG 1532 +#define IDC_SEARCH 1701 +#define IDC_VALUE 1702 +#define IDC_OLD 1703 +#define IDC_SET 1704 +#define IDC_EQ 1705 +#define IDC_GT 1706 +#define IDC_LT 1707 +#define IDC_GE 1708 +#define IDC_LE 1709 +#define IDC_NE 1710 +#define IDC_EE 1711 +#define IDC_IOP 1712 +#define IDC_RESET 1713 +#define IDC_ADD 1714 +#define IDC_STATUS 1716 +#define IDC_FW 1716 +#define IDC_FRAMELIMIT 1716 +#define IDC_ADDRESS 1716 +#define IDC_UNSIGNED 1717 +#define IDC_8B 1718 +#define IDC_16B 1719 +#define IDC_32B 1720 +#define IDC_64B 1721 +#define IDC_RESULTS 1722 +#define IDC_NAME 1723 +#define IDC_ADDR 1725 +#define IDC_MATCHES 1726 +#define ID_FILEOPEN 40001 +#define ID_DEBUG_MEMORY_DUMP 40002 +#define ID_FILE_EXIT 40003 +#define ID_RUN_EXECUTE 40004 +#define ID_RUN_RESET 40005 +#define ID_HELP_ABOUT 40006 +#define ID_CONFIG_CONFIGURE 40007 +#define ID_CONFIG_CPU 40008 +#define ID_CONFIG_GRAPHICS 40009 +#define ID_CONFIG_CONTROLLERS 40010 +#define ID_DEBUG_LOGGING 40011 +#define ID_CONFIG_SOUND 40012 +#define ID_CONFIG_CDVDROM 40013 +#define ID_RUN_CMDLINE 40014 +#define ID_FILE_RUNCD 40015 +#define ID_DEBUG_ENTERDEBUGGER 40016 +#define ID_CONFIG_DEV9 40017 +#define ID_FILE_STATES_LOAD_SLOT1 40018 +#define ID_FILE_STATES_LOAD_SLOT2 40019 +#define ID_FILE_STATES_LOAD_SLOT3 40020 +#define ID_FILE_STATES_LOAD_SLOT4 40021 +#define ID_FILE_STATES_LOAD_SLOT5 40022 +#define ID_FILE_STATES_LOAD_OTHER 40023 +#define ID_FILE_STATES_SAVE_SLOT1 40024 +#define ID_FILE_STATES_SAVE_SLOT2 40025 +#define ID_FILE_STATES_SAVE_SLOT3 40026 +#define ID_FILE_STATES_SAVE_SLOT4 40027 +#define ID_FILE_STATES_SAVE_SLOT5 40028 +#define ID_FILE_STATES_SAVE_OTHER 40029 +#define ID_CONFIG_FW 40030 +#define ID_CONFIG_USB 40031 +#define ID_CONFIG_MEMCARDS 40032 +#define IDC_FORMAT1 40033 +#define IDC_FORMAT2 40034 +#define IDC_LIST1 40035 +#define IDC_LIST2 40036 +#define IDC_RELOAD1 40037 +#define IDC_RELOAD2 40038 +#define IDC_COPYTO2 40039 +#define IDC_COPYTO1 40040 +#define IDC_PASTE 40041 +#define IDC_DELETE1 40042 +#define IDC_DELETE2 40043 +#define ID_PATCHBROWSER 40044 +#define ID_NEWPATCH 40045 +#define IDC_NEWPATCH 40045 +#define IDC_PATCHTEXT 40046 +#define IDC_SAVEPATCH 40047 +#define IDC_REFRESHPATCHLIST 40048 +#define IDC_PATCHCRCLIST 40049 +#define IDC_PATCHNAMELIST 40050 +#define IDC_GAMENAMESEARCH 40051 +#define IDC_SEARCHPATCHTEXT 40052 +#define ID_PROCESSLOW 40053 +#define ID_PROCESSNORMAL 40054 +#define ID_PROCESSHIGH 40055 +#define ID_CONSOLE 40056 +#define ID_PATCHES 40057 +#define ID_CONFIG_ADVANCED 40058 +#define ID_INTERLACEHACK 40059 +#define ID_SAFECNTS 40060 +#define ID_SPU2HACK 40061 +#define ID_VSYNCRATE 40062 +#define ID_HELP_HELP 40063 +#define ID_PROFILER 40066 +#define ID_CDVDPRINT 40067 +#define ID_CLOSEGS 40070 +#define ID_CHEAT_FINDER_SHOW 40100 +#define ID_CHEAT_BROWSER_SHOW 40101 +#define ID_HACKS 40102 +#define ID_GAMEFIXES 40103 +#define ID_ADVANCED_OPTIONS 40104 +#define ID_LANGS 50000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 141 +#define _APS_NEXT_COMMAND_VALUE 40018 +#define _APS_NEXT_CONTROL_VALUE 1314 +#define _APS_NEXT_SYMED_VALUE 104 +#endif +#endif diff --git a/pcsx2/x86/BaseblockEx.cpp b/pcsx2/x86/BaseblockEx.cpp new file mode 100644 index 0000000000..e48c5eee71 --- /dev/null +++ b/pcsx2/x86/BaseblockEx.cpp @@ -0,0 +1,149 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "BaseblockEx.h" + +#include + +using namespace std; + +struct BASEBLOCKS +{ + // 0 - ee, 1 - iop + void Add(BASEBLOCKEX*); + void Remove(BASEBLOCKEX*); + int Get(u32 startpc); + void Reset(); + + BASEBLOCKEX** GetAll(int* pnum); + + vector blocks; +}; + +void BASEBLOCKS::Add(BASEBLOCKEX* pex) +{ + assert( pex != NULL ); + + switch(blocks.size()) { + case 0: + blocks.push_back(pex); + return; + case 1: + assert( blocks.front()->startpc != pex->startpc ); + + if( blocks.front()->startpc < pex->startpc ) { + blocks.push_back(pex); + } + else blocks.insert(blocks.begin(), pex); + + return; + + default: + { + int imin = 0, imax = blocks.size(), imid; + + while(imin < imax) { + imid = (imin+imax)>>1; + + if( blocks[imid]->startpc > pex->startpc ) imax = imid; + else imin = imid+1; + } + + assert( imin == blocks.size() || blocks[imin]->startpc > pex->startpc ); + if( imin > 0 ) assert( blocks[imin-1]->startpc < pex->startpc ); + blocks.insert(blocks.begin()+imin, pex); + + return; + } + } +} + +int BASEBLOCKS::Get(u32 startpc) +{ + switch(blocks.size()) { + case 1: + return 0; + case 2: + return blocks.front()->startpc < startpc; + + default: + { + int imin = 0, imax = blocks.size()-1, imid; + + while(imin < imax) { + imid = (imin+imax)>>1; + + if( blocks[imid]->startpc > startpc ) imax = imid; + else if( blocks[imid]->startpc == startpc ) return imid; + else imin = imid+1; + } + + assert( blocks[imin]->startpc == startpc ); + return imin; + } + } +} + +void BASEBLOCKS::Remove(BASEBLOCKEX* pex) +{ + assert( pex != NULL ); + int i = Get(pex->startpc); + assert( blocks[i] == pex ); + blocks.erase(blocks.begin()+i); +} + +void BASEBLOCKS::Reset() +{ + blocks.resize(0); + blocks.reserve(512); +} + +BASEBLOCKEX** BASEBLOCKS::GetAll(int* pnum) +{ + assert( pnum != NULL ); + *pnum = blocks.size(); + return &blocks[0]; +} + +static BASEBLOCKS s_vecBaseBlocksEx[2]; + +void AddBaseBlockEx(BASEBLOCKEX* pex, int cpu) +{ + s_vecBaseBlocksEx[cpu].Add(pex); +} + +BASEBLOCKEX* GetBaseBlockEx(u32 startpc, int cpu) +{ + return s_vecBaseBlocksEx[cpu].blocks[s_vecBaseBlocksEx[cpu].Get(startpc)]; +} + +void RemoveBaseBlockEx(BASEBLOCKEX* pex, int cpu) +{ + s_vecBaseBlocksEx[cpu].Remove(pex); +} + +void ResetBaseBlockEx(int cpu) +{ + s_vecBaseBlocksEx[cpu].Reset(); +} + +BASEBLOCKEX** GetAllBaseBlocks(int* pnum, int cpu) +{ + return s_vecBaseBlocksEx[cpu].GetAll(pnum); +} diff --git a/pcsx2/x86/BaseblockEx.h b/pcsx2/x86/BaseblockEx.h new file mode 100644 index 0000000000..c3502262f2 --- /dev/null +++ b/pcsx2/x86/BaseblockEx.h @@ -0,0 +1,77 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef _BASEBLOCKEX_H_ +#define _BASEBLOCKEX_H_ + +// used to keep block information +#define BLOCKTYPE_STARTPC 4 // startpc offset +#define BLOCKTYPE_DELAYSLOT 1 // if bit set, delay slot + +// Every potential jump point in the PS2's addressable memory has a BASEBLOCK +// associated with it. So that means a BASEBLOCK for every 4 bytes of PS2 +// addressable memory. Yay! +struct BASEBLOCK +{ + u32 m_pFnptr : 28; + u32 uType : 4; + u32 startpc; + + const uptr GetFnptr() const { return ((u32)m_pFnptr)<<4; } + void SetFnptr( uptr ptr ) + { + // 16 byte alignments only, please! + jASSUME( (ptr & 0xf) == 0 ); + m_pFnptr = ptr>>4; + } +}; + +// extra block info (only valid for start of fn) +// The only "important" piece of information is size. Startpc is used as a debug/check +// var to make sure the baseblock is sane. (and it's used for some FFX hack involving +// a big snake in a sewer, but no one knows if the hack is relevant anymore). +struct BASEBLOCKEX +{ + u16 size; // size in dwords + u16 dummy; + u32 startpc; // for debugging? + +#ifdef PCSX2_DEVBUILD + u32 visited; // number of times called + LARGE_INTEGER ltime; // regs it assumes to have set already +#endif + +}; + +// This is an asinine macro that bases indexing on sizeof(BASEBLOCK) for no reason. (air) +#define GET_BLOCKTYPE(b) ((b)->Type) +#define PC_GETBLOCK_(x, reclut) ((BASEBLOCK*)(reclut[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff))) + +// This is needed because of the retarded GETBLOCK macro above. +C_ASSERT( sizeof(BASEBLOCK) == 8 ); + +// 0 - ee, 1 - iop +extern void AddBaseBlockEx(BASEBLOCKEX*, int cpu); +extern void RemoveBaseBlockEx(BASEBLOCKEX*, int cpu); +extern BASEBLOCKEX* GetBaseBlockEx(u32 startpc, int cpu); +extern void ResetBaseBlockEx(int cpu); + +extern BASEBLOCKEX** GetAllBaseBlocks(int* pnum, int cpu); + + +#endif \ No newline at end of file diff --git a/pcsx2/x86/Makefile.am b/pcsx2/x86/Makefile.am new file mode 100644 index 0000000000..c63319eac3 --- /dev/null +++ b/pcsx2/x86/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES = -I@srcdir@/../ -I@srcdir@/../common/ -I@srcdir@/../IPU/ +noinst_LIBRARIES = libx86recomp.a + +# have to add the sources instead of making a library since the linking is complicated + +archfiles = ix86-32/iR5900-32.cpp ix86-32/iR5900AritImm.cpp ix86-32/iR5900Jump.cpp \ +ix86-32/iR5900Move.cpp ix86-32/iR5900Shift.cpp ix86-32/iR5900Arit.cpp ix86-32/iR5900Branch.cpp \ +ix86-32/iR5900LoadStore.cpp ix86-32/iR5900MultDiv.cpp ix86-32/iCore-32.cpp ix86-32/aR5900-32.S \ +ix86-32/iR5900Templates.cpp ix86-32/recVTLB.cpp + +libx86recomp_a_SOURCES = \ +BaseblockEx.cpp iCOP0.cpp iCOP2.cpp iCore.cpp iFPU.cpp iGS.cpp iHw.cpp iIPU.cpp iMMI.cpp iPsxHw.cpp iPsxMem.cpp \ +iR3000A.cpp iR3000Atables.cpp iR5900CoissuedLoadStore.cpp iR5900Misc.cpp iVU0micro.cpp iVU1micro.cpp iVUmicro.cpp \ +iVUmicroLower.cpp iVUmicroUpper.cpp iVUzerorec.cpp iVif.cpp ir5900tables.cpp fast_routines.S aR3000A.S aVUzerorec.S aVif.S $(archfiles) + +libx86recomp_a_SOURCES += \ +BaseblockEx.h iCOP0.h iCore.h iFPU.h iMMI.h iR3000A.h iR5900.h iR5900Arit.h iR5900AritImm.h iR5900Branch.h iR5900Jump.h \ +iR5900LoadStore.h iR5900Move.h iR5900MultDiv.h iR5900Shift.h iVUmicro.h iVUops.h iVUzerorec.h + +libx86recomp_a_DEPENDENCIES = ix86/libix86.a + +SUBDIRS = ix86 \ No newline at end of file diff --git a/pcsx2/x86/README b/pcsx2/x86/README new file mode 100644 index 0000000000..8d82c5e482 --- /dev/null +++ b/pcsx2/x86/README @@ -0,0 +1,12 @@ +PCSX2 x86 Recompilers - zerofrog(@gmail.com) + +There are two types of x86 recompilers: x86-32 and x86-64. The code that both of them share is in the x86/ directory. Code for x86-32 is in the ix86-32/ and same goes for x86-64. The ix86/ dirctory contains the low level translation of x86 instructions to actual code, it is written to be used for both architectures. There are a lot of places where it was hard to separate 32bit from 64bit recompilers, so a lot of functions are surrounded by "#if(n)def __x86_64__" statements. + +It would be simple if the story ended here, but hte x86 recompilers use a lot of low level assembly routines, meaning that assembly has to be compiled for both windows x32/x64 versions and linux x32/x64 versions. + +For windows, the masm assembler is used (*.asm files) +For linux, gcc is used (*.S files) + +Both assemblers use very different syntax, also some routines in x86-64 differ a lot from their 32bit counterparts. Therefore, you might see the same function implemented up to 4 times in assembly. + +(no, it's not fun maintaining 4 versions of the same thing... and it might be easier to use nasm, but that probably has its own unforseeable problems) \ No newline at end of file diff --git a/pcsx2/x86/aR3000A.S b/pcsx2/x86/aR3000A.S new file mode 100644 index 0000000000..4dfc408232 --- /dev/null +++ b/pcsx2/x86/aR3000A.S @@ -0,0 +1,394 @@ +// iR3000A.c assembly routines +// zerofrog(@gmail.com) +.intel_syntax + +.extern psxRegs +.extern psxRecLUT +.extern psxRecRecompile +.extern b440 +.extern b440table + +#define PS2MEM_BASE_ 0x18000000 // has to match Memory.h + +#define BLOCKTYPE_STARTPC 4 // startpc offset +#define BLOCKTYPE_DELAYSLOT 1 // if bit set, delay slot + +#define BASEBLOCK_SIZE 2 // in dwords +#define PCOFFSET 0x208 + +#define PSX_MEMMASK 0x5fffffff + +#ifdef __x86_64__ + +#define REG_PC %edi +#define REG_BLOCK %r12 +#define REG_BLOCKd %r12d + +.globl R3000AExecute +R3000AExecute: + + push %rbx + push %rbp + push %r12 + push %r13 + push %r14 + push %r15 + + // calc PSX_GETBLOCK + // ((BASEBLOCK*)(recLUT[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff))) + mov %eax, dword ptr [psxRegs + PCOFFSET] + mov REG_PC, %eax + mov REG_BLOCKd, %eax + shl %rax, 32 + shr %rax, 48 + and REG_BLOCK, 0xfffc + shl %rax, 3 + add %rax, [psxRecLUT] + shl REG_BLOCK, 1 + add REG_BLOCK, [%rax] + + mov %r8d, [REG_BLOCK+4] + mov %r9d, REG_PC + and %r8d, 0x5fffffff + and %r9d, 0x5fffffff + cmp %r8d, %r9d + jne Execute_Recompile + mov %edx, [REG_BLOCK] + and %rdx, 0xfffffff // pFnptr + jnz Execute_Function + +Execute_Recompile: + call psxRecRecompile + mov %edx, [REG_BLOCK] + and %rdx, 0xfffffff // pFnptr + +Execute_Function: + call %rdx + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbp + pop %rbx + ret + + +// jumped to when invalid psxpc address +.globl psxDispatcher +psxDispatcher: + // EDX contains the current psxpc to jump to, stack contains the jump addr to modify + push %rdx + + // calc PSX_GETBLOCK + // ((BASEBLOCK*)(recLUT[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff))) + mov %eax, dword ptr [psxRegs + PCOFFSET] + mov REG_PC, %eax + mov REG_BLOCKd, %eax + shl %rax, 32 + shr %rax, 48 + and REG_BLOCK, 0xfffc + shl %rax, 3 + add %rax, [psxRecLUT] + shl REG_BLOCK, 1 + add REG_BLOCK, [%rax] + + // check if startpc&PSX_MEMMASK == psxRegs.pc&PSX_MEMMASK + mov %eax, REG_PC + mov %edx, [REG_BLOCK+BLOCKTYPE_STARTPC] + and %eax, PSX_MEMMASK // remove higher bits + and %edx, PSX_MEMMASK + cmp %eax, %edx + je psxDispatcher_CheckPtr + + // recompile + call psxRecRecompile +psxDispatcher_CheckPtr: + mov REG_BLOCKd, dword ptr [REG_BLOCK] + +#ifdef _DEBUG + test REG_BLOCKd, REG_BLOCKd + jnz psxDispatcher_CallFn + // throw an exception + int 10 + +psxDispatcher_CallFn: +#endif + + //and REG_BLOCK, 0x0fffffff + shl REG_BLOCK, 4 + mov %rdx, REG_BLOCK + pop %rcx // x86Ptr to mod + sub %rdx, %rcx + sub %rdx, 4 + mov [%rcx], %edx + + jmp REG_BLOCK + +.globl psxDispatcherClear +psxDispatcherClear: + // %EDX contains the current psxpc + mov dword ptr [psxRegs + PCOFFSET], %edx + mov %eax, %edx + + // calc PSX_GETBLOCK + // ((BASEBLOCK*)(recLUT[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff))) + mov REG_BLOCKd, %edx + shl %rax, 32 + shr %rax, 48 + and REG_BLOCK, 0xfffc + shl %rax, 3 + add %rax, [psxRecLUT] + shl REG_BLOCK, 1 + add REG_BLOCK, [%rax] + + // check if startpc&PSX_MEMMASK == psxRegs.pc&PSX_MEMMASK + mov REG_PC, %edx + mov %eax, REG_PC + mov %edx, [REG_BLOCK+BLOCKTYPE_STARTPC] + and %eax, PSX_MEMMASK // remove higher bits + and %edx, PSX_MEMMASK + cmp %eax, %edx + jne psxDispatcherClear_Recompile + + mov %eax, dword ptr [REG_BLOCK] + +#ifdef _DEBUG + test %eax, %eax + jnz psxDispatcherClear_CallFn + // throw an exception + int 10 + +psxDispatcherClear_CallFn: +#endif + + //and %rax, 0x0fffffff + shl %rax, 4 + jmp %rax + +psxDispatcherClear_Recompile: + call psxRecRecompile + mov %eax, dword ptr [REG_BLOCK] + + // r15 holds the prev x86 pointer + //and %rax, 0x0fffffff + shl %rax, 4 + mov byte ptr [%r15], 0xe9 // jmp32 + mov %rdx, %rax + sub %rdx, %r15 + sub %rdx, 5 + mov [%r15+1], %edx + + jmp %rax + +// called when jumping to variable psxpc address +.globl psxDispatcherReg +psxDispatcherReg: + + //s_pDispatchBlock = PSX_GETBLOCK(psxRegs.pc); + mov %eax, dword ptr [psxRegs + PCOFFSET] + mov REG_PC, %eax + mov REG_BLOCKd, %eax + shl %rax, 32 + shr %rax, 48 + and REG_BLOCK, 0xfffc + shl %rax, 3 + add %rax, [psxRecLUT] + shl REG_BLOCK, 1 + add REG_BLOCK, [%rax] + + // check if startpc == psxRegs.pc + cmp REG_PC, dword ptr [REG_BLOCK+BLOCKTYPE_STARTPC] + jne psxDispatcherReg_recomp + + mov REG_BLOCKd, dword ptr [REG_BLOCK] + +#ifdef _DEBUG + test %eax, %eax + jnz psxDispatcherReg_CallFn2 + // throw an exception + int 10 +psxDispatcherReg_CallFn2: +#endif + and REG_BLOCK, 0x0fffffff + jmp REG_BLOCK // fnptr + +psxDispatcherReg_recomp: + call psxRecRecompile + + mov %eax, dword ptr [REG_BLOCK] + //and %rax, 0x0fffffff + shl, REG_BLOCK, 4 + jmp %rax // fnprt + +#else // not x86-64 + +#define REG_PC %ecx +#define REG_BLOCK %esi + +// jumped to when invalid psxpc address +.globl psxDispatcher +psxDispatcher: + // EDX contains the current psxpc to jump to, stack contains the jump addr to modify + push %edx + + // calc PSX_GETBLOCK + // ((BASEBLOCK*)(recLUT[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff))) + mov %eax, dword ptr [psxRegs + PCOFFSET] + mov REG_BLOCK, %eax + mov REG_PC, %eax + shr %eax, 16 + and REG_BLOCK, 0xffff + shl %eax, 2 + add %eax, [psxRecLUT] + shl REG_BLOCK, 1 + add REG_BLOCK, dword ptr [%eax] + + // check if startpc&PSX_MEMMASK == psxRegs.pc&PSX_MEMMASK + mov %eax, REG_PC + mov %edx, [REG_BLOCK+BLOCKTYPE_STARTPC] + and %eax, PSX_MEMMASK // remove higher bits + and %edx, PSX_MEMMASK + cmp %eax, %edx + je psxDispatcher_CheckPtr + + // recompile + push REG_BLOCK + push REG_PC // psxpc + call psxRecRecompile + add %esp, 4 // pop old param + pop REG_BLOCK +psxDispatcher_CheckPtr: + mov REG_BLOCK, dword ptr [REG_BLOCK] + +#ifdef _DEBUG + test REG_BLOCK, REG_BLOCK + jnz psxDispatcher_CallFn + // throw an exception + int 10 + +psxDispatcher_CallFn: +#endif + + //and REG_BLOCK, 0x0fffffff + shl REG_BLOCK, 4 + mov %edx, REG_BLOCK + pop %ecx // x86Ptr to mod + sub %edx, %ecx + sub %edx, 4 + mov dword ptr [%ecx], %edx + + jmp REG_BLOCK + +.globl psxDispatcherClear +psxDispatcherClear: + // %EDX contains the current psxpc + mov dword ptr [psxRegs + PCOFFSET], %edx + + // calc PSX_GETBLOCK + // ((BASEBLOCK*)(recLUT[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff))) + mov %eax, %edx + mov REG_BLOCK, %edx + shr %eax, 16 + and REG_BLOCK, 0xffff + shl %eax, 2 + add %eax, [psxRecLUT] + shl REG_BLOCK, 1 + add REG_BLOCK, dword ptr [%eax]; + + // check if startpc&PSX_MEMMASK == psxRegs.pc&PSX_MEMMASK + mov %eax, %edx + mov REG_PC, %edx + mov %edx, [REG_BLOCK+BLOCKTYPE_STARTPC] + and %eax, PSX_MEMMASK // remove higher bits + and %edx, PSX_MEMMASK + cmp %eax, %edx + jne psxDispatcherClear_Recompile + + add %esp, 4 // ignore stack + mov %eax, dword ptr [REG_BLOCK] + +#ifdef _DEBUG + test %eax, %eax + jnz psxDispatcherClear_CallFn + // throw an exception + int 10 + +psxDispatcherClear_CallFn: +#endif + + //and %eax, 0x0fffffff + shl %eax, 4 + jmp %eax + +psxDispatcherClear_Recompile: + push REG_BLOCK + push REG_PC + call psxRecRecompile + add %esp, 4 // pop old param + pop REG_BLOCK + mov %eax, dword ptr [REG_BLOCK] + + pop %ecx // old fnptr + + //and %eax, 0x0fffffff + shl %eax, 4 + mov byte ptr [%ecx], 0xe9 // jmp32 + mov %edx, %eax + sub %edx, %ecx + sub %edx, 5 + mov dword ptr [%ecx+1], %edx + + jmp %eax + +// called when jumping to variable psxpc address +.globl psxDispatcherReg +psxDispatcherReg: + + //s_pDispatchBlock = PSX_GETBLOCK(psxRegs.pc); + mov %edx, dword ptr [psxRegs+PCOFFSET] + mov %ecx, %edx + + shr %edx, 14 + and %edx, 0xfffffffc + add %edx, [psxRecLUT] + mov %edx, dword ptr [%edx] + + mov %eax, %ecx + and %eax, 0xfffc + // %edx += 2*%eax + shl %eax, 1 + add %edx, %eax + + // check if startpc == psxRegs.pc + mov %eax, %ecx + cmp %eax, dword ptr [%edx+BLOCKTYPE_STARTPC] + jne psxDispatcherReg_recomp + + mov %eax, dword ptr [%edx] + +#ifdef _DEBUG + test %eax, %eax + jnz psxDispatcherReg_CallFn2 + // throw an exception + int 10 +psxDispatcherReg_CallFn2: +#endif + //and %eax, 0x0fffffff + shl %eax, 4 + jmp %eax // fnptr + +psxDispatcherReg_recomp: + sub %esp, 8 + mov dword ptr [%esp+4], %edx + mov dword ptr [%esp], %ecx + call psxRecRecompile + mov %edx, dword ptr [%esp+4] + add %esp, 8 + + mov %eax, dword ptr [%edx] + //and %eax, 0x0fffffff + shl %eax, 4 + jmp %eax // fnptr + +#endif diff --git a/pcsx2/x86/aVUzerorec.S b/pcsx2/x86/aVUzerorec.S new file mode 100644 index 0000000000..dcc61abcca --- /dev/null +++ b/pcsx2/x86/aVUzerorec.S @@ -0,0 +1,155 @@ +// iR3000A.c assembly routines +// zerofrog(@gmail.com) +.intel_syntax + +.extern svudispfntemp +.extern s_TotalVUCycles +.extern s_callstack +.extern s_vu1esp +.extern s_writeQ +.extern s_writeP +.extern g_curdebugvu +.extern SuperVUGetProgram +.extern SuperVUCleanupProgram +.extern g_sseVUMXCSR +.extern g_sseMXCSR + +// SuperVUExecuteProgram(u32 startpc, int vuindex) +.globl SuperVUExecuteProgram +SuperVUExecuteProgram: +#ifdef __x86_64__ + mov %rax, [%rsp] + mov dword ptr [s_TotalVUCycles], 0 + add %rsp, 8 + mov [s_callstack], %rax + call SuperVUGetProgram + push %rbp + push %rbx + push %r12 + push %r13 + push %r14 + push %r15 + // function arguments + push %rdi + push %rsi + +#ifdef _DEBUG + mov s_vu1esp, %rsp +#endif + + ldmxcsr g_sseVUMXCSR + mov dword ptr [s_writeQ], 0xffffffff + mov dword ptr [s_writeP], 0xffffffff + jmp %rax +#else + mov %eax, [%esp] + mov dword ptr s_TotalVUCycles, 0 + add %esp, 4 + mov dword ptr [s_callstack], %eax + call SuperVUGetProgram + mov s_vu1ebp, %ebp + mov s_vu1esi, %esi + mov s_vuedi, %edi + mov s_vuebx, %ebx +#ifdef _DEBUG + mov s_vu1esp, %esp +#endif + + ldmxcsr g_sseVUMXCSR + mov dword ptr s_writeQ, 0xffffffff + mov dword ptr s_writeP, 0xffffffff + jmp %eax +#endif // __x86_64__ + + +.globl SuperVUEndProgram +SuperVUEndProgram: + // restore cpu state + ldmxcsr g_sseMXCSR + +#ifdef __x86_64__ +#ifdef _DEBUG + sub s_vu1esp, %rsp +#endif + + // function arguments for SuperVUCleanupProgram + pop %rsi + pop %rdi + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbx + pop %rbp +#else + mov %ebp, s_vu1ebp + mov %esi, s_vu1esi + mov %edi, s_vuedi + mov %ebx, s_vuebx + +#ifdef _DEBUG + sub s_vu1esp, %esp +#endif +#endif + + call SuperVUCleanupProgram + jmp [s_callstack] // so returns correctly + + +.globl svudispfn +svudispfn: +#ifdef __x86_64__ + mov [g_curdebugvu], %rax + push %rax + push %rcx + push %rdx + push %rbp + push %rsi + push %rdi + push %rbx + push %r8 + push %r9 + push %r10 + push %r11 + push %r12 + push %r13 + push %r14 + push %r15 + + call svudispfntemp + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rbx + pop %rdi + pop %rsi + pop %rbp + pop %rdx + pop %rcx + pop %rax +#else + mov [g_curdebugvu], %eax + mov s_saveecx, %ecx + mov s_saveedx, %edx + mov s_saveebx, %ebx + mov s_saveesi, %esi + mov s_saveedi, %edi + mov s_saveebp, %ebp + + call svudispfntemp + + mov %ecx, s_saveecx + mov %edx, s_saveedx + mov %ebx, s_saveebx + mov %esi, s_saveesi + mov %edi, s_saveedi + mov %ebp, s_saveebp +#endif + ret \ No newline at end of file diff --git a/pcsx2/x86/aVif.S b/pcsx2/x86/aVif.S new file mode 100644 index 0000000000..a929a148a8 --- /dev/null +++ b/pcsx2/x86/aVif.S @@ -0,0 +1,1632 @@ +/*Pcsx2 - Pc Ps2 Emulator + Copyright (C) 2002-2007 Pcsx2 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 + +*/ +.intel_syntax + +.extern _vifRegs +.extern _vifMaskRegs +.extern _vifRow + +#ifdef __x86_64__ +#define VIF_ESP %rsp +#define VIF_SRC %rsi +#define VIF_INC %rcx +#define VIF_DST %rdi +#define VIF_SIZE %edx +#define VIF_TMPADDR %rax +#define VIF_SAVEEBX %r8 +#define VIF_SAVEEBXd %r8d +#else +#define VIF_ESP %esp +#define VIF_SRC %esi +#define VIF_INC %ecx +#define VIF_DST %edi +#define VIF_SIZE %edx +#define VIF_TMPADDR %eax +#define VIF_SAVEEBX %ebx +#define VIF_SAVEEBXd %ebx +#endif + +#define XMM_R0 %xmm0 +#define XMM_R1 %xmm1 +#define XMM_R2 %xmm2 +#define XMM_WRITEMASK %xmm3 +#define XMM_ROWMASK %xmm4 +#define XMM_ROWCOLMASK %xmm5 +#define XMM_ROW %xmm6 +#define XMM_COL %xmm7 + +#define XMM_R3 XMM_COL + +// writing masks +#define UNPACK_Write0_Regular(r0, CL, DEST_OFFSET, MOVDQA) \ + MOVDQA xmmword ptr [VIF_DST+DEST_OFFSET], r0; + +#define UNPACK_Write1_Regular(r0, CL, DEST_OFFSET, MOVDQA) \ + MOVDQA xmmword ptr [VIF_DST], r0; \ + add VIF_DST, VIF_INC; \ + +#define UNPACK_Write0_Mask UNPACK_Write0_Regular +#define UNPACK_Write1_Mask UNPACK_Write1_Regular + +// masked write (dest needs to be in edi) +#define UNPACK_Write0_WriteMask(r0, CL, DEST_OFFSET, MOVDQA) \ + movdqa XMM_WRITEMASK, xmmword ptr [VIF_TMPADDR + 64*(CL) + 48]; \ + pand r0, XMM_WRITEMASK; \ + pandn XMM_WRITEMASK, xmmword ptr [VIF_DST]; \ + por r0, XMM_WRITEMASK; \ + MOVDQA xmmword ptr [VIF_DST], r0; \ + add VIF_DST, 16; \ + +// masked write (dest needs to be in edi) +#define UNPACK_Write1_WriteMask(r0, CL, DEST_OFFSET, MOVDQA) \ + movdqa XMM_WRITEMASK, xmmword ptr [VIF_TMPADDR + 64*(0) + 48]; \ + pand r0, XMM_WRITEMASK; \ + pandn XMM_WRITEMASK, xmmword ptr [VIF_DST]; \ + por r0, XMM_WRITEMASK; \ + MOVDQA xmmword ptr [VIF_DST], r0; \ + add VIF_DST, VIF_INC; \ + +#define UNPACK_Mask_SSE_0(r0) \ + pand r0, XMM_WRITEMASK; \ + por r0, XMM_ROWCOLMASK; \ + +// once a xmmword is uncomprssed, applies masks and saves +// note: modifying XMM_WRITEMASK +// dest = row + write (only when mask=0), otherwise write +#define UNPACK_Mask_SSE_1(r0) \ + pand r0, XMM_WRITEMASK; \ + por r0, XMM_ROWCOLMASK; \ + pand XMM_WRITEMASK, XMM_ROW; \ + paddd r0, XMM_WRITEMASK; \ + +// dest = row + write (only when mask=0), otherwise write +// row = row + write (only when mask = 0), otherwise row +#define UNPACK_Mask_SSE_2(r0) \ + pand r0, XMM_WRITEMASK; \ + pand XMM_WRITEMASK, XMM_ROW; \ + paddd XMM_ROW, r0; \ + por r0, XMM_ROWCOLMASK; \ + paddd r0, XMM_WRITEMASK; \ + +#define UNPACK_WriteMask_SSE_0 UNPACK_Mask_SSE_0 +#define UNPACK_WriteMask_SSE_1 UNPACK_Mask_SSE_1 +#define UNPACK_WriteMask_SSE_2 UNPACK_Mask_SSE_2 + +#define UNPACK_Regular_SSE_0(r0) + +#define UNPACK_Regular_SSE_1(r0) \ + paddd r0, XMM_ROW; \ + +#define UNPACK_Regular_SSE_2(r0) \ + paddd r0, XMM_ROW; \ + movdqa XMM_ROW, r0; \ + +// setting up masks +#define UNPACK_Setup_Mask_SSE(CL) \ + mov VIF_TMPADDR, _vifMaskRegs; \ + movdqa XMM_ROWMASK, xmmword ptr [VIF_TMPADDR + 64*(CL) + 16]; \ + movdqa XMM_ROWCOLMASK, xmmword ptr [VIF_TMPADDR + 64*(CL) + 32]; \ + movdqa XMM_WRITEMASK, xmmword ptr [VIF_TMPADDR + 64*(CL)]; \ + pand XMM_ROWMASK, XMM_ROW; \ + pand XMM_ROWCOLMASK, XMM_COL; \ + por XMM_ROWCOLMASK, XMM_ROWMASK; \ + +#define UNPACK_Start_Setup_Mask_SSE_0(CL) UNPACK_Setup_Mask_SSE(CL) +#define UNPACK_Start_Setup_Mask_SSE_1(CL) \ + mov VIF_TMPADDR, _vifMaskRegs; \ + movdqa XMM_ROWMASK, xmmword ptr [VIF_TMPADDR + 64*(CL) + 16]; \ + movdqa XMM_ROWCOLMASK, xmmword ptr [VIF_TMPADDR + 64*(CL) + 32]; \ + pand XMM_ROWMASK, XMM_ROW; \ + pand XMM_ROWCOLMASK, XMM_COL; \ + por XMM_ROWCOLMASK, XMM_ROWMASK; \ + +#define UNPACK_Start_Setup_Mask_SSE_2(CL) + +#define UNPACK_Setup_Mask_SSE_0_1(CL) +#define UNPACK_Setup_Mask_SSE_1_1(CL) \ + mov VIF_TMPADDR, _vifMaskRegs; \ + movdqa XMM_WRITEMASK, xmmword ptr [VIF_TMPADDR + 64*(0)]; \ + +// ignore CL, since vif.cycle.wl == 1 +#define UNPACK_Setup_Mask_SSE_2_1(CL) \ + mov VIF_TMPADDR, _vifMaskRegs; \ + movdqa XMM_ROWMASK, xmmword ptr [VIF_TMPADDR + 64*(0) + 16]; \ + movdqa XMM_ROWCOLMASK, xmmword ptr [VIF_TMPADDR + 64*(0) + 32]; \ + movdqa XMM_WRITEMASK, xmmword ptr [VIF_TMPADDR + 64*(0)]; \ + pand XMM_ROWMASK, XMM_ROW; \ + pand XMM_ROWCOLMASK, XMM_COL; \ + por XMM_ROWCOLMASK, XMM_ROWMASK; \ + +#define UNPACK_Setup_Mask_SSE_0_0(CL) UNPACK_Setup_Mask_SSE(CL) +#define UNPACK_Setup_Mask_SSE_1_0(CL) UNPACK_Setup_Mask_SSE(CL) +#define UNPACK_Setup_Mask_SSE_2_0(CL) UNPACK_Setup_Mask_SSE(CL) + +// write mask always destroys XMM_WRITEMASK, so 0_0 = 1_0 +#define UNPACK_Setup_WriteMask_SSE_0_0(CL) UNPACK_Setup_Mask_SSE(CL) +#define UNPACK_Setup_WriteMask_SSE_1_0(CL) UNPACK_Setup_Mask_SSE(CL) +#define UNPACK_Setup_WriteMask_SSE_2_0(CL) UNPACK_Setup_Mask_SSE(CL) +#define UNPACK_Setup_WriteMask_SSE_0_1(CL) UNPACK_Setup_Mask_SSE_1_1(CL) +#define UNPACK_Setup_WriteMask_SSE_1_1(CL) UNPACK_Setup_Mask_SSE_1_1(CL) +#define UNPACK_Setup_WriteMask_SSE_2_1(CL) UNPACK_Setup_Mask_SSE_2_1(CL) + +#define UNPACK_Start_Setup_WriteMask_SSE_0(CL) UNPACK_Start_Setup_Mask_SSE_1(CL) +#define UNPACK_Start_Setup_WriteMask_SSE_1(CL) UNPACK_Start_Setup_Mask_SSE_1(CL) +#define UNPACK_Start_Setup_WriteMask_SSE_2(CL) UNPACK_Start_Setup_Mask_SSE_2(CL) + +#define UNPACK_Start_Setup_Regular_SSE_0(CL) +#define UNPACK_Start_Setup_Regular_SSE_1(CL) +#define UNPACK_Start_Setup_Regular_SSE_2(CL) +#define UNPACK_Setup_Regular_SSE_0_0(CL) +#define UNPACK_Setup_Regular_SSE_1_0(CL) +#define UNPACK_Setup_Regular_SSE_2_0(CL) +#define UNPACK_Setup_Regular_SSE_0_1(CL) +#define UNPACK_Setup_Regular_SSE_1_1(CL) +#define UNPACK_Setup_Regular_SSE_2_1(CL) + +#define UNPACK_INC_DST_0_Regular(qw) add VIF_DST, (16*qw) +#define UNPACK_INC_DST_1_Regular(qw) +#define UNPACK_INC_DST_0_Mask(qw) add VIF_DST, (16*qw) +#define UNPACK_INC_DST_1_Mask(qw) +#define UNPACK_INC_DST_0_WriteMask(qw) +#define UNPACK_INC_DST_1_WriteMask(qw) + +// unpacks for 1,2,3,4 elements (V3 uses this directly) +#define UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType) \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+0); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R0); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R0, CL, 0, movdqa); \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+1); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R1); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R1, CL+1, 16, movdqa); \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+2); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R2); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R2, CL+2, 32, movdqa); \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+3); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R3); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R3, CL+3, 48, movdqa); \ + \ + UNPACK_INC_DST_##TOTALCL##_##MaskType##(4) + +// V3 uses this directly +#define UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType) \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R0); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R0, CL, 0, movdqa); \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+1); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R1); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R1, CL+1, 16, movdqa); \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+2); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R2); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R2, CL+2, 32, movdqa); \ + \ + UNPACK_INC_DST_##TOTALCL##_##MaskType##(3); \ + +#define UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType) \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R0); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R0, CL, 0, movdqa); \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+1); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R1); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R1, CL+1, 16, movdqa); \ + \ + UNPACK_INC_DST_##TOTALCL##_##MaskType##(2); \ + +#define UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType) \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R0); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R0, CL, 0, movdqa); \ + \ + UNPACK_INC_DST_##TOTALCL##_##MaskType##(1); \ + +// S-32 +// only when cl==1 +#define UNPACK_S_32SSE_4x(CL, TOTALCL, MaskType, ModeType, MOVDQA) \ + MOVDQA XMM_R3, xmmword ptr [VIF_SRC]; \ + \ + pshufd XMM_R0, XMM_R3, 0; \ + pshufd XMM_R1, XMM_R3, 0x55; \ + pshufd XMM_R2, XMM_R3, 0xaa; \ + pshufd XMM_R3, XMM_R3, 0xff; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 16; \ + +#define UNPACK_S_32SSE_4A(CL, TOTALCL, MaskType, ModeType) UNPACK_S_32SSE_4x(CL, TOTALCL, MaskType, ModeType, movdqa) +#define UNPACK_S_32SSE_4(CL, TOTALCL, MaskType, ModeType) UNPACK_S_32SSE_4x(CL, TOTALCL, MaskType, ModeType, movdqu) + +#define UNPACK_S_32SSE_3x(CL, TOTALCL, MaskType, ModeType, MOVDQA) \ + MOVDQA XMM_R2, xmmword ptr [VIF_SRC]; \ + \ + pshufd XMM_R0, XMM_R2, 0; \ + pshufd XMM_R1, XMM_R2, 0x55; \ + pshufd XMM_R2, XMM_R2, 0xaa; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 12; \ + +#define UNPACK_S_32SSE_3A(CL, TOTALCL, MaskType, ModeType) UNPACK_S_32SSE_3x(CL, TOTALCL, MaskType, ModeType, movdqa) +#define UNPACK_S_32SSE_3(CL, TOTALCL, MaskType, ModeType) UNPACK_S_32SSE_3x(CL, TOTALCL, MaskType, ModeType, movdqu) + +#define UNPACK_S_32SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R1, qword ptr [VIF_SRC]; \ + \ + pshufd XMM_R0, XMM_R1, 0; \ + pshufd XMM_R1, XMM_R1, 0x55; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +#define UNPACK_S_32SSE_2A UNPACK_S_32SSE_2 + +#define UNPACK_S_32SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R0, dword ptr [VIF_SRC]; \ + pshufd XMM_R0, XMM_R0, 0; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 4; \ + +#define UNPACK_S_32SSE_1A UNPACK_S_32SSE_1 + +// S-16 +#define UNPACK_S_16SSE_4(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R3, qword ptr [VIF_SRC]; \ + punpcklwd XMM_R3, XMM_R3; \ + UNPACK_RIGHTSHIFT XMM_R3, 16; \ + \ + pshufd XMM_R0, XMM_R3, 0; \ + pshufd XMM_R1, XMM_R3, 0x55; \ + pshufd XMM_R2, XMM_R3, 0xaa; \ + pshufd XMM_R3, XMM_R3, 0xff; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +#define UNPACK_S_16SSE_4A UNPACK_S_16SSE_4 + +#define UNPACK_S_16SSE_3(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R2, qword ptr [VIF_SRC]; \ + punpcklwd XMM_R2, XMM_R2; \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + \ + pshufd XMM_R0, XMM_R2, 0; \ + pshufd XMM_R1, XMM_R2, 0x55; \ + pshufd XMM_R2, XMM_R2, 0xaa; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + add VIF_SRC, 6; \ + +#define UNPACK_S_16SSE_3A UNPACK_S_16SSE_3 + +#define UNPACK_S_16SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R1, dword ptr [VIF_SRC]; \ + punpcklwd XMM_R1, XMM_R1; \ + UNPACK_RIGHTSHIFT XMM_R1, 16; \ + \ + pshufd XMM_R0, XMM_R1, 0; \ + pshufd XMM_R1, XMM_R1, 0x55; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 4; \ + +#define UNPACK_S_16SSE_2A UNPACK_S_16SSE_2 + +#define UNPACK_S_16SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R0, dword ptr [VIF_SRC]; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + pshufd XMM_R0, XMM_R0, 0; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 2; \ + +#define UNPACK_S_16SSE_1A UNPACK_S_16SSE_1 + +// S-8 +#define UNPACK_S_8SSE_4(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R3, dword ptr [VIF_SRC]; \ + punpcklbw XMM_R3, XMM_R3; \ + punpcklwd XMM_R3, XMM_R3; \ + UNPACK_RIGHTSHIFT XMM_R3, 24; \ + \ + pshufd XMM_R0, XMM_R3, 0; \ + pshufd XMM_R1, XMM_R3, 0x55; \ + pshufd XMM_R2, XMM_R3, 0xaa; \ + pshufd XMM_R3, XMM_R3, 0xff; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 4; \ + +#define UNPACK_S_8SSE_4A UNPACK_S_8SSE_4 + +#define UNPACK_S_8SSE_3(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R2, dword ptr [VIF_SRC]; \ + punpcklbw XMM_R2, XMM_R2; \ + punpcklwd XMM_R2, XMM_R2; \ + UNPACK_RIGHTSHIFT XMM_R2, 24; \ + \ + pshufd XMM_R0, XMM_R2, 0; \ + pshufd XMM_R1, XMM_R2, 0x55; \ + pshufd XMM_R2, XMM_R2, 0xaa; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 3; \ + +#define UNPACK_S_8SSE_3A UNPACK_S_8SSE_3 + +#define UNPACK_S_8SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R1, dword ptr [VIF_SRC]; \ + punpcklbw XMM_R1, XMM_R1; \ + punpcklwd XMM_R1, XMM_R1; \ + UNPACK_RIGHTSHIFT XMM_R1, 24; \ + \ + pshufd XMM_R0, XMM_R1, 0; \ + pshufd XMM_R1, XMM_R1, 0x55; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 2; \ + +#define UNPACK_S_8SSE_2A UNPACK_S_8SSE_2 + +#define UNPACK_S_8SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R0, dword ptr [VIF_SRC]; \ + punpcklbw XMM_R0, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + pshufd XMM_R0, XMM_R0, 0; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + inc VIF_SRC; \ + +#define UNPACK_S_8SSE_1A UNPACK_S_8SSE_1 + +// V2-32 +#define UNPACK_V2_32SSE_4A(CL, TOTALCL, MaskType, ModeType) \ + MOVDQA XMM_R0, xmmword ptr [VIF_SRC]; \ + MOVDQA XMM_R2, xmmword ptr [VIF_SRC+16]; \ + \ + pshufd XMM_R1, XMM_R0, 0xee; \ + pshufd XMM_R3, XMM_R2, 0xee; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 32; \ + +#define UNPACK_V2_32SSE_4(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + movq XMM_R1, qword ptr [VIF_SRC+8]; \ + movq XMM_R2, qword ptr [VIF_SRC+16]; \ + movq XMM_R3, qword ptr [VIF_SRC+24]; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 32; \ + +#define UNPACK_V2_32SSE_3A(CL, TOTALCL, MaskType, ModeType) \ + MOVDQA XMM_R0, xmmword ptr [VIF_SRC]; \ + movq XMM_R2, qword ptr [VIF_SRC+16]; \ + pshufd XMM_R1, XMM_R0, 0xee; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 24; \ + +#define UNPACK_V2_32SSE_3(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + movq XMM_R1, qword ptr [VIF_SRC+8]; \ + movq XMM_R2, qword ptr [VIF_SRC+16]; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 24; \ + +#define UNPACK_V2_32SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + movq XMM_R1, qword ptr [VIF_SRC+8]; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 16; \ + +#define UNPACK_V2_32SSE_2A UNPACK_V2_32SSE_2 + +#define UNPACK_V2_32SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +#define UNPACK_V2_32SSE_1A UNPACK_V2_32SSE_1 + +// V2-16 +// due to lemmings, have to copy lower xmmword to the upper xmmword of every reg +#define UNPACK_V2_16SSE_4A(CL, TOTALCL, MaskType, ModeType) \ + punpcklwd XMM_R0, xmmword ptr [VIF_SRC]; \ + punpckhwd XMM_R2, xmmword ptr [VIF_SRC]; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + \ + punpckhqdq XMM_R1, XMM_R0; \ + punpckhqdq XMM_R3, XMM_R2; \ + \ + punpcklqdq XMM_R0, XMM_R0; \ + punpcklqdq XMM_R2, XMM_R2; \ + punpckhqdq XMM_R1, XMM_R1; \ + punpckhqdq XMM_R3, XMM_R3; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + add VIF_SRC, 16; \ + +#define UNPACK_V2_16SSE_4(CL, TOTALCL, MaskType, ModeType) \ + movdqu XMM_R0, xmmword ptr [VIF_SRC]; \ + \ + punpckhwd XMM_R2, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + \ + punpckhqdq XMM_R1, XMM_R0; \ + punpckhqdq XMM_R3, XMM_R2; \ + \ + punpcklqdq XMM_R0, XMM_R0; \ + punpcklqdq XMM_R2, XMM_R2; \ + punpckhqdq XMM_R1, XMM_R1; \ + punpckhqdq XMM_R3, XMM_R3; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 16; \ + +#define UNPACK_V2_16SSE_3A(CL, TOTALCL, MaskType, ModeType) \ + punpcklwd XMM_R0, xmmword ptr [VIF_SRC]; \ + punpckhwd XMM_R2, xmmword ptr [VIF_SRC]; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + \ + punpckhqdq XMM_R1, XMM_R0; \ + \ + punpcklqdq XMM_R0, XMM_R0; \ + punpcklqdq XMM_R2, XMM_R2; \ + punpckhqdq XMM_R1, XMM_R1; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 12; \ + +#define UNPACK_V2_16SSE_3(CL, TOTALCL, MaskType, ModeType) \ + movdqu XMM_R0, xmmword ptr [VIF_SRC]; \ + \ + punpckhwd XMM_R2, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + \ + punpckhqdq XMM_R1, XMM_R0; \ + \ + punpcklqdq XMM_R0, XMM_R0; \ + punpcklqdq XMM_R2, XMM_R2; \ + punpckhqdq XMM_R1, XMM_R1; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 12; \ + +#define UNPACK_V2_16SSE_2A(CL, TOTALCL, MaskType, ModeType) \ + punpcklwd XMM_R0, xmmword ptr [VIF_SRC]; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + \ + punpckhqdq XMM_R1, XMM_R0; \ + \ + punpcklqdq XMM_R0, XMM_R0; \ + punpckhqdq XMM_R1, XMM_R1; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +#define UNPACK_V2_16SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + \ + punpckhqdq XMM_R1, XMM_R0; \ + \ + punpcklqdq XMM_R0, XMM_R0; \ + punpckhqdq XMM_R1, XMM_R1; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +#define UNPACK_V2_16SSE_1A(CL, TOTALCL, MaskType, ModeType) \ + punpcklwd XMM_R0, xmmword ptr [VIF_SRC]; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + punpcklqdq XMM_R0, XMM_R0; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 4; \ + +#define UNPACK_V2_16SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R0, dword ptr [VIF_SRC]; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + punpcklqdq XMM_R0, XMM_R0; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 4; \ + +// V2-8 +// and1 streetball needs to copy lower xmmword to the upper xmmword of every reg +#define UNPACK_V2_8SSE_4(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + \ + punpcklbw XMM_R0, XMM_R0; \ + punpckhwd XMM_R2, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + UNPACK_RIGHTSHIFT XMM_R2, 24; \ + \ + punpckhqdq XMM_R1, XMM_R0; \ + punpckhqdq XMM_R3, XMM_R2; \ + \ + punpcklqdq XMM_R0, XMM_R0; \ + punpcklqdq XMM_R2, XMM_R2; \ + punpckhqdq XMM_R1, XMM_R1; \ + punpckhqdq XMM_R3, XMM_R3; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +#define UNPACK_V2_8SSE_4A UNPACK_V2_8SSE_4 + +#define UNPACK_V2_8SSE_3(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + \ + punpcklbw XMM_R0, XMM_R0; \ + punpckhwd XMM_R2, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + UNPACK_RIGHTSHIFT XMM_R2, 24; \ + \ + punpckhqdq XMM_R1, XMM_R0; \ + \ + punpcklqdq XMM_R0, XMM_R0; \ + punpcklqdq XMM_R2, XMM_R2; \ + punpckhqdq XMM_R1, XMM_R1; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 6; \ + +#define UNPACK_V2_8SSE_3A UNPACK_V2_8SSE_3 + +#define UNPACK_V2_8SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R0, dword ptr [VIF_SRC]; \ + punpcklbw XMM_R0, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + \ + punpckhqdq XMM_R1, XMM_R0; \ + \ + punpcklqdq XMM_R0, XMM_R0; \ + punpckhqdq XMM_R1, XMM_R1; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 4; \ + +#define UNPACK_V2_8SSE_2A UNPACK_V2_8SSE_2 + +#define UNPACK_V2_8SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R0, dword ptr [VIF_SRC]; \ + punpcklbw XMM_R0, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + punpcklqdq XMM_R0, XMM_R0; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 2; \ + +#define UNPACK_V2_8SSE_1A UNPACK_V2_8SSE_1 + +// V3-32 +// midnight club 2 crashes because reading a qw at +36 is out of bounds +#define UNPACK_V3_32SSE_4x(CL, TOTALCL, MaskType, ModeType, MOVDQA) \ + MOVDQA XMM_R0, xmmword ptr [VIF_SRC]; \ + movdqu XMM_R1, xmmword ptr [VIF_SRC+12]; \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+0); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R0); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R0, CL, 0, movdqa); \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+1); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R1); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R1, CL+1, 16, movdqa); \ + \ + MOVDQA XMM_R3, xmmword ptr [VIF_SRC+32]; \ + movdqu XMM_R2, xmmword ptr [VIF_SRC+24]; \ + psrldq XMM_R3, 4; \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+2); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R2); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R2, CL+2, 32, movdqa); \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+3); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R3); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R3, CL+3, 48, movdqa); \ + \ + UNPACK_INC_DST_##TOTALCL##_##MaskType##(4); \ + \ + add VIF_SRC, 48; \ + +#define UNPACK_V3_32SSE_4A(CL, TOTALCL, MaskType, ModeType) UNPACK_V3_32SSE_4x(CL, TOTALCL, MaskType, ModeType, movdqa) +#define UNPACK_V3_32SSE_4(CL, TOTALCL, MaskType, ModeType) UNPACK_V3_32SSE_4x(CL, TOTALCL, MaskType, ModeType, movdqu) + +#define UNPACK_V3_32SSE_3x(CL, TOTALCL, MaskType, ModeType, MOVDQA) \ + MOVDQA XMM_R0, xmmword ptr [VIF_SRC]; \ + movdqu XMM_R1, xmmword ptr [VIF_SRC+12]; \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R0); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R0, CL, 0, movdqa); \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+1); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R1); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R1, CL+1, 16, movdqa); \ + \ + movdqu XMM_R2, xmmword ptr [VIF_SRC+24]; \ + \ + UNPACK_Setup_##MaskType##_SSE_##ModeType##_##TOTALCL##(CL+2); \ + UNPACK_##MaskType##_SSE_##ModeType##(XMM_R2); \ + UNPACK_Write##TOTALCL##_##MaskType##(XMM_R2, CL+2, 32, movdqa); \ + \ + UNPACK_INC_DST_##TOTALCL##_##MaskType##(3); \ + \ + add VIF_SRC, 36; \ + +#define UNPACK_V3_32SSE_3A(CL, TOTALCL, MaskType, ModeType) UNPACK_V3_32SSE_3x(CL, TOTALCL, MaskType, ModeType, movdqa) +#define UNPACK_V3_32SSE_3(CL, TOTALCL, MaskType, ModeType) UNPACK_V3_32SSE_3x(CL, TOTALCL, MaskType, ModeType, movdqu) + +#define UNPACK_V3_32SSE_2x(CL, TOTALCL, MaskType, ModeType, MOVDQA) \ + MOVDQA XMM_R0, xmmword ptr [VIF_SRC]; \ + movdqu XMM_R1, xmmword ptr [VIF_SRC+12]; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 24; \ + +#define UNPACK_V3_32SSE_2A(CL, TOTALCL, MaskType, ModeType) UNPACK_V3_32SSE_2x(CL, TOTALCL, MaskType, ModeType, movdqa) +#define UNPACK_V3_32SSE_2(CL, TOTALCL, MaskType, ModeType) UNPACK_V3_32SSE_2x(CL, TOTALCL, MaskType, ModeType, movdqu) + +#define UNPACK_V3_32SSE_1x(CL, TOTALCL, MaskType, ModeType, MOVDQA) \ + MOVDQA XMM_R0, xmmword ptr [VIF_SRC]; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 12; \ + +#define UNPACK_V3_32SSE_1A(CL, TOTALCL, MaskType, ModeType) UNPACK_V3_32SSE_1x(CL, TOTALCL, MaskType, ModeType, movdqa) +#define UNPACK_V3_32SSE_1(CL, TOTALCL, MaskType, ModeType) UNPACK_V3_32SSE_1x(CL, TOTALCL, MaskType, ModeType, movdqu) + +// V3-16 +#define UNPACK_V3_16SSE_4(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + movq XMM_R1, qword ptr [VIF_SRC+6]; \ + \ + punpcklwd XMM_R0, XMM_R0; \ + movq XMM_R2, qword ptr [VIF_SRC+12]; \ + punpcklwd XMM_R1, XMM_R1; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + movq XMM_R3, qword ptr [VIF_SRC+18]; \ + UNPACK_RIGHTSHIFT XMM_R1, 16; \ + punpcklwd XMM_R2, XMM_R2; \ + punpcklwd XMM_R3, XMM_R3; \ + \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + UNPACK_RIGHTSHIFT XMM_R3, 16; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 24; \ + +#define UNPACK_V3_16SSE_4A UNPACK_V3_16SSE_4 + +#define UNPACK_V3_16SSE_3(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + movq XMM_R1, qword ptr [VIF_SRC+6]; \ + \ + punpcklwd XMM_R0, XMM_R0; \ + movq XMM_R2, qword ptr [VIF_SRC+12]; \ + punpcklwd XMM_R1, XMM_R1; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + punpcklwd XMM_R2, XMM_R2; \ + \ + UNPACK_RIGHTSHIFT XMM_R1, 16; \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 18; \ + +#define UNPACK_V3_16SSE_3A UNPACK_V3_16SSE_3 + +#define UNPACK_V3_16SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + movq XMM_R1, qword ptr [VIF_SRC+6]; \ + \ + punpcklwd XMM_R0, XMM_R0; \ + punpcklwd XMM_R1, XMM_R1; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R1, 16; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 12; \ + +#define UNPACK_V3_16SSE_2A UNPACK_V3_16SSE_2 + +#define UNPACK_V3_16SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 6; \ + +#define UNPACK_V3_16SSE_1A UNPACK_V3_16SSE_1 + +// V3-8 +#define UNPACK_V3_8SSE_4(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R1, qword ptr [VIF_SRC]; \ + movq XMM_R3, qword ptr [VIF_SRC+6]; \ + \ + punpcklbw XMM_R1, XMM_R1; \ + punpcklbw XMM_R3, XMM_R3; \ + punpcklwd XMM_R0, XMM_R1; \ + psrldq XMM_R1, 6; \ + punpcklwd XMM_R2, XMM_R3; \ + psrldq XMM_R3, 6; \ + punpcklwd XMM_R1, XMM_R1; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + punpcklwd XMM_R3, XMM_R3; \ + \ + UNPACK_RIGHTSHIFT XMM_R2, 24; \ + UNPACK_RIGHTSHIFT XMM_R1, 24; \ + UNPACK_RIGHTSHIFT XMM_R3, 24; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 12; \ + +#define UNPACK_V3_8SSE_4A UNPACK_V3_8SSE_4 + +#define UNPACK_V3_8SSE_3(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R0, dword ptr [VIF_SRC]; \ + movd XMM_R1, dword ptr [VIF_SRC+3]; \ + \ + punpcklbw XMM_R0, XMM_R0; \ + movd XMM_R2, dword ptr [VIF_SRC+6]; \ + punpcklbw XMM_R1, XMM_R1; \ + punpcklwd XMM_R0, XMM_R0; \ + punpcklbw XMM_R2, XMM_R2; \ + \ + punpcklwd XMM_R1, XMM_R1; \ + punpcklwd XMM_R2, XMM_R2; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + UNPACK_RIGHTSHIFT XMM_R1, 24; \ + UNPACK_RIGHTSHIFT XMM_R2, 24; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 9 \ + +#define UNPACK_V3_8SSE_3A UNPACK_V3_8SSE_3 + +#define UNPACK_V3_8SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R0, dword ptr [VIF_SRC]; \ + movd XMM_R1, dword ptr [VIF_SRC+3]; \ + \ + punpcklbw XMM_R0, XMM_R0; \ + punpcklbw XMM_R1, XMM_R1; \ + \ + punpcklwd XMM_R0, XMM_R0; \ + punpcklwd XMM_R1, XMM_R1; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + UNPACK_RIGHTSHIFT XMM_R1, 24; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 6; \ + +#define UNPACK_V3_8SSE_2A UNPACK_V3_8SSE_2 + +#define UNPACK_V3_8SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R0, dword ptr [VIF_SRC]; \ + punpcklbw XMM_R0, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 3; \ + +#define UNPACK_V3_8SSE_1A UNPACK_V3_8SSE_1 + +// V4-32 +#define UNPACK_V4_32SSE_4A(CL, TOTALCL, MaskType, ModeType) \ + movdqa XMM_R0, xmmword ptr [VIF_SRC]; \ + movdqa XMM_R1, xmmword ptr [VIF_SRC+16]; \ + movdqa XMM_R2, xmmword ptr [VIF_SRC+32]; \ + movdqa XMM_R3, xmmword ptr [VIF_SRC+48]; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 64; \ + +#define UNPACK_V4_32SSE_4(CL, TOTALCL, MaskType, ModeType) \ + movdqu XMM_R0, xmmword ptr [VIF_SRC]; \ + movdqu XMM_R1, xmmword ptr [VIF_SRC+16]; \ + movdqu XMM_R2, xmmword ptr [VIF_SRC+32]; \ + movdqu XMM_R3, xmmword ptr [VIF_SRC+48]; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 64; \ + +#define UNPACK_V4_32SSE_3A(CL, TOTALCL, MaskType, ModeType) \ + movdqa XMM_R0, xmmword ptr [VIF_SRC]; \ + movdqa XMM_R1, xmmword ptr [VIF_SRC+16]; \ + movdqa XMM_R2, xmmword ptr [VIF_SRC+32]; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 48; \ + +#define UNPACK_V4_32SSE_3(CL, TOTALCL, MaskType, ModeType) \ + movdqu XMM_R0, xmmword ptr [VIF_SRC]; \ + movdqu XMM_R1, xmmword ptr [VIF_SRC+16]; \ + movdqu XMM_R2, xmmword ptr [VIF_SRC+32]; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 48; \ + +#define UNPACK_V4_32SSE_2A(CL, TOTALCL, MaskType, ModeType) \ + movdqa XMM_R0, xmmword ptr [VIF_SRC]; \ + movdqa XMM_R1, xmmword ptr [VIF_SRC+16]; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 32; \ + +#define UNPACK_V4_32SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movdqu XMM_R0, xmmword ptr [VIF_SRC]; \ + movdqu XMM_R1, xmmword ptr [VIF_SRC+16]; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 32; \ + +#define UNPACK_V4_32SSE_1A(CL, TOTALCL, MaskType, ModeType) \ + movdqa XMM_R0, xmmword ptr [VIF_SRC]; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 16; \ + +#define UNPACK_V4_32SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movdqu XMM_R0, xmmword ptr [VIF_SRC]; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 16; \ + +// V4-16 +#define UNPACK_V4_16SSE_4A(CL, TOTALCL, MaskType, ModeType) \ + \ + punpcklwd XMM_R0, xmmword ptr [VIF_SRC]; \ + punpckhwd XMM_R1, xmmword ptr [VIF_SRC]; \ + punpcklwd XMM_R2, xmmword ptr [VIF_SRC+16]; \ + punpckhwd XMM_R3, xmmword ptr [VIF_SRC+16]; \ + \ + UNPACK_RIGHTSHIFT XMM_R1, 16; \ + UNPACK_RIGHTSHIFT XMM_R3, 16; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 32; \ + +#define UNPACK_V4_16SSE_4(CL, TOTALCL, MaskType, ModeType) \ + movdqu XMM_R0, xmmword ptr [VIF_SRC]; \ + movdqu XMM_R2, xmmword ptr [VIF_SRC+16]; \ + \ + punpckhwd XMM_R1, XMM_R0; \ + punpckhwd XMM_R3, XMM_R2; \ + punpcklwd XMM_R0, XMM_R0; \ + punpcklwd XMM_R2, XMM_R2; \ + \ + UNPACK_RIGHTSHIFT XMM_R1, 16; \ + UNPACK_RIGHTSHIFT XMM_R3, 16; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 32; \ + +#define UNPACK_V4_16SSE_3A(CL, TOTALCL, MaskType, ModeType) \ + punpcklwd XMM_R0, xmmword ptr [VIF_SRC]; \ + punpckhwd XMM_R1, xmmword ptr [VIF_SRC]; \ + punpcklwd XMM_R2, xmmword ptr [VIF_SRC+16]; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R1, 16; \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 24; \ + +#define UNPACK_V4_16SSE_3(CL, TOTALCL, MaskType, ModeType) \ + movdqu XMM_R0, xmmword ptr [VIF_SRC]; \ + movq XMM_R2, qword ptr [VIF_SRC+16]; \ + \ + punpckhwd XMM_R1, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + punpcklwd XMM_R2, XMM_R2; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R1, 16; \ + UNPACK_RIGHTSHIFT XMM_R2, 16; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 24; \ + +#define UNPACK_V4_16SSE_2A(CL, TOTALCL, MaskType, ModeType) \ + punpcklwd XMM_R0, xmmword ptr [VIF_SRC]; \ + punpckhwd XMM_R1, xmmword ptr [VIF_SRC]; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R1, 16; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 16; \ + +#define UNPACK_V4_16SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + movq XMM_R1, qword ptr [VIF_SRC+8]; \ + \ + punpcklwd XMM_R0, XMM_R0; \ + punpcklwd XMM_R1, XMM_R1; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + UNPACK_RIGHTSHIFT XMM_R1, 16; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 16; \ + +#define UNPACK_V4_16SSE_1A(CL, TOTALCL, MaskType, ModeType) \ + punpcklwd XMM_R0, xmmword ptr [VIF_SRC]; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +#define UNPACK_V4_16SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 16; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +// V4-8 +#define UNPACK_V4_8SSE_4A(CL, TOTALCL, MaskType, ModeType) \ + punpcklbw XMM_R0, xmmword ptr [VIF_SRC]; \ + punpckhbw XMM_R2, xmmword ptr [VIF_SRC]; \ + \ + punpckhwd XMM_R1, XMM_R0; \ + punpckhwd XMM_R3, XMM_R2; \ + punpcklwd XMM_R0, XMM_R0; \ + punpcklwd XMM_R2, XMM_R2; \ + \ + UNPACK_RIGHTSHIFT XMM_R1, 24; \ + UNPACK_RIGHTSHIFT XMM_R3, 24; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + UNPACK_RIGHTSHIFT XMM_R2, 24; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 16; \ + +#define UNPACK_V4_8SSE_4(CL, TOTALCL, MaskType, ModeType) \ + movdqu XMM_R0, xmmword ptr [VIF_SRC]; \ + \ + punpckhbw XMM_R2, XMM_R0; \ + punpcklbw XMM_R0, XMM_R0; \ + \ + punpckhwd XMM_R3, XMM_R2; \ + punpckhwd XMM_R1, XMM_R0; \ + punpcklwd XMM_R2, XMM_R2; \ + punpcklwd XMM_R0, XMM_R0; \ + \ + UNPACK_RIGHTSHIFT XMM_R3, 24; \ + UNPACK_RIGHTSHIFT XMM_R2, 24; \ + \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + UNPACK_RIGHTSHIFT XMM_R1, 24; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 16; \ + +#define UNPACK_V4_8SSE_3A(CL, TOTALCL, MaskType, ModeType) \ + punpcklbw XMM_R0, xmmword ptr [VIF_SRC]; \ + punpckhbw XMM_R2, xmmword ptr [VIF_SRC]; \ + \ + punpckhwd XMM_R1, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + punpcklwd XMM_R2, XMM_R2; \ + \ + UNPACK_RIGHTSHIFT XMM_R1, 24; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + UNPACK_RIGHTSHIFT XMM_R2, 24; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 12; \ + +#define UNPACK_V4_8SSE_3(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + movd XMM_R2, dword ptr [VIF_SRC+8]; \ + \ + punpcklbw XMM_R0, XMM_R0; \ + punpcklbw XMM_R2, XMM_R2; \ + \ + punpckhwd XMM_R1, XMM_R0; \ + punpcklwd XMM_R2, XMM_R2; \ + punpcklwd XMM_R0, XMM_R0; \ + \ + UNPACK_RIGHTSHIFT XMM_R1, 24; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + UNPACK_RIGHTSHIFT XMM_R2, 24; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 12; \ + +#define UNPACK_V4_8SSE_2A(CL, TOTALCL, MaskType, ModeType) \ + punpcklbw XMM_R0, xmmword ptr [VIF_SRC]; \ + \ + punpckhwd XMM_R1, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + \ + UNPACK_RIGHTSHIFT XMM_R1, 24; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +#define UNPACK_V4_8SSE_2(CL, TOTALCL, MaskType, ModeType) \ + movq XMM_R0, qword ptr [VIF_SRC]; \ + \ + punpcklbw XMM_R0, XMM_R0; \ + \ + punpckhwd XMM_R1, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + \ + UNPACK_RIGHTSHIFT XMM_R1, 24; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +#define UNPACK_V4_8SSE_1A(CL, TOTALCL, MaskType, ModeType) \ + punpcklbw XMM_R0, xmmword ptr [VIF_SRC]; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 4; \ + +#define UNPACK_V4_8SSE_1(CL, TOTALCL, MaskType, ModeType) \ + movd XMM_R0, dword ptr [VIF_SRC]; \ + punpcklbw XMM_R0, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + UNPACK_RIGHTSHIFT XMM_R0, 24; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 4; \ + +// V4-5 +.extern s_TempDecompress + +#define DECOMPRESS_RGBA(OFFSET) \ + mov %bl, %al; \ + shl %bl, 3; \ + mov byte ptr [s_TempDecompress+OFFSET], %bl; \ + \ + mov %bx, %ax; \ + shr %bx, 2; \ + and %bx, 0xf8; \ + mov byte ptr [s_TempDecompress+OFFSET+1], %bl; \ + \ + mov %bx, %ax; \ + shr %bx, 7; \ + and %bx, 0xf8; \ + mov byte ptr [s_TempDecompress+OFFSET+2], %bl; \ + mov %bx, %ax; \ + shr %bx, 8; \ + and %bx, 0x80; \ + mov byte ptr [s_TempDecompress+OFFSET+3], %bl; \ + +#define UNPACK_V4_5SSE_4(CL, TOTALCL, MaskType, ModeType) \ + mov %eax, dword ptr [VIF_SRC]; \ + DECOMPRESS_RGBA(0); \ + \ + shr %eax, 16; \ + DECOMPRESS_RGBA(4); \ + \ + mov %eax, dword ptr [VIF_SRC+4]; \ + DECOMPRESS_RGBA(8); \ + \ + shr %eax, 16; \ + DECOMPRESS_RGBA(12); \ + \ + movdqa XMM_R0, xmmword ptr [s_TempDecompress]; \ + \ + punpckhbw XMM_R2, XMM_R0; \ + punpcklbw XMM_R0, XMM_R0; \ + \ + punpckhwd XMM_R3, XMM_R2; \ + punpckhwd XMM_R1, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + punpcklwd XMM_R2, XMM_R2; \ + \ + psrld XMM_R0, 24; \ + psrld XMM_R1, 24; \ + psrld XMM_R2, 24; \ + psrld XMM_R3, 24; \ + \ + UNPACK4_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 8; \ + +#define UNPACK_V4_5SSE_4A UNPACK_V4_5SSE_4 + +#define UNPACK_V4_5SSE_3(CL, TOTALCL, MaskType, ModeType) \ + mov %eax, dword ptr [VIF_SRC]; \ + DECOMPRESS_RGBA(0); \ + \ + shr %eax, 16; \ + DECOMPRESS_RGBA(4); \ + \ + mov %eax, dword ptr [VIF_SRC]; \ + DECOMPRESS_RGBA(8); \ + \ + movdqa XMM_R0, xmmword ptr [s_TempDecompress]; \ + \ + punpckhbw XMM_R2, XMM_R0; \ + punpcklbw XMM_R0, XMM_R0; \ + \ + punpckhwd XMM_R1, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + punpcklwd XMM_R2, XMM_R2; \ + \ + psrld XMM_R0, 24; \ + psrld XMM_R1, 24; \ + psrld XMM_R2, 24; \ + \ + UNPACK3_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 6; \ + +#define UNPACK_V4_5SSE_3A UNPACK_V4_5SSE_3 + +#define UNPACK_V4_5SSE_2(CL, TOTALCL, MaskType, ModeType) \ + mov %eax, dword ptr [VIF_SRC]; \ + DECOMPRESS_RGBA(0); \ + \ + shr %eax, 16; \ + DECOMPRESS_RGBA(4); \ + \ + movq XMM_R0, qword ptr [s_TempDecompress]; \ + \ + punpcklbw XMM_R0, XMM_R0; \ + \ + punpckhwd XMM_R1, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + \ + psrld XMM_R0, 24; \ + psrld XMM_R1, 24; \ + \ + UNPACK2_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 4; \ + +#define UNPACK_V4_5SSE_2A UNPACK_V4_5SSE_2 + +#define UNPACK_V4_5SSE_1(CL, TOTALCL, MaskType, ModeType) \ + mov %ax, word ptr [VIF_SRC]; \ + DECOMPRESS_RGBA(0) \ + \ + movd XMM_R0, dword ptr [s_TempDecompress]; \ + punpcklbw XMM_R0, XMM_R0; \ + punpcklwd XMM_R0, XMM_R0; \ + \ + psrld XMM_R0, 24; \ + \ + UNPACK1_SSE(CL, TOTALCL, MaskType, ModeType); \ + \ + add VIF_SRC, 2; \ + +#define UNPACK_V4_5SSE_1A UNPACK_V4_5SSE_1 + +#pragma warning(disable:4731) + +#define SAVE_ROW_REG_BASE \ + mov VIF_TMPADDR, _vifRow; \ + movdqa xmmword ptr [VIF_TMPADDR], XMM_ROW; \ + mov VIF_TMPADDR, _vifRegs; \ + movss dword ptr [VIF_TMPADDR+0x100], XMM_ROW; \ + psrldq XMM_ROW, 4; \ + movss dword ptr [VIF_TMPADDR+0x110], XMM_ROW; \ + psrldq XMM_ROW, 4; \ + movss dword ptr [VIF_TMPADDR+0x120], XMM_ROW; \ + psrldq XMM_ROW, 4; \ + movss dword ptr [VIF_TMPADDR+0x130], XMM_ROW; \ + +#define SAVE_NO_REG + +#ifdef __x86_64__ +#define INIT_ARGS() + +#define POP_REGS() + +#define INC_STACK(reg) add %rsp, 8; + +#else + +// 32 bit versions have the args on the stack +#define INIT_ARGS() \ + push %edi; \ + push %esi; \ + push %ebx; \ + mov VIF_DST, dword ptr [%esp+4+12]; \ + mov VIF_SRC, dword ptr [%esp+8+12]; \ + mov VIF_SIZE, dword ptr [%esp+12+12]; \ + + +#define POP_REGS() \ + pop %ebx; \ + pop %esi; \ + pop %edi; \ + +#define INC_STACK(reg) add %esp, 4; + +#endif + +// qsize - bytes of compressed size of 1 decompressed xmmword +// int UNPACK_SkippingWrite_##name##_##sign##_##MaskType##_##ModeType(u32* dest, u32* data, int dmasize) + +#define defUNPACK_SkippingWrite(name, MaskType, ModeType, qsize, sign, SAVE_ROW_REG) \ +.globl UNPACK_SkippingWrite_##name##_##sign##_##MaskType##_##ModeType; \ +UNPACK_SkippingWrite_##name##_##sign##_##MaskType##_##ModeType: \ + INIT_ARGS(); \ + mov VIF_TMPADDR, _vifRegs; \ + movzx VIF_INC, byte ptr [VIF_TMPADDR + 0x40]; \ + movzx VIF_SAVEEBX, byte ptr [VIF_TMPADDR + 0x41]; \ + sub VIF_INC, VIF_SAVEEBX; \ + shl VIF_INC, 4; \ + \ + cmp VIF_SAVEEBXd, 1; \ + je name##_##sign##_##MaskType##_##ModeType##_WL1; \ + cmp VIF_SAVEEBXd, 2; \ + je name##_##sign##_##MaskType##_##ModeType##_WL2; \ + cmp VIF_SAVEEBXd, 3; \ + je name##_##sign##_##MaskType##_##ModeType##_WL3; \ + jmp name##_##sign##_##MaskType##_##ModeType##_WL4; \ + \ +name##_##sign##_##MaskType##_##ModeType##_WL1: \ + UNPACK_Start_Setup_##MaskType##_SSE_##ModeType##(0); \ + \ + cmp VIF_SIZE, qsize; \ + jl name##_##sign##_##MaskType##_##ModeType##_C1_Done3; \ + \ + add VIF_INC, 16; \ + \ + /* first align VIF_SRC to 16 bytes */ \ +name##_##sign##_##MaskType##_##ModeType##_C1_Align16: \ + \ + test VIF_SRC, 15; \ + jz name##_##sign##_##MaskType##_##ModeType##_C1_UnpackAligned; \ + \ + UNPACK_##name##SSE_1(0, 1, MaskType, ModeType); \ + \ + cmp VIF_SIZE, (2*qsize); \ + jl name##_##sign##_##MaskType##_##ModeType##_C1_DoneWithDec; \ + sub VIF_SIZE, qsize; \ + jmp name##_##sign##_##MaskType##_##ModeType##_C1_Align16; \ + \ +name##_##sign##_##MaskType##_##ModeType##_C1_UnpackAligned: \ + \ + cmp VIF_SIZE, (2*qsize); \ + jl name##_##sign##_##MaskType##_##ModeType##_C1_Unpack1; \ + cmp VIF_SIZE, (3*qsize); \ + jl name##_##sign##_##MaskType##_##ModeType##_C1_Unpack2; \ + cmp VIF_SIZE, (4*qsize); \ + jl name##_##sign##_##MaskType##_##ModeType##_C1_Unpack3; \ + prefetchnta [VIF_SRC + 64]; \ + \ +name##_##sign##_##MaskType##_##ModeType##_C1_Unpack4: \ + UNPACK_##name##SSE_4A(0, 1, MaskType, ModeType); \ + \ + cmp VIF_SIZE, (8*qsize); \ + jl name##_##sign##_##MaskType##_##ModeType##_C1_DoneUnpack4; \ + sub VIF_SIZE, (4*qsize); \ + jmp name##_##sign##_##MaskType##_##ModeType##_C1_Unpack4; \ + \ +name##_##sign##_##MaskType##_##ModeType##_C1_DoneUnpack4: \ + \ + sub VIF_SIZE, (4*qsize); \ + cmp VIF_SIZE, qsize; \ + jl name##_##sign##_##MaskType##_##ModeType##_C1_Done3; \ + cmp VIF_SIZE, (2*qsize); \ + jl name##_##sign##_##MaskType##_##ModeType##_C1_Unpack1; \ + cmp VIF_SIZE, (3*qsize); \ + jl name##_##sign##_##MaskType##_##ModeType##_C1_Unpack2; \ + /* fall through */ \ + \ +name##_##sign##_##MaskType##_##ModeType##_C1_Unpack3: \ + UNPACK_##name##SSE_3A(0, 1, MaskType, ModeType); \ + \ + sub VIF_SIZE, (3*qsize); \ + jmp name##_##sign##_##MaskType##_##ModeType##_C1_Done3; \ + \ +name##_##sign##_##MaskType##_##ModeType##_C1_Unpack2: \ + UNPACK_##name##SSE_2A(0, 1, MaskType, ModeType); \ + \ + sub VIF_SIZE, (2*qsize); \ + jmp name##_##sign##_##MaskType##_##ModeType##_C1_Done3; \ + \ +name##_##sign##_##MaskType##_##ModeType##_C1_Unpack1: \ + UNPACK_##name##SSE_1A(0, 1, MaskType, ModeType); \ +name##_##sign##_##MaskType##_##ModeType##_C1_DoneWithDec: \ + sub VIF_SIZE, qsize; \ +name##_##sign##_##MaskType##_##ModeType##_C1_Done3: \ + SAVE_ROW_REG; \ + mov %eax, VIF_SIZE; \ + POP_REGS(); \ + ret; \ + \ +name##_##sign##_##MaskType##_##ModeType##_WL2: \ + cmp VIF_SIZE, (2*qsize); \ + \ + jl name##_##sign##_##MaskType##_##ModeType##_C2_Done3; \ +name##_##sign##_##MaskType##_##ModeType##_C2_Unpack: \ + UNPACK_##name##SSE_2(0, 0, MaskType, ModeType); \ + \ + add VIF_DST, VIF_INC; /* take into account wl */ \ + cmp VIF_SIZE, (4*qsize); \ + jl name##_##sign##_##MaskType##_##ModeType##_C2_Done2; \ + sub VIF_SIZE, (2*qsize); \ + jmp name##_##sign##_##MaskType##_##ModeType##_C2_Unpack; /* unpack next */ \ + \ +name##_##sign##_##MaskType##_##ModeType##_C2_Done2: \ + sub VIF_SIZE, (2*qsize); \ +name##_##sign##_##MaskType##_##ModeType##_C2_Done3: \ + cmp VIF_SIZE, qsize; \ + /* execute left over qw */ \ + jl name##_##sign##_##MaskType##_##ModeType##_C2_Done4; \ + UNPACK_##name##SSE_1(0, 0, MaskType, ModeType); \ + \ + sub VIF_SIZE, qsize; \ +name##_##sign##_##MaskType##_##ModeType##_C2_Done4: \ + \ + SAVE_ROW_REG; \ + mov %eax, VIF_SIZE; \ + POP_REGS(); \ + ret; \ + \ +name##_##sign##_##MaskType##_##ModeType##_WL3: \ + cmp VIF_SIZE, (3*qsize); \ + \ + jl name##_##sign##_##MaskType##_##ModeType##_C3_Done5; \ +name##_##sign##_##MaskType##_##ModeType##_C3_Unpack: \ + UNPACK_##name##SSE_3(0, 0, MaskType, ModeType); \ + \ + add VIF_DST, VIF_INC; /* take into account wl */ \ + cmp VIF_SIZE, (6*qsize); \ + jl name##_##sign##_##MaskType##_##ModeType##_C3_Done2; \ + sub VIF_SIZE, (3*qsize); \ + jmp name##_##sign##_##MaskType##_##ModeType##_C3_Unpack; /* unpack next */ \ +name##_##sign##_##MaskType##_##ModeType##_C3_Done2: \ + sub VIF_SIZE, (3*qsize); \ +name##_##sign##_##MaskType##_##ModeType##_C3_Done5: \ + cmp VIF_SIZE, qsize; \ + jl name##_##sign##_##MaskType##_##ModeType##_C3_Done4; \ + \ + /* execute left over qw */ \ + cmp VIF_SIZE, (2*qsize); \ + jl name##_##sign##_##MaskType##_##ModeType##_C3_Done3; \ + \ + /* process 2 qws */ \ + UNPACK_##name##SSE_2(0, 0, MaskType, ModeType); \ + \ + sub VIF_SIZE, (2*qsize); \ + jmp name##_##sign##_##MaskType##_##ModeType##_C3_Done4; \ +name##_##sign##_##MaskType##_##ModeType##_C3_Done3: \ + /* process 1 qw */ \ + sub VIF_SIZE, qsize; \ + UNPACK_##name##SSE_1(0, 0, MaskType, ModeType); \ +name##_##sign##_##MaskType##_##ModeType##_C3_Done4: \ + SAVE_ROW_REG; \ + mov %eax, VIF_SIZE; \ + POP_REGS(); \ + ret; \ + \ +name##_##sign##_##MaskType##_##ModeType##_WL4: /* >= 4 */ \ + sub VIF_SAVEEBX, 3; \ + push VIF_INC; \ + cmp VIF_SIZE, qsize; \ + jl name##_##sign##_##MaskType##_##ModeType##_C4_Done; \ + \ +name##_##sign##_##MaskType##_##ModeType##_C4_Unpack: \ + cmp VIF_SIZE, (3*qsize); \ + jge name##_##sign##_##MaskType##_##ModeType##_C4_Unpack3; \ + cmp VIF_SIZE, (2*qsize); \ + jge name##_##sign##_##MaskType##_##ModeType##_C4_Unpack2; \ + \ + UNPACK_##name##SSE_1(0, 0, MaskType, ModeType) \ + \ + /* not enough data left */ \ + sub VIF_SIZE, qsize; \ + jmp name##_##sign##_##MaskType##_##ModeType##_C4_Done; \ +name##_##sign##_##MaskType##_##ModeType##_C4_Unpack2: \ + UNPACK_##name##SSE_2(0, 0, MaskType, ModeType); \ + \ + /* not enough data left */ \ + sub VIF_SIZE, (2*qsize); \ + jmp name##_##sign##_##MaskType##_##ModeType##_C4_Done; \ +name##_##sign##_##MaskType##_##ModeType##_C4_Unpack3: \ + UNPACK_##name##SSE_3(0, 0, MaskType, ModeType); \ + \ + sub VIF_SIZE, (3*qsize); \ + /* more data left, process 1qw at a time */ \ + mov VIF_INC, VIF_SAVEEBX; \ + \ +name##_##sign##_##MaskType##_##ModeType##_C4_UnpackX: \ + /* check if any data left */ \ + cmp VIF_SIZE, qsize; \ + jl name##_##sign##_##MaskType##_##ModeType##_C4_Done; \ + \ + UNPACK_##name##SSE_1(3, 0, MaskType, ModeType); \ + \ + sub VIF_SIZE, qsize; \ + cmp VIF_INC, 1; \ + je name##_##sign##_##MaskType##_##ModeType##_C4_DoneLoop; \ + sub VIF_INC, 1; \ + jmp name##_##sign##_##MaskType##_##ModeType##_C4_UnpackX; \ +name##_##sign##_##MaskType##_##ModeType##_C4_DoneLoop: \ + add VIF_DST, [VIF_ESP]; /* take into account wl */ \ + cmp VIF_SIZE, qsize; \ + jl name##_##sign##_##MaskType##_##ModeType##_C4_Done; \ + jmp name##_##sign##_##MaskType##_##ModeType##_C4_Unpack; /* unpack next */ \ +name##_##sign##_##MaskType##_##ModeType##_C4_Done: \ + \ + SAVE_ROW_REG; \ + INC_STACK(); \ + mov %eax, VIF_SIZE; \ + POP_REGS(); \ + ret; \ + +#define UNPACK_RIGHTSHIFT psrld +#define defUNPACK_SkippingWrite2(name, qsize) \ + defUNPACK_SkippingWrite(name, Regular, 0, qsize, u, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, Regular, 1, qsize, u, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, Regular, 2, qsize, u, SAVE_ROW_REG_BASE) \ + defUNPACK_SkippingWrite(name, Mask, 0, qsize, u, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, Mask, 1, qsize, u, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, Mask, 2, qsize, u, SAVE_ROW_REG_BASE) \ + defUNPACK_SkippingWrite(name, WriteMask, 0, qsize, u, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, WriteMask, 1, qsize, u, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, WriteMask, 2, qsize, u, SAVE_ROW_REG_BASE) \ + +defUNPACK_SkippingWrite2(S_32, 4) +defUNPACK_SkippingWrite2(S_16, 2) +defUNPACK_SkippingWrite2(S_8, 1) +defUNPACK_SkippingWrite2(V2_32, 8) +defUNPACK_SkippingWrite2(V2_16, 4) +defUNPACK_SkippingWrite2(V2_8, 2) +defUNPACK_SkippingWrite2(V3_32, 12) +defUNPACK_SkippingWrite2(V3_16, 6) +defUNPACK_SkippingWrite2(V3_8, 3) +defUNPACK_SkippingWrite2(V4_32, 16) +defUNPACK_SkippingWrite2(V4_16, 8) +defUNPACK_SkippingWrite2(V4_8, 4) +defUNPACK_SkippingWrite2(V4_5, 2) + +#undef UNPACK_RIGHTSHIFT +#undef defUNPACK_SkippingWrite2 + +#define UNPACK_RIGHTSHIFT psrad +#define defUNPACK_SkippingWrite2(name, qsize) \ + defUNPACK_SkippingWrite(name, Mask, 0, qsize, s, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, Regular, 0, qsize, s, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, Regular, 1, qsize, s, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, Regular, 2, qsize, s, SAVE_ROW_REG_BASE) \ + defUNPACK_SkippingWrite(name, Mask, 1, qsize, s, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, Mask, 2, qsize, s, SAVE_ROW_REG_BASE) \ + defUNPACK_SkippingWrite(name, WriteMask, 0, qsize, s, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, WriteMask, 1, qsize, s, SAVE_NO_REG) \ + defUNPACK_SkippingWrite(name, WriteMask, 2, qsize, s, SAVE_ROW_REG_BASE) \ + +defUNPACK_SkippingWrite2(S_16, 2) +defUNPACK_SkippingWrite2(S_8, 1) +defUNPACK_SkippingWrite2(V2_16, 4) +defUNPACK_SkippingWrite2(V2_8, 2) +defUNPACK_SkippingWrite2(V3_16, 6) +defUNPACK_SkippingWrite2(V3_8, 3) +defUNPACK_SkippingWrite2(V4_16, 8) +defUNPACK_SkippingWrite2(V4_8, 4) + +#undef UNPACK_RIGHTSHIFT +#undef defUNPACK_SkippingWrite2 diff --git a/pcsx2/x86/aVif.asm b/pcsx2/x86/aVif.asm new file mode 100644 index 0000000000..d23423de40 --- /dev/null +++ b/pcsx2/x86/aVif.asm @@ -0,0 +1,1941 @@ +; Pcsx2 - Pc Ps2 Emulator +; Copyright (C) 2002-2008 Pcsx2 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 + +;; Fast VIF assembly routines for UNPACK zerofrog(@gmail.com) +;; NOTE: This file is used to build aVif_proc-[32/64].asm because ml has a very +;; weak preprocessor. To generate the files, install nasm and run the following command: +;; aVif_proc-32.asm: nasmw -e aVif.asm > aVif_proc-32.asm +;; aVif_proc-64.asm: nasmw -e -D__x86_64__ aVif.asm > aVif_proc-64.asm +;; once the files are built, remove all lines starting with %line +;; and remove the brackets from the exports + +%ifndef __x86_64__ +.686 +.model flat, c +.mmx +.xmm +%endif + +extern _vifRegs:abs +extern _vifMaskRegs:abs +extern _vifRow:abs +extern _vifCol:abs +extern s_TempDecompress:abs + + +.code + + +%ifdef __x86_64__ +%define VIF_ESP rsp +%define VIF_SRC rdx +%define VIF_INC rdi +%define VIF_DST rcx +%define VIF_SIZE r8d +%define VIF_TMPADDR rax +%define VIF_SAVEEBX r9 +%define VIF_SAVEEBXd r9d +%else +%define VIF_ESP esp +%define VIF_SRC esi +%define VIF_INC ecx +%define VIF_DST edi +%define VIF_SIZE edx +%define VIF_TMPADDR eax +%define VIF_SAVEEBX ebx +%define VIF_SAVEEBXd ebx +%endif + +%define XMM_R0 xmm0 +%define XMM_R1 xmm1 +%define XMM_R2 xmm2 +%define XMM_WRITEMASK xmm3 +%define XMM_ROWMASK xmm4 +%define XMM_ROWCOLMASK xmm5 +%define XMM_ROW xmm6 +%define XMM_COL xmm7 +%define XMM_R3 XMM_COL + +;; writing masks +UNPACK_Write0_Regular macro r0, CL, DEST_OFFSET, MOVDQA + MOVDQA [VIF_DST+DEST_OFFSET], r0 + endm + +UNPACK_Write1_Regular macro r0, CL, DEST_OFFSET, MOVDQA + MOVDQA [VIF_DST], r0 + add VIF_DST, VIF_INC + endm + +UNPACK_Write0_Mask macro r0, CL, DEST_OFFSET, MOVDQA + UNPACK_Write0_Regular r0, CL, DEST_OFFSET, MOVDQA + endm + +UNPACK_Write1_Mask macro r0, CL, DEST_OFFSET, MOVDQA + UNPACK_Write1_Regular r0, CL, DEST_OFFSET, MOVDQA + endm + +;; masked write (dest needs to be in edi) +UNPACK_Write0_WriteMask macro r0, CL, DEST_OFFSET, MOVDQA + ;; masked write (dest needs to be in edi) + movdqa XMM_WRITEMASK, [VIF_TMPADDR + 64*(CL) + 48] + pand r0, XMM_WRITEMASK + pandn XMM_WRITEMASK, [VIF_DST] + por r0, XMM_WRITEMASK + MOVDQA [VIF_DST], r0 + add VIF_DST, 16 + endm + +;; masked write (dest needs to be in edi) +UNPACK_Write1_WriteMask macro r0, CL, DEST_OFFSET, MOVDQA + ;; masked write (dest needs to be in edi) + movdqa XMM_WRITEMASK, [VIF_TMPADDR + 64*(0) + 48] + pand r0, XMM_WRITEMASK + pandn XMM_WRITEMASK, [VIF_DST] + por r0, XMM_WRITEMASK + MOVDQA [VIF_DST], r0 + add VIF_DST, VIF_INC + endm + +UNPACK_Mask_SSE_0 macro r0 + pand r0, XMM_WRITEMASK + por r0, XMM_ROWCOLMASK + endm + +;; once a qword is uncomprssed, applies masks and saves +;; note: modifying XMM_WRITEMASK +;; dest = row + write (only when mask=0), otherwise write +UNPACK_Mask_SSE_1 macro r0 + ;; dest = row + write (only when mask=0), otherwise write + pand r0, XMM_WRITEMASK + por r0, XMM_ROWCOLMASK + pand XMM_WRITEMASK, XMM_ROW + paddd r0, XMM_WRITEMASK + endm + +;; dest = row + write (only when mask=0), otherwise write +;; row = row + write (only when mask = 0), otherwise row +UNPACK_Mask_SSE_2 macro r0 + ;; dest = row + write (only when mask=0), otherwise write + ;; row = row + write (only when mask = 0), otherwise row + pand r0, XMM_WRITEMASK + pand XMM_WRITEMASK, XMM_ROW + paddd XMM_ROW, r0 + por r0, XMM_ROWCOLMASK + paddd r0, XMM_WRITEMASK + endm + +UNPACK_WriteMask_SSE_0 macro r0 + UNPACK_Mask_SSE_0 r0 + endm +UNPACK_WriteMask_SSE_1 macro r0 + UNPACK_Mask_SSE_1 r0 + endm +UNPACK_WriteMask_SSE_2 macro r0 + UNPACK_Mask_SSE_2 r0 + endm + +UNPACK_Regular_SSE_0 macro r0 + endm + +UNPACK_Regular_SSE_1 macro r0 + paddd r0, XMM_ROW + endm + +UNPACK_Regular_SSE_2 macro r0 + paddd r0, XMM_ROW + movdqa XMM_ROW, r0 + endm + +;; setting up masks +UNPACK_Setup_Mask_SSE macro CL + mov VIF_TMPADDR, [_vifMaskRegs] + movdqa XMM_ROWMASK, [VIF_TMPADDR + 64*(CL) + 16] + movdqa XMM_ROWCOLMASK, [VIF_TMPADDR + 64*(CL) + 32] + movdqa XMM_WRITEMASK, [VIF_TMPADDR + 64*(CL)] + pand XMM_ROWMASK, XMM_ROW + pand XMM_ROWCOLMASK, XMM_COL + por XMM_ROWCOLMASK, XMM_ROWMASK + endm + +UNPACK_Start_Setup_Mask_SSE_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm + +UNPACK_Start_Setup_Mask_SSE_1 macro CL + mov VIF_TMPADDR, [_vifMaskRegs] + movdqa XMM_ROWMASK, [VIF_TMPADDR + 64*(CL) + 16] + movdqa XMM_ROWCOLMASK, [VIF_TMPADDR + 64*(CL) + 32] + pand XMM_ROWMASK, XMM_ROW + pand XMM_ROWCOLMASK, XMM_COL + por XMM_ROWCOLMASK, XMM_ROWMASK + endm + +UNPACK_Start_Setup_Mask_SSE_2 macro CL + endm + +UNPACK_Setup_Mask_SSE_0_1 macro CL + endm +UNPACK_Setup_Mask_SSE_1_1 macro CL + mov VIF_TMPADDR, [_vifMaskRegs] + movdqa XMM_WRITEMASK, [VIF_TMPADDR + 64*(0)] + endm + +;; ignore CL, since vif.cycle.wl == 1 +UNPACK_Setup_Mask_SSE_2_1 macro CL + ;; ignore CL, since vif.cycle.wl == 1 + mov VIF_TMPADDR, [_vifMaskRegs] + movdqa XMM_ROWMASK, [VIF_TMPADDR + 64*(0) + 16] + movdqa XMM_ROWCOLMASK, [VIF_TMPADDR + 64*(0) + 32] + movdqa XMM_WRITEMASK, [VIF_TMPADDR + 64*(0)] + pand XMM_ROWMASK, XMM_ROW + pand XMM_ROWCOLMASK, XMM_COL + por XMM_ROWCOLMASK, XMM_ROWMASK + endm + +UNPACK_Setup_Mask_SSE_0_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm +UNPACK_Setup_Mask_SSE_1_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm +UNPACK_Setup_Mask_SSE_2_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm + +;; write mask always destroys XMM_WRITEMASK, so 0_0 = 1_0 +UNPACK_Setup_WriteMask_SSE_0_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm +UNPACK_Setup_WriteMask_SSE_1_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm +UNPACK_Setup_WriteMask_SSE_2_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm +UNPACK_Setup_WriteMask_SSE_0_1 macro CL + UNPACK_Setup_Mask_SSE_1_1 CL + endm + +UNPACK_Setup_WriteMask_SSE_1_1 macro CL + UNPACK_Setup_Mask_SSE_1_1 CL + endm + +UNPACK_Setup_WriteMask_SSE_2_1 macro CL + UNPACK_Setup_Mask_SSE_2_1 CL + endm + +UNPACK_Start_Setup_WriteMask_SSE_0 macro CL + UNPACK_Start_Setup_Mask_SSE_1 CL + endm +UNPACK_Start_Setup_WriteMask_SSE_1 macro CL + UNPACK_Start_Setup_Mask_SSE_1 CL + endm +UNPACK_Start_Setup_WriteMask_SSE_2 macro CL + UNPACK_Start_Setup_Mask_SSE_2 CL + endm + +UNPACK_Start_Setup_Regular_SSE_0 macro CL + endm +UNPACK_Start_Setup_Regular_SSE_1 macro CL + endm +UNPACK_Start_Setup_Regular_SSE_2 macro CL + endm +UNPACK_Setup_Regular_SSE_0_0 macro CL + endm +UNPACK_Setup_Regular_SSE_1_0 macro CL + endm +UNPACK_Setup_Regular_SSE_2_0 macro CL + endm +UNPACK_Setup_Regular_SSE_0_1 macro CL + endm +UNPACK_Setup_Regular_SSE_1_1 macro CL + endm +UNPACK_Setup_Regular_SSE_2_1 macro CL + endm + +UNPACK_INC_DST_0_Regular macro qw + add VIF_DST, (16*qw) + endm +UNPACK_INC_DST_1_Regular macro qw + endm +UNPACK_INC_DST_0_Mask macro qw + add VIF_DST, (16*qw) + endm +UNPACK_INC_DST_1_Mask macro qw + endm +UNPACK_INC_DST_0_WriteMask macro qw + endm +UNPACK_INC_DST_1_WriteMask macro qw + endm + +;; unpacks for 1,2,3,4 elements (V3 uses this directly) +UNPACK4_SSE macro CL, TOTALCL, MaskType, ModeType + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+0 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R0, CL, 0, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+1 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R1 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R1, CL+1, 16, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+2 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R2 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R2, CL+2, 32, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+3 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R3 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R3, CL+3, 48, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 4 + endm + +;; V3 uses this directly +UNPACK3_SSE macro CL, TOTALCL, MaskType, ModeType + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R0, CL, 0, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+1 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R1 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R1, CL+1, 16, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+2 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R2 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R2, CL+2, 32, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 3 + endm + +UNPACK2_SSE macro CL, TOTALCL, MaskType, ModeType + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R0, CL, 0, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+1 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R1 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R1, CL+1, 16, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 2 + endm + +UNPACK1_SSE macro CL, TOTALCL, MaskType, ModeType + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R0, CL, 0, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 1 + endm + +;; S-32 +;; only when cl==1 +UNPACK_S_32SSE_4x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA XMM_R3, [VIF_SRC] + + pshufd XMM_R0, XMM_R3, 0 + pshufd XMM_R1, XMM_R3, 055h + pshufd XMM_R2, XMM_R3, 0aah + pshufd XMM_R3, XMM_R3, 0ffh + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 16 + endm + +UNPACK_S_32SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_4x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_S_32SSE_4 macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_4x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +UNPACK_S_32SSE_3x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA XMM_R2, [VIF_SRC] + + pshufd XMM_R0, XMM_R2, 0 + pshufd XMM_R1, XMM_R2, 055h + pshufd XMM_R2, XMM_R2, 0aah + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 12 + endm + +UNPACK_S_32SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_3x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_S_32SSE_3 macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_3x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +UNPACK_S_32SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R1, QWORD PTR [VIF_SRC] + + pshufd XMM_R0, XMM_R1, 0 + pshufd XMM_R1, XMM_R1, 055h + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +UNPACK_S_32SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_32SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R0, dword ptr [VIF_SRC] + pshufd XMM_R0, XMM_R0, 0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 4 + endm + +UNPACK_S_32SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_1 CL, TOTALCL, MaskType, ModeType + endm + +;; S-16 +UNPACK_S_16SSE_4 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R3, QWORD PTR [VIF_SRC] + punpcklwd XMM_R3, XMM_R3 + UNPACK_RIGHTSHIFT XMM_R3, 16 + + pshufd XMM_R0, XMM_R3, 0 + pshufd XMM_R1, XMM_R3, 055h + pshufd XMM_R2, XMM_R3, 0aah + pshufd XMM_R3, XMM_R3, 0ffh + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +UNPACK_S_16SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_16SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_16SSE_3 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R2, QWORD PTR [VIF_SRC] + punpcklwd XMM_R2, XMM_R2 + UNPACK_RIGHTSHIFT XMM_R2, 16 + + pshufd XMM_R0, XMM_R2, 0 + pshufd XMM_R1, XMM_R2, 055h + pshufd XMM_R2, XMM_R2, 0aah + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + add VIF_SRC, 6 + endm + +UNPACK_S_16SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_16SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_16SSE_2 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R1, dword ptr [VIF_SRC] + punpcklwd XMM_R1, XMM_R1 + UNPACK_RIGHTSHIFT XMM_R1, 16 + + pshufd XMM_R0, XMM_R1, 0 + pshufd XMM_R1, XMM_R1, 055h + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 4 + endm + +UNPACK_S_16SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_16SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_16SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R0, dword ptr [VIF_SRC] + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 16 + pshufd XMM_R0, XMM_R0, 0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 2 + endm + +UNPACK_S_16SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_16SSE_1 CL, TOTALCL, MaskType, ModeType + endm + +;; S-8 +UNPACK_S_8SSE_4 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R3, dword ptr [VIF_SRC] + punpcklbw XMM_R3, XMM_R3 + punpcklwd XMM_R3, XMM_R3 + UNPACK_RIGHTSHIFT XMM_R3, 24 + + pshufd XMM_R0, XMM_R3, 0 + pshufd XMM_R1, XMM_R3, 055h + pshufd XMM_R2, XMM_R3, 0aah + pshufd XMM_R3, XMM_R3, 0ffh + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 4 + endm + +UNPACK_S_8SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_8SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_8SSE_3 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R2, dword ptr [VIF_SRC] + punpcklbw XMM_R2, XMM_R2 + punpcklwd XMM_R2, XMM_R2 + UNPACK_RIGHTSHIFT XMM_R2, 24 + + pshufd XMM_R0, XMM_R2, 0 + pshufd XMM_R1, XMM_R2, 055h + pshufd XMM_R2, XMM_R2, 0aah + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 3 + endm + +UNPACK_S_8SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_8SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_8SSE_2 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R1, dword ptr [VIF_SRC] + punpcklbw XMM_R1, XMM_R1 + punpcklwd XMM_R1, XMM_R1 + UNPACK_RIGHTSHIFT XMM_R1, 24 + + pshufd XMM_R0, XMM_R1, 0 + pshufd XMM_R1, XMM_R1, 055h + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 2 + endm + +UNPACK_S_8SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_8SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_8SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R0, dword ptr [VIF_SRC] + punpcklbw XMM_R0, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 24 + pshufd XMM_R0, XMM_R0, 0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + inc VIF_SRC + endm + +UNPACK_S_8SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_8SSE_1 CL, TOTALCL, MaskType, ModeType + endm + +;; V2-32 +UNPACK_V2_32SSE_4A macro CL, TOTALCL, MaskType, ModeType + MOVDQA XMM_R0, [VIF_SRC] + MOVDQA XMM_R2, [VIF_SRC+16] + + pshufd XMM_R1, XMM_R0, 0eeh + pshufd XMM_R3, XMM_R2, 0eeh + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 32 + endm + +UNPACK_V2_32SSE_4 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + movq XMM_R1, QWORD PTR [VIF_SRC+8] + movq XMM_R2, QWORD PTR [VIF_SRC+16] + movq XMM_R3, QWORD PTR [VIF_SRC+24] + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 32 + endm + +UNPACK_V2_32SSE_3A macro CL, TOTALCL, MaskType, ModeType + MOVDQA XMM_R0, [VIF_SRC] + movq XMM_R2, QWORD PTR [VIF_SRC+16] + pshufd XMM_R1, XMM_R0, 0eeh + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 24 + endm + +UNPACK_V2_32SSE_3 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + movq XMM_R1, QWORD PTR [VIF_SRC+8] + movq XMM_R2, QWORD PTR [VIF_SRC+16] + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 24 + endm + +UNPACK_V2_32SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + movq XMM_R1, QWORD PTR [VIF_SRC+8] + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 16 + endm + +UNPACK_V2_32SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_32SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V2_32SSE_1 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +UNPACK_V2_32SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_32SSE_1 CL, TOTALCL, MaskType, ModeType + endm + +;; V2-16 +;; due to lemmings, have to copy lower qword to the upper qword of every reg +UNPACK_V2_16SSE_4A macro CL, TOTALCL, MaskType, ModeType + punpcklwd XMM_R0, [VIF_SRC] + punpckhwd XMM_R2, [VIF_SRC] + + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R2, 16 + ;; move the lower 64 bits down + punpckhqdq XMM_R1, XMM_R0 + punpckhqdq XMM_R3, XMM_R2 + + punpcklqdq XMM_R0, XMM_R0 + punpcklqdq XMM_R2, XMM_R2 + punpckhqdq XMM_R1, XMM_R1 + punpckhqdq XMM_R3, XMM_R3 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + add VIF_SRC, 16 + endm + +UNPACK_V2_16SSE_4 macro CL, TOTALCL, MaskType, ModeType + movdqu XMM_R0, [VIF_SRC] + + punpckhwd XMM_R2, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R2, 16 + + ;; move the lower 64 bits down + punpckhqdq XMM_R1, XMM_R0 + punpckhqdq XMM_R3, XMM_R2 + + punpcklqdq XMM_R0, XMM_R0 + punpcklqdq XMM_R2, XMM_R2 + punpckhqdq XMM_R1, XMM_R1 + punpckhqdq XMM_R3, XMM_R3 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 16 + endm + +UNPACK_V2_16SSE_3A macro CL, TOTALCL, MaskType, ModeType + punpcklwd XMM_R0, [VIF_SRC] + punpckhwd XMM_R2, [VIF_SRC] + + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R2, 16 + + ;; move the lower 64 bits down + punpckhqdq XMM_R1, XMM_R0 + + punpcklqdq XMM_R0, XMM_R0 + punpcklqdq XMM_R2, XMM_R2 + punpckhqdq XMM_R1, XMM_R1 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 12 + endm + +UNPACK_V2_16SSE_3 macro CL, TOTALCL, MaskType, ModeType + movdqu XMM_R0, [VIF_SRC] + + punpckhwd XMM_R2, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R2, 16 + + ;; move the lower 64 bits down + punpckhqdq XMM_R1, XMM_R0 + + punpcklqdq XMM_R0, XMM_R0 + punpcklqdq XMM_R2, XMM_R2 + punpckhqdq XMM_R1, XMM_R1 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 12 + endm + +UNPACK_V2_16SSE_2A macro CL, TOTALCL, MaskType, ModeType + punpcklwd XMM_R0, [VIF_SRC] + UNPACK_RIGHTSHIFT XMM_R0, 16 + + ;; move the lower 64 bits down + punpckhqdq XMM_R1, XMM_R0 + + punpcklqdq XMM_R0, XMM_R0 + punpckhqdq XMM_R1, XMM_R1 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +UNPACK_V2_16SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 16 + + ;; move the lower 64 bits down + punpckhqdq XMM_R1, XMM_R0 + + punpcklqdq XMM_R0, XMM_R0 + punpckhqdq XMM_R1, XMM_R1 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +UNPACK_V2_16SSE_1A macro CL, TOTALCL, MaskType, ModeType + punpcklwd XMM_R0, [VIF_SRC] + UNPACK_RIGHTSHIFT XMM_R0, 16 + punpcklqdq XMM_R0, XMM_R0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 4 + endm + +UNPACK_V2_16SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R0, dword ptr [VIF_SRC] + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 16 + punpcklqdq XMM_R0, XMM_R0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 4 + endm + +;; V2-8 +;; and1 streetball needs to copy lower qword to the upper qword of every reg +UNPACK_V2_8SSE_4 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + + punpcklbw XMM_R0, XMM_R0 + punpckhwd XMM_R2, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + + UNPACK_RIGHTSHIFT XMM_R0, 24 + UNPACK_RIGHTSHIFT XMM_R2, 24 + + ;; move the lower 64 bits down + punpckhqdq XMM_R1, XMM_R0 + punpckhqdq XMM_R3, XMM_R2 + + punpcklqdq XMM_R0, XMM_R0 + punpcklqdq XMM_R2, XMM_R2 + punpckhqdq XMM_R1, XMM_R1 + punpckhqdq XMM_R3, XMM_R3 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +UNPACK_V2_8SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_8SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V2_8SSE_3 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + + punpcklbw XMM_R0, XMM_R0 + punpckhwd XMM_R2, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + + UNPACK_RIGHTSHIFT XMM_R0, 24 + UNPACK_RIGHTSHIFT XMM_R2, 24 + + ;; move the lower 64 bits down + punpckhqdq XMM_R1, XMM_R0 + + punpcklqdq XMM_R0, XMM_R0 + punpcklqdq XMM_R2, XMM_R2 + punpckhqdq XMM_R1, XMM_R1 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 6 + endm + +UNPACK_V2_8SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_8SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V2_8SSE_2 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R0, dword ptr [VIF_SRC] + punpcklbw XMM_R0, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 24 + + ;; move the lower 64 bits down + punpckhqdq XMM_R1, XMM_R0 + + punpcklqdq XMM_R0, XMM_R0 + punpckhqdq XMM_R1, XMM_R1 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 4 + endm + +UNPACK_V2_8SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_8SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V2_8SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R0, dword ptr [VIF_SRC] + punpcklbw XMM_R0, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 24 + punpcklqdq XMM_R0, XMM_R0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 2 + endm + +UNPACK_V2_8SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_8SSE_1 CL, TOTALCL, MaskType, ModeType + endm + +;; V3-32 +UNPACK_V3_32SSE_4x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA XMM_R0, [VIF_SRC] + movdqu XMM_R1, [VIF_SRC+12] + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+0 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R0, CL, 0, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+1 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R1 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R1, CL+1, 16, movdqa + + ;; midnight club 2 crashes because reading a qw at +36 is out of bounds + MOVDQA XMM_R3, [VIF_SRC+32] + movdqu XMM_R2, [VIF_SRC+24] + psrldq XMM_R3, 4 + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+2 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R2 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R2, CL+2, 32, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+3 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R3 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R3, CL+3, 48, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 4 + + add VIF_SRC, 48 + endm + +UNPACK_V3_32SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_4x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_V3_32SSE_4 macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_4x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +UNPACK_V3_32SSE_3x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA XMM_R0, [VIF_SRC] + movdqu XMM_R1, [VIF_SRC+12] + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R0, CL, 0, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+1 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R1 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R1, CL+1, 16, movdqa + + movdqu XMM_R2, [VIF_SRC+24] + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+2 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) XMM_R2 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) XMM_R2, CL+2, 32, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 3 + + add VIF_SRC, 36 + endm + +UNPACK_V3_32SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_3x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_V3_32SSE_3 macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_3x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +UNPACK_V3_32SSE_2x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA XMM_R0, [VIF_SRC] + movdqu XMM_R1, [VIF_SRC+12] + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 24 + endm + +UNPACK_V3_32SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_2x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_V3_32SSE_2 macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_2x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +UNPACK_V3_32SSE_1x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA XMM_R0, [VIF_SRC] + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 12 + endm + +UNPACK_V3_32SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_1x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_V3_32SSE_1 macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_1x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +;; V3-16 +UNPACK_V3_16SSE_4 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + movq XMM_R1, QWORD PTR [VIF_SRC+6] + + punpcklwd XMM_R0, XMM_R0 + movq XMM_R2, QWORD PTR [VIF_SRC+12] + punpcklwd XMM_R1, XMM_R1 + UNPACK_RIGHTSHIFT XMM_R0, 16 + movq XMM_R3, QWORD PTR [VIF_SRC+18] + UNPACK_RIGHTSHIFT XMM_R1, 16 + punpcklwd XMM_R2, XMM_R2 + punpcklwd XMM_R3, XMM_R3 + + UNPACK_RIGHTSHIFT XMM_R2, 16 + UNPACK_RIGHTSHIFT XMM_R3, 16 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 24 + endm + +UNPACK_V3_16SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_16SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_16SSE_3 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + movq XMM_R1, QWORD PTR [VIF_SRC+6] + + punpcklwd XMM_R0, XMM_R0 + movq XMM_R2, QWORD PTR [VIF_SRC+12] + punpcklwd XMM_R1, XMM_R1 + UNPACK_RIGHTSHIFT XMM_R0, 16 + punpcklwd XMM_R2, XMM_R2 + + UNPACK_RIGHTSHIFT XMM_R1, 16 + UNPACK_RIGHTSHIFT XMM_R2, 16 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 18 + endm + +UNPACK_V3_16SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_16SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_16SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + movq XMM_R1, QWORD PTR [VIF_SRC+6] + + punpcklwd XMM_R0, XMM_R0 + punpcklwd XMM_R1, XMM_R1 + + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R1, 16 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 12 + endm + +UNPACK_V3_16SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_16SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_16SSE_1 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 16 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 6 + endm + +UNPACK_V3_16SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_16SSE_1 CL, TOTALCL, MaskType, ModeType + endm + +;; V3-8 +UNPACK_V3_8SSE_4 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R1, QWORD PTR [VIF_SRC] + movq XMM_R3, QWORD PTR [VIF_SRC+6] + + punpcklbw XMM_R1, XMM_R1 + punpcklbw XMM_R3, XMM_R3 + punpcklwd XMM_R0, XMM_R1 + psrldq XMM_R1, 6 + punpcklwd XMM_R2, XMM_R3 + psrldq XMM_R3, 6 + punpcklwd XMM_R1, XMM_R1 + UNPACK_RIGHTSHIFT XMM_R0, 24 + punpcklwd XMM_R3, XMM_R3 + + UNPACK_RIGHTSHIFT XMM_R2, 24 + UNPACK_RIGHTSHIFT XMM_R1, 24 + UNPACK_RIGHTSHIFT XMM_R3, 24 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 12 + endm + +UNPACK_V3_8SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_8SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_8SSE_3 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R0, dword ptr [VIF_SRC] + movd XMM_R1, dword ptr [VIF_SRC+3] + + punpcklbw XMM_R0, XMM_R0 + movd XMM_R2, dword ptr [VIF_SRC+6] + punpcklbw XMM_R1, XMM_R1 + punpcklwd XMM_R0, XMM_R0 + punpcklbw XMM_R2, XMM_R2 + + punpcklwd XMM_R1, XMM_R1 + punpcklwd XMM_R2, XMM_R2 + + UNPACK_RIGHTSHIFT XMM_R0, 24 + UNPACK_RIGHTSHIFT XMM_R1, 24 + UNPACK_RIGHTSHIFT XMM_R2, 24 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 9 + endm + +UNPACK_V3_8SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_8SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_8SSE_2 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R0, dword ptr [VIF_SRC] + movd XMM_R1, dword ptr [VIF_SRC+3] + + punpcklbw XMM_R0, XMM_R0 + punpcklbw XMM_R1, XMM_R1 + + punpcklwd XMM_R0, XMM_R0 + punpcklwd XMM_R1, XMM_R1 + + UNPACK_RIGHTSHIFT XMM_R0, 24 + UNPACK_RIGHTSHIFT XMM_R1, 24 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 6 + endm + +UNPACK_V3_8SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_8SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_8SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R0, dword ptr [VIF_SRC] + punpcklbw XMM_R0, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 24 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 3 + endm + +UNPACK_V3_8SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_8SSE_1 CL, TOTALCL, MaskType, ModeType + endm + +;; V4-32 +UNPACK_V4_32SSE_4A macro CL, TOTALCL, MaskType, ModeType + movdqa XMM_R0, [VIF_SRC] + movdqa XMM_R1, [VIF_SRC+16] + movdqa XMM_R2, [VIF_SRC+32] + movdqa XMM_R3, [VIF_SRC+48] + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 64 + endm + +UNPACK_V4_32SSE_4 macro CL, TOTALCL, MaskType, ModeType + movdqu XMM_R0, [VIF_SRC] + movdqu XMM_R1, [VIF_SRC+16] + movdqu XMM_R2, [VIF_SRC+32] + movdqu XMM_R3, [VIF_SRC+48] + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 64 + endm + +UNPACK_V4_32SSE_3A macro CL, TOTALCL, MaskType, ModeType + movdqa XMM_R0, [VIF_SRC] + movdqa XMM_R1, [VIF_SRC+16] + movdqa XMM_R2, [VIF_SRC+32] + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 48 + endm + +UNPACK_V4_32SSE_3 macro CL, TOTALCL, MaskType, ModeType + movdqu XMM_R0, [VIF_SRC] + movdqu XMM_R1, [VIF_SRC+16] + movdqu XMM_R2, [VIF_SRC+32] + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 48 + endm + +UNPACK_V4_32SSE_2A macro CL, TOTALCL, MaskType, ModeType + movdqa XMM_R0, [VIF_SRC] + movdqa XMM_R1, [VIF_SRC+16] + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 32 + endm + +UNPACK_V4_32SSE_2 macro CL, TOTALCL, MaskType, ModeType + movdqu XMM_R0, [VIF_SRC] + movdqu XMM_R1, [VIF_SRC+16] + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 32 + endm + +UNPACK_V4_32SSE_1A macro CL, TOTALCL, MaskType, ModeType + movdqa XMM_R0, [VIF_SRC] + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 16 + endm + +UNPACK_V4_32SSE_1 macro CL, TOTALCL, MaskType, ModeType + movdqu XMM_R0, [VIF_SRC] + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 16 + endm + +;; V4-16 +UNPACK_V4_16SSE_4A macro CL, TOTALCL, MaskType, ModeType + + punpcklwd XMM_R0, [VIF_SRC] + punpckhwd XMM_R1, [VIF_SRC] + punpcklwd XMM_R2, [VIF_SRC+16] + punpckhwd XMM_R3, [VIF_SRC+16] + + UNPACK_RIGHTSHIFT XMM_R1, 16 + UNPACK_RIGHTSHIFT XMM_R3, 16 + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R2, 16 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 32 + endm + +UNPACK_V4_16SSE_4 macro CL, TOTALCL, MaskType, ModeType + movdqu XMM_R0, [VIF_SRC] + movdqu XMM_R2, [VIF_SRC+16] + + punpckhwd XMM_R1, XMM_R0 + punpckhwd XMM_R3, XMM_R2 + punpcklwd XMM_R0, XMM_R0 + punpcklwd XMM_R2, XMM_R2 + + UNPACK_RIGHTSHIFT XMM_R1, 16 + UNPACK_RIGHTSHIFT XMM_R3, 16 + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R2, 16 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 32 + endm + +UNPACK_V4_16SSE_3A macro CL, TOTALCL, MaskType, ModeType + punpcklwd XMM_R0, [VIF_SRC] + punpckhwd XMM_R1, [VIF_SRC] + punpcklwd XMM_R2, [VIF_SRC+16] + + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R1, 16 + UNPACK_RIGHTSHIFT XMM_R2, 16 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 24 + endm + +UNPACK_V4_16SSE_3 macro CL, TOTALCL, MaskType, ModeType + movdqu XMM_R0, [VIF_SRC] + movq XMM_R2, QWORD PTR [VIF_SRC+16] + + punpckhwd XMM_R1, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + punpcklwd XMM_R2, XMM_R2 + + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R1, 16 + UNPACK_RIGHTSHIFT XMM_R2, 16 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 24 + endm + +UNPACK_V4_16SSE_2A macro CL, TOTALCL, MaskType, ModeType + punpcklwd XMM_R0, [VIF_SRC] + punpckhwd XMM_R1, [VIF_SRC] + + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R1, 16 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 16 + endm + +UNPACK_V4_16SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + movq XMM_R1, QWORD PTR [VIF_SRC+8] + + punpcklwd XMM_R0, XMM_R0 + punpcklwd XMM_R1, XMM_R1 + + UNPACK_RIGHTSHIFT XMM_R0, 16 + UNPACK_RIGHTSHIFT XMM_R1, 16 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 16 + endm + +UNPACK_V4_16SSE_1A macro CL, TOTALCL, MaskType, ModeType + punpcklwd XMM_R0, [VIF_SRC] + UNPACK_RIGHTSHIFT XMM_R0, 16 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +UNPACK_V4_16SSE_1 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 16 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +;; V4-8 +UNPACK_V4_8SSE_4A macro CL, TOTALCL, MaskType, ModeType + punpcklbw XMM_R0, [VIF_SRC] + punpckhbw XMM_R2, [VIF_SRC] + + punpckhwd XMM_R1, XMM_R0 + punpckhwd XMM_R3, XMM_R2 + punpcklwd XMM_R0, XMM_R0 + punpcklwd XMM_R2, XMM_R2 + + UNPACK_RIGHTSHIFT XMM_R1, 24 + UNPACK_RIGHTSHIFT XMM_R3, 24 + UNPACK_RIGHTSHIFT XMM_R0, 24 + UNPACK_RIGHTSHIFT XMM_R2, 24 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 16 + endm + +UNPACK_V4_8SSE_4 macro CL, TOTALCL, MaskType, ModeType + movdqu XMM_R0, [VIF_SRC] + + punpckhbw XMM_R2, XMM_R0 + punpcklbw XMM_R0, XMM_R0 + + punpckhwd XMM_R3, XMM_R2 + punpckhwd XMM_R1, XMM_R0 + punpcklwd XMM_R2, XMM_R2 + punpcklwd XMM_R0, XMM_R0 + + UNPACK_RIGHTSHIFT XMM_R3, 24 + UNPACK_RIGHTSHIFT XMM_R2, 24 + + UNPACK_RIGHTSHIFT XMM_R0, 24 + UNPACK_RIGHTSHIFT XMM_R1, 24 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 16 + endm + +UNPACK_V4_8SSE_3A macro CL, TOTALCL, MaskType, ModeType + punpcklbw XMM_R0, [VIF_SRC] + punpckhbw XMM_R2, [VIF_SRC] + + punpckhwd XMM_R1, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + punpcklwd XMM_R2, XMM_R2 + + UNPACK_RIGHTSHIFT XMM_R1, 24 + UNPACK_RIGHTSHIFT XMM_R0, 24 + UNPACK_RIGHTSHIFT XMM_R2, 24 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 12 + endm + +UNPACK_V4_8SSE_3 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + movd XMM_R2, dword ptr [VIF_SRC+8] + + punpcklbw XMM_R0, XMM_R0 + punpcklbw XMM_R2, XMM_R2 + + punpckhwd XMM_R1, XMM_R0 + punpcklwd XMM_R2, XMM_R2 + punpcklwd XMM_R0, XMM_R0 + + UNPACK_RIGHTSHIFT XMM_R1, 24 + UNPACK_RIGHTSHIFT XMM_R0, 24 + UNPACK_RIGHTSHIFT XMM_R2, 24 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 12 + endm + +UNPACK_V4_8SSE_2A macro CL, TOTALCL, MaskType, ModeType + punpcklbw XMM_R0, [VIF_SRC] + + punpckhwd XMM_R1, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + + UNPACK_RIGHTSHIFT XMM_R1, 24 + UNPACK_RIGHTSHIFT XMM_R0, 24 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +UNPACK_V4_8SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq XMM_R0, QWORD PTR [VIF_SRC] + + punpcklbw XMM_R0, XMM_R0 + + punpckhwd XMM_R1, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + + UNPACK_RIGHTSHIFT XMM_R1, 24 + UNPACK_RIGHTSHIFT XMM_R0, 24 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +UNPACK_V4_8SSE_1A macro CL, TOTALCL, MaskType, ModeType + punpcklbw XMM_R0, [VIF_SRC] + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 24 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 4 + endm + +UNPACK_V4_8SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd XMM_R0, dword ptr [VIF_SRC] + punpcklbw XMM_R0, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + UNPACK_RIGHTSHIFT XMM_R0, 24 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 4 + endm + +;; V4-5 +DECOMPRESS_RGBA macro OFFSET + mov bl, al + shl bl, 3 + mov byte ptr [s_TempDecompress+OFFSET], bl + + mov bx, ax + shr bx, 2 + and bx, 0f8h + mov byte ptr [s_TempDecompress+OFFSET+1], bl + + mov bx, ax + shr bx, 7 + and bx, 0f8h + mov byte ptr [s_TempDecompress+OFFSET+2], bl + mov bx, ax + shr bx, 8 + and bx, 080h + mov byte ptr [s_TempDecompress+OFFSET+3], bl + endm + +UNPACK_V4_5SSE_4 macro CL, TOTALCL, MaskType, ModeType + mov eax, dword ptr [VIF_SRC] + DECOMPRESS_RGBA 0 + + shr eax, 16 + DECOMPRESS_RGBA 4 + + mov eax, dword ptr [VIF_SRC+4] + DECOMPRESS_RGBA 8 + + shr eax, 16 + DECOMPRESS_RGBA 12 + + ;; have to use movaps instead of movdqa +%ifdef __x86_64__ + movdqa XMM_R0, XMMWORD PTR [s_TempDecompress] +%else + movaps XMM_R0, [s_TempDecompress] +%endif + + punpckhbw XMM_R2, XMM_R0 + punpcklbw XMM_R0, XMM_R0 + + punpckhwd XMM_R3, XMM_R2 + punpckhwd XMM_R1, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + punpcklwd XMM_R2, XMM_R2 + + psrld XMM_R0, 24 + psrld XMM_R1, 24 + psrld XMM_R2, 24 + psrld XMM_R3, 24 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 8 + endm + +UNPACK_V4_5SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V4_5SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V4_5SSE_3 macro CL, TOTALCL, MaskType, ModeType + mov eax, dword ptr [VIF_SRC] + DECOMPRESS_RGBA 0 + + shr eax, 16 + DECOMPRESS_RGBA 4 + + mov eax, dword ptr [VIF_SRC] + DECOMPRESS_RGBA 8 + + ;; have to use movaps instead of movdqa +%ifdef __x86_64__ + movdqa XMM_R0, XMMWORD PTR [s_TempDecompress] +%else + movaps XMM_R0, [s_TempDecompress] +%endif + + punpckhbw XMM_R2, XMM_R0 + punpcklbw XMM_R0, XMM_R0 + + punpckhwd XMM_R1, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + punpcklwd XMM_R2, XMM_R2 + + psrld XMM_R0, 24 + psrld XMM_R1, 24 + psrld XMM_R2, 24 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 6 + endm + +UNPACK_V4_5SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V4_5SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V4_5SSE_2 macro CL, TOTALCL, MaskType, ModeType + mov eax, dword ptr [VIF_SRC] + DECOMPRESS_RGBA 0 + + shr eax, 16 + DECOMPRESS_RGBA 4 + + movq XMM_R0, QWORD PTR [s_TempDecompress] + + punpcklbw XMM_R0, XMM_R0 + + punpckhwd XMM_R1, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + + psrld XMM_R0, 24 + psrld XMM_R1, 24 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 4 + endm + +UNPACK_V4_5SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V4_5SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V4_5SSE_1 macro CL, TOTALCL, MaskType, ModeType + mov ax, word ptr [VIF_SRC] + DECOMPRESS_RGBA 0 + + movd XMM_R0, DWORD PTR [s_TempDecompress] + punpcklbw XMM_R0, XMM_R0 + punpcklwd XMM_R0, XMM_R0 + + psrld XMM_R0, 24 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add VIF_SRC, 2 + endm + +UNPACK_V4_5SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V4_5SSE_1 CL, TOTALCL, MaskType, ModeType + endm + +;; save the row reg +SAVE_ROW_REG_BASE macro + mov VIF_TMPADDR, [_vifRow] + movdqa [VIF_TMPADDR], XMM_ROW + mov VIF_TMPADDR, [_vifRegs] + movss dword ptr [VIF_TMPADDR+0100h], XMM_ROW + psrldq XMM_ROW, 4 + movss dword ptr [VIF_TMPADDR+0110h], XMM_ROW + psrldq XMM_ROW, 4 + movss dword ptr [VIF_TMPADDR+0120h], XMM_ROW + psrldq XMM_ROW, 4 + movss dword ptr [VIF_TMPADDR+0130h], XMM_ROW + endm + +SAVE_NO_REG macro + endm + +%ifdef __x86_64__ + +INIT_ARGS macro + mov rax, qword ptr [_vifRow] + mov r9, qword ptr [_vifCol] + movaps xmm6, XMMWORD PTR [rax] + movaps xmm7, XMMWORD PTR [r9] + endm + +INC_STACK macro reg + add rsp, 8 + endm + +%else + +%define STACKOFFSET 12 + +;; 32 bit versions have the args on the stack +INIT_ARGS macro + mov VIF_DST, dword ptr [esp+4+STACKOFFSET] + mov VIF_SRC, dword ptr [esp+8+STACKOFFSET] + mov VIF_SIZE, dword ptr [esp+12+STACKOFFSET] + endm + +INC_STACK macro reg + add esp, 4 + endm + +%endif + +;; qsize - bytes of compressed size of 1 decompressed qword +;; int UNPACK_SkippingWrite_##name##_##sign##_##MaskType##_##ModeType(u32* dest, u32* data, int dmasize) +defUNPACK_SkippingWrite macro name, MaskType, ModeType, qsize, sign, SAVE_ROW_REG +@CatStr(UNPACK_SkippingWrite_, name, _, sign, _, MaskType, _, ModeType) proc public +%ifdef __x86_64__ + push rdi +%else + push edi + push esi + push ebx +%endif + INIT_ARGS + mov VIF_TMPADDR, [_vifRegs] + movzx VIF_INC, byte ptr [VIF_TMPADDR + 040h] + movzx VIF_SAVEEBX, byte ptr [VIF_TMPADDR + 041h] + sub VIF_INC, VIF_SAVEEBX + shl VIF_INC, 4 + + cmp VIF_SAVEEBXd, 1 + je @CatStr(name, _, sign, _, MaskType, _, ModeType, _WL1) + cmp VIF_SAVEEBXd, 2 + je @CatStr(name, _, sign, _, MaskType, _, ModeType, _WL2) + cmp VIF_SAVEEBXd, 3 + je @CatStr(name, _, sign, _, MaskType, _, ModeType, _WL3) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _WL4) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _WL1): + @CatStr(UNPACK_Start_Setup_, MaskType, _SSE_, ModeType) 0 + + cmp VIF_SIZE, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Done3) + + add VIF_INC, 16 + + ;; first align VIF_SRC to 16 bytes +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Align16): + + test VIF_SRC, 15 + jz @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_UnpackAligned) + + @CatStr(UNPACK_, name, SSE_1) 0, 1, MaskType, ModeType + + cmp VIF_SIZE, (2*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_DoneWithDec) + sub VIF_SIZE, qsize + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Align16) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_UnpackAligned): + + cmp VIF_SIZE, (2*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack1) + cmp VIF_SIZE, (3*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack2) + cmp VIF_SIZE, (4*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack3) + prefetchnta [VIF_SRC + 64] + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack4): + @CatStr(UNPACK_, name, SSE_4A) 0, 1, MaskType, ModeType + + cmp VIF_SIZE, (8*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_DoneUnpack4) + sub VIF_SIZE, (4*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack4) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_DoneUnpack4): + + sub VIF_SIZE, (4*qsize) + cmp VIF_SIZE, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Done3) + cmp VIF_SIZE, (2*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack1) + cmp VIF_SIZE, (3*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack2) + ;; fall through + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack3): + @CatStr(UNPACK_, name, SSE_3A) 0, 1, MaskType, ModeType + + sub VIF_SIZE, (3*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Done3) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack2): + @CatStr(UNPACK_, name, SSE_2A) 0, 1, MaskType, ModeType + + sub VIF_SIZE, (2*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Done3) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack1): + @CatStr(UNPACK_, name, SSE_1A) 0, 1, MaskType, ModeType +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_DoneWithDec): + sub VIF_SIZE, qsize +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Done3): + SAVE_ROW_REG + mov eax, VIF_SIZE +%ifdef __x86_64__ + pop rdi +%else + pop ebx + pop esi + pop edi +%endif + ret + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _WL2): + cmp VIF_SIZE, (2*qsize) + + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done3) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Unpack): + @CatStr(UNPACK_, name, SSE_2) 0, 0, MaskType, ModeType + + ;; take into account wl + add VIF_DST, VIF_INC + cmp VIF_SIZE, (4*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done2) + sub VIF_SIZE, (2*qsize) + ;; unpack next + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Unpack) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done2): + sub VIF_SIZE, (2*qsize) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done3): + cmp VIF_SIZE, qsize + + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done4) + + ;; execute left over qw + @CatStr(UNPACK_, name, SSE_1) 0, 0, MaskType, ModeType + + sub VIF_SIZE, qsize +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done4): + + SAVE_ROW_REG + mov eax, VIF_SIZE +%ifdef __x86_64__ + pop rdi +%else + pop ebx + pop esi + pop edi +%endif + ret + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _WL3): + cmp VIF_SIZE, (3*qsize) + + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done5) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Unpack): + @CatStr(UNPACK_, name, SSE_3) 0, 0, MaskType, ModeType + + add VIF_DST, VIF_INC + cmp VIF_SIZE, (6*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done2) + sub VIF_SIZE, (3*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Unpack) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done2): + sub VIF_SIZE, (3*qsize) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done5): + cmp VIF_SIZE, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done4) + + cmp VIF_SIZE, (2*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done3) + + @CatStr(UNPACK_, name, SSE_2) 0, 0, MaskType, ModeType + + sub VIF_SIZE, (2*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done4) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done3): + sub VIF_SIZE, qsize + @CatStr(UNPACK_, name, SSE_1) 0, 0, MaskType, ModeType +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done4): + SAVE_ROW_REG + mov eax, VIF_SIZE +%ifdef __x86_64__ + pop rdi +%else + pop ebx + pop esi + pop edi +%endif + ret + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _WL4): + sub VIF_SAVEEBX, 3 + push VIF_INC + cmp VIF_SIZE, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack): + cmp VIF_SIZE, (3*qsize) + jge @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack3) + cmp VIF_SIZE, (2*qsize) + jge @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack2) + + @CatStr(UNPACK_, name, SSE_1) 0, 0, MaskType, ModeType + + ;; not enough data left + sub VIF_SIZE, qsize + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack2): + @CatStr(UNPACK_, name, SSE_2) 0, 0, MaskType, ModeType + + ;; not enough data left + sub VIF_SIZE, (2*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack3): + @CatStr(UNPACK_, name, SSE_3) 0, 0, MaskType, ModeType + + ;; more data left, process 1qw at a time + sub VIF_SIZE, (3*qsize) + mov VIF_INC, VIF_SAVEEBX + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_UnpackX): + + ;; check if any data left + cmp VIF_SIZE, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done) + + @CatStr(UNPACK_, name, SSE_1) 3, 0, MaskType, ModeType + + sub VIF_SIZE, qsize + cmp VIF_INC, 1 + je @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_DoneLoop) + sub VIF_INC, 1 + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_UnpackX) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_DoneLoop): + add VIF_DST, [VIF_ESP] + cmp VIF_SIZE, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done): + + SAVE_ROW_REG + INC_STACK() + mov eax, VIF_SIZE + +%ifdef __x86_64__ + pop rdi +%else + pop ebx + pop esi + pop edi +%endif + ret +@CatStr(UNPACK_SkippingWrite_, name, _, sign, _, MaskType, _, ModeType endp) +endm + +UNPACK_RIGHTSHIFT macro reg, shift + psrld reg, shift + endm + +defUNPACK_SkippingWrite2 macro name, qsize + defUNPACK_SkippingWrite name, Regular, 0, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, Regular, 1, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, Regular, 2, qsize, u, SAVE_ROW_REG_BASE + defUNPACK_SkippingWrite name, Mask, 0, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, Mask, 1, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, Mask, 2, qsize, u, SAVE_ROW_REG_BASE + defUNPACK_SkippingWrite name, WriteMask, 0, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, WriteMask, 1, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, WriteMask, 2, qsize, u, SAVE_ROW_REG_BASE + endm + +defUNPACK_SkippingWrite2 S_32, 4 +defUNPACK_SkippingWrite2 S_16, 2 +defUNPACK_SkippingWrite2 S_8, 1 +defUNPACK_SkippingWrite2 V2_32, 8 +defUNPACK_SkippingWrite2 V2_16, 4 +defUNPACK_SkippingWrite2 V2_8, 2 +defUNPACK_SkippingWrite2 V3_32, 12 +defUNPACK_SkippingWrite2 V3_16, 6 +defUNPACK_SkippingWrite2 V3_8, 3 +defUNPACK_SkippingWrite2 V4_32, 16 +defUNPACK_SkippingWrite2 V4_16, 8 +defUNPACK_SkippingWrite2 V4_8, 4 +defUNPACK_SkippingWrite2 V4_5, 2 + +UNPACK_RIGHTSHIFT macro reg, shift + psrad reg, shift + endm + + +defUNPACK_SkippingWrite2a macro name, qsize + defUNPACK_SkippingWrite name, Mask, 0, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, Regular, 0, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, Regular, 1, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, Regular, 2, qsize, s, SAVE_ROW_REG_BASE + defUNPACK_SkippingWrite name, Mask, 1, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, Mask, 2, qsize, s, SAVE_ROW_REG_BASE + defUNPACK_SkippingWrite name, WriteMask, 0, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, WriteMask, 1, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, WriteMask, 2, qsize, s, SAVE_ROW_REG_BASE + endm + +defUNPACK_SkippingWrite2a S_16, 2 +defUNPACK_SkippingWrite2a S_8, 1 +defUNPACK_SkippingWrite2a V2_16, 4 +defUNPACK_SkippingWrite2a V2_8, 2 +defUNPACK_SkippingWrite2a V3_16, 6 +defUNPACK_SkippingWrite2a V3_8, 3 +defUNPACK_SkippingWrite2a V4_16, 8 +defUNPACK_SkippingWrite2a V4_8, 4 + +end diff --git a/pcsx2/x86/fast_routines.S b/pcsx2/x86/fast_routines.S new file mode 100644 index 0000000000..c194cdb78a --- /dev/null +++ b/pcsx2/x86/fast_routines.S @@ -0,0 +1,349 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2005 Pcsx2 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 + */ + +// Fast assembly routines for x86-64 +// zerofrog(@gmail.com) +.intel_syntax +.extern g_EEFreezeRegs +.extern FreezeMMXRegs_ + +// mmx memcmp implementation, size has to be a multiple of 8 +// returns 0 is equal, nonzero value if not equal +// ~10 times faster than standard memcmp +// (zerofrog) +// u8 memcmp_mmx(const void* src1, const void* src2, int cmpsize) +#ifdef __x86_64__ +#define MEMCMP_SRC1 %rdi +#define MEMCMP_SRC2 %rsi +#define MEMCMP_SIZE %edx +#else +#define MEMCMP_SRC1 %edx +#define MEMCMP_SRC2 %esi +#define MEMCMP_SIZE %ecx +#endif + +.globl memcmp_mmx +memcmp_mmx: +#ifndef __x86_64__ + // make sure mmx regs are stored + // FreezeMMXRegs(1); + cmp dword ptr [g_EEFreezeRegs], 0 + je memcmp_mmx_begin + push 1 + call FreezeMMXRegs_ + add %esp, 4 + +memcmp_mmx_begin: + push %esi + mov MEMCMP_SRC1, dword ptr [%esp+8] + mov MEMCMP_SRC2, dword ptr [%esp+12] + mov MEMCMP_SIZE, dword ptr [%esp+16] +#endif + + cmp MEMCMP_SIZE, 32 + jl memcmp_Done4 + + // custom test first 8 to make sure things are ok + movq %mm0, [MEMCMP_SRC2] + movq %mm1, [MEMCMP_SRC2+8] + pcmpeqd %mm0, [MEMCMP_SRC1] + pcmpeqd %mm1, [MEMCMP_SRC1+8] + pand %mm0, %mm1 + movq %mm2, [MEMCMP_SRC2+16] + pmovmskb %eax, %mm0 + movq %mm3, [MEMCMP_SRC2+24] + + // check if eq + cmp %eax, 0xff + je memcmp_NextComp + mov %eax, 1 + jmp memcmp_End + +memcmp_NextComp: + pcmpeqd %mm2, [MEMCMP_SRC1+16] + pcmpeqd %mm3, [MEMCMP_SRC1+24] + pand %mm2, %mm3 + pmovmskb %eax, %mm2 + + sub MEMCMP_SIZE, 32 + add MEMCMP_SRC2, 32 + add MEMCMP_SRC1, 32 + + // check if eq + cmp %eax, 0xff + je memcmp_ContinueTest + mov %eax, 1 + jmp memcmp_End + + cmp MEMCMP_SIZE, 64 + jl memcmp_Done8 + +memcmp_Cmp8: + movq %mm0, [MEMCMP_SRC2] + movq %mm1, [MEMCMP_SRC2+8] + movq %mm2, [MEMCMP_SRC2+16] + movq %mm3, [MEMCMP_SRC2+24] + movq %mm4, [MEMCMP_SRC2+32] + movq %mm5, [MEMCMP_SRC2+40] + movq %mm6, [MEMCMP_SRC2+48] + movq %mm7, [MEMCMP_SRC2+56] + pcmpeqd %mm0, [MEMCMP_SRC1] + pcmpeqd %mm1, [MEMCMP_SRC1+8] + pcmpeqd %mm2, [MEMCMP_SRC1+16] + pcmpeqd %mm3, [MEMCMP_SRC1+24] + pand %mm0, %mm1 + pcmpeqd %mm4, [MEMCMP_SRC1+32] + pand %mm0, %mm2 + pcmpeqd %mm5, [MEMCMP_SRC1+40] + pand %mm0, %mm3 + pcmpeqd %mm6, [MEMCMP_SRC1+48] + pand %mm0, %mm4 + pcmpeqd %mm7, [MEMCMP_SRC1+56] + pand %mm0, %mm5 + pand %mm0, %mm6 + pand %mm0, %mm7 + pmovmskb %eax, %mm0 + + // check if eq + cmp %eax, 0xff + je memcmp_Continue + mov %eax, 1 + jmp memcmp_End + +memcmp_Continue: + sub MEMCMP_SIZE, 64 + add MEMCMP_SRC2, 64 + add MEMCMP_SRC1, 64 +memcmp_ContinueTest: + cmp MEMCMP_SIZE, 64 + jge memcmp_Cmp8 + +memcmp_Done8: + test MEMCMP_SIZE, 0x20 + jz memcmp_Done4 + movq %mm0, [MEMCMP_SRC2] + movq %mm1, [MEMCMP_SRC2+8] + movq %mm2, [MEMCMP_SRC2+16] + movq %mm3, [MEMCMP_SRC2+24] + pcmpeqd %mm0, [MEMCMP_SRC1] + pcmpeqd %mm1, [MEMCMP_SRC1+8] + pcmpeqd %mm2, [MEMCMP_SRC1+16] + pcmpeqd %mm3, [MEMCMP_SRC1+24] + pand %mm0, %mm1 + pand %mm0, %mm2 + pand %mm0, %mm3 + pmovmskb %eax, %mm0 + sub MEMCMP_SIZE, 32 + add MEMCMP_SRC2, 32 + add MEMCMP_SRC1, 32 + + // check if eq + cmp %eax, 0xff + je memcmp_Done4 + mov %eax, 1 + jmp memcmp_End + +memcmp_Done4: + cmp MEMCMP_SIZE, 24 + jne memcmp_Done2 + movq %mm0, [MEMCMP_SRC2] + movq %mm1, [MEMCMP_SRC2+8] + movq %mm2, [MEMCMP_SRC2+16] + pcmpeqd %mm0, [MEMCMP_SRC1] + pcmpeqd %mm1, [MEMCMP_SRC1+8] + pcmpeqd %mm2, [MEMCMP_SRC1+16] + pand %mm0, %mm1 + pand %mm0, %mm2 + pmovmskb %eax, %mm0 + + // check if eq + cmp %eax, 0xff + je memcmp_Done + mov %eax, 1 + jmp memcmp_End + +memcmp_Done2: + cmp MEMCMP_SIZE, 16 + jne memcmp_Done1 + + movq %mm0, [MEMCMP_SRC2] + movq %mm1, [MEMCMP_SRC2+8] + pcmpeqd %mm0, [MEMCMP_SRC1] + pcmpeqd %mm1, [MEMCMP_SRC1+8] + pand %mm0, %mm1 + pmovmskb %eax, %mm0 + + // check if eq + cmp %eax, 0xff + je memcmp_Done + mov %eax, 1 + jmp memcmp_End + +memcmp_Done1: + cmp MEMCMP_SIZE, 8 + jne memcmp_Done + + mov %eax, [MEMCMP_SRC2] + mov MEMCMP_SRC2, [MEMCMP_SRC2+4] + cmp %eax, [MEMCMP_SRC1] + je memcmp_Next + mov %eax, 1 + jmp memcmp_End + +memcmp_Next: + cmp MEMCMP_SRC2, [MEMCMP_SRC1+4] + je memcmp_Done + mov %eax, 1 + jmp memcmp_End + +memcmp_Done: + xor %eax, %eax + +memcmp_End: + emms +#ifndef __x86_64__ + pop %esi +#endif + ret + +// memxor_mmx +#ifdef __x86_64__ +#define MEMXOR_SRC1 %rdi +#define MEMXOR_SRC2 %rsi +#define MEMXOR_SIZE %edx +#else +#define MEMXOR_SRC1 %edx +#define MEMXOR_SRC2 %esi +#define MEMXOR_SIZE %ecx +#endif + +.globl memxor_mmx +memxor_mmx: +#ifndef __x86_64__ + // make sure mmx regs are stored + // FreezeMMXRegs(1); + cmp dword ptr [g_EEFreezeRegs], 0 + je memxor_mmx_begin + push 1 + call FreezeMMXRegs_ + add %esp, 4 + +memxor_mmx_begin: + push %esi + mov MEMXOR_SRC1, dword ptr [%esp+8] + mov MEMXOR_SRC2, dword ptr [%esp+12] + mov MEMXOR_SIZE, dword ptr [%esp+16] +#endif + cmp MEMXOR_SIZE, 64 + jl memxor_Setup4 + + movq %mm0, [MEMXOR_SRC2] + movq %mm1, [MEMXOR_SRC2+8] + movq %mm2, [MEMXOR_SRC2+16] + movq %mm3, [MEMXOR_SRC2+24] + movq %mm4, [MEMXOR_SRC2+32] + movq %mm5, [MEMXOR_SRC2+40] + movq %mm6, [MEMXOR_SRC2+48] + movq %mm7, [MEMXOR_SRC2+56] + sub MEMXOR_SIZE, 64 + add MEMXOR_SRC2, 64 + cmp MEMXOR_SIZE, 64 + jl memxor_End8 + +memxor_Cmp8: + pxor %mm0, [MEMXOR_SRC2] + pxor %mm1, [MEMXOR_SRC2+8] + pxor %mm2, [MEMXOR_SRC2+16] + pxor %mm3, [MEMXOR_SRC2+24] + pxor %mm4, [MEMXOR_SRC2+32] + pxor %mm5, [MEMXOR_SRC2+40] + pxor %mm6, [MEMXOR_SRC2+48] + pxor %mm7, [MEMXOR_SRC2+56] + + sub MEMXOR_SIZE, 64 + add MEMXOR_SRC2, 64 + cmp MEMXOR_SIZE, 64 + jge memxor_Cmp8 + +memxor_End8: + pxor %mm0, %mm4 + pxor %mm1, %mm5 + pxor %mm2, %mm6 + pxor %mm3, %mm7 + + cmp MEMXOR_SIZE, 32 + jl memxor_End4 + pxor %mm0, [MEMXOR_SRC2] + pxor %mm1, [MEMXOR_SRC2+8] + pxor %mm2, [MEMXOR_SRC2+16] + pxor %mm3, [MEMXOR_SRC2+24] + sub MEMXOR_SIZE, 32 + add MEMXOR_SRC2, 32 + jmp memxor_End4 + +memxor_Setup4: + cmp MEMXOR_SIZE, 32 + jl memxor_Setup2 + + movq %mm0, [MEMXOR_SRC2] + movq %mm1, [MEMXOR_SRC2+8] + movq %mm2, [MEMXOR_SRC2+16] + movq %mm3, [MEMXOR_SRC2+24] + sub MEMXOR_SIZE, 32 + add MEMXOR_SRC2, 32 + +memxor_End4: + pxor %mm0, %mm2 + pxor %mm1, %mm3 + + cmp MEMXOR_SIZE, 16 + jl memxor_End2 + pxor %mm0, [MEMXOR_SRC2] + pxor %mm1, [MEMXOR_SRC2+8] + sub MEMXOR_SIZE, 16 + add MEMXOR_SRC2, 16 + jmp memxor_End2 + +memxor_Setup2: + cmp MEMXOR_SIZE, 16 + jl memxor_Setup1 + + movq %mm0, [MEMXOR_SRC2] + movq %mm1, [MEMXOR_SRC2+8] + sub MEMXOR_SIZE, 16 + add MEMXOR_SRC2, 16 + +memxor_End2: + pxor %mm0, %mm1 + + cmp MEMXOR_SIZE, 8 + jl memxor_End1 + pxor %mm0, [MEMXOR_SRC2] +memxor_End1: + movq [MEMXOR_SRC1], %mm0 + jmp memxor_End + +memxor_Setup1: + movq %mm0, [MEMXOR_SRC2] + movq [MEMXOR_SRC1], %mm0 +memxor_End: + emms +#ifndef __x86_64__ + pop %esi +#endif + ret diff --git a/pcsx2/x86/fast_routines.cpp b/pcsx2/x86/fast_routines.cpp new file mode 100644 index 0000000000..c9ab4fc55d --- /dev/null +++ b/pcsx2/x86/fast_routines.cpp @@ -0,0 +1,884 @@ +/****************************************************************************** + + Copyright (c) 2001 Advanced Micro Devices, Inc. + + LIMITATION OF LIABILITY: THE MATERIALS ARE PROVIDED *AS IS* WITHOUT ANY + EXPRESS OR IMPLIED WARRANTY OF ANY KIND INCLUDING WARRANTIES OF MERCHANTABILITY, + NONINFRINGEMENT OF THIRD-PARTY INTELLECTUAL PROPERTY, OR FITNESS FOR ANY + PARTICULAR PURPOSE. IN NO EVENT SHALL AMD OR ITS SUPPLIERS BE LIABLE FOR ANY + DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, + BUSINESS INTERRUPTION, LOSS OF INFORMATION) ARISING OUT OF THE USE OF OR + INABILITY TO USE THE MATERIALS, EVEN IF AMD HAS BEEN ADVISED OF THE POSSIBILITY + OF SUCH DAMAGES. BECAUSE SOME JURISDICTIONS PROHIBIT THE EXCLUSION OR LIMITATION + OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE LIMITATION MAY + NOT APPLY TO YOU. + + AMD does not assume any responsibility for any errors which may appear in the + Materials nor any responsibility to support or update the Materials. AMD retains + the right to make changes to its test specifications at any time, without notice. + + NO SUPPORT OBLIGATION: AMD is not obligated to furnish, support, or make any + further information, software, technical information, know-how, or show-how + available to you. + + So that all may benefit from your experience, please report any problems + or suggestions about this software to 3dsdk.support@amd.com + + AMD Developer Technologies, M/S 585 + Advanced Micro Devices, Inc. + 5900 E. Ben White Blvd. + Austin, TX 78741 + 3dsdk.support@amd.com +******************************************************************************/ + +#include "PrecompiledHeader.h" + +#ifdef _MSC_VER +#pragma warning(disable:4414) +#endif + +/***************************************************************************** +MEMCPY_AMD.CPP +******************************************************************************/ + +// Very optimized memcpy() routine for AMD Athlon and Duron family. +// This code uses any of FOUR different basic copy methods, depending +// on the transfer size. +// NOTE: Since this code uses MOVNTQ (also known as "Non-Temporal MOV" or +// "Streaming Store"), and also uses the software prefetch instructions, +// be sure you're running on Athlon/Duron or other recent CPU before calling! + +#define TINY_BLOCK_COPY 64 // upper limit for movsd type copy +// The smallest copy uses the X86 "movsd" instruction, in an optimized +// form which is an "unrolled loop". + +#define IN_CACHE_COPY 2 * 1024 // upper limit for movq/movq copy w/SW prefetch +// Next is a copy that uses the MMX registers to copy 8 bytes at a time, +// also using the "unrolled loop" optimization. This code uses +// the software prefetch instruction to get the data into the cache. + +#define UNCACHED_COPY 4 * 1024 // upper limit for movq/movntq w/SW prefetch +// For larger blocks, which will spill beyond the cache, it's faster to +// use the Streaming Store instruction MOVNTQ. This write instruction +// bypasses the cache and writes straight to main memory. This code also +// uses the software prefetch instruction to pre-read the data. +// USE 64 * 1024 FOR THIS VALUE IF YOU'RE ALWAYS FILLING A "CLEAN CACHE" + +#define BLOCK_PREFETCH_COPY infinity // no limit for movq/movntq w/block prefetch +#define CACHEBLOCK 80h // number of 64-byte blocks (cache lines) for block prefetch +// For the largest size blocks, a special technique called Block Prefetch +// can be used to accelerate the read operations. Block Prefetch reads +// one address per cache line, for a series of cache lines, in a short loop. +// This is faster than using software prefetch. The technique is great for +// getting maximum read bandwidth, especially in DDR memory systems. + +#include "Misc.h" + +// Inline assembly syntax for use with Visual C++ + +#if defined(_MSC_VER) + +#ifdef _DEBUG +extern u8 g_globalMMXSaved; + +void checkregs() +{ + if( g_EEFreezeRegs ) assert( g_globalMMXSaved ); +} +#endif + + +PCSX2_ALIGNED16( static u8 _xmm_backup[16*2] ); +PCSX2_ALIGNED16( static u8 _mmx_backup[8*4] ); + +static __declspec(naked) void __fastcall _memcpy_raz_usrc(void *dest, const void *src, size_t bytes) +{ + // MOVSRC = opcode used to read. I use the same code for the aligned version, with a different define :) + #define MOVSRC movdqu + #define MOVDST movdqa + + __asm + { + //Reads before reads, to avoid stalls + mov eax,[esp+4]; + //Make sure to save xmm0, it must be preserved ... + movaps [_xmm_backup],xmm0; + + //if >=128 bytes use 128 byte unrolled loop + //i use cmp ..,127 + jna because 127 is encodable using the simm8 form + cmp eax,127; + jna _loop_1; + + //since this is a common branch target it could be good to align it -- no idea if it has any effect :p + align 16 + + //128 byte unrolled loop +_loop_8: + + MOVSRC xmm0,[edx+0x00]; //read first to avoid read-after-write stalls + MOVDST [ecx+0x00],xmm0; //then write :p + MOVSRC xmm0,[edx+0x10]; + MOVDST [ecx+0x10],xmm0; + sub edx,-128; //edx won't be used for a while, so update it here. sub/-128 for simm8 encoding + sub ecx,-128; //ecx won't be used for a while, so update it here. sub/-128 for simm8 encoding + + MOVSRC xmm0,[edx+0x20-128]; + MOVDST [ecx+0x20-128],xmm0; + MOVSRC xmm0,[edx+0x30-128]; + MOVDST [ecx+0x30-128],xmm0; + add eax,-128; //eax won't be used for a while, so update it here. add/-128 for simm8 encoding + + MOVSRC xmm0,[edx+0x40-128]; + MOVDST [ecx+0x40-128],xmm0; + MOVSRC xmm0,[edx+0x50-128]; + MOVDST [ecx+0x50-128],xmm0; + + MOVSRC xmm0,[edx+0x60-128]; + MOVDST [ecx+0x60-128],xmm0; + MOVSRC xmm0,[edx+0x70-128]; + MOVDST [ecx+0x70-128],xmm0; + + //127~ja, 127 is encodable as simm8 :) + cmp eax,127; + ja _loop_8; + + //direct copy for 0~7 qwords + //in order to avoid the inc/dec of all 3 registers + //i use negative relative addressing from the top of the buffers + //[top-current index] + +_loop_1: + //prepare the regs for 'negative relative addressing' + add edx,eax; + add ecx,eax; + neg eax; + jz cleanup; //exit if nothing to do + +_loop_1_inner: + MOVSRC xmm0,[edx+eax]; + MOVDST [ecx+eax],xmm0; + + add eax,16; //while the offset is still negative we have data to copy + js _loop_1_inner; + + //done ! +cleanup: + //restore xmm and exit ~) + movaps xmm0,[_xmm_backup]; + ret 4; + } + #undef MOVSRC + #undef MOVDST +} + + +static __declspec(naked) void __fastcall _memcpy_raz_udst(void *dest, const void *src, size_t bytes) +{ + // MOVDST = opcode used to read. I use the same code for the aligned version, with a different define :) + #define MOVSRC movaps + #define MOVDST movups + __asm + { + //Reads before reads, to avoid stalls + mov eax,[esp+4]; + //Make sure to save xmm0, it must be preserved ... + movaps [_xmm_backup],xmm0; + + //if >=128 bytes use 128 byte unrolled loop + //i use cmp ..,127 + jna because 127 is encodable using the simm8 form + cmp eax,127; + jna _loop_1; + + //since this is a common branch target it could be good to align it -- no idea if it has any effect :p + align 16 + + //128 byte unrolled loop +_loop_8: + + MOVSRC xmm0,[edx+0x00]; //read first to avoid read-after-write stalls + MOVDST [ecx+0x00],xmm0; //then write :p + MOVSRC xmm0,[edx+0x10]; + MOVDST [ecx+0x10],xmm0; + sub edx,-128; //edx won't be used for a while, so update it here. sub/-128 for simm8 encoding + sub ecx,-128; //ecx won't be used for a while, so update it here. sub/-128 for simm8 encoding + + MOVSRC xmm0,[edx+0x20-128]; + MOVDST [ecx+0x20-128],xmm0; + MOVSRC xmm0,[edx+0x30-128]; + MOVDST [ecx+0x30-128],xmm0; + add eax,-128; //eax won't be used for a while, so update it here. add/-128 for simm8 encoding + + MOVSRC xmm0,[edx+0x40-128]; + MOVDST [ecx+0x40-128],xmm0; + MOVSRC xmm0,[edx+0x50-128]; + MOVDST [ecx+0x50-128],xmm0; + + MOVSRC xmm0,[edx+0x60-128]; + MOVDST [ecx+0x60-128],xmm0; + MOVSRC xmm0,[edx+0x70-128]; + MOVDST [ecx+0x70-128],xmm0; + + //127~ja, 127 is encodable as simm8 :) + cmp eax,127; + ja _loop_8; + + //direct copy for 0~7 qwords + //in order to avoid the inc/dec of all 3 registers + //i use negative relative addressing from the top of the buffers + //[top-current index] + +_loop_1: + //prepare the regs for 'negative relative addressing' + add edx,eax; + add ecx,eax; + neg eax; + jz cleanup; //exit if nothing to do + +_loop_1_inner: + MOVSRC xmm0,[edx+eax]; + movaps [ecx+eax],xmm0; + + add eax,16; //while the offset is still negative we have data to copy + js _loop_1_inner; + + //done ! +cleanup: + //restore xmm and exit ~) + movaps xmm0,[_xmm_backup]; + ret 4; + } + #undef MOVSRC + #undef MOVDST +} + +// Custom memcpy, only for 16 byte aligned stuff (used for mtgs) +// This function is optimized for medium-small transfer sizes (<2048, >=128). No prefetching is +// used since the reads are linear and the cache logic can predict em :) +// *OBSOLETE* -- memcpy_amd_ has been optimized and is now faster. +__declspec(naked) void __fastcall memcpy_raz_(void *dest, const void *src, size_t bytes) +{ + // Code Implementation Notes: + // Uses a forward copy, in 128 byte blocks, and then does the remaining in 16 byte blocks :) + + // MOVSRC = opcode used to read. I use the same code for the unaligned version, with a different define :) + #define MOVSRC movaps + #define MOVDST movaps + __asm + { + //Reads before reads, to avoid stalls + mov eax,[esp+4]; + //Make sure to save xmm0, it must be preserved ... + movaps [_xmm_backup],xmm0; + + //if >=128 bytes use 128 byte unrolled loop + //i use cmp ..,127 + jna because 127 is encodable using the simm8 form + cmp eax,127; + jna _loop_1; + + //since this is a common branch target it could be good to align it -- no idea if it has any effect :p + align 16 + + //128 byte unrolled loop +_loop_8: + + MOVSRC xmm0,[edx+0x00]; //read first to avoid read-after-write stalls + MOVDST [ecx+0x00],xmm0; //then write :p + MOVSRC xmm0,[edx+0x10]; + MOVDST [ecx+0x10],xmm0; + sub edx,-128; //edx won't be used for a while, so update it here. sub/-128 for simm8 encoding + sub ecx,-128; //ecx won't be used for a while, so update it here. sub/-128 for simm8 encoding + + MOVSRC xmm0,[edx+0x20-128]; + MOVDST [ecx+0x20-128],xmm0; + MOVSRC xmm0,[edx+0x30-128]; + MOVDST [ecx+0x30-128],xmm0; + add eax,-128; //eax won't be used for a while, so update it here. add/-128 for simm8 encoding + + MOVSRC xmm0,[edx+0x40-128]; + MOVDST [ecx+0x40-128],xmm0; + MOVSRC xmm0,[edx+0x50-128]; + MOVDST [ecx+0x50-128],xmm0; + + MOVSRC xmm0,[edx+0x60-128]; + MOVDST [ecx+0x60-128],xmm0; + MOVSRC xmm0,[edx+0x70-128]; + MOVDST [ecx+0x70-128],xmm0; + + //127~ja, 127 is encodable as simm8 :) + cmp eax,127; + ja _loop_8; + + //direct copy for 0~7 qwords + //in order to avoid the inc/dec of all 3 registers + //i use negative relative addressing from the top of the buffers + //[top-current index] + +_loop_1: + //prepare the regs for 'negative relative addressing' + add edx,eax; + add ecx,eax; + neg eax; + jz cleanup; //exit if nothing to do + +_loop_1_inner: + MOVSRC xmm0,[edx+eax]; + MOVDST [ecx+eax],xmm0; + + add eax,16; //while the offset is still negative we have data to copy + js _loop_1_inner; + + //done ! +cleanup: + //restore xmm and exit ~) + movaps xmm0,[_xmm_backup]; + ret 4; + } + #undef MOVSRC + #undef MOVDST +} + +// This memcpy routine is for use in situations where the source buffer's alignment is indeterminate. +__forceinline void __fastcall memcpy_raz_usrc(void *dest, const void *src, size_t bytes) +{ + if( ((uptr)src & 0xf) == 0 ) + memcpy_raz_( dest, src, bytes ); + else + _memcpy_raz_usrc( dest, src, bytes ); +} + +// This memcpy routine is for use in situations where the destination buffer's alignment is indeterminate. +__forceinline void __fastcall memcpy_raz_udst(void *dest, const void *src, size_t bytes) +{ + if( ((uptr)dest & 0xf) == 0 ) + memcpy_raz_( dest, src, bytes ); + else + _memcpy_raz_udst( dest, src, bytes ); +} + + +////////////////////////////////////////////////////////////////////////// +// Fast memcpy as coded by AMD, and thn improved by air. +// +// This routine preserves mmx registers! It's the complete real deal! +__declspec(naked) void __fastcall memcpy_amd_(void *dest, const void *src, size_t n) +{ + __asm + { + push edi + push esi + + mov edi, ecx ; destination + mov esi, edx ; source + mov ecx, [esp+12] ; number of bytes to copy + mov eax, ecx ; keep a copy of count + + cld + cmp eax, TINY_BLOCK_COPY + jb $memcpy_ic_3 ; tiny? skip mmx copy + + cmp eax, 32*1024 ; don't align between 32k-64k because + jbe $memcpy_do_align ; it appears to be slower + cmp eax, 64*1024 + jbe $memcpy_align_done +$memcpy_do_align: + mov eax, 8 ; a trick that's faster than rep movsb... + sub eax, edi ; align destination to qword + and eax, 111b ; get the low bits + sub ecx, eax ; update copy count + neg eax ; set up to jump into the array + add eax, offset $memcpy_align_done + jmp eax ; jump to array of movsb's + +align 4 + movsb + movsb + movsb + movsb + movsb + movsb + movsb + movsb + +$memcpy_align_done: ; destination is dword aligned + mov eax, ecx ; number of bytes left to copy + shr eax, 6 ; get 64-byte block count + jz $memcpy_ic_2 ; finish the last few bytes + + cmp eax, IN_CACHE_COPY/64 ; too big 4 cache? use uncached copy + jae $memcpy_uc_test + + movq [_mmx_backup+0x00],mm0 + movq [_mmx_backup+0x08],mm1 + movq [_mmx_backup+0x10],mm2 + movq [_mmx_backup+0x18],mm3 + +// This is small block copy that uses the MMX registers to copy 8 bytes +// at a time. It uses the "unrolled loop" optimization, and also uses +// the software prefetch instruction to get the data into the cache. +align 16 +$memcpy_ic_1: ; 64-byte block copies, in-cache copy + + prefetchnta [esi + (200*64/34+192)] ; start reading ahead + + movq mm0, [esi+0] ; read 64 bits + movq mm1, [esi+8] + movq [edi+0], mm0 ; write 64 bits + movq [edi+8], mm1 ; note: the normal movq writes the + movq mm2, [esi+16] ; data to cache; a cache line will be + movq mm3, [esi+24] ; allocated as needed, to store the data + movq [edi+16], mm2 + movq [edi+24], mm3 + movq mm0, [esi+32] + movq mm1, [esi+40] + movq [edi+32], mm0 + movq [edi+40], mm1 + movq mm2, [esi+48] + movq mm3, [esi+56] + movq [edi+48], mm2 + movq [edi+56], mm3 + + add esi, 64 ; update source pointer + add edi, 64 ; update destination pointer + dec eax ; count down + jnz $memcpy_ic_1 ; last 64-byte block? + + movq mm0,[_mmx_backup+0x00] + movq mm1,[_mmx_backup+0x08] + movq mm2,[_mmx_backup+0x10] + movq mm3,[_mmx_backup+0x18] + +$memcpy_ic_2: + mov eax, ecx ; has valid low 6 bits of the byte count +$memcpy_ic_3: + shr eax, 2 ; dword count + and eax, 1111b ; only look at the "remainder" bits + neg eax ; set up to jump into the array + add eax, offset $memcpy_last_few + jmp eax ; jump to array of movsd's + +$memcpy_uc_test: + /*cmp ecx, UNCACHED_COPY/64 ; big enough? use block prefetch copy + jae $memcpy_bp_1 +$memcpy_64_test:*/ + or eax, eax ; tail end of block prefetch will jump here + jz $memcpy_ic_2 ; no more 64-byte blocks left + +// For larger blocks, which will spill beyond the cache, it's faster to +// use the Streaming Store instruction MOVNTQ. This write instruction +// bypasses the cache and writes straight to main memory. This code also +// uses the software prefetch instruction to pre-read the data. + + movq [_mmx_backup+0x00],mm0 + movq [_mmx_backup+0x08],mm1 + movq [_mmx_backup+0x10],mm2 + +align 16 +$memcpy_uc_1: ; 64-byte blocks, uncached copy + + prefetchnta [esi + (200*64/34+192)] ; start reading ahead + + movq mm0,[esi+0] ; read 64 bits + add edi,64 ; update destination pointer + movq mm1,[esi+8] + add esi,64 ; update source pointer + movq mm2,[esi-48] + movntq [edi-64], mm0 ; write 64 bits, bypassing the cache + movq mm0,[esi-40] ; note: movntq also prevents the CPU + movntq [edi-56], mm1 ; from READING the destination address + movq mm1,[esi-32] ; into the cache, only to be over-written + movntq [edi-48], mm2 ; so that also helps performance + movq mm2,[esi-24] + movntq [edi-40], mm0 + movq mm0,[esi-16] + movntq [edi-32], mm1 + movq mm1,[esi-8] + movntq [edi-24], mm2 + movntq [edi-16], mm0 + dec eax + movntq [edi-8], mm1 + jnz $memcpy_uc_1 ; last 64-byte block? + + movq mm0,[_mmx_backup+0x00] + movq mm1,[_mmx_backup+0x08] + movq mm2,[_mmx_backup+0x10] + + jmp $memcpy_ic_2 ; almost done (not needed because large copy below was removed) + +// For the largest size blocks, a special technique called Block Prefetch +// can be used to accelerate the read operations. Block Prefetch reads +// one address per cache line, for a series of cache lines, in a short loop. +// This is faster than using software prefetch. The technique is great for +// getting maximum read bandwidth, especially in DDR memory systems. + +// Note: Pcsx2 rarely invokes large copies, so this mode has been disabled to +// help keep the code cache footprint of memcpy_fast to a minimum. +/* +$memcpy_bp_1: ; large blocks, block prefetch copy + + cmp ecx, CACHEBLOCK ; big enough to run another prefetch loop? + jl $memcpy_64_test ; no, back to regular uncached copy + + mov eax, CACHEBLOCK / 2 ; block prefetch loop, unrolled 2X + add esi, CACHEBLOCK * 64 ; move to the top of the block +align 16 +$memcpy_bp_2: + mov edx, [esi-64] ; grab one address per cache line + mov edx, [esi-128] ; grab one address per cache line + sub esi, 128 ; go reverse order to suppress HW prefetcher + dec eax ; count down the cache lines + jnz $memcpy_bp_2 ; keep grabbing more lines into cache + + mov eax, CACHEBLOCK ; now that it's in cache, do the copy +align 16 +$memcpy_bp_3: + movq mm0, [esi ] ; read 64 bits + movq mm1, [esi+ 8] + movq mm2, [esi+16] + movq mm3, [esi+24] + movq mm4, [esi+32] + movq mm5, [esi+40] + movq mm6, [esi+48] + movq mm7, [esi+56] + add esi, 64 ; update source pointer + movntq [edi ], mm0 ; write 64 bits, bypassing cache + movntq [edi+ 8], mm1 ; note: movntq also prevents the CPU + movntq [edi+16], mm2 ; from READING the destination address + movntq [edi+24], mm3 ; into the cache, only to be over-written, + movntq [edi+32], mm4 ; so that also helps performance + movntq [edi+40], mm5 + movntq [edi+48], mm6 + movntq [edi+56], mm7 + add edi, 64 ; update dest pointer + + dec eax ; count down + + jnz $memcpy_bp_3 ; keep copying + sub ecx, CACHEBLOCK ; update the 64-byte block count + jmp $memcpy_bp_1 ; keep processing chunks +*/ + +// The smallest copy uses the X86 "movsd" instruction, in an optimized +// form which is an "unrolled loop". Then it handles the last few bytes. +align 4 + movsd + movsd ; perform last 1-15 dword copies + movsd + movsd + movsd + movsd + movsd + movsd + movsd + movsd ; perform last 1-7 dword copies + movsd + movsd + movsd + movsd + movsd + movsd + +$memcpy_last_few: ; dword aligned from before movsd's + mov eax, ecx ; has valid low 2 bits of the byte count + and eax, 11b ; the last few cows must come home + jz $memcpy_final ; no more, let's leave + rep movsb ; the last 1, 2, or 3 bytes + +$memcpy_final: + emms ; clean up the MMX state + sfence ; flush the write buffer + //mov eax, [dest] ; ret value = destination pointer + + pop esi + pop edi + + ret 4 + } +} + +// mmx mem-compare implementation, size has to be a multiple of 8 +// returns 0 is equal, nonzero value if not equal +// ~10 times faster than standard memcmp +// (zerofrog) +u8 memcmp_mmx(const void* src1, const void* src2, int cmpsize) +{ + assert( (cmpsize&7) == 0 ); + + __asm { + push esi + mov ecx, cmpsize + mov edx, src1 + mov esi, src2 + + cmp ecx, 32 + jl Done4 + + // custom test first 8 to make sure things are ok + movq mm0, [esi] + movq mm1, [esi+8] + pcmpeqd mm0, [edx] + pcmpeqd mm1, [edx+8] + pand mm0, mm1 + movq mm2, [esi+16] + pmovmskb eax, mm0 + movq mm3, [esi+24] + + // check if eq + cmp eax, 0xff + je NextComp + mov eax, 1 + jmp End + +NextComp: + pcmpeqd mm2, [edx+16] + pcmpeqd mm3, [edx+24] + pand mm2, mm3 + pmovmskb eax, mm2 + + sub ecx, 32 + add esi, 32 + add edx, 32 + + // check if eq + cmp eax, 0xff + je ContinueTest + mov eax, 1 + jmp End + + cmp ecx, 64 + jl Done8 + +Cmp8: + movq mm0, [esi] + movq mm1, [esi+8] + movq mm2, [esi+16] + movq mm3, [esi+24] + movq mm4, [esi+32] + movq mm5, [esi+40] + movq mm6, [esi+48] + movq mm7, [esi+56] + pcmpeqd mm0, [edx] + pcmpeqd mm1, [edx+8] + pcmpeqd mm2, [edx+16] + pcmpeqd mm3, [edx+24] + pand mm0, mm1 + pcmpeqd mm4, [edx+32] + pand mm0, mm2 + pcmpeqd mm5, [edx+40] + pand mm0, mm3 + pcmpeqd mm6, [edx+48] + pand mm0, mm4 + pcmpeqd mm7, [edx+56] + pand mm0, mm5 + pand mm0, mm6 + pand mm0, mm7 + pmovmskb eax, mm0 + + // check if eq + cmp eax, 0xff + je Continue + mov eax, 1 + jmp End + +Continue: + sub ecx, 64 + add esi, 64 + add edx, 64 +ContinueTest: + cmp ecx, 64 + jge Cmp8 + +Done8: + test ecx, 0x20 + jz Done4 + movq mm0, [esi] + movq mm1, [esi+8] + movq mm2, [esi+16] + movq mm3, [esi+24] + pcmpeqd mm0, [edx] + pcmpeqd mm1, [edx+8] + pcmpeqd mm2, [edx+16] + pcmpeqd mm3, [edx+24] + pand mm0, mm1 + pand mm0, mm2 + pand mm0, mm3 + pmovmskb eax, mm0 + sub ecx, 32 + add esi, 32 + add edx, 32 + + // check if eq + cmp eax, 0xff + je Done4 + mov eax, 1 + jmp End + +Done4: + cmp ecx, 24 + jne Done2 + movq mm0, [esi] + movq mm1, [esi+8] + movq mm2, [esi+16] + pcmpeqd mm0, [edx] + pcmpeqd mm1, [edx+8] + pcmpeqd mm2, [edx+16] + pand mm0, mm1 + pand mm0, mm2 + pmovmskb eax, mm0 + + // check if eq + cmp eax, 0xff + setne al + jmp End + +Done2: + cmp ecx, 16 + jne Done1 + + movq mm0, [esi] + movq mm1, [esi+8] + pcmpeqd mm0, [edx] + pcmpeqd mm1, [edx+8] + pand mm0, mm1 + pmovmskb eax, mm0 + + // check if eq + cmp eax, 0xff + setne al + jmp End + +Done1: + cmp ecx, 8 + jne Done + + mov eax, [esi] + mov esi, [esi+4] + cmp eax, [edx] + je Next + mov eax, 1 + jmp End + +Next: + cmp esi, [edx+4] + setne al + jmp End + +Done: + xor eax, eax + +End: + pop esi + emms + } +} + + +// returns the xor of all elements, cmpsize has to be mult of 8 +void memxor_mmx(void* dst, const void* src1, int cmpsize) +{ + FreezeMMXRegs(1); + assert( (cmpsize&7) == 0 ); + + __asm { + mov ecx, cmpsize + mov eax, src1 + mov edx, dst + + cmp ecx, 64 + jl Setup4 + + movq mm0, [eax] + movq mm1, [eax+8] + movq mm2, [eax+16] + movq mm3, [eax+24] + movq mm4, [eax+32] + movq mm5, [eax+40] + movq mm6, [eax+48] + movq mm7, [eax+56] + sub ecx, 64 + add eax, 64 + cmp ecx, 64 + jl End8 + +Cmp8: + pxor mm0, [eax] + pxor mm1, [eax+8] + pxor mm2, [eax+16] + pxor mm3, [eax+24] + pxor mm4, [eax+32] + pxor mm5, [eax+40] + pxor mm6, [eax+48] + pxor mm7, [eax+56] + + sub ecx, 64 + add eax, 64 + cmp ecx, 64 + jge Cmp8 + +End8: + pxor mm0, mm4 + pxor mm1, mm5 + pxor mm2, mm6 + pxor mm3, mm7 + + cmp ecx, 32 + jl End4 + pxor mm0, [eax] + pxor mm1, [eax+8] + pxor mm2, [eax+16] + pxor mm3, [eax+24] + sub ecx, 32 + add eax, 32 + jmp End4 + +Setup4: + cmp ecx, 32 + jl Setup2 + + movq mm0, [eax] + movq mm1, [eax+8] + movq mm2, [eax+16] + movq mm3, [eax+24] + sub ecx, 32 + add eax, 32 + +End4: + pxor mm0, mm2 + pxor mm1, mm3 + + cmp ecx, 16 + jl End2 + pxor mm0, [eax] + pxor mm1, [eax+8] + sub ecx, 16 + add eax, 16 + jmp End2 + +Setup2: + cmp ecx, 16 + jl Setup1 + + movq mm0, [eax] + movq mm1, [eax+8] + sub ecx, 16 + add eax, 16 + +End2: + pxor mm0, mm1 + + cmp ecx, 8 + jl End1 + pxor mm0, [eax] +End1: + movq [edx], mm0 + jmp End + +Setup1: + movq mm0, [eax] + movq [edx], mm0 +End: + emms + } + FreezeMMXRegs(0); +} + +#endif diff --git a/pcsx2/x86/iCOP0.cpp b/pcsx2/x86/iCOP0.cpp new file mode 100644 index 0000000000..72ec062b02 --- /dev/null +++ b/pcsx2/x86/iCOP0.cpp @@ -0,0 +1,406 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// Important Note to Future Developers: +// None of the COP0 instructions are really critical performance items, +// so don't waste time converting any more them into recompiled code +// unless it can make them nicely compact. Calling the C versions will +// suffice. + +#include "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "iCOP0.h" + +namespace Interp = R5900::Interpreter::OpcodeImpl::COP0; + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { +namespace COP0 { + +/********************************************************* +* COP0 opcodes * +* * +*********************************************************/ + +// emits "setup" code for a COP0 branch test. The instruction immediately following +// this should be a conditional Jump -- JZ or JNZ normally. +static void _setupBranchTest() +{ + _eeFlushAllUnused(); + + // COP0 branch conditionals are based on the following equation: + // (((psHu16(DMAC_STAT) & psHu16(DMAC_PCR)) & 0x3ff) == (psHu16(DMAC_PCR) & 0x3ff)) + // BC0F checks if the statement is false, BC0T checks if the statement is true. + + // note: We only want to compare the 16 bit values of DMAC_STAT and PCR. + // But using 32-bit loads here is ok (and faster), because we mask off + // everything except the lower 10 bits away. + + MOV32MtoR( EAX, (uptr)&psHu32(DMAC_STAT) ); + MOV32MtoR( ECX, (uptr)&psHu32(DMAC_PCR) ); + AND32ItoR( EAX, 0x3ff ); // masks off all but lower 10 bits. + AND32ItoR( ECX, 0x3ff ); + CMP32RtoR( EAX, ECX ); +} + +void recBC0F() +{ + _setupBranchTest(); + recDoBranchImm(JNZ32(0)); +} + +void recBC0T() +{ + _setupBranchTest(); + recDoBranchImm(JZ32(0)); +} + +void recBC0FL() +{ + _setupBranchTest(); + recDoBranchImm_Likely(JNZ32(0)); +} + +void recBC0TL() +{ + _setupBranchTest(); + recDoBranchImm_Likely(JZ32(0)); +} + +void recTLBR() { recCall( Interp::TLBR, -1 ); } +void recTLBP() { recCall( Interp::TLBP, -1 ); } +void recTLBWI() { recCall( Interp::TLBWI, -1 ); } +void recTLBWR() { recCall( Interp::TLBWR, -1 ); } + +void recERET() +{ + recBranchCall( Interp::ERET ); +} + +void recEI() +{ + // must branch after enabling interrupts, so that anything + // pending gets triggered properly. + recBranchCall( Interp::EI ); +} + +void recDI() +{ + // No need to branch after disabling interrupts... + + iFlushCall(0); + + MOV32MtoR( EAX, (uptr)&cpuRegs.cycle ); + MOV32RtoM( (uptr)&g_nextBranchCycle, EAX ); + + CALLFunc( (uptr)Interp::DI ); +} + + +#ifndef CP0_RECOMPILE + +REC_SYS( MFC0 ); +REC_SYS( MTC0 ); + +#else + +void recMFC0( void ) +{ + int mmreg; + + if ( ! _Rt_ ) return; + + if( _Rd_ == 9 ) { + MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); + MOV32RtoR(EAX,ECX); + SUB32MtoR(EAX, (uptr)&s_iLastCOP0Cycle); + ADD32RtoM((uptr)&cpuRegs.CP0.n.Count, EAX); + MOV32RtoM((uptr)&s_iLastCOP0Cycle, ECX); + MOV32MtoR( EAX, (uptr)&cpuRegs.CP0.r[ _Rd_ ] ); + + _deleteEEreg(_Rt_, 0); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0],EAX); + + if(EEINST_ISLIVE1(_Rt_)) { + CDQ(); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[1], EDX); + } + else EEINST_RESETHASLIVE1(_Rt_); + return; + } + if( _Rd_ == 25 ) { + + _deleteEEreg(_Rt_, 0); + switch(_Imm_ & 0x3F){ + case 0: + MOV32MtoR(EAX, (uptr)&cpuRegs.PERF.n.pccr); + + break; + case 1: + // check if needs to be incremented + MOV32MtoR(ECX, (uptr)&cpuRegs.PERF.n.pccr); + MOV32MtoR(EAX, (uptr)&cpuRegs.PERF.n.pcr0); + AND32ItoR(ECX, 0x800003E0); + + CMP32ItoR(ECX, 0x80000020); + j8Ptr[0] = JNE8(0); + + MOV32MtoR(EDX, (uptr)&cpuRegs.cycle); + SUB32MtoR(EAX, (uptr)&s_iLastPERFCycle[0]); + ADD32RtoR(EAX, EDX); + MOV32RtoM((uptr)&s_iLastPERFCycle[0], EDX); + MOV32RtoM((uptr)&cpuRegs.PERF.n.pcr0, EAX); + + x86SetJ8(j8Ptr[0]); + break; + case 3: + // check if needs to be incremented + MOV32MtoR(ECX, (uptr)&cpuRegs.PERF.n.pccr); + MOV32MtoR(EAX, (uptr)&cpuRegs.PERF.n.pcr1); + AND32ItoR(ECX, 0x800F8000); + + CMP32ItoR(ECX, 0x80008000); + j8Ptr[0] = JNE8(0); + + MOV32MtoR(EDX, (uptr)&cpuRegs.cycle); + SUB32MtoR(EAX, (uptr)&s_iLastPERFCycle[1]); + ADD32RtoR(EAX, EDX); + MOV32RtoM((uptr)&s_iLastPERFCycle[1], EDX); + MOV32RtoM((uptr)&cpuRegs.PERF.n.pcr1, EAX); + + x86SetJ8(j8Ptr[0]); + + break; + } + + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0],EAX); + + if(EEINST_ISLIVE1(_Rt_)) { + CDQ(); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[1], EDX); + } + else EEINST_RESETHASLIVE1(_Rt_); + +#ifdef PCSX2_DEVBUILD + COP0_LOG("MFC0 PCCR = %x PCR0 = %x PCR1 = %x IMM= %x\n", + cpuRegs.PERF.n.pccr, cpuRegs.PERF.n.pcr0, cpuRegs.PERF.n.pcr1, _Imm_ & 0x3F); +#endif + return; + } + else if( _Rd_ == 24){ + COP0_LOG("MFC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF); + return; + } + _eeOnWriteReg(_Rt_, 1); + + if( EEINST_ISLIVE1(_Rt_) ) { + _deleteEEreg(_Rt_, 0); + MOV32MtoR(EAX, (uptr)&cpuRegs.CP0.r[ _Rd_ ]); + CDQ(); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0], EAX); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[1], EDX); + } + else { + EEINST_RESETHASLIVE1(_Rt_); + + if( (mmreg = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE)) >= 0 ) { + MOVDMtoMMX(mmreg, (uptr)&cpuRegs.CP0.r[ _Rd_ ]); + SetMMXstate(); + } + else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ)) >= 0) { + + if( EEINST_ISLIVE2(_Rt_) ) { + if( xmmregs[mmreg].mode & MODE_WRITE ) { + SSE_MOVHPS_XMM_to_M64((uptr)&cpuRegs.GPR.r[_Rt_].UL[2], mmreg); + } + xmmregs[mmreg].inuse = 0; + + MOV32MtoR(EAX, (uptr)&cpuRegs.CP0.r[ _Rd_ ]); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0],EAX); + } + else { + SSE_MOVLPS_M64_to_XMM(mmreg, (uptr)&cpuRegs.CP0.r[ _Rd_ ]); + } + } + else { + MOV32MtoR(EAX, (uptr)&cpuRegs.CP0.r[ _Rd_ ]); + if(_Rd_ == 12) AND32ItoR(EAX, 0xf0c79c1f); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0],EAX); + if(EEINST_ISLIVE1(_Rt_)) { + CDQ(); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[1], EDX); + } + else { + EEINST_RESETHASLIVE1(_Rt_); + } + } + } +} + +void updatePCCR() +{ + // read the old pccr and update pcr0/1 + MOV32MtoR(EAX, (uptr)&cpuRegs.PERF.n.pccr); + MOV32RtoR(EDX, EAX); + MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); + + AND32ItoR(EAX, 0x800003E0); + CMP32ItoR(EAX, 0x80000020); + j8Ptr[0] = JNE8(0); + MOV32MtoR(EAX, (uptr)&s_iLastPERFCycle[0]); + ADD32RtoM((uptr)&cpuRegs.PERF.n.pcr0, ECX); + SUB32RtoM((uptr)&cpuRegs.PERF.n.pcr0, EAX); + x86SetJ8(j8Ptr[0]); + + AND32ItoR(EDX, 0x800F8000); + CMP32ItoR(EDX, 0x80008000); + j8Ptr[0] = JNE8(0); + MOV32MtoR(EAX, (uptr)&s_iLastPERFCycle[1]); + ADD32RtoM((uptr)&cpuRegs.PERF.n.pcr1, ECX); + SUB32RtoM((uptr)&cpuRegs.PERF.n.pcr1, EAX); + x86SetJ8(j8Ptr[0]); +} + +void recMTC0() +{ + if( GPR_IS_CONST1(_Rt_) ) { + switch (_Rd_) { + case 12: + iFlushCall(FLUSH_NODESTROY); + //_flushCachedRegs(); //NOTE: necessary? + _callFunctionArg1((uptr)WriteCP0Status, MEM_CONSTTAG, g_cpuConstRegs[_Rt_].UL[0]); + break; + case 9: + MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); + MOV32RtoM((uptr)&s_iLastCOP0Cycle, ECX); + MOV32ItoM((uptr)&cpuRegs.CP0.r[9], g_cpuConstRegs[_Rt_].UL[0]); + break; + case 25: + COP0_LOG("MTC0 PCCR = %x PCR0 = %x PCR1 = %x IMM= %x\n", + cpuRegs.PERF.n.pccr, cpuRegs.PERF.n.pcr0, cpuRegs.PERF.n.pcr1, _Imm_ & 0x3F); + switch(_Imm_ & 0x3F){ + case 0: + + updatePCCR(); + MOV32ItoM((uptr)&cpuRegs.PERF.n.pccr, g_cpuConstRegs[_Rt_].UL[0]); + + // update the cycles + MOV32RtoM((uptr)&s_iLastPERFCycle[0], ECX); + MOV32RtoM((uptr)&s_iLastPERFCycle[1], ECX); + break; + case 1: + MOV32MtoR(EAX, (uptr)&cpuRegs.cycle); + MOV32ItoM((uptr)&cpuRegs.PERF.n.pcr0, g_cpuConstRegs[_Rt_].UL[0]); + MOV32RtoM((uptr)&s_iLastPERFCycle[0], EAX); + break; + case 3: + MOV32MtoR(EAX, (uptr)&cpuRegs.cycle); + MOV32ItoM((uptr)&cpuRegs.PERF.n.pcr1, g_cpuConstRegs[_Rt_].UL[0]); + MOV32RtoM((uptr)&s_iLastPERFCycle[1], EAX); + break; + } + break; + case 24: + COP0_LOG("MTC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF); + break; + default: + MOV32ItoM((uptr)&cpuRegs.CP0.r[_Rd_], g_cpuConstRegs[_Rt_].UL[0]); + break; + } + } + else { + switch (_Rd_) { + case 12: + iFlushCall(FLUSH_NODESTROY); + //_flushCachedRegs(); //NOTE: necessary? + _callFunctionArg1((uptr)WriteCP0Status, MEM_GPRTAG|_Rt_, 0); + break; + case 9: + MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); + _eeMoveGPRtoM((uptr)&cpuRegs.CP0.r[9], _Rt_); + MOV32RtoM((uptr)&s_iLastCOP0Cycle, ECX); + break; + case 25: + COP0_LOG("MTC0 PCCR = %x PCR0 = %x PCR1 = %x IMM= %x\n", + cpuRegs.PERF.n.pccr, cpuRegs.PERF.n.pcr0, cpuRegs.PERF.n.pcr1, _Imm_ & 0x3F); + switch(_Imm_ & 0x3F){ + case 0: + updatePCCR(); + _eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pccr, _Rt_); + + // update the cycles + MOV32RtoM((uptr)&s_iLastPERFCycle[0], ECX); + MOV32RtoM((uptr)&s_iLastPERFCycle[1], ECX); + break; + case 1: + MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); + _eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pcr0, _Rt_); + MOV32RtoM((uptr)&s_iLastPERFCycle[0], ECX); + break; + case 3: + MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); + _eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pcr1, _Rt_); + MOV32RtoM((uptr)&s_iLastPERFCycle[1], ECX); + break; + } + break; + case 24: + COP0_LOG("MTC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF); + break; + default: + _eeMoveGPRtoM((uptr)&cpuRegs.CP0.r[_Rd_], _Rt_); + break; + } + } +} +#endif + + +/*void rec(COP0) { +} + +void rec(BC0F) { +} + +void rec(BC0T) { +} + +void rec(BC0FL) { +} + +void rec(BC0TL) { +} + +void rec(TLBR) { +} + +void rec(TLBWI) { +} + +void rec(TLBWR) { +} + +void rec(TLBP) { +}*/ + +}}}} diff --git a/pcsx2/x86/iCOP0.h b/pcsx2/x86/iCOP0.h new file mode 100644 index 0000000000..9112cb3898 --- /dev/null +++ b/pcsx2/x86/iCOP0.h @@ -0,0 +1,49 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __iCOP0_H__ +#define __iCOP0_H__ + +#include "COP0.h" + +/********************************************************* +* COP0 opcodes * +* * +*********************************************************/ + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { +namespace COP0 +{ + void recMFC0( void ); + void recMTC0( void ); + void recBC0F( void ); + void recBC0T( void ); + void recBC0FL( void ); + void recBC0TL( void ); + void recTLBR( void ); + void recTLBWI( void ); + void recTLBWR( void ); + void recTLBP( void ); + void recERET( void ); + void recDI( void ); + void recEI( void ); + +}}}} +#endif diff --git a/pcsx2/x86/iCOP2.cpp b/pcsx2/x86/iCOP2.cpp new file mode 100644 index 0000000000..7ed6f7e322 --- /dev/null +++ b/pcsx2/x86/iCOP2.cpp @@ -0,0 +1,721 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" + +#include "Common.h" +#include "DebugTools/Debug.h" +#include "R5900.h" +#include "R5900OpcodeTables.h" +#include "VUmicro.h" +#include "iVUmicro.h" + +extern void _vu0WaitMicro(); + +#define _Ft_ _Rt_ +#define _Fs_ _Rd_ +#define _Fd_ _Sa_ + +#define _Fsf_ ((cpuRegs.code >> 21) & 0x03) +#define _Ftf_ ((cpuRegs.code >> 23) & 0x03) +#define _Cc_ (cpuRegs.code & 0x03) + +void recCop2BranchCall( void (*func)() ) +{ + SetFPUstate(); + recBranchCall( func ); + _freeX86regs(); +} + +#define REC_COP2_FUNC( f ) \ + void rec##f(s32 info) \ + { \ + Console::Notice("Warning > cop2 "#f" called"); \ + recCop2BranchCall( f ); \ + } + +#define INTERPRETATE_COP2_FUNC(f) \ +void recV##f(s32 info) { \ + MOV32ItoM((uptr)&cpuRegs.code, cpuRegs.code); \ + MOV32ItoM((uptr)&cpuRegs.pc, pc); \ + iFlushCall(FLUSH_EVERYTHING); \ + CALLFunc((uptr)V##f); \ + _freeX86regs(); \ +} + +#define REC_COP2_VU0(f) \ +void recV##f( s32 info ) { \ + recVUMI_##f( &VU0, info ); \ +} + +#define REC_COP2_VU0_Q(f) \ +void recV##f( s32 info ) { \ + recVUMI_##f( &VU0, info ); \ +} + +void rec_C2UNK( s32 info ) +{ + Console::Error("Cop2 bad opcode: %x", params cpuRegs.code); +} + +void _vuRegs_C2UNK(VURegs * VU, _VURegsNum *VUregsn) +{ + Console::Error("Cop2 bad _vuRegs code:%x", params cpuRegs.code); +} + +void recCOP2(s32 info); +void recCOP2_SPECIAL(s32 info); +void recCOP2_BC2(s32 info); +void recCOP2_SPECIAL2(s32 info); + +static void recCFC2(s32 info) +{ + int mmreg; + + if (cpuRegs.code & 1) + { + iFlushCall(FLUSH_NOCONST); + CALLFunc((uptr)_vu0WaitMicro); + } + + if(!_Rt_) return; + + _deleteGPRtoXMMreg(_Rt_, 2); + mmreg = _checkMMXreg(MMX_GPR+_Rt_, MODE_WRITE); + + if( mmreg >= 0 ) + { + if( _Fs_ >= 16 ) + { + MOVDMtoMMX(mmreg, (uptr)&VU0.VI[ _Fs_ ].UL); + if( EEINST_ISLIVE1(_Rt_) ) + { + _signExtendGPRtoMMX(mmreg, _Rt_, 0); + } + else + { + EEINST_RESETHASLIVE1(_Rt_); + } + } + else + { + MOVDMtoMMX(mmreg, (uptr)&VU0.VI[ _Fs_ ].UL); + } + SetMMXstate(); + } + else + { + MOV32MtoR(EAX, (uptr)&VU0.VI[ _Fs_ ].UL); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0],EAX); + + if(EEINST_ISLIVE1(_Rt_)) + { + if( _Fs_ < 16 ) + { + // no sign extending + MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[1],0); + } + else + { + CDQ(); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[1], EDX); + } + } + else + { + EEINST_RESETHASLIVE1(_Rt_); + } + } + + _eeOnWriteReg(_Rt_, 1); +} + +static void recCTC2(s32 info) +{ + if (cpuRegs.code & 1) { + iFlushCall(FLUSH_NOCONST); + CALLFunc((uptr)_vu0WaitMicro); + } + + if(!_Fs_) return; + + if( GPR_IS_CONST1(_Rt_) ) + { + switch(_Fs_) { + case REG_MAC_FLAG: // read-only + case REG_TPC: // read-only + case REG_VPU_STAT: // read-only + break; + case REG_FBRST: + if( g_cpuConstRegs[_Rt_].UL[0] & 0x202 ) + iFlushCall(FLUSH_FREE_TEMPX86); + + _deleteX86reg(X86TYPE_VI, REG_FBRST, 2); + + if( g_cpuConstRegs[_Rt_].UL[0] & 2 ) + CALLFunc((uptr)vu0ResetRegs); + if( g_cpuConstRegs[_Rt_].UL[0] & 0x200 ) + CALLFunc((uptr)vu1ResetRegs); + MOV16ItoM((uptr)&VU0.VI[REG_FBRST].UL,g_cpuConstRegs[_Rt_].UL[0]&0x0c0c); + break; + case REG_CMSAR1: // REG_CMSAR1 + iFlushCall(FLUSH_NOCONST);// since CALLFunc + assert( _checkX86reg(X86TYPE_VI, REG_VPU_STAT, 0) < 0 && + _checkX86reg(X86TYPE_VI, REG_TPC, 0) < 0 ); + // Execute VU1 Micro SubRoutine + _callFunctionArg1((uptr)vu1ExecMicro, MEM_CONSTTAG, g_cpuConstRegs[_Rt_].UL[0]&0xffff); + break; + default: + { + if( _Fs_ < 16 ) + assert( (g_cpuConstRegs[_Rt_].UL[0]&0xffff0000)==0); + + // a lot of games have vu0 spinning on some integer + // then they modify the register and expect vu0 to stop spinning within 10 cycles (donald duck) + + // Use vu0ExecMicro instead because it properly stalls for already-running micro + // instructions, and also sets the nextBranchCycle as needed. (air) + + MOV32ItoM((uptr)&VU0.VI[_Fs_].UL,g_cpuConstRegs[_Rt_].UL[0]); + //PUSH32I( -1 ); + iFlushCall(FLUSH_NOCONST); + CALLFunc((uptr)CpuVU0.ExecuteBlock); + //CALLFunc((uptr)vu0ExecMicro); + //ADD32ItoR( ESP, 4 ); + break; + } + } + } + else + { + switch(_Fs_) { + case REG_MAC_FLAG: // read-only + case REG_TPC: // read-only + case REG_VPU_STAT: // read-only + break; + case REG_FBRST: + iFlushCall(FLUSH_FREE_TEMPX86); + assert( _checkX86reg(X86TYPE_VI, REG_FBRST, 0) < 0 ); + + _eeMoveGPRtoR(EAX, _Rt_); + + TEST32ItoR(EAX,0x2); + j8Ptr[0] = JZ8(0); + CALLFunc((uptr)vu0ResetRegs); + _eeMoveGPRtoR(EAX, _Rt_); + x86SetJ8(j8Ptr[0]); + + TEST32ItoR(EAX,0x200); + j8Ptr[0] = JZ8(0); + CALLFunc((uptr)vu1ResetRegs); + _eeMoveGPRtoR(EAX, _Rt_); + x86SetJ8(j8Ptr[0]); + + AND32ItoR(EAX,0x0C0C); + MOV16RtoM((uptr)&VU0.VI[REG_FBRST].UL,EAX); + break; + case REG_CMSAR1: // REG_CMSAR1 + iFlushCall(FLUSH_NOCONST); + _eeMoveGPRtoR(EAX, _Rt_); + _callFunctionArg1((uptr)vu1ExecMicro, MEM_X86TAG|EAX, 0); // Execute VU1 Micro SubRoutine + break; + default: + _eeMoveGPRtoM((uptr)&VU0.VI[_Fs_].UL,_Rt_); + + // a lot of games have vu0 spinning on some integer + // then they modify the register and expect vu0 to stop spinning within 10 cycles (donald duck) + iFlushCall(FLUSH_NOCONST); + break; + } + } +} + +static void recQMFC2(s32 info) +{ + int t0reg, fsreg; + + if (cpuRegs.code & 1) + { + iFlushCall(FLUSH_NOCONST); + CALLFunc((uptr)_vu0WaitMicro); + } + + if(!_Rt_) return; + + _deleteMMXreg(MMX_GPR+_Rt_, 2); + _deleteX86reg(X86TYPE_GPR, _Rt_, 2); + _eeOnWriteReg(_Rt_, 0); + + // could 'borrow' the reg + fsreg = _checkXMMreg(XMMTYPE_VFREG, _Fs_, MODE_READ); + + if( fsreg >= 0 ) { + if( xmmregs[fsreg].mode & MODE_WRITE ) + { + _xmmregs temp; + + t0reg = _allocGPRtoXMMreg(-1, _Rt_, MODE_WRITE); + SSEX_MOVDQA_XMM_to_XMM(t0reg, fsreg); + + // change regs + temp = xmmregs[t0reg]; + xmmregs[t0reg] = xmmregs[fsreg]; + xmmregs[fsreg] = temp; + } + else + { + // swap regs + t0reg = _allocGPRtoXMMreg(-1, _Rt_, MODE_WRITE); + + xmmregs[fsreg] = xmmregs[t0reg]; + xmmregs[t0reg].inuse = 0; + } + } + else { + t0reg = _allocGPRtoXMMreg(-1, _Rt_, MODE_WRITE); + + if( t0reg >= 0 ) + SSE_MOVAPS_M128_to_XMM( t0reg, (uptr)&VU0.VF[_Fs_].UD[0]); + else + _recMove128MtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0], (uptr)&VU0.VF[_Fs_].UL[0]); + } + + _clearNeededXMMregs(); +} + +static void recQMTC2(s32 info) +{ + int mmreg; + + if (cpuRegs.code & 1) { + iFlushCall(FLUSH_NOCONST); + CALLFunc((uptr)_vu0WaitMicro); + } + + if (!_Fs_) return; + + mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ); + + if( mmreg >= 0) { + int fsreg = _checkXMMreg(XMMTYPE_VFREG, _Fs_, MODE_WRITE); + int flag = ((xmmregs[mmreg].mode&MODE_WRITE) && (g_pCurInstInfo->regs[_Rt_]&(EEINST_LIVE0|EEINST_LIVE1|EEINST_LIVE2))); + + if( fsreg >= 0 ) { + + if (flag) { + SSE_MOVAPS_XMM_to_XMM(fsreg, mmreg); + } + else { + // swap regs + xmmregs[mmreg] = xmmregs[fsreg]; + xmmregs[mmreg].mode = MODE_WRITE; + xmmregs[fsreg].inuse = 0; + g_xmmtypes[mmreg] = XMMT_FPS; + } + } + else { + if (flag) SSE_MOVAPS_XMM_to_M128((uptr)&cpuRegs.GPR.r[_Rt_], mmreg); + + // swap regs + xmmregs[mmreg].type = XMMTYPE_VFREG; + xmmregs[mmreg].VU = 0; + xmmregs[mmreg].reg = _Fs_; + xmmregs[mmreg].mode = MODE_WRITE; + g_xmmtypes[mmreg] = XMMT_FPS; + } + } + else { + int fsreg = _allocVFtoXMMreg(&VU0, -1, _Fs_, MODE_WRITE); + + if( fsreg >= 0 ) { + mmreg = _checkMMXreg(MMX_GPR+_Rt_, MODE_READ); + + if( mmreg >= 0) { + SetMMXstate(); + SSE2_MOVQ2DQ_MM_to_XMM(fsreg, mmreg); + SSE_MOVHPS_M64_to_XMM(fsreg, (uptr)&cpuRegs.GPR.r[_Rt_].UL[2]); + } + else { + if( GPR_IS_CONST1( _Rt_ ) ) { + assert( _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ) == -1 ); + _flushConstReg(_Rt_); + } + + SSE_MOVAPS_M128_to_XMM(fsreg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + } + } + else { + _deleteEEreg(_Rt_, 0); + _recMove128MtoM((uptr)&VU0.VF[_Fs_].UL[0], (uptr)&cpuRegs.GPR.r[_Rt_].UL[0]); + } + } + + _clearNeededXMMregs(); +} + +// Removed _cop2AnalyzeOp because it was outdated and unreliable (And currently unused). +// A new version should be rebuilt using the SuperVU's AnalyzeOp as a reference. (air) + +////////////////////////////////////////////////////////////////////////// +// BC2: Instructions +////////////////////////////////////////////////////////////////////////// +//REC_COP2_FUNC(BC2F); +//REC_COP2_FUNC(BC2T); +//REC_COP2_FUNC(BC2FL); +//REC_COP2_FUNC(BC2TL); + +using namespace R5900::Dynarec; + +static void _setupBranchTest() +{ + _eeFlushAllUnused(); + + // COP2 branch conditionals are based on the following equation: + // ((VU0.VI[REG_VPU_STAT].US[0] >> 8) & 1) + // BC2F checks if the statement is false, BC2T checks if the statement is true. + + MOV32MtoR( EAX, (uptr)&VU0.VI[REG_VPU_STAT].UL ); + TEST32ItoR( EAX, 0x100 ); +} + +void recBC2F( s32 info ) +{ + _setupBranchTest(); + recDoBranchImm(JNZ32(0)); +} + +void recBC2T( s32 info ) +{ + _setupBranchTest(); + recDoBranchImm(JZ32(0)); +} + +void recBC2FL( s32 info ) +{ + _setupBranchTest(); + recDoBranchImm_Likely(JNZ32(0)); +} + +void recBC2TL( s32 info ) +{ + _setupBranchTest(); + recDoBranchImm_Likely(JZ32(0)); +} + + +////////////////////////////////////////////////////////////////////////// +// Special1 instructions +////////////////////////////////////////////////////////////////////////// +//TODO: redirect all the opcodes to the ivu0micro same functions +REC_COP2_VU0(IADD); +REC_COP2_VU0(IADDI); +REC_COP2_VU0(ISUB); +REC_COP2_VU0(IOR); +REC_COP2_VU0(IAND); +REC_COP2_VU0(OPMSUB); +REC_COP2_VU0(MADDq); +REC_COP2_VU0(MADDi); +REC_COP2_VU0(MSUBq); +REC_COP2_VU0(MSUBi); +REC_COP2_VU0(SUBi); +REC_COP2_VU0(SUBq); +REC_COP2_VU0(MULi); +REC_COP2_VU0(MULq); +REC_COP2_VU0(MAXi); +REC_COP2_VU0(MINIi); +REC_COP2_VU0(MUL); +REC_COP2_VU0(MAX); +REC_COP2_VU0(MADD); +REC_COP2_VU0(MSUB); + +REC_COP2_VU0(MINIx); +REC_COP2_VU0(MINIy); +REC_COP2_VU0(MINIz); +REC_COP2_VU0(MINIw); + +REC_COP2_VU0(MAXx); +REC_COP2_VU0(MAXy); +REC_COP2_VU0(MAXz); +REC_COP2_VU0(MAXw); + +REC_COP2_VU0(MULx); +REC_COP2_VU0(MULy); +REC_COP2_VU0(MULz); +REC_COP2_VU0(MULw); + +REC_COP2_VU0(ADD); +REC_COP2_VU0(ADDi); +REC_COP2_VU0(ADDq); +REC_COP2_VU0(ADDx); +REC_COP2_VU0(ADDy); +REC_COP2_VU0(ADDz); +REC_COP2_VU0(ADDw); + +REC_COP2_VU0(SUBx); +REC_COP2_VU0(SUBy); +REC_COP2_VU0(SUBz); +REC_COP2_VU0(SUBw); + +REC_COP2_VU0(MADDx); +REC_COP2_VU0(MADDy); +REC_COP2_VU0(MADDz); +REC_COP2_VU0(MADDw); + +REC_COP2_VU0(MSUBx); +REC_COP2_VU0(MSUBy); +REC_COP2_VU0(MSUBz); +REC_COP2_VU0(MSUBw); + +REC_COP2_VU0(SUB); +REC_COP2_VU0(MINI); + +////////////////////////////////////////////////////////////////////////// +// Special2 instructions +////////////////////////////////////////////////////////////////////////// + +REC_COP2_VU0(CLIP); +REC_COP2_VU0(LQI); +REC_COP2_VU0(SQI); +REC_COP2_VU0(LQD); +REC_COP2_VU0(SQD); +REC_COP2_VU0(MTIR); +REC_COP2_VU0(MFIR); +REC_COP2_VU0(ILWR); +REC_COP2_VU0(ISWR); +REC_COP2_VU0(RINIT); +REC_COP2_VU0(RXOR); +REC_COP2_VU0(RNEXT); +REC_COP2_VU0(RGET); + +REC_COP2_VU0(ITOF0); +REC_COP2_VU0(ITOF4); +REC_COP2_VU0(ITOF12); +REC_COP2_VU0(ITOF15); +REC_COP2_VU0(FTOI0); +REC_COP2_VU0(FTOI4); +REC_COP2_VU0(FTOI12); +REC_COP2_VU0(FTOI15); +REC_COP2_VU0(MADDA); +REC_COP2_VU0(MSUBA); +REC_COP2_VU0(MADDAi); +REC_COP2_VU0(MADDAq); +REC_COP2_VU0(MADDAx); +REC_COP2_VU0(MADDAy); +REC_COP2_VU0(MADDAz); +REC_COP2_VU0(MADDAw); +REC_COP2_VU0(MSUBAi); +REC_COP2_VU0(MSUBAq); +REC_COP2_VU0(MSUBAx); +REC_COP2_VU0(MSUBAy); +REC_COP2_VU0(MSUBAz); +REC_COP2_VU0(MSUBAw); +REC_COP2_VU0(ADDAi); +REC_COP2_VU0(ADDA); +REC_COP2_VU0(SUBA); +REC_COP2_VU0(MULA); +REC_COP2_VU0(ADDAq); +REC_COP2_VU0(ADDAx); +REC_COP2_VU0(ADDAy); +REC_COP2_VU0(ADDAz); +REC_COP2_VU0(ADDAw); +REC_COP2_VU0(SUBAi); +REC_COP2_VU0(SUBAq); +REC_COP2_VU0(SUBAx); +REC_COP2_VU0(SUBAy); +REC_COP2_VU0(SUBAz); +REC_COP2_VU0(SUBAw); +REC_COP2_VU0(MULAi); +REC_COP2_VU0(MULAq); +REC_COP2_VU0(MULAx); +REC_COP2_VU0(MULAy); +REC_COP2_VU0(MULAz); +REC_COP2_VU0(MULAw); +REC_COP2_VU0(OPMULA); +REC_COP2_VU0(MOVE); +REC_COP2_VU0_Q(DIV); +REC_COP2_VU0_Q(SQRT); +REC_COP2_VU0_Q(RSQRT); +REC_COP2_VU0(MR32); +REC_COP2_VU0(ABS); + +void recVNOP(s32 info){} +void recVWAITQ(s32 info){} +INTERPRETATE_COP2_FUNC(CALLMS); +INTERPRETATE_COP2_FUNC(CALLMSR); + +void _vuRegsCOP2_SPECIAL(VURegs * VU, _VURegsNum *VUregsn); +void _vuRegsCOP2_SPECIAL2(VURegs * VU, _VURegsNum *VUregsn); + +// information +void _vuRegsQMFC2(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->VFread0 = _Fs_; + VUregsn->VFr0xyzw= 0xf; +} + +void _vuRegsCFC2(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->VIread = 1<<_Fs_; +} + +void _vuRegsQMTC2(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->VFwrite = _Fs_; + VUregsn->VFwxyzw= 0xf; +} + +void _vuRegsCTC2(VURegs * VU, _VURegsNum *VUregsn) { + VUregsn->VIwrite = 1<<_Fs_; +} + +void (*_vuRegsCOP2t[32])(VURegs * VU, _VURegsNum *VUregsn) = { + _vuRegs_C2UNK, _vuRegsQMFC2, _vuRegsCFC2, _vuRegs_C2UNK, _vuRegs_C2UNK, _vuRegsQMTC2, _vuRegsCTC2, _vuRegs_C2UNK, + _vuRegsNOP, _vuRegs_C2UNK, _vuRegs_C2UNK, _vuRegs_C2UNK, _vuRegs_C2UNK, _vuRegs_C2UNK, _vuRegs_C2UNK, _vuRegs_C2UNK, + _vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL, + _vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL,_vuRegsCOP2_SPECIAL, +}; + +void (*_vuRegsCOP2SPECIAL1t[64])(VURegs * VU, _VURegsNum *VUregsn) = { + _vuRegsADDx, _vuRegsADDy, _vuRegsADDz, _vuRegsADDw, _vuRegsSUBx, _vuRegsSUBy, _vuRegsSUBz, _vuRegsSUBw, + _vuRegsMADDx, _vuRegsMADDy, _vuRegsMADDz, _vuRegsMADDw, _vuRegsMSUBx, _vuRegsMSUBy, _vuRegsMSUBz, _vuRegsMSUBw, + _vuRegsMAXx, _vuRegsMAXy, _vuRegsMAXz, _vuRegsMAXw, _vuRegsMINIx, _vuRegsMINIy, _vuRegsMINIz, _vuRegsMINIw, + _vuRegsMULx, _vuRegsMULy, _vuRegsMULz, _vuRegsMULw, _vuRegsMULq, _vuRegsMAXi, _vuRegsMULi, _vuRegsMINIi, + _vuRegsADDq, _vuRegsMADDq, _vuRegsADDi, _vuRegsMADDi, _vuRegsSUBq, _vuRegsMSUBq, _vuRegsSUBi, _vuRegsMSUBi, + _vuRegsADD, _vuRegsMADD, _vuRegsMUL, _vuRegsMAX, _vuRegsSUB, _vuRegsMSUB, _vuRegsOPMSUB, _vuRegsMINI, + _vuRegsIADD, _vuRegsISUB, _vuRegsIADDI, _vuRegs_C2UNK, _vuRegsIAND, _vuRegsIOR, _vuRegs_C2UNK, _vuRegs_C2UNK, + _vuRegsNOP, _vuRegsNOP, _vuRegs_C2UNK, _vuRegs_C2UNK, _vuRegsCOP2_SPECIAL2,_vuRegsCOP2_SPECIAL2,_vuRegsCOP2_SPECIAL2,_vuRegsCOP2_SPECIAL2, +}; + +void (*_vuRegsCOP2SPECIAL2t[128])(VURegs * VU, _VURegsNum *VUregsn) = { + _vuRegsADDAx ,_vuRegsADDAy ,_vuRegsADDAz ,_vuRegsADDAw ,_vuRegsSUBAx ,_vuRegsSUBAy ,_vuRegsSUBAz ,_vuRegsSUBAw, + _vuRegsMADDAx ,_vuRegsMADDAy ,_vuRegsMADDAz ,_vuRegsMADDAw ,_vuRegsMSUBAx ,_vuRegsMSUBAy ,_vuRegsMSUBAz ,_vuRegsMSUBAw, + _vuRegsITOF0 ,_vuRegsITOF4 ,_vuRegsITOF12 ,_vuRegsITOF15 ,_vuRegsFTOI0 ,_vuRegsFTOI4 ,_vuRegsFTOI12 ,_vuRegsFTOI15, + _vuRegsMULAx ,_vuRegsMULAy ,_vuRegsMULAz ,_vuRegsMULAw ,_vuRegsMULAq ,_vuRegsABS ,_vuRegsMULAi ,_vuRegsCLIP, + _vuRegsADDAq ,_vuRegsMADDAq ,_vuRegsADDAi ,_vuRegsMADDAi ,_vuRegsSUBAq ,_vuRegsMSUBAq ,_vuRegsSUBAi ,_vuRegsMSUBAi, + _vuRegsADDA ,_vuRegsMADDA ,_vuRegsMULA ,_vuRegs_C2UNK ,_vuRegsSUBA ,_vuRegsMSUBA ,_vuRegsOPMULA ,_vuRegsNOP, + _vuRegsMOVE ,_vuRegsMR32 ,_vuRegs_C2UNK ,_vuRegs_C2UNK ,_vuRegsLQI ,_vuRegsSQI ,_vuRegsLQD ,_vuRegsSQD, + _vuRegsDIV ,_vuRegsSQRT ,_vuRegsRSQRT ,_vuRegsWAITQ ,_vuRegsMTIR ,_vuRegsMFIR ,_vuRegsILWR ,_vuRegsISWR, + _vuRegsRNEXT ,_vuRegsRGET ,_vuRegsRINIT ,_vuRegsRXOR ,_vuRegs_C2UNK ,_vuRegs_C2UNK ,_vuRegs_C2UNK ,_vuRegs_C2UNK, + _vuRegs_C2UNK ,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK, + _vuRegs_C2UNK ,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK, + _vuRegs_C2UNK ,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK, + _vuRegs_C2UNK ,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK, + _vuRegs_C2UNK ,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK, + _vuRegs_C2UNK ,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK, + _vuRegs_C2UNK ,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK,_vuRegs_C2UNK, +}; + +void _vuRegsCOP22(VURegs * VU, _VURegsNum *VUregsn) +{ + _vuRegsCOP2t[_Rs_](VU, VUregsn); +} + +void _vuRegsCOP2_SPECIAL(VURegs * VU, _VURegsNum *VUregsn) +{ + _vuRegsCOP2SPECIAL1t[_Funct_](VU, VUregsn); +} + +void _vuRegsCOP2_SPECIAL2(VURegs * VU, _VURegsNum *VUregsn) +{ + int opc=(cpuRegs.code & 0x3) | ((cpuRegs.code >> 4) & 0x7c); + _vuRegsCOP2SPECIAL2t[opc](VU, VUregsn); +} + +// recompilation +void (*recCOP2t[32])(s32 info) = { + rec_C2UNK, recQMFC2, recCFC2, rec_C2UNK, rec_C2UNK, recQMTC2, recCTC2, rec_C2UNK, + recCOP2_BC2, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, + recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL, + recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL,recCOP2_SPECIAL, +}; + +void (*recCOP2_BC2t[32])(s32 info) = { + recBC2F, recBC2T, recBC2FL, recBC2TL, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, + rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, + rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, + rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, rec_C2UNK, +}; + +void (*recCOP2SPECIAL1t[64])(s32 info) = { + recVADDx, recVADDy, recVADDz, recVADDw, recVSUBx, recVSUBy, recVSUBz, recVSUBw, + recVMADDx, recVMADDy, recVMADDz, recVMADDw, recVMSUBx, recVMSUBy, recVMSUBz, recVMSUBw, + recVMAXx, recVMAXy, recVMAXz, recVMAXw, recVMINIx, recVMINIy, recVMINIz, recVMINIw, + recVMULx, recVMULy, recVMULz, recVMULw, recVMULq, recVMAXi, recVMULi, recVMINIi, + recVADDq, recVMADDq, recVADDi, recVMADDi, recVSUBq, recVMSUBq, recVSUBi, recVMSUBi, + recVADD, recVMADD, recVMUL, recVMAX, recVSUB, recVMSUB, recVOPMSUB, recVMINI, + recVIADD, recVISUB, recVIADDI, rec_C2UNK, recVIAND, recVIOR, rec_C2UNK, rec_C2UNK, + recVCALLMS, recVCALLMSR, rec_C2UNK, rec_C2UNK, recCOP2_SPECIAL2,recCOP2_SPECIAL2,recCOP2_SPECIAL2,recCOP2_SPECIAL2, +}; + +void (*recCOP2SPECIAL2t[128])(s32 info) = { + recVADDAx ,recVADDAy ,recVADDAz ,recVADDAw ,recVSUBAx ,recVSUBAy ,recVSUBAz ,recVSUBAw, + recVMADDAx ,recVMADDAy ,recVMADDAz ,recVMADDAw ,recVMSUBAx ,recVMSUBAy ,recVMSUBAz ,recVMSUBAw, + recVITOF0 ,recVITOF4 ,recVITOF12 ,recVITOF15 ,recVFTOI0 ,recVFTOI4 ,recVFTOI12 ,recVFTOI15, + recVMULAx ,recVMULAy ,recVMULAz ,recVMULAw ,recVMULAq ,recVABS ,recVMULAi ,recVCLIP, + recVADDAq ,recVMADDAq ,recVADDAi ,recVMADDAi ,recVSUBAq ,recVMSUBAq ,recVSUBAi ,recVMSUBAi, + recVADDA ,recVMADDA ,recVMULA ,rec_C2UNK ,recVSUBA ,recVMSUBA ,recVOPMULA ,recVNOP, + recVMOVE ,recVMR32 ,rec_C2UNK ,rec_C2UNK ,recVLQI ,recVSQI ,recVLQD ,recVSQD, + recVDIV ,recVSQRT ,recVRSQRT ,recVWAITQ ,recVMTIR ,recVMFIR ,recVILWR ,recVISWR, + recVRNEXT ,recVRGET ,recVRINIT ,recVRXOR ,rec_C2UNK ,rec_C2UNK ,rec_C2UNK ,rec_C2UNK, + rec_C2UNK ,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK, + rec_C2UNK ,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK, + rec_C2UNK ,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK, + rec_C2UNK ,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK, + rec_C2UNK ,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK, + rec_C2UNK ,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK, + rec_C2UNK ,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK,rec_C2UNK, +}; + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl +{ + void recCOP2() + { + VU0.code = cpuRegs.code; + + g_pCurInstInfo->vuregs.pipe = 0xff; // to notify eeVURecompileCode that COP2 + s32 info = eeVURecompileCode(&VU0, &g_pCurInstInfo->vuregs); + + info |= PROCESS_VU_COP2; + info |= PROCESS_VU_UPDATEFLAGS; + + recCOP2t[_Rs_]( info ); + + _freeX86regs(); + } +}}} + +void recCOP2_SPECIAL(s32 info ) +{ + recCOP2SPECIAL1t[_Funct_]( info ); +} + +void recCOP2_BC2(s32 info) +{ + recCOP2_BC2t[_Rt_](info); +} + +void recCOP2_SPECIAL2(s32 info) +{ + int opc=(cpuRegs.code & 0x3) | ((cpuRegs.code >> 4) & 0x7c); + recCOP2SPECIAL2t[opc](info); +} + diff --git a/pcsx2/x86/iCore.cpp b/pcsx2/x86/iCore.cpp new file mode 100644 index 0000000000..c82dfb74c0 --- /dev/null +++ b/pcsx2/x86/iCore.cpp @@ -0,0 +1,1206 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Misc.h" +#include "iR5900.h" +#include "Vif.h" +#include "VU.h" +#include "ix86/ix86.h" +#include "R3000A.h" + +u16 g_x86AllocCounter = 0; +u16 g_xmmAllocCounter = 0; + +EEINST* g_pCurInstInfo = NULL; + +u32 g_cpuRegHasLive1 = 0, g_cpuPrevRegHasLive1 = 0; // set if upper 32 bits are live +u32 g_cpuRegHasSignExt = 0, g_cpuPrevRegHasSignExt = 0; // set if upper 32 bits are the sign extension of the lower integer + +// used to make sure regs don't get changed while in recompiler +// use FreezeMMXRegs, FreezeXMMRegs +u32 g_recWriteback = 0; + +#ifdef _DEBUG +char g_globalXMMLocked = 0; +#endif + +_xmmregs xmmregs[XMMREGS], s_saveXMMregs[XMMREGS]; + +// X86 caching +_x86regs x86regs[X86REGS], s_saveX86regs[X86REGS]; + +#include +using namespace std; + +//void _eeSetLoadStoreReg(int gprreg, u32 offset, int x86reg) +//{ +// int regs[2] = {ESI, EDI}; +// +// int i = _checkX86reg(X86TYPE_MEMOFFSET, gprreg, MODE_WRITE); +// if( i < 0 ) { +// for(i = 0; i < 2; ++i) { +// if( !x86regs[regs[i]].inuse ) break; +// } +// +// assert( i < 2 ); +// i = regs[i]; +// } +// +// if( i != x86reg ) MOV32RtoR(x86reg, i); +// x86regs[i].extra = offset; +//} + +//int _eeGeLoadStoreReg(int gprreg, int* poffset) +//{ +// int i = _checkX86reg(X86TYPE_MEMOFFSET, gprreg, MODE_READ); +// if( i >= 0 ) return -1; +// +// if( poffset ) *poffset = x86regs[i].extra; +// return i; +//} + +// XMM Caching +#define VU_VFx_ADDR(x) (uptr)&VU->VF[x].UL[0] +#define VU_ACCx_ADDR (uptr)&VU->ACC.UL[0] + +static int s_xmmchecknext = 0; + +void _initXMMregs() { + memzero_obj( xmmregs ); + g_xmmAllocCounter = 0; + s_xmmchecknext = 0; +} + +__forceinline void* _XMMGetAddr(int type, int reg, VURegs *VU) +{ + switch (type) { + case XMMTYPE_VFREG: + return (void*)VU_VFx_ADDR(reg); + + case XMMTYPE_ACC: + return (void*)VU_ACCx_ADDR; + + case XMMTYPE_GPRREG: + if( reg < 32 ) + assert( !(g_cpuHasConstReg & (1<regs[xmmregs[i].reg] & (EEINST_LIVE0|EEINST_LIVE1|EEINST_LIVE2)) ) { + _freeXMMreg(i); + return i; + } + } + } + + // check for future xmm usage + for (i=0; iregs[xmmregs[i].reg] & EEINST_XMM) ) { + _freeXMMreg(i); + return i; + } + } + } + + tempi = -1; + bestcount = 0xffff; + for (i=0; i= 0 ) { + // requested specific reg, so return that instead + if( i != xmmreg ) { + if( xmmregs[i].mode & MODE_READ ) readfromreg = i; + //if( xmmregs[i].mode & MODE_WRITE ) mode |= MODE_WRITE; + mode |= xmmregs[i].mode&MODE_WRITE; + xmmregs[i].inuse = 0; + break; + } + } + + xmmregs[i].needed = 1; + + if( !(xmmregs[i].mode & MODE_READ) && (mode&MODE_READ) ) { + SSE_MOVAPS_M128_to_XMM(i, VU_VFx_ADDR(vfreg)); + xmmregs[i].mode |= MODE_READ; + } + + g_xmmtypes[i] = XMMT_FPS; + xmmregs[i].counter = g_xmmAllocCounter++; // update counter + xmmregs[i].mode|= mode; + return i; + } + + if (xmmreg == -1) + xmmreg = _getFreeXMMreg(); + else + _freeXMMreg(xmmreg); + + g_xmmtypes[xmmreg] = XMMT_FPS; + xmmregs[xmmreg].inuse = 1; + xmmregs[xmmreg].type = XMMTYPE_VFREG; + xmmregs[xmmreg].reg = vfreg; + xmmregs[xmmreg].mode = mode; + xmmregs[xmmreg].needed = 1; + xmmregs[xmmreg].VU = XMM_CONV_VU(VU); + xmmregs[xmmreg].counter = g_xmmAllocCounter++; + if (mode & MODE_READ) { + if( readfromreg >= 0 ) SSE_MOVAPS_XMM_to_XMM(xmmreg, readfromreg); + else SSE_MOVAPS_M128_to_XMM(xmmreg, VU_VFx_ADDR(xmmregs[xmmreg].reg)); + } + + return xmmreg; +} + +int _checkXMMreg(int type, int reg, int mode) +{ + int i; + + for (i=0; i= 0 ) { + // requested specific reg, so return that instead + if( i != xmmreg ) { + if( xmmregs[i].mode & MODE_READ ) readfromreg = i; + //if( xmmregs[i].mode & MODE_WRITE ) mode |= MODE_WRITE; + mode |= xmmregs[i].mode&MODE_WRITE; + xmmregs[i].inuse = 0; + break; + } + } + + if( !(xmmregs[i].mode & MODE_READ) && (mode&MODE_READ)) { + SSE_MOVAPS_M128_to_XMM(i, VU_ACCx_ADDR); + xmmregs[i].mode |= MODE_READ; + } + + g_xmmtypes[i] = XMMT_FPS; + xmmregs[i].counter = g_xmmAllocCounter++; // update counter + xmmregs[i].needed = 1; + xmmregs[i].mode|= mode; + return i; + } + + if (xmmreg == -1) + xmmreg = _getFreeXMMreg(); + else + _freeXMMreg(xmmreg); + + g_xmmtypes[xmmreg] = XMMT_FPS; + xmmregs[xmmreg].inuse = 1; + xmmregs[xmmreg].type = XMMTYPE_ACC; + xmmregs[xmmreg].mode = mode; + xmmregs[xmmreg].needed = 1; + xmmregs[xmmreg].VU = XMM_CONV_VU(VU); + xmmregs[xmmreg].counter = g_xmmAllocCounter++; + xmmregs[xmmreg].reg = 0; + + if (mode & MODE_READ) + { + if( readfromreg >= 0 ) + SSE_MOVAPS_XMM_to_XMM(xmmreg, readfromreg); + else + SSE_MOVAPS_M128_to_XMM(xmmreg, VU_ACCx_ADDR); + } + + return xmmreg; +} + +int _allocFPtoXMMreg(int xmmreg, int fpreg, int mode) { + int i; + + for (i=0; i= 0 ) + { + // transfer + SetMMXstate(); + SSE2_MOVQ2DQ_MM_to_XMM(xmmreg, mmxreg); + SSE2_PUNPCKLQDQ_XMM_to_XMM(xmmreg, xmmreg); + SSE2_PUNPCKHQDQ_M128_to_XMM(xmmreg, (u32)&cpuRegs.GPR.r[gprreg].UL[0]); + + if (mmxregs[mmxreg].mode & MODE_WRITE ) + { + // instead of setting to write, just flush to mem + if (!(mode & MODE_WRITE)) + { + SetMMXstate(); + MOVQRtoM((u32)&cpuRegs.GPR.r[gprreg].UL[0], mmxreg); + } + //xmmregs[xmmreg].mode |= MODE_WRITE; + } + + // don't flush + mmxregs[mmxreg].inuse = 0; + } + else + SSEX_MOVDQA_M128_to_XMM(xmmreg, (uptr)&cpuRegs.GPR.r[gprreg].UL[0]); + } + } + else + _deleteMMXreg(MMX_GPR+gprreg, 0); + + return xmmreg; +} + +int _allocFPACCtoXMMreg(int xmmreg, int mode) +{ + int i; + + for (i=0; iregs[xmmregs[i].reg]&EEINST_USED) ) { + return 1; + } + } + } + return 0; +} + +void _moveXMMreg(int xmmreg) +{ + int i; + if( !xmmregs[xmmreg].inuse ) return; + + for (i=0; iregs[gprreg] & EEINST_XMM ) return _allocGPRtoXMMreg(-1, gprreg, mode); + + return _checkXMMreg(XMMTYPE_GPRREG, gprreg, mode); +} + +int _allocCheckFPUtoXMM(EEINST* pinst, int fpureg, int mode) +{ + if( pinst->fpuregs[fpureg] & EEINST_XMM ) return _allocFPtoXMMreg(-1, fpureg, mode); + + return _checkXMMreg(XMMTYPE_FPREG, fpureg, mode); +} + +int _allocCheckGPRtoX86(EEINST* pinst, int gprreg, int mode) +{ + if( pinst->regs[gprreg] & EEINST_USED ) + return _allocX86reg(-1, X86TYPE_GPR, gprreg, mode); + + return _checkX86reg(X86TYPE_GPR, gprreg, mode); +} + +void _recClearInst(EEINST* pinst) +{ + memzero_obj( *pinst ); + memset8_obj( pinst->regs ); + memset8_obj( pinst->fpuregs ); +} + +// returns nonzero value if reg has been written between [startpc, endpc-4] +u32 _recIsRegWritten(EEINST* pinst, int size, u8 xmmtype, u8 reg) +{ + u32 i, inst = 1; + + while(size-- > 0) { + for(i = 0; i < ARRAYSIZE(pinst->writeType); ++i) { + if ((pinst->writeType[i] == xmmtype) && (pinst->writeReg[i] == reg)) + return inst; + } + ++inst; + pinst++; + } + + return 0; +} + +u32 _recIsRegUsed(EEINST* pinst, int size, u8 xmmtype, u8 reg) +{ + u32 i, inst = 1; + while(size-- > 0) { + for(i = 0; i < ARRAYSIZE(pinst->writeType); ++i) { + if( pinst->writeType[i] == xmmtype && pinst->writeReg[i] == reg ) + return inst; + } + for(i = 0; i < ARRAYSIZE(pinst->readType); ++i) { + if( pinst->readType[i] == xmmtype && pinst->readReg[i] == reg ) + return inst; + } + ++inst; + pinst++; + } + + return 0; +} + +void _recFillRegister(EEINST& pinst, int type, int reg, int write) +{ + u32 i = 0; + if (write ) { + for(i = 0; i < ARRAYSIZE(pinst.writeType); ++i) { + if( pinst.writeType[i] == XMMTYPE_TEMP ) { + pinst.writeType[i] = type; + pinst.writeReg[i] = reg; + return; + } + } + assert(0); + } + else { + for(i = 0; i < ARRAYSIZE(pinst.readType); ++i) { + if( pinst.readType[i] == XMMTYPE_TEMP ) { + pinst.readType[i] = type; + pinst.readReg[i] = reg; + return; + } + } + assert(0); + } +} + +void SetMMXstate() { + x86FpuState = MMX_STATE; +} + +//////////////////////////////////////////////////// +//#include "R3000A.h" +//#include "PsxCounters.h" +//#include "PsxMem.h" +//extern tIPU_BP g_BP; + +#if 0 +extern u32 psxdump; +extern void iDumpPsxRegisters(u32 startpc, u32 temp); +extern Counter counters[6]; +extern int rdram_devices; // put 8 for TOOL and 2 for PS2 and PSX +extern int rdram_sdevid; +#endif + +void iDumpRegisters(u32 startpc, u32 temp) +{ +// [TODO] fixme : this code is broken and has no labels. Needs a rewrite to be useful. + +#if 0 + + int i; + const char* pstr;// = temp ? "t" : ""; + const u32 dmacs[] = {0x8000, 0x9000, 0xa000, 0xb000, 0xb400, 0xc000, 0xc400, 0xc800, 0xd000, 0xd400 }; + const char* psymb; + + if (temp) + pstr = "t"; + else + pstr = ""; + + psymb = disR5900GetSym(startpc); + + if( psymb != NULL ) + __Log("%sreg(%s): %x %x c:%x\n", pstr, psymb, startpc, cpuRegs.interrupt, cpuRegs.cycle); + else + __Log("%sreg: %x %x c:%x\n", pstr, startpc, cpuRegs.interrupt, cpuRegs.cycle); + for(i = 1; i < 32; ++i) __Log("%s: %x_%x_%x_%x\n", disRNameGPR[i], cpuRegs.GPR.r[i].UL[3], cpuRegs.GPR.r[i].UL[2], cpuRegs.GPR.r[i].UL[1], cpuRegs.GPR.r[i].UL[0]); + //for(i = 0; i < 32; i+=4) __Log("cp%d: %x_%x_%x_%x\n", i, cpuRegs.CP0.r[i], cpuRegs.CP0.r[i+1], cpuRegs.CP0.r[i+2], cpuRegs.CP0.r[i+3]); + //for(i = 0; i < 32; ++i) __Log("%sf%d: %f %x\n", pstr, i, fpuRegs.fpr[i].f, fpuRegs.fprc[i]); + //for(i = 1; i < 32; ++i) __Log("%svf%d: %f %f %f %f, vi: %x\n", pstr, i, VU0.VF[i].F[3], VU0.VF[i].F[2], VU0.VF[i].F[1], VU0.VF[i].F[0], VU0.VI[i].UL); + for(i = 0; i < 32; ++i) __Log("%sf%d: %x %x\n", pstr, i, fpuRegs.fpr[i].UL, fpuRegs.fprc[i]); + for(i = 1; i < 32; ++i) __Log("%svf%d: %x %x %x %x, vi: %x\n", pstr, i, VU0.VF[i].UL[3], VU0.VF[i].UL[2], VU0.VF[i].UL[1], VU0.VF[i].UL[0], VU0.VI[i].UL); + __Log("%svfACC: %x %x %x %x\n", pstr, VU0.ACC.UL[3], VU0.ACC.UL[2], VU0.ACC.UL[1], VU0.ACC.UL[0]); + __Log("%sLO: %x_%x_%x_%x, HI: %x_%x_%x_%x\n", pstr, cpuRegs.LO.UL[3], cpuRegs.LO.UL[2], cpuRegs.LO.UL[1], cpuRegs.LO.UL[0], + cpuRegs.HI.UL[3], cpuRegs.HI.UL[2], cpuRegs.HI.UL[1], cpuRegs.HI.UL[0]); + __Log("%sCycle: %x %x, Count: %x\n", pstr, cpuRegs.cycle, g_nextBranchCycle, cpuRegs.CP0.n.Count); + iDumpPsxRegisters(psxRegs.pc, temp); + + __Log("f410,30,40: %x %x %x, %d %d\n", psHu32(0xf410), psHu32(0xf430), psHu32(0xf440), rdram_sdevid, rdram_devices); + __Log("cyc11: %x %x; vu0: %x, vu1: %x\n", cpuRegs.sCycle[1], cpuRegs.eCycle[1], VU0.cycle, VU1.cycle); + + __Log("%scounters: %x %x; psx: %x %x\n", pstr, nextsCounter, nextCounter, psxNextsCounter, psxNextCounter); + for(i = 0; i < 4; ++i) { + __Log("eetimer%d: count: %x mode: %x target: %x %x; %x %x; %x %x %x %x\n", i, + counters[i].count, counters[i].mode, counters[i].target, counters[i].hold, counters[i].rate, + counters[i].interrupt, counters[i].Cycle, counters[i].sCycle, counters[i].CycleT, counters[i].sCycleT); + } + __Log("VIF0_STAT = %x, VIF1_STAT = %x\n", psHu32(0x3800), psHu32(0x3C00)); + __Log("ipu %x %x %x %x; bp: %x %x %x %x\n", psHu32(0x2000), psHu32(0x2010), psHu32(0x2020), psHu32(0x2030), g_BP.BP, g_BP.bufferhasnew, g_BP.FP, g_BP.IFC); + __Log("gif: %x %x %x\n", psHu32(0x3000), psHu32(0x3010), psHu32(0x3020)); + for(i = 0; i < ARRAYSIZE(dmacs); ++i) { + DMACh* p = (DMACh*)(PS2MEM_HW+dmacs[i]); + __Log("dma%d c%x m%x q%x t%x s%x\n", i, p->chcr, p->madr, p->qwc, p->tadr, p->sadr); + } + __Log("dmac %x %x %x %x\n", psHu32(DMAC_CTRL), psHu32(DMAC_STAT), psHu32(DMAC_RBSR), psHu32(DMAC_RBOR)); + __Log("intc %x %x\n", psHu32(INTC_STAT), psHu32(INTC_MASK)); + __Log("sif: %x %x %x %x %x\n", psHu32(0xf200), psHu32(0xf220), psHu32(0xf230), psHu32(0xf240), psHu32(0xf260)); +#endif +} diff --git a/pcsx2/x86/iCore.h b/pcsx2/x86/iCore.h new file mode 100644 index 0000000000..b0d28d884f --- /dev/null +++ b/pcsx2/x86/iCore.h @@ -0,0 +1,429 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef _PCSX2_CORE_RECOMPILER_ +#define _PCSX2_CORE_RECOMPILER_ + +#include "ix86/ix86.h" +#include "iVUmicro.h" + +// Namespace Note : iCore32 contains all of the Register Allocation logic, in addition to a handful +// of utility functions for emitting frequent code. + +//////////////////////////////////////////////////////////////////////////////// +// Shared Register allocation flags (apply to X86, XMM, MMX, etc). + +#define MODE_READ 1 +#define MODE_WRITE 2 +#define MODE_READHALF 4 // read only low 64 bits +#define MODE_VUXY 0x8 // vector only has xy valid (real zw are in mem), not the same as MODE_READHALF +#define MODE_VUZ 0x10 // z only doesn't work for now +#define MODE_VUXYZ (MODE_VUZ|MODE_VUXY) // vector only has xyz valid (real w is in memory) +#define MODE_NOFLUSH 0x20 // can't flush reg to mem +#define MODE_NOFRAME 0x40 // when allocating x86regs, don't use ebp reg +#define MODE_8BITREG 0x80 // when allocating x86regs, use only eax, ecx, edx, and ebx + +#define PROCESS_EE_MMX 0x01 +#define PROCESS_EE_XMM 0x02 + +// currently only used in FPU +#define PROCESS_EE_S 0x04 // S is valid, otherwise take from mem +#define PROCESS_EE_T 0x08 // T is valid, otherwise take from mem + +// not used in VU recs +#define PROCESS_EE_MODEWRITES 0x10 // if s is a reg, set if not in cpuRegs +#define PROCESS_EE_MODEWRITET 0x20 // if t is a reg, set if not in cpuRegs +#define PROCESS_EE_LO 0x40 // lo reg is valid +#define PROCESS_EE_HI 0x80 // hi reg is valid +#define PROCESS_EE_ACC 0x40 // acc reg is valid + +// used in VU recs +#define PROCESS_VU_UPDATEFLAGS 0x10 +#define PROCESS_VU_SUPER 0x40 // set if using supervu recompilation +#define PROCESS_VU_COP2 0x80 // simple cop2 + +#define EEREC_S (((info)>>8)&0xf) +#define EEREC_T (((info)>>12)&0xf) +#define EEREC_D (((info)>>16)&0xf) +#define EEREC_LO (((info)>>20)&0xf) +#define EEREC_HI (((info)>>24)&0xf) +#define EEREC_ACC (((info)>>20)&0xf) +#define EEREC_TEMP (((info)>>24)&0xf) +#define VUREC_FMAC ((info)&0x80000000) + +#define PROCESS_EE_SET_S(reg) ((reg)<<8) +#define PROCESS_EE_SET_T(reg) ((reg)<<12) +#define PROCESS_EE_SET_D(reg) ((reg)<<16) +#define PROCESS_EE_SET_LO(reg) ((reg)<<20) +#define PROCESS_EE_SET_HI(reg) ((reg)<<24) +#define PROCESS_EE_SET_ACC(reg) ((reg)<<20) + +#define PROCESS_VU_SET_ACC(reg) PROCESS_EE_SET_ACC(reg) +#define PROCESS_VU_SET_TEMP(reg) ((reg)<<24) + +#define PROCESS_VU_SET_FMAC() 0x80000000 + +// special info not related to above flags +#define PROCESS_CONSTS 1 +#define PROCESS_CONSTT 2 + +//////////////////////////////////////////////////////////////////////////////// +// X86 (32-bit) Register Allocation Tools + +#define X86TYPE_TEMP 0 +#define X86TYPE_GPR 1 +#define X86TYPE_VI 2 +#define X86TYPE_MEMOFFSET 3 +#define X86TYPE_VIMEMOFFSET 4 +#define X86TYPE_VUQREAD 5 +#define X86TYPE_VUPREAD 6 +#define X86TYPE_VUQWRITE 7 +#define X86TYPE_VUPWRITE 8 +#define X86TYPE_PSX 9 +#define X86TYPE_PCWRITEBACK 10 +#define X86TYPE_VUJUMP 12 // jump from random mem (g_recWriteback) +#define X86TYPE_VITEMP 13 +#define X86TYPE_FNARG 14 // function parameter, max is 4 + +#define X86TYPE_VU1 0x80 + +#define X86_ISVI(type) ((type&~X86TYPE_VU1) == X86TYPE_VI) + +struct _x86regs { + u8 inuse; + u8 reg; // value of 0 - not used + u8 mode; + u8 needed; + u8 type; // X86TYPE_ + u16 counter; + u32 extra; // extra info assoc with the reg +}; + +extern _x86regs x86regs[X86REGS], s_saveX86regs[X86REGS]; + +uptr _x86GetAddr(int type, int reg); +void _initX86regs(); +int _getFreeX86reg(int mode); +int _allocX86reg(int x86reg, int type, int reg, int mode); +void _deleteX86reg(int type, int reg, int flush); +int _checkX86reg(int type, int reg, int mode); +void _addNeededX86reg(int type, int reg); +void _clearNeededX86regs(); +void _freeX86reg(int x86reg); +void _flushX86regs(); +void _freeX86regs(); +void _freeX86tempregs(); +u8 _hasFreeX86reg(); +void _flushCachedRegs(); +void _flushConstRegs(); +void _flushConstReg(int reg); + +//////////////////////////////////////////////////////////////////////////////// +// XMM (128-bit) Register Allocation Tools + +#define XMM_CONV_VU(VU) (VU==&VU1) + +#define XMMTYPE_TEMP 0 // has to be 0 +#define XMMTYPE_VFREG 1 +#define XMMTYPE_ACC 2 +#define XMMTYPE_FPREG 3 +#define XMMTYPE_FPACC 4 +#define XMMTYPE_GPRREG 5 + +// lo and hi regs +#define XMMGPR_LO 33 +#define XMMGPR_HI 32 +#define XMMFPU_ACC 32 + +struct _xmmregs { + u8 inuse; + u8 reg; + u8 type; + u8 mode; + u8 needed; + u8 VU; // 0 = VU0, 1 = VU1 + u16 counter; +}; + +void _initXMMregs(); +int _getFreeXMMreg(); +int _allocTempXMMreg(XMMSSEType type, int xmmreg); +int _allocVFtoXMMreg(VURegs *VU, int xmmreg, int vfreg, int mode); +int _allocFPtoXMMreg(int xmmreg, int fpreg, int mode); +int _allocGPRtoXMMreg(int xmmreg, int gprreg, int mode); +int _allocACCtoXMMreg(VURegs *VU, int xmmreg, int mode); +int _allocFPACCtoXMMreg(int xmmreg, int mode); +int _checkXMMreg(int type, int reg, int mode); +void _addNeededVFtoXMMreg(int vfreg); +void _addNeededACCtoXMMreg(); +void _addNeededFPtoXMMreg(int fpreg); +void _addNeededFPACCtoXMMreg(); +void _addNeededGPRtoXMMreg(int gprreg); +void _clearNeededXMMregs(); +void _deleteVFtoXMMreg(int reg, int vu, int flush); +void _deleteACCtoXMMreg(int vu, int flush); +void _deleteGPRtoXMMreg(int reg, int flush); +void _deleteFPtoXMMreg(int reg, int flush); +void _freeXMMreg(int xmmreg); +void _moveXMMreg(int xmmreg); // instead of freeing, moves it to a diff location +void _flushXMMregs(); +u8 _hasFreeXMMreg(); +void _freeXMMregs(); +int _getNumXMMwrite(); + +// uses MEM_MMXTAG/MEM_XMMTAG to differentiate between the regs +void _recPushReg(int mmreg); +void _signExtendSFtoM(u32 mem); + +// returns new index of reg, lower 32 bits already in mmx +// shift is used when the data is in the top bits of the mmx reg to begin with +// a negative shift is for sign extension +int _signExtendXMMtoM(u32 to, x86SSERegType from, int candestroy); // returns true if reg destroyed + +// Defines for passing register info + +// only valid during writes. If write128, then upper 64bits are in an mmxreg +// (mmreg&0xf). Constant is used from gprreg ((mmreg>>16)&0x1f) +#define MEM_EECONSTTAG 0x0100 // argument is a GPR and comes from g_cpuConstRegs +#define MEM_PSXCONSTTAG 0x0200 +#define MEM_MEMORYTAG 0x0400 +#define MEM_MMXTAG 0x0800 // mmreg is mmxreg +#define MEM_XMMTAG 0x8000 // mmreg is xmmreg +#define MEM_X86TAG 0x4000 // ignored most of the time +#define MEM_GPRTAG 0x2000 // argument is a GPR reg +#define MEM_CONSTTAG 0x1000 // argument is a const + +#define IS_EECONSTREG(reg) (reg>=0&&((reg)&MEM_EECONSTTAG)) +#define IS_PSXCONSTREG(reg) (reg>=0&&((reg)&MEM_PSXCONSTTAG)) +#define IS_MMXREG(reg) (reg>=0&&((reg)&MEM_MMXTAG)) +#define IS_XMMREG(reg) (reg>=0&&((reg)&MEM_XMMTAG)) + +// fixme - these 4 are only called for u32 registers; should the reg>=0 really be there? +#define IS_X86REG(reg) (reg>=0&&((reg)&MEM_X86TAG)) +#define IS_GPRREG(reg) (reg>=0&&((reg)&MEM_GPRTAG)) +#define IS_CONSTREG(reg) (reg>=0&&((reg)&MEM_CONSTTAG)) +#define IS_MEMORYREG(reg) (reg>=0&&((reg)&MEM_MEMORYTAG)) + +////////////////////// +// Instruction Info // +////////////////////// +#define EEINST_LIVE0 1 // if var is ever used (read or write) +#define EEINST_LIVE1 2 // if cur var's next 32 bits are needed +#define EEINST_LIVE2 4 // if cur var's next 64 bits are needed +#define EEINST_LASTUSE 8 // if var isn't written/read anymore +#define EEINST_MMX 0x10 // var will be used in mmx ops +#define EEINST_XMM 0x20 // var will be used in xmm ops (takes precedence +#define EEINST_USED 0x40 + +#define EEINSTINFO_COP1 1 +#define EEINSTINFO_COP2 2 +#ifdef PCSX2_VM_COISSUE +#define EEINSTINFO_NOREC 4 // if set, inst is recompiled alone +#define EEINSTINFO_COREC 8 // if set, inst is recompiled with another similar inst +#endif +#define EEINSTINFO_MMX EEINST_MMX +#define EEINSTINFO_XMM EEINST_XMM + +struct EEINST +{ + u8 regs[34]; // includes HI/LO (HI=32, LO=33) + u8 fpuregs[33]; // ACC=32 + u8 info; // extra info, if 1 inst is COP1, 2 inst is COP2. Also uses EEINST_MMX|EEINST_XMM + + // uses XMMTYPE_ flags; if type == XMMTYPE_TEMP, not used + u8 writeType[3], writeReg[3]; // reg written in this inst, 0 if no reg + u8 readType[4], readReg[4]; + + // valid if info & EEINSTINFO_COP2 + int cycle; // cycle of inst (at offset from block) + _VURegsNum vuregs; + + u8 numpeeps; // number of peephole optimizations +}; + +extern EEINST* g_pCurInstInfo; // info for the cur instruction +extern void _recClearInst(EEINST* pinst); + +// returns the number of insts + 1 until written (0 if not written) +extern u32 _recIsRegWritten(EEINST* pinst, int size, u8 xmmtype, u8 reg); +// returns the number of insts + 1 until used (0 if not used) +extern u32 _recIsRegUsed(EEINST* pinst, int size, u8 xmmtype, u8 reg); +extern void _recFillRegister(EEINST& pinst, int type, int reg, int write); + +#define EEINST_ISLIVE64(reg) (g_pCurInstInfo->regs[reg] & (EEINST_LIVE0|EEINST_LIVE1)) +#define EEINST_ISLIVEXMM(reg) (g_pCurInstInfo->regs[reg] & (EEINST_LIVE0|EEINST_LIVE1|EEINST_LIVE2)) +#define EEINST_ISLIVE1(reg) (g_pCurInstInfo->regs[reg] & EEINST_LIVE1) +#define EEINST_ISLIVE2(reg) (g_pCurInstInfo->regs[reg] & EEINST_LIVE2) + +#define FPUINST_ISLIVE(reg) (g_pCurInstInfo->fpuregs[reg] & EEINST_LIVE0) +#define FPUINST_LASTUSE(reg) (g_pCurInstInfo->fpuregs[reg] & EEINST_LASTUSE) + +// if set, then the variable at this inst really has its upper 32 bits valid +// The difference between EEINST_LIVE1 is that the latter is used in back propagation +// The former is set at recompile time. +#define EEINST_RESETHASLIVE1(reg) { if( (reg) < 32 ) g_cpuRegHasLive1 &= ~(1<<(reg)); } +#define EEINST_HASLIVE1(reg) (g_cpuPrevRegHasLive1&(1<<(reg))) + +#define EEINST_SETSIGNEXT(reg) { if( (reg) < 32 ) g_cpuRegHasSignExt |= (1<<(reg)); } +#define EEINST_RESETSIGNEXT(reg) { if( (reg) < 32 ) g_cpuRegHasSignExt &= ~(1<<(reg)); } +#define EEINST_ISSIGNEXT(reg) (g_cpuPrevRegHasSignExt&(1<<(reg))) + +extern u32 g_recWriteback; // used for jumps (VUrec mess!) +extern u32 g_cpuRegHasLive1, g_cpuPrevRegHasLive1; +extern u32 g_cpuRegHasSignExt, g_cpuPrevRegHasSignExt; + +extern _xmmregs xmmregs[XMMREGS], s_saveXMMregs[XMMREGS]; + +extern u16 g_x86AllocCounter; +extern u16 g_xmmAllocCounter; + +#ifdef _DEBUG +extern char g_globalXMMLocked; +#endif + +// allocates only if later insts use XMM, otherwise checks +int _allocCheckGPRtoXMM(EEINST* pinst, int gprreg, int mode); +int _allocCheckFPUtoXMM(EEINST* pinst, int fpureg, int mode); + +// allocates only if later insts use this register +int _allocCheckGPRtoX86(EEINST* pinst, int gprreg, int mode); + +//////////////////////////////////////////////////////////////////////////////// +// MMX (64-bit) Register Allocation Tools + +#define FPU_STATE 0 +#define MMX_STATE 1 + +void SetMMXstate(); +void SetFPUstate(); + +// max is 0x7f, when 0x80 is set, need to flush reg +#define MMX_GET_CACHE(ptr, index) ((u8*)ptr)[index] +#define MMX_SET_CACHE(ptr, ind3, ind2, ind1, ind0) ((u32*)ptr)[0] = (ind3<<24)|(ind2<<16)|(ind1<<8)|ind0; +#define MMX_GPR 0 +#define MMX_HI XMMGPR_HI +#define MMX_LO XMMGPR_LO +#define MMX_FPUACC 34 +#define MMX_FPU 64 +#define MMX_COP0 96 +#define MMX_TEMP 0x7f + +#define MMX_IS32BITS(x) (((x)>=MMX_FPU&&(x)= MMX_GPR && (x) < MMX_GPR+34) + +struct _mmxregs { + u8 inuse; + u8 reg; // value of 0 - not used + u8 mode; + u8 needed; + u16 counter; +}; + +void _initMMXregs(); +int _getFreeMMXreg(); +int _allocMMXreg(int MMXreg, int reg, int mode); +void _addNeededMMXreg(int reg); +int _checkMMXreg(int reg, int mode); +void _clearNeededMMXregs(); +void _deleteMMXreg(int reg, int flush); +void _freeMMXreg(int mmxreg); +void _moveMMXreg(int mmxreg); // instead of freeing, moves it to a diff location +void _flushMMXregs(); +u8 _hasFreeMMXreg(); +void _freeMMXregs(); +int _getNumMMXwrite(); + +int _signExtendMtoMMX(x86MMXRegType to, u32 mem); +int _signExtendGPRMMXtoMMX(x86MMXRegType to, u32 gprreg, x86MMXRegType from, u32 gprfromreg); +int _allocCheckGPRtoMMX(EEINST* pinst, int reg, int mode); + +void _recMove128RmOffsettoM(u32 to, u32 offset); +void _recMove128MtoRmOffset(u32 offset, u32 from); + +// returns new index of reg, lower 32 bits already in mmx +// shift is used when the data is in the top bits of the mmx reg to begin with +// a negative shift is for sign extension +extern int _signExtendGPRtoMMX(x86MMXRegType to, u32 gprreg, int shift); + +extern _mmxregs mmxregs[MMXREGS], s_saveMMXregs[MMXREGS]; +extern u16 x86FpuState, iCWstate; + +extern void iDumpRegisters(u32 startpc, u32 temp); + +////////////////////////////////////////////////////////////////////////// +// iFlushCall / _psxFlushCall Parameters + +// Flushing vs. Freeing, as understood by Air (I could be wrong still....) + +// "Freeing" registers means that the contents of the registers are flushed to memory. +// This is good for any sort of C code function that plans to modify the actual +// registers. When the Recs resume, they'll reload the registers with values saved +// as needed. (similar to a "FreezeXMMRegs") + +// "Flushing" means that in addition to the standard free (which is actually a flush) +// the register allocations are additionally wiped. This should only be necessary if +// the code being called is going to modify register allocations -- ie, be doing +// some kind of recompiling of its own. + +#define FLUSH_CACHED_REGS 1 +#define FLUSH_FLUSH_XMM 2 +#define FLUSH_FREE_XMM 4 // both flushes and frees +#define FLUSH_FLUSH_MMX 8 +#define FLUSH_FREE_MMX 16 // both flushes and frees +#define FLUSH_FLUSH_ALLX86 32 // flush x86 +#define FLUSH_FREE_TEMPX86 64 // flush and free temporary x86 regs +#define FLUSH_FREE_ALLX86 128 // free all x86 regs +#define FLUSH_FREE_VU0 0x100 // free all vu0 related regs + +#define FLUSH_EVERYTHING 0xfff +// no freeing, used when callee won't destroy mmx/xmm regs +#define FLUSH_NODESTROY (FLUSH_CACHED_REGS|FLUSH_FLUSH_XMM|FLUSH_FLUSH_MMX|FLUSH_FLUSH_ALLX86) +// used when regs aren't going to be changed be callee +#define FLUSH_NOCONST (FLUSH_FREE_XMM|FLUSH_FREE_MMX|FLUSH_FREE_TEMPX86) + + +////////////////////////////////////////////////////////////////////////// +// Utility Functions -- that should probably be part of the Emitter. + +// see MEM_X defines for argX format +extern void _callPushArg(u32 arg, uptr argmem); /// X86ARG is ignored for 32bit recs +extern void _callFunctionArg1(uptr fn, u32 arg1, uptr arg1mem); +extern void _callFunctionArg2(uptr fn, u32 arg1, u32 arg2, uptr arg1mem, uptr arg2mem); +extern void _callFunctionArg3(uptr fn, u32 arg1, u32 arg2, u32 arg3, uptr arg1mem, uptr arg2mem, uptr arg3mem); + +// Moves 128 bits of data using EAX/EDX (used by iCOP2 only currently) +extern void _recMove128MtoM(u32 to, u32 from); + +// op = 0, and +// op = 1, or +// op = 2, xor +// op = 3, nor (the 32bit versoins only do OR) +extern void LogicalOpRtoR(x86MMXRegType to, x86MMXRegType from, int op); +extern void LogicalOpMtoR(x86MMXRegType to, u32 from, int op); + +extern void LogicalOp32RtoM(uptr to, x86IntRegType from, int op); +extern void LogicalOp32MtoR(x86IntRegType to, uptr from, int op); +extern void LogicalOp32ItoR(x86IntRegType to, u32 from, int op); +extern void LogicalOp32ItoM(uptr to, u32 from, int op); + +#ifdef ARITHMETICIMM_RECOMPILE +extern void LogicalOpRtoR(x86MMXRegType to, x86MMXRegType from, int op); +extern void LogicalOpMtoR(x86MMXRegType to, u32 from, int op); +#endif + +#endif diff --git a/pcsx2/x86/iFPU.cpp b/pcsx2/x86/iFPU.cpp new file mode 100644 index 0000000000..8bb8a9452b --- /dev/null +++ b/pcsx2/x86/iFPU.cpp @@ -0,0 +1,1733 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "iFPU.h" + +extern PCSX2_ALIGNED16_DECL(u32 g_minvals[4]); +extern PCSX2_ALIGNED16_DECL(u32 g_maxvals[4]); + +//------------------------------------------------------------------ +// Misc... +//------------------------------------------------------------------ +//static u32 _mxcsr = 0x7F80; +//static u32 _mxcsrs; +static u32 fpucw = 0x007f; +static u32 fpucws = 0; + +void SaveCW(int type) { + if (iCWstate & type) return; + + if (type == 2) { +// SSE_STMXCSR((uptr)&_mxcsrs); +// SSE_LDMXCSR((uptr)&_mxcsr); + } else { + FNSTCW( (uptr)&fpucws ); + FLDCW( (uptr)&fpucw ); + } + iCWstate|= type; +} + +void LoadCW() { + if (iCWstate == 0) return; + + if (iCWstate & 2) { + //SSE_LDMXCSR((uptr)&_mxcsrs); + } + if (iCWstate & 1) { + FLDCW( (uptr)&fpucws ); + } + iCWstate = 0; +} + +//------------------------------------------------------------------ +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { +namespace COP1 { + +//------------------------------------------------------------------ +// Helper Macros +//------------------------------------------------------------------ +#define _Ft_ _Rt_ +#define _Fs_ _Rd_ +#define _Fd_ _Sa_ + +// FCR31 Flags +#define FPUflagC 0X00800000 +#define FPUflagI 0X00020000 +#define FPUflagD 0X00010000 +#define FPUflagO 0X00008000 +#define FPUflagU 0X00004000 +#define FPUflagSI 0X00000040 +#define FPUflagSD 0X00000020 +#define FPUflagSO 0X00000010 +#define FPUflagSU 0X00000008 + +#define FPU_ADD_SUB_HACK 1 // Add/Sub opcodes produce more ps2-like results if set to 1 + +static u32 PCSX2_ALIGNED16(s_neg[4]) = { 0x80000000, 0xffffffff, 0xffffffff, 0xffffffff }; +static u32 PCSX2_ALIGNED16(s_pos[4]) = { 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff }; + +#define REC_FPUBRANCH(f) \ + void f(); \ + void rec##f() { \ + MOV32ItoM((uptr)&cpuRegs.code, cpuRegs.code); \ + MOV32ItoM((uptr)&cpuRegs.pc, pc); \ + iFlushCall(FLUSH_EVERYTHING); \ + CALLFunc((uptr)R5900::Interpreter::OpcodeImpl::COP1::f); \ + branch = 2; \ +} + +#define REC_FPUFUNC(f) \ + void f(); \ + void rec##f() { \ + MOV32ItoM((uptr)&cpuRegs.code, cpuRegs.code); \ + MOV32ItoM((uptr)&cpuRegs.pc, pc); \ + iFlushCall(FLUSH_EVERYTHING); \ + CALLFunc((uptr)R5900::Interpreter::OpcodeImpl::COP1::f); \ +} +//------------------------------------------------------------------ + +//------------------------------------------------------------------ +// *FPU Opcodes!* +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// CFC1 / CTC1 +//------------------------------------------------------------------ +void recCFC1(void) +{ + if ( !_Rt_ || ( (_Fs_ != 0) && (_Fs_ != 31) ) ) return; + + _eeOnWriteReg(_Rt_, 1); + + MOV32MtoR( EAX, (uptr)&fpuRegs.fprc[ _Fs_ ] ); + _deleteEEreg(_Rt_, 0); + + if(EEINST_ISLIVE1(_Rt_)) + { + CDQ( ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + } + else + { + EEINST_RESETHASLIVE1(_Rt_); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + } +} + +void recCTC1( void ) +{ + if ( _Fs_ != 31 ) return; + + if ( GPR_IS_CONST1(_Rt_) ) + { + MOV32ItoM((uptr)&fpuRegs.fprc[ _Fs_ ], g_cpuConstRegs[_Rt_].UL[0]); + } + else + { + int mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ); + + if( mmreg >= 0 ) + { + SSEX_MOVD_XMM_to_M32((uptr)&fpuRegs.fprc[ _Fs_ ], mmreg); + } + + else + { + mmreg = _checkMMXreg(MMX_GPR+_Rt_, MODE_READ); + + if ( mmreg >= 0 ) + { + MOVDMMXtoM((uptr)&fpuRegs.fprc[ _Fs_ ], mmreg); + SetMMXstate(); + } + else + { + _deleteGPRtoXMMreg(_Rt_, 1); + _deleteMMXreg(MMX_GPR+_Rt_, 1); + + MOV32MtoR( EAX, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + MOV32RtoM( (uptr)&fpuRegs.fprc[ _Fs_ ], EAX ); + } + } + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MFC1 +//------------------------------------------------------------------ + +void recMFC1(void) +{ + int regt, regs; + if ( ! _Rt_ ) return; + + _eeOnWriteReg(_Rt_, 1); + + regs = _checkXMMreg(XMMTYPE_FPREG, _Fs_, MODE_READ); + + if( regs >= 0 ) + { + _deleteGPRtoXMMreg(_Rt_, 2); + regt = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); + + if( regt >= 0 ) + { + SSE2_MOVDQ2Q_XMM_to_MM(regt, regs); + + if(EEINST_ISLIVE1(_Rt_)) + _signExtendGPRtoMMX(regt, _Rt_, 0); + else + EEINST_RESETHASLIVE1(_Rt_); + } + else + { + if(EEINST_ISLIVE1(_Rt_)) + { + _signExtendXMMtoM((uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], regs, 0); + } + else + { + EEINST_RESETHASLIVE1(_Rt_); + SSE_MOVSS_XMM_to_M32((uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], regs); + } + } + } + else + { + regs = _checkMMXreg(MMX_FPU+_Fs_, MODE_READ); + + if( regs >= 0 ) + { + // convert to mmx reg + mmxregs[regs].reg = MMX_GPR+_Rt_; + mmxregs[regs].mode |= MODE_READ|MODE_WRITE; + _signExtendGPRtoMMX(regs, _Rt_, 0); + } + else + { + regt = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ); + + if( regt >= 0 ) + { + if( xmmregs[regt].mode & MODE_WRITE ) + { + SSE_MOVHPS_XMM_to_M64((uptr)&cpuRegs.GPR.r[_Rt_].UL[2], regt); + } + xmmregs[regt].inuse = 0; + } + + _deleteEEreg(_Rt_, 0); + MOV32MtoR( EAX, (uptr)&fpuRegs.fpr[ _Fs_ ].UL ); + + if(EEINST_ISLIVE1(_Rt_)) + { + CDQ( ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + } + else + { + EEINST_RESETHASLIVE1(_Rt_); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + } + } + } +} + +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MTC1 +//------------------------------------------------------------------ +void recMTC1(void) +{ + if( GPR_IS_CONST1(_Rt_) ) + { + _deleteFPtoXMMreg(_Fs_, 0); + MOV32ItoM((uptr)&fpuRegs.fpr[ _Fs_ ].UL, g_cpuConstRegs[_Rt_].UL[0]); + } + else + { + int mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ); + + if( mmreg >= 0 ) + { + if( g_pCurInstInfo->regs[_Rt_] & EEINST_LASTUSE ) + { + // transfer the reg directly + _deleteGPRtoXMMreg(_Rt_, 2); + _deleteFPtoXMMreg(_Fs_, 2); + _allocFPtoXMMreg(mmreg, _Fs_, MODE_WRITE); + } + else + { + int mmreg2 = _allocCheckFPUtoXMM(g_pCurInstInfo, _Fs_, MODE_WRITE); + + if( mmreg2 >= 0 ) + SSE_MOVSS_XMM_to_XMM(mmreg2, mmreg); + else + SSE_MOVSS_XMM_to_M32((uptr)&fpuRegs.fpr[ _Fs_ ].UL, mmreg); + } + } + else + { + int mmreg2; + + mmreg = _checkMMXreg(MMX_GPR+_Rt_, MODE_READ); + mmreg2 = _allocCheckFPUtoXMM(g_pCurInstInfo, _Fs_, MODE_WRITE); + + if( mmreg >= 0 ) + { + if( mmreg2 >= 0 ) + { + SetMMXstate(); + SSE2_MOVQ2DQ_MM_to_XMM(mmreg2, mmreg); + } + else + { + SetMMXstate(); + MOVDMMXtoM((uptr)&fpuRegs.fpr[ _Fs_ ].UL, mmreg); + } + } + else + { + if( mmreg2 >= 0 ) + { + SSE_MOVSS_M32_to_XMM(mmreg2, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + } + else + { + MOV32MtoR(EAX, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + MOV32RtoM((uptr)&fpuRegs.fpr[ _Fs_ ].UL, EAX); + } + } + } + } +} +//------------------------------------------------------------------ + + +#ifndef FPU_RECOMPILE // If FPU_RECOMPILE is not defined, then use the interpreter opcodes. (CFC1, CTC1, MFC1, and MTC1 are special because they work specifically with the EE rec so they're defined above) + +REC_FPUFUNC(ABS_S); +REC_FPUFUNC(ADD_S); +REC_FPUFUNC(ADDA_S); +REC_FPUBRANCH(BC1F); +REC_FPUBRANCH(BC1T); +REC_FPUBRANCH(BC1FL); +REC_FPUBRANCH(BC1TL); +REC_FPUFUNC(C_EQ); +REC_FPUFUNC(C_F); +REC_FPUFUNC(C_LE); +REC_FPUFUNC(C_LT); +REC_FPUFUNC(CVT_S); +REC_FPUFUNC(CVT_W); +REC_FPUFUNC(DIV_S); +REC_FPUFUNC(MAX_S); +REC_FPUFUNC(MIN_S); +REC_FPUFUNC(MADD_S); +REC_FPUFUNC(MADDA_S); +REC_FPUFUNC(MOV_S); +REC_FPUFUNC(MSUB_S); +REC_FPUFUNC(MSUBA_S); +REC_FPUFUNC(MUL_S); +REC_FPUFUNC(MULA_S); +REC_FPUFUNC(NEG_S); +REC_FPUFUNC(SUB_S); +REC_FPUFUNC(SUBA_S); +REC_FPUFUNC(SQRT_S); +REC_FPUFUNC(RSQRT_S); + +#else // FPU_RECOMPILE + +//------------------------------------------------------------------ +// Clamp Functions (Converts NaN's and Infinities to Normal Numbers) +//------------------------------------------------------------------ +void fpuFloat(int regd) { // +/-NaN -> +fMax, +Inf -> +fMax, -Inf -> -fMax + if (CHECK_FPU_OVERFLOW && !CHECK_FPUCLAMPHACK) { // Tekken 5 doesn't like clamping infinities. + SSE_MINSS_M32_to_XMM(regd, (uptr)&g_maxvals[0]); // MIN() must be before MAX()! So that NaN's become +Maximum + SSE_MAXSS_M32_to_XMM(regd, (uptr)&g_minvals[0]); + } +} + +PCSX2_ALIGNED16(u64 FPU_FLOAT_TEMP[2]); +void fpuFloat2(int regd) { // +NaN -> +fMax, -NaN -> -fMax, +Inf -> +fMax, -Inf -> -fMax + if (CHECK_FPU_OVERFLOW && !CHECK_FPUCLAMPHACK) { // Tekken 5 doesn't like clamping infinities. + int t1reg = _allocTempXMMreg(XMMT_FPS, -1); + if (t1reg >= 0) { + SSE_MOVSS_XMM_to_XMM(t1reg, regd); + SSE_ANDPS_M128_to_XMM(t1reg, (uptr)&s_neg[0]); + SSE_MINSS_M32_to_XMM(regd, (uptr)&g_maxvals[0]); + SSE_MAXSS_M32_to_XMM(regd, (uptr)&g_minvals[0]); + SSE_ORPS_XMM_to_XMM(regd, t1reg); + _freeXMMreg(t1reg); + } + else { + Console::Error("fpuFloat2() allocation error"); + t1reg = (regd == 0) ? 1 : 0; // get a temp reg thats not regd + SSE_MOVAPS_XMM_to_M128( (uptr)&FPU_FLOAT_TEMP[0], t1reg ); // backup data in t1reg to a temp address + SSE_MOVSS_XMM_to_XMM(t1reg, regd); + SSE_ANDPS_M128_to_XMM(t1reg, (uptr)&s_neg[0]); + SSE_MINSS_M32_to_XMM(regd, (uptr)&g_maxvals[0]); + SSE_MAXSS_M32_to_XMM(regd, (uptr)&g_minvals[0]); + SSE_ORPS_XMM_to_XMM(regd, t1reg); + SSE_MOVAPS_M128_to_XMM( t1reg, (uptr)&FPU_FLOAT_TEMP[0] ); // restore t1reg data + } + } +} + +void ClampValues(int regd) { + fpuFloat(regd); +} + +void ClampValues2(int regd) { + if (CHECK_FPUCLAMPHACK) { // Fixes Tekken 5 ( Makes NaN equal 0, infinities stay the same ) + int t5reg = _allocTempXMMreg(XMMT_FPS, -1); + + SSE_XORPS_XMM_to_XMM(t5reg, t5reg); + SSE_CMPORDSS_XMM_to_XMM(t5reg, regd); + + SSE_ANDPS_XMM_to_XMM(regd, t5reg); + + /* --- Its odd but tekken dosn't like Infinities to be clamped. --- */ + //SSE_MINSS_M32_to_XMM(regd, (uptr)&g_maxvals[0]); + //SSE_MAXSS_M32_to_XMM(regd, (uptr)&g_minvals[0]); + + _freeXMMreg(t5reg); + } + else fpuFloat(regd); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ABS XMM +//------------------------------------------------------------------ +void recABS_S_xmm(int info) +{ + if( info & PROCESS_EE_S ) SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + else SSE_MOVSS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); + + SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)&s_pos[0]); + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + + if (CHECK_FPU_OVERFLOW) // Only need to do positive clamp, since EEREC_D is positive + SSE_MINSS_M32_to_XMM(EEREC_D, (uptr)&g_maxvals[0]); +} + +FPURECOMPILE_CONSTCODE(ABS_S, XMMINFO_WRITED|XMMINFO_READS); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FPU_ADD_SUB (Used to mimic PS2's FPU add/sub behavior) +//------------------------------------------------------------------ +// Compliant IEEE FPU uses, in computations, uses additional "guard" bits to the right of the mantissa +// but EE-FPU doesn't. Substraction (and addition of positive and negative) may shift the mantissa left, +// causing those bits to appear in the result; this function masks out the bits of the mantissa that will +// get shifted right to the guard bits to ensure that the guard bits are empty. +// The difference of the exponents = the amount that the smaller operand will be shifted right by. +// Modification - the PS2 uses a single guard bit? (Coded by Nneeve) +//------------------------------------------------------------------ +void FPU_ADD_SUB(int regd, int regt, int issub) +{ + int tempecx = _allocX86reg(ECX, X86TYPE_TEMP, 0, 0); //receives regd + int temp2 = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); //receives regt + int xmmtemp = _allocTempXMMreg(XMMT_FPS, -1); //temporary for anding with regd/regt + + if (tempecx != ECX) { Console::Error("FPU: ADD/SUB Allocation Error!"); tempecx = ECX;} + if (temp2 == -1) { Console::Error("FPU: ADD/SUB Allocation Error!"); temp2 = EAX;} + if (xmmtemp == -1) { Console::Error("FPU: ADD/SUB Allocation Error!"); xmmtemp = XMM0;} + + SSE2_MOVD_XMM_to_R(tempecx, regd); + SSE2_MOVD_XMM_to_R(temp2, regt); + + //mask the exponents + SHR32ItoR(tempecx, 23); + SHR32ItoR(temp2, 23); + AND32ItoR(tempecx, 0xff); + AND32ItoR(temp2, 0xff); + + SUB32RtoR(tempecx, temp2); //tempecx = exponent difference + CMP32ItoR(tempecx, 25); + j8Ptr[0] = JGE8(0); + CMP32ItoR(tempecx, 0); + j8Ptr[1] = JG8(0); + j8Ptr[2] = JE8(0); + CMP32ItoR(tempecx, -25); + j8Ptr[3] = JLE8(0); + + //diff = -24 .. -1 , expd < expt + NEG32R(tempecx); + DEC32R(tempecx); + MOV32ItoR(temp2, 0xffffffff); + SHL32CLtoR(temp2); //temp2 = 0xffffffff << tempecx + SSE2_MOVD_R_to_XMM(xmmtemp, temp2); + SSE_ANDPS_XMM_to_XMM(regd, xmmtemp); + if (issub) + SSE_SUBSS_XMM_to_XMM(regd, regt); + else + SSE_ADDSS_XMM_to_XMM(regd, regt); + j8Ptr[4] = JMP8(0); + + x86SetJ8(j8Ptr[0]); + //diff = 25 .. 255 , expt < expd + SSE_MOVAPS_XMM_to_XMM(xmmtemp, regt); + SSE_ANDPS_M128_to_XMM(xmmtemp, (uptr)s_neg); + if (issub) + SSE_SUBSS_XMM_to_XMM(regd, xmmtemp); + else + SSE_ADDSS_XMM_to_XMM(regd, xmmtemp); + j8Ptr[5] = JMP8(0); + + x86SetJ8(j8Ptr[1]); + //diff = 1 .. 24, expt < expd + DEC32R(tempecx); + MOV32ItoR(temp2, 0xffffffff); + SHL32CLtoR(temp2); //temp2 = 0xffffffff << tempecx + SSE2_MOVD_R_to_XMM(xmmtemp, temp2); + SSE_ANDPS_XMM_to_XMM(xmmtemp, regt); + if (issub) + SSE_SUBSS_XMM_to_XMM(regd, xmmtemp); + else + SSE_ADDSS_XMM_to_XMM(regd, xmmtemp); + j8Ptr[6] = JMP8(0); + + x86SetJ8(j8Ptr[3]); + //diff = -255 .. -25, expd < expt + SSE_ANDPS_M128_to_XMM(regd, (uptr)s_neg); + if (issub) + SSE_SUBSS_XMM_to_XMM(regd, regt); + else + SSE_ADDSS_XMM_to_XMM(regd, regt); + j8Ptr[7] = JMP8(0); + + x86SetJ8(j8Ptr[2]); + //diff == 0 + if (issub) + SSE_SUBSS_XMM_to_XMM(regd, regt); + else + SSE_ADDSS_XMM_to_XMM(regd, regt); + + x86SetJ8(j8Ptr[4]); + x86SetJ8(j8Ptr[5]); + x86SetJ8(j8Ptr[6]); + x86SetJ8(j8Ptr[7]); + + _freeXMMreg(xmmtemp); + _freeX86reg(temp2); + _freeX86reg(tempecx); +} + +void FPU_ADD(int regd, int regt) { + if (FPU_ADD_SUB_HACK) FPU_ADD_SUB(regd, regt, 0); + else SSE_ADDSS_XMM_to_XMM(regd, regt); +} + +void FPU_SUB(int regd, int regt) { + if (FPU_ADD_SUB_HACK) FPU_ADD_SUB(regd, regt, 1); + else SSE_SUBSS_XMM_to_XMM(regd, regt); +} + + +//------------------------------------------------------------------ +// CommutativeOp XMM (used for ADD, MUL, MAX, and MIN opcodes) +//------------------------------------------------------------------ +static void (*recComOpXMM_to_XMM[] )(x86SSERegType, x86SSERegType) = { + FPU_ADD, SSE_MULSS_XMM_to_XMM, SSE_MAXSS_XMM_to_XMM, SSE_MINSS_XMM_to_XMM }; + +//static void (*recComOpM32_to_XMM[] )(x86SSERegType, uptr) = { +// SSE_ADDSS_M32_to_XMM, SSE_MULSS_M32_to_XMM, SSE_MAXSS_M32_to_XMM, SSE_MINSS_M32_to_XMM }; + +int recCommutativeOp(int info, int regd, int op) +{ + int t0reg = _allocTempXMMreg(XMMT_FPS, -1); + //if (t0reg == -1) {SysPrintf("FPU: CommutativeOp Allocation Error!\n");} + + switch(info & (PROCESS_EE_S|PROCESS_EE_T) ) { + case PROCESS_EE_S: + if (regd == EEREC_S) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW /*&& !CHECK_FPUCLAMPHACK */ || (op >= 2)) { fpuFloat2(regd); fpuFloat2(t0reg); } + recComOpXMM_to_XMM[op](regd, t0reg); + } + else { + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW || (op >= 2)) { fpuFloat2(regd); fpuFloat2(EEREC_S); } + recComOpXMM_to_XMM[op](regd, EEREC_S); + } + break; + case PROCESS_EE_T: + if (regd == EEREC_T) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_OVERFLOW || (op >= 2)) { fpuFloat2(regd); fpuFloat2(t0reg); } + recComOpXMM_to_XMM[op](regd, t0reg); + } + else { + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_OVERFLOW || (op >= 2)) { fpuFloat2(regd); fpuFloat2(EEREC_T); } + recComOpXMM_to_XMM[op](regd, EEREC_T); + } + break; + case (PROCESS_EE_S|PROCESS_EE_T): + if (regd == EEREC_T) { + if (CHECK_FPU_EXTRA_OVERFLOW || (op >= 2)) { fpuFloat2(regd); fpuFloat2(EEREC_S); } + recComOpXMM_to_XMM[op](regd, EEREC_S); + } + else { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_FPU_EXTRA_OVERFLOW || (op >= 2)) { fpuFloat2(regd); fpuFloat2(EEREC_T); } + recComOpXMM_to_XMM[op](regd, EEREC_T); + } + break; + default: + Console::Status("FPU: recCommutativeOp case 4"); + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Fs_]); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW || (op >= 2)) { fpuFloat2(regd); fpuFloat2(t0reg); } + recComOpXMM_to_XMM[op](regd, t0reg); + break; + } + + _freeXMMreg(t0reg); + return regd; +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ADD XMM +//------------------------------------------------------------------ +void recADD_S_xmm(int info) +{ + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + ClampValues2(recCommutativeOp(info, EEREC_D, 0)); + //REC_FPUOP(ADD_S); +} + +FPURECOMPILE_CONSTCODE(ADD_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + +void recADDA_S_xmm(int info) +{ + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + ClampValues(recCommutativeOp(info, EEREC_ACC, 0)); +} + +FPURECOMPILE_CONSTCODE(ADDA_S, XMMINFO_WRITEACC|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + +//------------------------------------------------------------------ +// BC1x XMM +//------------------------------------------------------------------ + +static void _setupBranchTest() +{ + _eeFlushAllUnused(); + + // COP1 branch conditionals are based on the following equation: + // (fpuRegs.fprc[31] & 0x00800000) + // BC2F checks if the statement is false, BC2T checks if the statement is true. + + MOV32MtoR(EAX, (uptr)&fpuRegs.fprc[31]); + TEST32ItoR(EAX, FPUflagC); +} + +void recBC1F( void ) +{ + _setupBranchTest(); + recDoBranchImm(JNZ32(0)); +} + +void recBC1T( void ) +{ + _setupBranchTest(); + recDoBranchImm(JZ32(0)); +} + +void recBC1FL( void ) +{ + _setupBranchTest(); + recDoBranchImm_Likely(JNZ32(0)); +} + +void recBC1TL( void ) +{ + _setupBranchTest(); + recDoBranchImm_Likely(JZ32(0)); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// C.x.S XMM +//------------------------------------------------------------------ +void recC_EQ_xmm(int info) +{ + int tempReg; + int t0reg; + + //SysPrintf("recC_EQ_xmm()\n"); + + switch(info & (PROCESS_EE_S|PROCESS_EE_T) ) { + case PROCESS_EE_S: + SSE_MINSS_M32_to_XMM(EEREC_S, (uptr)&g_maxvals[0]); + t0reg = _allocTempXMMreg(XMMT_FPS, -1); + if (t0reg >= 0) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + SSE_MINSS_M32_to_XMM(t0reg, (uptr)&g_maxvals[0]); + SSE_UCOMISS_XMM_to_XMM(EEREC_S, t0reg); + _freeXMMreg(t0reg); + } + else SSE_UCOMISS_M32_to_XMM(EEREC_S, (uptr)&fpuRegs.fpr[_Ft_]); + break; + case PROCESS_EE_T: + SSE_MINSS_M32_to_XMM(EEREC_T, (uptr)&g_maxvals[0]); + t0reg = _allocTempXMMreg(XMMT_FPS, -1); + if (t0reg >= 0) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Fs_]); + SSE_MINSS_M32_to_XMM(t0reg, (uptr)&g_maxvals[0]); + SSE_UCOMISS_XMM_to_XMM(t0reg, EEREC_T); + _freeXMMreg(t0reg); + } + else SSE_UCOMISS_M32_to_XMM(EEREC_T, (uptr)&fpuRegs.fpr[_Fs_]); + break; + case (PROCESS_EE_S|PROCESS_EE_T): + SSE_MINSS_M32_to_XMM(EEREC_S, (uptr)&g_maxvals[0]); + SSE_MINSS_M32_to_XMM(EEREC_T, (uptr)&g_maxvals[0]); + SSE_UCOMISS_XMM_to_XMM(EEREC_S, EEREC_T); + break; + default: + Console::Status("recC_EQ_xmm: Default"); + tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); + if (tempReg < 0) {Console::Error("FPU: DIV Allocation Error!"); tempReg = EAX;} + MOV32MtoR(tempReg, (uptr)&fpuRegs.fpr[_Fs_]); + CMP32MtoR(tempReg, (uptr)&fpuRegs.fpr[_Ft_]); + + j8Ptr[0] = JZ8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); + + if (tempReg >= 0) _freeX86reg(tempReg); + return; + } + + j8Ptr[0] = JZ8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); +} + +FPURECOMPILE_CONSTCODE(C_EQ, XMMINFO_READS|XMMINFO_READT); +//REC_FPUFUNC(C_EQ); + +void recC_F() +{ + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); +} +//REC_FPUFUNC(C_F); + +void recC_LE_xmm(int info ) +{ + int tempReg; //tempX86reg + int t0reg; //tempXMMreg + + //SysPrintf("recC_LE_xmm()\n"); + + switch(info & (PROCESS_EE_S|PROCESS_EE_T) ) { + case PROCESS_EE_S: + SSE_MINSS_M32_to_XMM(EEREC_S, (uptr)&g_maxvals[0]); + t0reg = _allocTempXMMreg(XMMT_FPS, -1); + if (t0reg >= 0) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + SSE_MINSS_M32_to_XMM(t0reg, (uptr)&g_maxvals[0]); + SSE_UCOMISS_XMM_to_XMM(EEREC_S, t0reg); + _freeXMMreg(t0reg); + } + else SSE_UCOMISS_M32_to_XMM(EEREC_S, (uptr)&fpuRegs.fpr[_Ft_]); + break; + case PROCESS_EE_T: + SSE_MINSS_M32_to_XMM(EEREC_T, (uptr)&g_maxvals[0]); + t0reg = _allocTempXMMreg(XMMT_FPS, -1); + if (t0reg >= 0) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Fs_]); + SSE_MINSS_M32_to_XMM(t0reg, (uptr)&g_maxvals[0]); + SSE_UCOMISS_XMM_to_XMM(t0reg, EEREC_T); + _freeXMMreg(t0reg); + } + else { + SSE_UCOMISS_M32_to_XMM(EEREC_T, (uptr)&fpuRegs.fpr[_Fs_]); + + j8Ptr[0] = JAE8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); + return; + } + break; + case (PROCESS_EE_S|PROCESS_EE_T): + SSE_MINSS_M32_to_XMM(EEREC_S, (uptr)&g_maxvals[0]); + SSE_MINSS_M32_to_XMM(EEREC_T, (uptr)&g_maxvals[0]); + SSE_UCOMISS_XMM_to_XMM(EEREC_S, EEREC_T); + break; + default: // Untested and incorrect, but this case is never reached AFAIK (cottonvibes) + Console::Status("recC_LE_xmm: Default"); + tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); + if (tempReg < 0) {Console::Error("FPU: DIV Allocation Error!"); tempReg = EAX;} + MOV32MtoR(tempReg, (uptr)&fpuRegs.fpr[_Fs_]); + CMP32MtoR(tempReg, (uptr)&fpuRegs.fpr[_Ft_]); + + j8Ptr[0] = JLE8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); + + if (tempReg >= 0) _freeX86reg(tempReg); + return; + } + + j8Ptr[0] = JBE8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); +} + +FPURECOMPILE_CONSTCODE(C_LE, XMMINFO_READS|XMMINFO_READT); +//REC_FPUFUNC(C_LE); + +void recC_LT_xmm(int info) +{ + int tempReg; + int t0reg; + + //SysPrintf("recC_LT_xmm()\n"); + + switch(info & (PROCESS_EE_S|PROCESS_EE_T) ) { + case PROCESS_EE_S: + SSE_MINSS_M32_to_XMM(EEREC_S, (uptr)&g_maxvals[0]); + t0reg = _allocTempXMMreg(XMMT_FPS, -1); + if (t0reg >= 0) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + SSE_MINSS_M32_to_XMM(t0reg, (uptr)&g_maxvals[0]); + SSE_UCOMISS_XMM_to_XMM(EEREC_S, t0reg); + _freeXMMreg(t0reg); + } + else SSE_UCOMISS_M32_to_XMM(EEREC_S, (uptr)&fpuRegs.fpr[_Ft_]); + break; + case PROCESS_EE_T: + SSE_MINSS_M32_to_XMM(EEREC_T, (uptr)&g_maxvals[0]); + t0reg = _allocTempXMMreg(XMMT_FPS, -1); + if (t0reg >= 0) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Fs_]); + SSE_MINSS_M32_to_XMM(t0reg, (uptr)&g_maxvals[0]); + SSE_UCOMISS_XMM_to_XMM(t0reg, EEREC_T); + _freeXMMreg(t0reg); + } + else { + SSE_UCOMISS_M32_to_XMM(EEREC_T, (uptr)&fpuRegs.fpr[_Fs_]); + + j8Ptr[0] = JA8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); + return; + } + break; + case (PROCESS_EE_S|PROCESS_EE_T): + // Makes NaNs and +Infinity be +maximum; -Infinity stays + // the same, but this is okay for a Compare operation. + // Note: This fixes a crash in Rule of Rose. + SSE_MINSS_M32_to_XMM(EEREC_S, (uptr)&g_maxvals[0]); + SSE_MINSS_M32_to_XMM(EEREC_T, (uptr)&g_maxvals[0]); + SSE_UCOMISS_XMM_to_XMM(EEREC_S, EEREC_T); + break; + default: + Console::Status("recC_LT_xmm: Default"); + tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); + if (tempReg < 0) {Console::Error("FPU: DIV Allocation Error!"); tempReg = EAX;} + MOV32MtoR(tempReg, (uptr)&fpuRegs.fpr[_Fs_]); + CMP32MtoR(tempReg, (uptr)&fpuRegs.fpr[_Ft_]); + + j8Ptr[0] = JL8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); + + if (tempReg >= 0) _freeX86reg(tempReg); + return; + } + + j8Ptr[0] = JB8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); +} + +FPURECOMPILE_CONSTCODE(C_LT, XMMINFO_READS|XMMINFO_READT); +//REC_FPUFUNC(C_LT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// CVT.x XMM +//------------------------------------------------------------------ +void recCVT_S_xmm(int info) +{ + if( !(info&PROCESS_EE_S) || (EEREC_D != EEREC_S && !(info&PROCESS_EE_MODEWRITES)) ) { + SSE_CVTSI2SS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); + } + else { + SSE2_CVTDQ2PS_XMM_to_XMM(EEREC_D, EEREC_S); + } +} + +FPURECOMPILE_CONSTCODE(CVT_S, XMMINFO_WRITED|XMMINFO_READS); + +void recCVT_W() +{ + int regs = _checkXMMreg(XMMTYPE_FPREG, _Fs_, MODE_READ); + + if( regs >= 0 ) + { + if (CHECK_FPU_EXTRA_OVERFLOW) fpuFloat2(regs); + SSE_CVTTSS2SI_XMM_to_R32(EAX, regs); + SSE_MOVMSKPS_XMM_to_R32(EDX, regs); //extract the signs + AND32ItoR(EDX,1); //keep only LSB + } + else + { + SSE_CVTTSS2SI_M32_to_R32(EAX, (uptr)&fpuRegs.fpr[ _Fs_ ]); + MOV32MtoR(EDX, (uptr)&fpuRegs.fpr[ _Fs_ ]); + SHR32ItoR(EDX, 31); //mov sign to lsb + } + + //kill register allocation for dst because we write directly to fpuRegs.fpr[_Fd_] + _deleteFPtoXMMreg(_Fd_, 2); + + ADD32ItoR(EDX, 0x7FFFFFFF); //0x7FFFFFFF if positive, 0x8000 0000 if negative + + CMP32ItoR(EAX, 0x80000000); //If the result is indefinitive + CMOVE32RtoR(EAX, EDX); //Saturate it + + //Write the result + MOV32RtoM((uptr)&fpuRegs.fpr[_Fd_], EAX); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// DIV XMM +//------------------------------------------------------------------ +void recDIVhelper1(int regd, int regt) // Sets flags +{ + u8 *pjmp1, *pjmp2; + u32 *ajmp32, *bjmp32; + int t1reg = _allocTempXMMreg(XMMT_FPS, -1); + int tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); + //if (t1reg == -1) {Console::Error("FPU: DIV Allocation Error!");} + if (tempReg == -1) {Console::Error("FPU: DIV Allocation Error!"); tempReg = EAX;} + + AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagI|FPUflagD)); // Clear I and D flags + + /*--- Check for divide by zero ---*/ + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); + SSE_CMPEQSS_XMM_to_XMM(t1reg, regt); + SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); + AND32ItoR(tempReg, 1); //Check sign (if regt == zero, sign will be set) + ajmp32 = JZ32(0); //Skip if not set + + /*--- Check for 0/0 ---*/ + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); + SSE_CMPEQSS_XMM_to_XMM(t1reg, regd); + SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); + AND32ItoR(tempReg, 1); //Check sign (if regd == zero, sign will be set) + pjmp1 = JZ8(0); //Skip if not set + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags ( 0/0 ) + pjmp2 = JMP8(0); + x86SetJ8(pjmp1); //x/0 but not 0/0 + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagD|FPUflagSD); // Set D and SD flags ( x/0 ) + x86SetJ8(pjmp2); + + /*--- Make regd +/- Maximum ---*/ + SSE_XORPS_XMM_to_XMM(regd, regt); // Make regd Positive or Negative + SSE_ANDPS_M128_to_XMM(regd, (uptr)&s_neg[0]); // Get the sign bit + SSE_ORPS_M128_to_XMM(regd, (uptr)&g_maxvals[0]); // regd = +/- Maximum + //SSE_MOVSS_M32_to_XMM(regd, (uptr)&g_maxvals[0]); + bjmp32 = JMP32(0); + + x86SetJ32(ajmp32); + + /*--- Normal Divide ---*/ + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(regt); } + SSE_DIVSS_XMM_to_XMM(regd, regt); + + ClampValues(regd); + x86SetJ32(bjmp32); + + _freeXMMreg(t1reg); + _freeX86reg(tempReg); +} + +void recDIVhelper2(int regd, int regt) // Doesn't sets flags +{ + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(regt); } + SSE_DIVSS_XMM_to_XMM(regd, regt); + ClampValues(regd); +} + +void recDIV_S_xmm(int info) +{ + static u32 PCSX2_ALIGNED16(roundmode_temp[4]) = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + int roundmodeFlag = 0; + int t0reg = _allocTempXMMreg(XMMT_FPS, -1); + //if (t0reg == -1) {Console::Error("FPU: DIV Allocation Error!");} + //SysPrintf("DIV\n"); + + if ((g_sseMXCSR & 0x00006000) != 0x00000000) { // Set roundmode to nearest if it isn't already + //SysPrintf("div to nearest\n"); + roundmode_temp[0] = (g_sseMXCSR & 0xFFFF9FFF); // Set new roundmode + roundmode_temp[1] = g_sseMXCSR; // Backup old Roundmode + SSE_LDMXCSR ((uptr)&roundmode_temp[0]); // Recompile Roundmode Change + roundmodeFlag = 1; + } + + switch(info & (PROCESS_EE_S|PROCESS_EE_T) ) { + case PROCESS_EE_S: + //SysPrintf("FPU: DIV case 1\n"); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_FLAGS) recDIVhelper1(EEREC_D, t0reg); + else recDIVhelper2(EEREC_D, t0reg); + break; + case PROCESS_EE_T: + //SysPrintf("FPU: DIV case 2\n"); + if (EEREC_D == EEREC_T) { + SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_T); + SSE_MOVSS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_FLAGS) recDIVhelper1(EEREC_D, t0reg); + else recDIVhelper2(EEREC_D, t0reg); + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_FLAGS) recDIVhelper1(EEREC_D, EEREC_T); + else recDIVhelper2(EEREC_D, EEREC_T); + } + break; + case (PROCESS_EE_S|PROCESS_EE_T): + //SysPrintf("FPU: DIV case 3\n"); + if (EEREC_D == EEREC_T) { + SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_T); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + if (CHECK_FPU_EXTRA_FLAGS) recDIVhelper1(EEREC_D, t0reg); + else recDIVhelper2(EEREC_D, t0reg); + } + else { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + if (CHECK_FPU_EXTRA_FLAGS) recDIVhelper1(EEREC_D, EEREC_T); + else recDIVhelper2(EEREC_D, EEREC_T); + } + break; + default: + //SysPrintf("FPU: DIV case 4\n"); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + SSE_MOVSS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_FLAGS) recDIVhelper1(EEREC_D, t0reg); + else recDIVhelper2(EEREC_D, t0reg); + break; + } + if (roundmodeFlag == 1) { // Set roundmode back if it was changed + SSE_LDMXCSR ((uptr)&roundmode_temp[1]); + } + _freeXMMreg(t0reg); +} + +FPURECOMPILE_CONSTCODE(DIV_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MADD XMM +//------------------------------------------------------------------ +void recMADDtemp(int info, int regd) +{ + int t1reg; + int t0reg = _allocTempXMMreg(XMMT_FPS, -1); + + switch(info & (PROCESS_EE_S|PROCESS_EE_T) ) { + case PROCESS_EE_S: + if(regd == EEREC_S) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(t0reg); } + SSE_MULSS_XMM_to_XMM(regd, t0reg); + if (info & PROCESS_EE_ACC) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(EEREC_ACC); fpuFloat(regd); } + FPU_ADD(regd, EEREC_ACC); + } + else { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(EEREC_ACC); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + } + else if (regd == EEREC_ACC){ + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(EEREC_S); fpuFloat2(t0reg); } + SSE_MULSS_XMM_to_XMM(t0reg, EEREC_S); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + else { + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(EEREC_S); } + SSE_MULSS_XMM_to_XMM(regd, EEREC_S); + if (info & PROCESS_EE_ACC) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(EEREC_ACC); fpuFloat(regd); } + FPU_ADD(regd, EEREC_ACC); + } + else { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(EEREC_ACC); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + } + break; + case PROCESS_EE_T: + if(regd == EEREC_T) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(t0reg); } + SSE_MULSS_XMM_to_XMM(regd, t0reg); + if (info & PROCESS_EE_ACC) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(EEREC_ACC); fpuFloat(regd); } + FPU_ADD(regd, EEREC_ACC); + } + else { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(EEREC_ACC); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + } + else if (regd == EEREC_ACC){ + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(EEREC_T); fpuFloat2(t0reg); } + SSE_MULSS_XMM_to_XMM(t0reg, EEREC_T); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + else { + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(EEREC_T); } + SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + if (info & PROCESS_EE_ACC) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(EEREC_ACC); fpuFloat(regd); } + FPU_ADD(regd, EEREC_ACC); + } + else { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(EEREC_ACC); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + } + break; + case (PROCESS_EE_S|PROCESS_EE_T): + if(regd == EEREC_S) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(EEREC_T); } + SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + if (info & PROCESS_EE_ACC) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(EEREC_ACC); } + FPU_ADD(regd, EEREC_ACC); + } + else { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + } + else if(regd == EEREC_T) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(EEREC_S); } + SSE_MULSS_XMM_to_XMM(regd, EEREC_S); + if (info & PROCESS_EE_ACC) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(EEREC_ACC); } + FPU_ADD(regd, EEREC_ACC); + } + else { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + } + else if(regd == EEREC_ACC) { + SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_S); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(t0reg); fpuFloat2(EEREC_T); } + SSE_MULSS_XMM_to_XMM(t0reg, EEREC_T); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + else { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(EEREC_T); } + SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + if (info & PROCESS_EE_ACC) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(EEREC_ACC); } + FPU_ADD(regd, EEREC_ACC); + } + else { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + } + break; + default: + if(regd == EEREC_ACC){ + t1reg = _allocTempXMMreg(XMMT_FPS, -1); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Fs_]); + SSE_MOVSS_M32_to_XMM(t1reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(t0reg); fpuFloat2(t1reg); } + SSE_MULSS_XMM_to_XMM(t0reg, t1reg); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + _freeXMMreg(t1reg); + } + else + { + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Fs_]); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(t0reg); } + SSE_MULSS_XMM_to_XMM(regd, t0reg); + if (info & PROCESS_EE_ACC) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(EEREC_ACC); } + FPU_ADD(regd, EEREC_ACC); + } + else { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_ADD(regd, t0reg); + } + } + break; + } + + ClampValues(regd); + _freeXMMreg(t0reg); +} + +void recMADD_S_xmm(int info) +{ + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + recMADDtemp(info, EEREC_D); +} + +FPURECOMPILE_CONSTCODE(MADD_S, XMMINFO_WRITED|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); + +void recMADDA_S_xmm(int info) +{ + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + recMADDtemp(info, EEREC_ACC); +} + +FPURECOMPILE_CONSTCODE(MADDA_S, XMMINFO_WRITEACC|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MAX / MIN XMM +//------------------------------------------------------------------ +void recMAX_S_xmm(int info) +{ + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + recCommutativeOp(info, EEREC_D, 2); +} + +FPURECOMPILE_CONSTCODE(MAX_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + +void recMIN_S_xmm(int info) +{ + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + recCommutativeOp(info, EEREC_D, 3); +} + +FPURECOMPILE_CONSTCODE(MIN_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MOV XMM +//------------------------------------------------------------------ +void recMOV_S_xmm(int info) +{ + if( info & PROCESS_EE_S ) SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + else SSE_MOVSS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); +} + +FPURECOMPILE_CONSTCODE(MOV_S, XMMINFO_WRITED|XMMINFO_READS); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MSUB XMM +//------------------------------------------------------------------ +void recMSUBtemp(int info, int regd) +{ +int t1reg; + int t0reg = _allocTempXMMreg(XMMT_FPS, -1); + + switch(info & (PROCESS_EE_S|PROCESS_EE_T) ) { + case PROCESS_EE_S: + if(regd == EEREC_S) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(t0reg); } + SSE_MULSS_XMM_to_XMM(regd, t0reg); + if (info & PROCESS_EE_ACC) { SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_ACC); } + else { SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); } + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(t0reg, regd); + SSE_MOVSS_XMM_to_XMM(regd, t0reg); + } + else if (regd == EEREC_ACC){ + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(EEREC_S); fpuFloat2(t0reg); } + SSE_MULSS_XMM_to_XMM(t0reg, EEREC_S); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(regd, t0reg); + } + else { + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(EEREC_S); } + SSE_MULSS_XMM_to_XMM(regd, EEREC_S); + if (info & PROCESS_EE_ACC) { SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_ACC); } + else { SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); } + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(t0reg, regd); + SSE_MOVSS_XMM_to_XMM(regd, t0reg); + } + break; + case PROCESS_EE_T: + if(regd == EEREC_T) { + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(t0reg); } + SSE_MULSS_XMM_to_XMM(regd, t0reg); + if (info & PROCESS_EE_ACC) { SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_ACC); } + else { SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); } + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(t0reg, regd); + SSE_MOVSS_XMM_to_XMM(regd, t0reg); + } + else if (regd == EEREC_ACC){ + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(EEREC_T); fpuFloat2(t0reg); } + SSE_MULSS_XMM_to_XMM(t0reg, EEREC_T); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(regd, t0reg); + } + else { + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(EEREC_T); } + SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + if (info & PROCESS_EE_ACC) { SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_ACC); } + else { SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); } + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(t0reg, regd); + SSE_MOVSS_XMM_to_XMM(regd, t0reg); + } + break; + case (PROCESS_EE_S|PROCESS_EE_T): + if(regd == EEREC_S) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(EEREC_T); } + SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + if (info & PROCESS_EE_ACC) { SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_ACC); } + else { SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); } + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(t0reg, regd); + SSE_MOVSS_XMM_to_XMM(regd, t0reg); + } + else if(regd == EEREC_T) { + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(EEREC_S); } + SSE_MULSS_XMM_to_XMM(regd, EEREC_S); + if (info & PROCESS_EE_ACC) { SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_ACC); } + else { SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); } + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(t0reg, regd); + SSE_MOVSS_XMM_to_XMM(regd, t0reg); + } + else if(regd == EEREC_ACC) { + SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_S); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(t0reg); fpuFloat2(EEREC_T); } + SSE_MULSS_XMM_to_XMM(t0reg, EEREC_T); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(regd, t0reg); + } + else { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(EEREC_T); } + SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + if (info & PROCESS_EE_ACC) { SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_ACC); } + else { SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); } + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(t0reg, regd); + SSE_MOVSS_XMM_to_XMM(regd, t0reg); + } + break; + default: + if(regd == EEREC_ACC){ + t1reg = _allocTempXMMreg(XMMT_FPS, -1); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Fs_]); + SSE_MOVSS_M32_to_XMM(t1reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(t0reg); fpuFloat2(t1reg); } + SSE_MULSS_XMM_to_XMM(t0reg, t1reg); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(regd, t0reg); + _freeXMMreg(t1reg); + } + else + { + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Fs_]); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat2(regd); fpuFloat2(t0reg); } + SSE_MULSS_XMM_to_XMM(regd, t0reg); + if (info & PROCESS_EE_ACC) { SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_ACC); } + else { SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.ACC); } + if (CHECK_FPU_EXTRA_OVERFLOW) { fpuFloat(regd); fpuFloat(t0reg); } + FPU_SUB(t0reg, regd); + SSE_MOVSS_XMM_to_XMM(regd, t0reg); + } + break; + } + + ClampValues(regd); + _freeXMMreg(t0reg); + +} + +void recMSUB_S_xmm(int info) +{ + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + recMSUBtemp(info, EEREC_D); +} + +FPURECOMPILE_CONSTCODE(MSUB_S, XMMINFO_WRITED|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); + +void recMSUBA_S_xmm(int info) +{ + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + recMSUBtemp(info, EEREC_ACC); +} + +FPURECOMPILE_CONSTCODE(MSUBA_S, XMMINFO_WRITEACC|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MUL XMM +//------------------------------------------------------------------ +void recMUL_S_xmm(int info) +{ + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + ClampValues(recCommutativeOp(info, EEREC_D, 1)); +} + +FPURECOMPILE_CONSTCODE(MUL_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + +void recMULA_S_xmm(int info) +{ + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + ClampValues(recCommutativeOp(info, EEREC_ACC, 1)); +} + +FPURECOMPILE_CONSTCODE(MULA_S, XMMINFO_WRITEACC|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// NEG XMM +//------------------------------------------------------------------ +void recNEG_S_xmm(int info) { + if( info & PROCESS_EE_S ) SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + else SSE_MOVSS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); + + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + SSE_XORPS_M128_to_XMM(EEREC_D, (uptr)&s_neg[0]); + ClampValues(EEREC_D); +} + +FPURECOMPILE_CONSTCODE(NEG_S, XMMINFO_WRITED|XMMINFO_READS); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// SUB XMM +//------------------------------------------------------------------ +void recSUBhelper(int regd, int regt) +{ + if (CHECK_FPU_EXTRA_OVERFLOW /*&& !CHECK_FPUCLAMPHACK*/) { fpuFloat2(regd); fpuFloat2(regt); } + FPU_SUB(regd, regt); +} + +void recSUBop(int info, int regd) +{ + int t0reg = _allocTempXMMreg(XMMT_FPS, -1); + //if (t0reg == -1) {SysPrintf("FPU: SUB Allocation Error!\n");} + + //AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO|FPUflagU)); // Clear O and U flags + + switch(info & (PROCESS_EE_S|PROCESS_EE_T) ) { + case PROCESS_EE_S: + //SysPrintf("FPU: SUB case 1\n"); + if (regd != EEREC_S) SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + recSUBhelper(regd, t0reg); + break; + case PROCESS_EE_T: + //SysPrintf("FPU: SUB case 2\n"); + if (regd == EEREC_T) { + SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_T); + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Fs_]); + recSUBhelper(regd, t0reg); + } + else { + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Fs_]); + recSUBhelper(regd, EEREC_T); + } + break; + case (PROCESS_EE_S|PROCESS_EE_T): + //SysPrintf("FPU: SUB case 3\n"); + if (regd == EEREC_T) { + SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_T); + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + recSUBhelper(regd, t0reg); + } + else { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + recSUBhelper(regd, EEREC_T); + } + break; + default: + Console::Notice("FPU: SUB case 4"); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + SSE_MOVSS_M32_to_XMM(regd, (uptr)&fpuRegs.fpr[_Fs_]); + recSUBhelper(regd, t0reg); + break; + } + + ClampValues2(regd); + _freeXMMreg(t0reg); +} + +void recSUB_S_xmm(int info) +{ + recSUBop(info, EEREC_D); +} + +FPURECOMPILE_CONSTCODE(SUB_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + + +void recSUBA_S_xmm(int info) +{ + recSUBop(info, EEREC_ACC); +} + +FPURECOMPILE_CONSTCODE(SUBA_S, XMMINFO_WRITEACC|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// SQRT XMM +//------------------------------------------------------------------ +void recSQRT_S_xmm(int info) +{ + u8* pjmp; + static u32 PCSX2_ALIGNED16(roundmode_temp[4]) = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + int roundmodeFlag = 0; + //SysPrintf("FPU: SQRT\n"); + + if ((g_sseMXCSR & 0x00006000) != 0x00000000) { // Set roundmode to nearest if it isn't already + //SysPrintf("sqrt to nearest\n"); + roundmode_temp[0] = (g_sseMXCSR & 0xFFFF9FFF); // Set new roundmode + roundmode_temp[1] = g_sseMXCSR; // Backup old Roundmode + SSE_LDMXCSR ((uptr)&roundmode_temp[0]); // Recompile Roundmode Change + roundmodeFlag = 1; + } + + if( info & PROCESS_EE_T ) SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_T); + else SSE_MOVSS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Ft_]); + + if (CHECK_FPU_EXTRA_FLAGS) { + int tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); + if (tempReg == -1) {Console::Error("FPU: SQRT Allocation Error!"); tempReg = EAX;} + + AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagI|FPUflagD)); // Clear I and D flags + + /*--- Check for negative SQRT ---*/ + SSE_MOVMSKPS_XMM_to_R32(tempReg, EEREC_D); + AND32ItoR(tempReg, 1); //Check sign + pjmp = JZ8(0); //Skip if none are + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags + SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)&s_pos[0]); // Make EEREC_D Positive + x86SetJ8(pjmp); + + _freeX86reg(tempReg); + } + else SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)&s_pos[0]); // Make EEREC_D Positive + + if (CHECK_FPU_OVERFLOW) SSE_MINSS_M32_to_XMM(EEREC_D, (uptr)&g_maxvals[0]);// Only need to do positive clamp, since EEREC_D is positive + SSE_SQRTSS_XMM_to_XMM(EEREC_D, EEREC_D); + if (CHECK_FPU_EXTRA_OVERFLOW) ClampValues(EEREC_D); // Shouldn't need to clamp again since SQRT of a number will always be smaller than the original number, doing it just incase :/ + + if (roundmodeFlag == 1) { // Set roundmode back if it was changed + SSE_LDMXCSR ((uptr)&roundmode_temp[1]); + } +} + +FPURECOMPILE_CONSTCODE(SQRT_S, XMMINFO_WRITED|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// RSQRT XMM +//------------------------------------------------------------------ +void recRSQRThelper1(int regd, int t0reg) // Preforms the RSQRT function when regd <- Fs and t0reg <- Ft (Sets correct flags) +{ + u8 *pjmp1, *pjmp2; + u32 *pjmp32; + int t1reg = _allocTempXMMreg(XMMT_FPS, -1); + int tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); + //if (t1reg == -1) {Console::Error("FPU: RSQRT Allocation Error!");} + if (tempReg == -1) {Console::Error("FPU: RSQRT Allocation Error!"); tempReg = EAX;} + + AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagI|FPUflagD)); // Clear I and D flags + + /*--- Check for zero ---*/ + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); + SSE_CMPEQSS_XMM_to_XMM(t1reg, t0reg); + SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); + AND32ItoR(tempReg, 1); //Check sign (if t0reg == zero, sign will be set) + pjmp1 = JZ8(0); //Skip if not set + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagD|FPUflagSD); // Set D and SD flags + SSE_XORPS_XMM_to_XMM(regd, t0reg); // Make regd Positive or Negative + SSE_ANDPS_M128_to_XMM(regd, (uptr)&s_neg[0]); // Get the sign bit + SSE_ORPS_M128_to_XMM(regd, (uptr)&g_maxvals[0]); // regd = +/- Maximum + pjmp32 = JMP32(0); + x86SetJ8(pjmp1); + + /*--- Check for negative SQRT ---*/ + SSE_MOVMSKPS_XMM_to_R32(tempReg, t0reg); + AND32ItoR(tempReg, 1); //Check sign + pjmp2 = JZ8(0); //Skip if not set + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags + SSE_ANDPS_M128_to_XMM(t0reg, (uptr)&s_pos[0]); // Make t0reg Positive + x86SetJ8(pjmp2); + + if (CHECK_FPU_EXTRA_OVERFLOW) { + SSE_MINSS_M32_to_XMM(t0reg, (uptr)&g_maxvals[0]); // Only need to do positive clamp, since t0reg is positive + fpuFloat2(regd); + } + + SSE_SQRTSS_XMM_to_XMM(t0reg, t0reg); + SSE_DIVSS_XMM_to_XMM(regd, t0reg); + + ClampValues(regd); + x86SetJ32(pjmp32); + + _freeXMMreg(t1reg); + _freeX86reg(tempReg); +} + +void recRSQRThelper2(int regd, int t0reg) // Preforms the RSQRT function when regd <- Fs and t0reg <- Ft (Doesn't set flags) +{ + SSE_ANDPS_M128_to_XMM(t0reg, (uptr)&s_pos[0]); // Make t0reg Positive + if (CHECK_FPU_EXTRA_OVERFLOW) { + SSE_MINSS_M32_to_XMM(t0reg, (uptr)&g_maxvals[0]); // Only need to do positive clamp, since t0reg is positive + fpuFloat2(regd); + } + SSE_SQRTSS_XMM_to_XMM(t0reg, t0reg); + SSE_DIVSS_XMM_to_XMM(regd, t0reg); + ClampValues(regd); +} + +void recRSQRT_S_xmm(int info) +{ + int t0reg = _allocTempXMMreg(XMMT_FPS, -1); + //if (t0reg == -1) {Console::Error("FPU: RSQRT Allocation Error!");} + //SysPrintf("FPU: RSQRT\n"); + + switch(info & (PROCESS_EE_S|PROCESS_EE_T) ) { + case PROCESS_EE_S: + //SysPrintf("FPU: RSQRT case 1\n"); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + if (CHECK_FPU_EXTRA_FLAGS) recRSQRThelper1(EEREC_D, t0reg); + else recRSQRThelper2(EEREC_D, t0reg); + break; + case PROCESS_EE_T: + //SysPrintf("FPU: RSQRT case 2\n"); + SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_T); + SSE_MOVSS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_FLAGS) recRSQRThelper1(EEREC_D, t0reg); + else recRSQRThelper2(EEREC_D, t0reg); + break; + case (PROCESS_EE_S|PROCESS_EE_T): + //SysPrintf("FPU: RSQRT case 3\n"); + SSE_MOVSS_XMM_to_XMM(t0reg, EEREC_T); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + if (CHECK_FPU_EXTRA_FLAGS) recRSQRThelper1(EEREC_D, t0reg); + else recRSQRThelper2(EEREC_D, t0reg); + break; + default: + //SysPrintf("FPU: RSQRT case 4\n"); + SSE_MOVSS_M32_to_XMM(t0reg, (uptr)&fpuRegs.fpr[_Ft_]); + SSE_MOVSS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); + if (CHECK_FPU_EXTRA_FLAGS) recRSQRThelper1(EEREC_D, t0reg); + else recRSQRThelper2(EEREC_D, t0reg); + break; + } + _freeXMMreg(t0reg); +} + +FPURECOMPILE_CONSTCODE(RSQRT_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + +#endif // FPU_RECOMPILE + +} } } } diff --git a/pcsx2/x86/iFPU.h b/pcsx2/x86/iFPU.h new file mode 100644 index 0000000000..f0872aa92d --- /dev/null +++ b/pcsx2/x86/iFPU.h @@ -0,0 +1,71 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IFPU_H__ +#define __IFPU_H__ + +namespace R5900 { +namespace Dynarec { + + void SaveCW(); + void LoadCW(); + + namespace OpcodeImpl { + namespace COP1 + { + void recMFC1( void ); + void recCFC1( void ); + void recMTC1( void ); + void recCTC1( void ); + void recCOP1_BC1( void ); + void recCOP1_S( void ); + void recCOP1_W( void ); + void recC_EQ( void ); + void recC_F( void ); + void recC_LT( void ); + void recC_LE( void ); + void recADD_S( void ); + void recSUB_S( void ); + void recMUL_S( void ); + void recDIV_S( void ); + void recSQRT_S( void ); + void recABS_S( void ); + void recMOV_S( void ); + void recNEG_S( void ); + void recRSQRT_S( void ); + void recADDA_S( void ); + void recSUBA_S( void ); + void recMULA_S( void ); + void recMADD_S( void ); + void recMSUB_S( void ); + void recMADDA_S( void ); + void recMSUBA_S( void ); + void recCVT_S( void ); + void recCVT_W( void ); + void recMAX_S( void ); + void recMIN_S( void ); + void recBC1F( void ); + void recBC1T( void ); + void recBC1FL( void ); + void recBC1TL( void ); + } } +} } + +#endif + + diff --git a/pcsx2/x86/iGS.cpp b/pcsx2/x86/iGS.cpp new file mode 100644 index 0000000000..22e669225f --- /dev/null +++ b/pcsx2/x86/iGS.cpp @@ -0,0 +1,299 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "VU.h" + +#include "ix86/ix86.h" +#include "iR5900.h" + +#include "GS.h" +#include "DebugTools/Debug.h" + +extern u8 g_RealGSMem[0x2000]; +#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff)) + +// __thiscall -- Calling Convention Notes. + +// ** MSVC passes the pointer to the object as ECX. Other parameters are passed normally +// (_cdecl style). Stack is cleaned by the callee. + +// ** GCC works just like a __cdecl, except the pointer to the object is pushed onto the +// stack last (passed as the first parameter). Caller cleans up the stack. + +// The GCC code below is untested. Hope it works. :| (air) + + +// Used to send 8, 16, and 32 bit values to the MTGS. +static void __fastcall _rec_mtgs_Send32orSmaller( GS_RINGTYPE ringtype, u32 mem, int mmreg ) +{ + iFlushCall(0); + + PUSH32I( 0 ); + _callPushArg( mmreg, 0 ); + PUSH32I( mem&0x13ff ); + PUSH32I( ringtype ); + +#ifdef _MSC_VER + MOV32ItoR( ECX, (uptr)mtgsThread ); + CALLFunc( mtgsThread->FnPtr_SimplePacket() ); +#else // GCC --> + PUSH32I( (uptr)mtgsThread ); + CALLFunc( mtgsThread->FnPtr_SimplePacket() ); + ADD32ItoR( ESP, 20 ); +#endif +} + +// Used to send 64 and 128 bit values to the MTGS (called twice for 128's, which +// is why it doesn't call iFlushCall) +static void __fastcall _rec_mtgs_Send64( uptr gsbase, u32 mem, int mmreg ) +{ + PUSH32M( gsbase+4 ); + PUSH32M( gsbase ); + PUSH32I( mem&0x13ff ); + PUSH32I( GS_RINGTYPE_MEMWRITE64 ); + +#ifdef _MSC_VER + MOV32ItoR( ECX, (uptr)mtgsThread ); + CALLFunc( mtgsThread->FnPtr_SimplePacket() ); +#else // GCC --> + PUSH32I( (uptr)mtgsThread ); + CALLFunc( mtgsThread->FnPtr_SimplePacket() ); + ADD32ItoR( ESP, 20 ); +#endif + +} + +void gsConstWrite8(u32 mem, int mmreg) +{ + switch (mem&~3) { + case 0x12001000: // GS_CSR + _eeMoveMMREGtoR(EAX, mmreg); + iFlushCall(0); + MOV32MtoR(ECX, (uptr)&CSRw); + AND32ItoR(EAX, 0xff<<(mem&3)*8); + AND32ItoR(ECX, ~(0xff<<(mem&3)*8)); + OR32ItoR(EAX, ECX); + _callFunctionArg1((uptr)gsCSRwrite, EAX|MEM_X86TAG, 0); + break; + default: + _eeWriteConstMem8( (uptr)PS2GS_BASE(mem), mmreg ); + + if( mtgsThread != NULL ) + _rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE8, mem, mmreg ); + + break; + } +} + +void gsConstWrite16(u32 mem, int mmreg) +{ + switch (mem&~3) { + + case 0x12000010: // GS_SMODE1 + case 0x12000020: // GS_SMODE2 + // SMODE1 and SMODE2 fall back on the gsWrite library. + iFlushCall(0); + _callFunctionArg2((uptr)gsWrite16, MEM_CONSTTAG, mmreg, mem, 0 ); + break; + + case 0x12001000: // GS_CSR + + assert( !(mem&2) ); + _eeMoveMMREGtoR(EAX, mmreg); + iFlushCall(0); + + MOV32MtoR(ECX, (uptr)&CSRw); + AND32ItoR(EAX, 0xffff<<(mem&2)*8); + AND32ItoR(ECX, ~(0xffff<<(mem&2)*8)); + OR32ItoR(EAX, ECX); + _callFunctionArg1((uptr)gsCSRwrite, EAX|MEM_X86TAG, 0); + break; + + default: + _eeWriteConstMem16( (uptr)PS2GS_BASE(mem), mmreg ); + + if( mtgsThread != NULL ) + _rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE16, mem, mmreg ); + + break; + } +} + +// (value&0x1f00)|0x6000 +void gsConstWriteIMR(int mmreg) +{ + const u32 mem = 0x12001010; + if( mmreg & MEM_XMMTAG ) { + SSE2_MOVD_XMM_to_M32((uptr)PS2GS_BASE(mem), mmreg&0xf); + AND32ItoM((uptr)PS2GS_BASE(mem), 0x1f00); + OR32ItoM((uptr)PS2GS_BASE(mem), 0x6000); + } + else if( mmreg & MEM_MMXTAG ) { + SetMMXstate(); + MOVDMMXtoM((uptr)PS2GS_BASE(mem), mmreg&0xf); + AND32ItoM((uptr)PS2GS_BASE(mem), 0x1f00); + OR32ItoM((uptr)PS2GS_BASE(mem), 0x6000); + } + else if( mmreg & MEM_EECONSTTAG ) { + MOV32ItoM( (uptr)PS2GS_BASE(mem), (g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]&0x1f00)|0x6000); + } + else { + AND32ItoR(mmreg, 0x1f00); + OR32ItoR(mmreg, 0x6000); + MOV32RtoM( (uptr)PS2GS_BASE(mem), mmreg ); + } + + // IMR doesn't need to be updated in MTGS mode +} + +void gsConstWrite32(u32 mem, int mmreg) { + + switch (mem) { + + case 0x12000010: // GS_SMODE1 + case 0x12000020: // GS_SMODE2 + // SMODE1 and SMODE2 fall back on the gsWrite library. + iFlushCall(0); + _callFunctionArg2((uptr)gsWrite32, MEM_CONSTTAG, mmreg, mem, 0 ); + break; + + case 0x12001000: // GS_CSR + iFlushCall(0); + _callFunctionArg1((uptr)gsCSRwrite, mmreg, 0); + break; + + case 0x12001010: // GS_IMR + gsConstWriteIMR(mmreg); + break; + default: + _eeWriteConstMem32( (uptr)PS2GS_BASE(mem), mmreg ); + + if( mtgsThread != NULL ) + _rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE32, mem, mmreg ); + + break; + } +} + +void gsConstWrite64(u32 mem, int mmreg) +{ + switch (mem) { + case 0x12000010: // GS_SMODE1 + case 0x12000020: // GS_SMODE2 + // SMODE1 and SMODE2 fall back on the gsWrite library. + // the low 32 bit dword is all the SMODE regs care about. + iFlushCall(0); + _callFunctionArg2((uptr)gsWrite32, MEM_CONSTTAG, mmreg, mem, 0 ); + break; + + case 0x12001000: // GS_CSR + iFlushCall(0); + _callFunctionArg1((uptr)gsCSRwrite, mmreg, 0); + break; + + case 0x12001010: // GS_IMR + gsConstWriteIMR(mmreg); + break; + + default: + _eeWriteConstMem64((uptr)PS2GS_BASE(mem), mmreg); + + if( mtgsThread != NULL ) + { + iFlushCall( 0 ); + _rec_mtgs_Send64( (uptr)PS2GS_BASE(mem), mem, mmreg ); + } + + break; + } +} + +void gsConstWrite128(u32 mem, int mmreg) +{ + switch (mem) { + case 0x12000010: // GS_SMODE1 + case 0x12000020: // GS_SMODE2 + // SMODE1 and SMODE2 fall back on the gsWrite library. + // the low 32 bit dword is all the SMODE regs care about. + iFlushCall(0); + _callFunctionArg2((uptr)gsWrite32, MEM_CONSTTAG, mmreg, mem, 0 ); + break; + + case 0x12001000: // GS_CSR + iFlushCall(0); + _callFunctionArg1((uptr)gsCSRwrite, mmreg, 0); + break; + + case 0x12001010: // GS_IMR + // (value&0x1f00)|0x6000 + gsConstWriteIMR(mmreg); + break; + + default: + _eeWriteConstMem128( (uptr)PS2GS_BASE(mem), mmreg); + + if( mtgsThread != NULL ) + { + iFlushCall(0); + _rec_mtgs_Send64( (uptr)PS2GS_BASE(mem), mem, mmreg ); + _rec_mtgs_Send64( (uptr)PS2GS_BASE(mem)+8, mem+8, mmreg ); + } + + break; + } +} + +int gsConstRead8(u32 x86reg, u32 mem, u32 sign) +{ + GIF_LOG("GS read 8 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem); + _eeReadConstMem8(x86reg, (uptr)PS2GS_BASE(mem), sign); + return 0; +} + +int gsConstRead16(u32 x86reg, u32 mem, u32 sign) +{ + GIF_LOG("GS read 16 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem); + _eeReadConstMem16(x86reg, (uptr)PS2GS_BASE(mem), sign); + return 0; +} + +int gsConstRead32(u32 x86reg, u32 mem) +{ + GIF_LOG("GS read 32 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem); + _eeReadConstMem32(x86reg, (uptr)PS2GS_BASE(mem)); + return 0; +} + +void gsConstRead64(u32 mem, int mmreg) +{ + GIF_LOG("GS read 64 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem); + if( IS_XMMREG(mmreg) ) SSE_MOVLPS_M64_to_XMM(mmreg&0xff, (uptr)PS2GS_BASE(mem)); + else { + MOVQMtoR(mmreg, (uptr)PS2GS_BASE(mem)); + SetMMXstate(); + } +} + +void gsConstRead128(u32 mem, int xmmreg) +{ + GIF_LOG("GS read 128 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem); + _eeReadConstMem128( xmmreg, (uptr)PS2GS_BASE(mem)); +} diff --git a/pcsx2/x86/iHw.cpp b/pcsx2/x86/iHw.cpp new file mode 100644 index 0000000000..c43d0d5063 --- /dev/null +++ b/pcsx2/x86/iHw.cpp @@ -0,0 +1,1153 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "iR5900.h" +#include "VUmicro.h" +#include "IopMem.h" + +// The full suite of hardware APIs: +#include "IPU/IPU.h" +#include "GS.h" +#include "Counters.h" +#include "Vif.h" +#include "VifDma.h" +#include "SPR.h" +#include "Sif.h" + + +extern int rdram_devices; // put 8 for TOOL and 2 for PS2 and PSX +extern int rdram_sdevid; +extern char sio_buffer[1024]; +extern int sio_count; + +int hwConstRead8(u32 x86reg, u32 mem, u32 sign) +{ + if( mem >= 0x10000000 && mem < 0x10008000 ) + DevCon::WriteLn("hwRead8 to %x", params mem); + + if ((mem & 0xffffff0f) == 0x1000f200) { + if(mem == 0x1000f260) { + MMXONLY(if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf); + else) + XOR32RtoR(x86reg, x86reg); + return 0; + } + else if(mem == 0x1000F240) { + + _eeReadConstMem8(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff], sign); + //psHu32(mem) &= ~0x4000; + return 0; + } + } + + if (mem < 0x10010000) + { + _eeReadConstMem8(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff], sign); + } + else { + MMXONLY(if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf); + else ) + XOR32RtoR(x86reg, x86reg); + } + + return 0; +} + +#define CONSTREAD16_CALL(name) { \ + iFlushCall(0); \ + CALLFunc((uptr)name); \ + if( sign ) MOVSX32R16toR(EAX, EAX); \ + else MOVZX32R16toR(EAX, EAX); \ +} \ + +static u32 s_regreads[3] = {0x010200000, 0xbfff0000, 0xF0000102}; +int hwConstRead16(u32 x86reg, u32 mem, u32 sign) +{ + if( mem >= 0x10002000 && mem < 0x10008000 ) + DevCon::WriteLn("hwRead16 to %x", params mem); + + if( mem >= 0x10000000 && mem < 0x10002000 ) + EECNT_LOG("cnt read to %x\n", params mem); + + switch (mem) { + case 0x10000000: + PUSH32I(0); + CONSTREAD16_CALL(rcntRcount); + ADD32ItoR(ESP, 4); + return 1; + case 0x10000010: + _eeReadConstMem16(x86reg, (uptr)&counters[0].mode, sign); + return 0; + case 0x10000020: + _eeReadConstMem16(x86reg, (uptr)&counters[0].mode, sign); + return 0; + case 0x10000030: + _eeReadConstMem16(x86reg, (uptr)&counters[0].hold, sign); + return 0; + + case 0x10000800: + PUSH32I(1); + CONSTREAD16_CALL(rcntRcount); + ADD32ItoR(ESP, 4); + return 1; + + case 0x10000810: + _eeReadConstMem16(x86reg, (uptr)&counters[1].mode, sign); + return 0; + + case 0x10000820: + _eeReadConstMem16(x86reg, (uptr)&counters[1].target, sign); + return 0; + + case 0x10000830: + _eeReadConstMem16(x86reg, (uptr)&counters[1].hold, sign); + return 0; + + case 0x10001000: + PUSH32I(2); + CONSTREAD16_CALL(rcntRcount); + ADD32ItoR(ESP, 4); + return 1; + + case 0x10001010: + _eeReadConstMem16(x86reg, (uptr)&counters[2].mode, sign); + return 0; + + case 0x10001020: + _eeReadConstMem16(x86reg, (uptr)&counters[2].target, sign); + return 0; + + case 0x10001800: + PUSH32I(3); + CONSTREAD16_CALL(rcntRcount); + ADD32ItoR(ESP, 4); + return 1; + + case 0x10001810: + _eeReadConstMem16(x86reg, (uptr)&counters[3].mode, sign); + return 0; + + case 0x10001820: + _eeReadConstMem16(x86reg, (uptr)&counters[3].target, sign); + return 0; + + default: + if ((mem & 0xffffff0f) == 0x1000f200) { + if(mem == 0x1000f260) { + MMXONLY(if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf); + else ) + XOR32RtoR(x86reg, x86reg); + return 0; + } + else if(mem == 0x1000F240) { + + MMXONLY(if( IS_MMXREG(x86reg) ) { + MOVDMtoMMX(x86reg&0xf, (uptr)&PS2MEM_HW[(mem) & 0xffff] - 2); + PORMtoR(x86reg&0xf, (uptr)&s_regreads[0]); + PANDMtoR(x86reg&0xf, (uptr)&s_regreads[1]); + } + else ) + { + if( sign ) MOVSX32M16toR(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff]); + else MOVZX32M16toR(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff]); + + OR32ItoR(x86reg, 0x0102); + AND32ItoR(x86reg, ~0x4000); + } + return 0; + } + } + if (mem < 0x10010000) { + _eeReadConstMem16(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff], sign); + } + else { + MMXONLY(if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf); + else ) + XOR32RtoR(x86reg, x86reg); + } + + return 0; + } +} + +int hwContRead32_f440() +{ + if ((psHu32(0xf430) >> 6) & 0xF) + return 0; + else + switch ((psHu32(0xf430)>>16) & 0xFFF){//MCH_RICM: x:4|SA:12|x:5|SDEV:1|SOP:4|SBC:1|SDEV:5 + case 0x21://INIT + { + int ret = 0x1F * (rdram_sdevid < rdram_devices); + rdram_sdevid += (rdram_sdevid < rdram_devices); + return ret; + } + case 0x23://CNFGA + return 0x0D0D; //PVER=3 | MVER=16 | DBL=1 | REFBIT=5 + case 0x24://CNFGB + //0x0110 for PSX SVER=0 | CORG=8(5x9x7) | SPT=1 | DEVTYP=0 | BYTE=0 + return 0x0090; //SVER=0 | CORG=4(5x9x6) | SPT=1 | DEVTYP=0 | BYTE=0 + case 0x40://DEVID + return psHu32(0xf430) & 0x1F; // =SDEV + } + + return 0; +} + +int hwConstRead32(u32 x86reg, u32 mem) +{ + //IPU regs + if ((mem>=0x10002000) && (mem<0x10003000)) { + //return ipuConstRead32(x86reg, mem); + iFlushCall(0); + PUSH32I( mem ); + CALLFunc( (uptr)ipuRead32 ); + } + + switch (mem) { + case 0x10000000: + iFlushCall(0); + PUSH32I(0); + CALLFunc((uptr)rcntRcount); + EECNT_LOG("Counter 0 count read = %x\n", rcntRcount(0)); + ADD32ItoR(ESP, 4); + return 1; + case 0x10000010: + _eeReadConstMem32(x86reg, (uptr)&counters[0].mode); + EECNT_LOG("Counter 0 mode read = %x\n", counters[0].modeval); + return 0; + case 0x10000020: + _eeReadConstMem32(x86reg, (uptr)&counters[0].target); + EECNT_LOG("Counter 0 target read = %x\n", counters[0].target); + return 0; + case 0x10000030: + _eeReadConstMem32(x86reg, (uptr)&counters[0].hold); + return 0; + + case 0x10000800: + iFlushCall(0); + PUSH32I(1); + CALLFunc((uptr)rcntRcount); + EECNT_LOG("Counter 1 count read = %x\n", rcntRcount(1)); + ADD32ItoR(ESP, 4); + return 1; + case 0x10000810: + _eeReadConstMem32(x86reg, (uptr)&counters[1].mode); + EECNT_LOG("Counter 1 mode read = %x\n", counters[1].modeval); + return 0; + case 0x10000820: + _eeReadConstMem32(x86reg, (uptr)&counters[1].target); + EECNT_LOG("Counter 1 target read = %x\n", counters[1].target); + return 0; + case 0x10000830: + _eeReadConstMem32(x86reg, (uptr)&counters[1].hold); + return 0; + + case 0x10001000: + iFlushCall(0); + PUSH32I(2); + CALLFunc((uptr)rcntRcount); + EECNT_LOG("Counter 2 count read = %x\n", rcntRcount(2)); + ADD32ItoR(ESP, 4); + return 1; + case 0x10001010: + _eeReadConstMem32(x86reg, (uptr)&counters[2].mode); + EECNT_LOG("Counter 2 mode read = %x\n", counters[2].modeval); + return 0; + case 0x10001020: + _eeReadConstMem32(x86reg, (uptr)&counters[2].target); + EECNT_LOG("Counter 2 target read = %x\n", counters[2].target); + return 0; + case 0x10001030: + // fixme: Counters[2].hold and Counters[3].hold are never assigned values + // anywhere in Pcsx2. + _eeReadConstMem32(x86reg, (uptr)&counters[2].hold); + return 0; + + case 0x10001800: + iFlushCall(0); + PUSH32I(3); + CALLFunc((uptr)rcntRcount); + EECNT_LOG("Counter 3 count read = %x\n", rcntRcount(3)); + ADD32ItoR(ESP, 4); + return 1; + case 0x10001810: + _eeReadConstMem32(x86reg, (uptr)&counters[3].mode); + EECNT_LOG("Counter 3 mode read = %x\n", counters[3].modeval); + return 0; + case 0x10001820: + _eeReadConstMem32(x86reg, (uptr)&counters[3].target); + EECNT_LOG("Counter 3 target read = %x\n", counters[3].target); + return 0; + case 0x10001830: + // fixme: Counters[2].hold and Counters[3].hold are never assigned values + // anywhere in Pcsx2. + _eeReadConstMem32(x86reg, (uptr)&counters[3].hold); + return 0; + + case 0x1000f130: + case 0x1000f410: + case 0x1000f430: + if( IS_XMMREG(x86reg) ) SSEX_PXOR_XMM_to_XMM(x86reg&0xf, x86reg&0xf); + MMXONLY(else if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf);) + else XOR32RtoR(x86reg, x86reg); + return 0; + + case 0x1000f440: + iFlushCall(0); + CALLFunc((uptr)hwContRead32_f440); + return 1; + + case 0x1000f520: // DMAC_ENABLER + _eeReadConstMem32(x86reg, (uptr)&PS2MEM_HW[0xf590]); + return 0; + + default: + if ((mem & 0xffffff0f) == 0x1000f200) { + if(mem == 0x1000f260) { + if( IS_XMMREG(x86reg) ) SSEX_PXOR_XMM_to_XMM(x86reg&0xf, x86reg&0xf); + MMXONLY(else if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf);) + else XOR32RtoR(x86reg, x86reg); + return 0; + } + else if(mem == 0x1000F240) { + + if( IS_XMMREG(x86reg) ) { + SSEX_MOVD_M32_to_XMM(x86reg&0xf, (uptr)&PS2MEM_HW[(mem) & 0xffff]); + SSEX_POR_M128_to_XMM(x86reg&0xf, (uptr)&s_regreads[2]); + } + MMXONLY(else if( IS_MMXREG(x86reg) ) { + MOVDMtoMMX(x86reg&0xf, (uptr)&PS2MEM_HW[(mem) & 0xffff]); + PORMtoR(x86reg&0xf, (uptr)&s_regreads[2]); + }) + else { + MOV32MtoR(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff]); + OR32ItoR(x86reg, 0xF0000102); + } + return 0; + } + } + + if (mem < 0x10010000) { + _eeReadConstMem32(x86reg, (uptr)&PS2MEM_HW[(mem) & 0xffff]); + } + else { + if( IS_XMMREG(x86reg) ) SSEX_PXOR_XMM_to_XMM(x86reg&0xf, x86reg&0xf); + MMXONLY(else if( IS_MMXREG(x86reg) ) PXORRtoR(x86reg&0xf, x86reg&0xf);) + else XOR32RtoR(x86reg, x86reg); + } + + return 0; + } +} + +void hwConstRead64(u32 mem, int mmreg) { + if ((mem>=0x10002000) && (mem<0x10003000)) { + ipuConstRead64(mem, mmreg); + return; + } + + if( IS_XMMREG(mmreg) ) SSE_MOVLPS_M64_to_XMM(mmreg&0xff, (uptr)PSM(mem)); + else { + MMXONLY(MOVQMtoR(mmreg, (uptr)PSM(mem)); + SetMMXstate();) + } +} + +PCSX2_ALIGNED16(u32 s_TempFIFO[4]); +void hwConstRead128(u32 mem, int xmmreg) { + if (mem >= 0x10004000 && mem < 0x10008000) { + iFlushCall(0); + PUSH32I((uptr)&s_TempFIFO[0]); + PUSH32I(mem); + CALLFunc((uptr)ReadFIFO); + ADD32ItoR(ESP, 8); + _eeReadConstMem128( xmmreg, (uptr)&s_TempFIFO[0]); + return; + } + + _eeReadConstMem128( xmmreg, (uptr)PSM(mem)); +} + +// when writing imm +static void recDmaExecI8(void (*name)(), u32 mem, int mmreg) +{ + MOV8ItoM((uptr)&PS2MEM_HW[(mem) & 0xffff], g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]); + if( g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0] & 1 ) { + TEST8ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); + j8Ptr[6] = JZ8(0); + CALLFunc((uptr)name); + x86SetJ8( j8Ptr[6] ); + } +} + +static void recDmaExec8(void (*name)(), u32 mem, int mmreg) +{ + // Flushcall Note : DMA transfers are almost always "involved" operations + // that use memcpys and/or threading. Freeing all XMM and MMX regs is the + // best option. + + iFlushCall(FLUSH_NOCONST); + if( IS_EECONSTREG(mmreg) ) { + recDmaExecI8(name, mem, mmreg); + } + else { + _eeMoveMMREGtoR(EAX, mmreg); + _eeWriteConstMem8((uptr)&PS2MEM_HW[(mem) & 0xffff], mmreg); + + TEST8ItoR(EAX, 1); + j8Ptr[5] = JZ8(0); + TEST8ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); + j8Ptr[6] = JZ8(0); + + CALLFunc((uptr)name); + + x86SetJ8( j8Ptr[5] ); + x86SetJ8( j8Ptr[6] ); + } +} + +static void __fastcall PrintDebug(u8 value) +{ + // Note: This is where the EE's diagonstic messages originate from (like the ones that + // start with hash # marks) + + if (value == '\n') { + sio_buffer[sio_count] = 0; + Console::WriteLn( Color_Cyan, sio_buffer ); + sio_count = 0; + } else { + if (sio_count < 1023) { + sio_buffer[sio_count++] = value; + } + } +} + +// fixme: this would be more optimal as a C++ template (with bit as the template parameter) +template< uint bit > +static void ConstWrite_ExecTimer( uptr func, u8 index, int mmreg) +{ + if( bit != 32 ) + { + if( !IS_EECONSTREG(mmreg) ) + { + if( bit == 8 ) MOVZX32R8toR(mmreg&0xf, mmreg&0xf); + else if( bit == 16 ) MOVZX32R16toR(mmreg&0xf, mmreg&0xf); + } + } + + // FlushCall Note : All counter functions are short and sweet, full flush not needed. + + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(index); + CALLFunc(func); + ADD32ItoR(ESP, 8); +} + +#define CONSTWRITE_TIMERS(bit) \ + case 0x10000000: ConstWrite_ExecTimer((uptr)&rcntWcount, 0, mmreg); break; \ + case 0x10000010: ConstWrite_ExecTimer((uptr)&rcntWmode, 0, mmreg); break; \ + case 0x10000020: ConstWrite_ExecTimer((uptr)&rcntWtarget, 0, mmreg); break; \ + case 0x10000030: ConstWrite_ExecTimer((uptr)&rcntWhold, 0, mmreg); break; \ + \ + case 0x10000800: ConstWrite_ExecTimer((uptr)&rcntWcount, 1, mmreg); break; \ + case 0x10000810: ConstWrite_ExecTimer((uptr)&rcntWmode, 1, mmreg); break; \ + case 0x10000820: ConstWrite_ExecTimer((uptr)&rcntWtarget, 1, mmreg); break; \ + case 0x10000830: ConstWrite_ExecTimer((uptr)&rcntWhold, 1, mmreg); break; \ + \ + case 0x10001000: ConstWrite_ExecTimer((uptr)&rcntWcount, 2, mmreg); break; \ + case 0x10001010: ConstWrite_ExecTimer((uptr)&rcntWmode, 2, mmreg); break; \ + case 0x10001020: ConstWrite_ExecTimer((uptr)&rcntWtarget, 2, mmreg); break; \ + \ + case 0x10001800: ConstWrite_ExecTimer((uptr)&rcntWcount, 3, mmreg); break; \ + case 0x10001810: ConstWrite_ExecTimer((uptr)&rcntWmode, 3, mmreg); break; \ + case 0x10001820: ConstWrite_ExecTimer((uptr)&rcntWtarget, 3, mmreg); break; \ + +void hwConstWrite8(u32 mem, int mmreg) +{ + switch (mem) { + CONSTWRITE_TIMERS(8) + + case 0x1000f180: + // Yay fastcall! + _eeMoveMMREGtoR( ECX, mmreg ); + iFlushCall(0); + CALLFunc((uptr)PrintDebug); + break; + + case 0x10008001: // dma0 - vif0 + recDmaExec8(dmaVIF0, mem, mmreg); + break; + + case 0x10009001: // dma1 - vif1 + recDmaExec8(dmaVIF1, mem, mmreg); + break; + + case 0x1000a001: // dma2 - gif + recDmaExec8(dmaGIF, mem, mmreg); + break; + + case 0x1000b001: // dma3 - fromIPU + recDmaExec8(dmaIPU0, mem, mmreg); + break; + + case 0x1000b401: // dma4 - toIPU + recDmaExec8(dmaIPU1, mem, mmreg); + break; + + case 0x1000c001: // dma5 - sif0 + //if (value == 0) psxSu32(0x30) = 0x40000; + recDmaExec8(dmaSIF0, mem, mmreg); + break; + + case 0x1000c401: // dma6 - sif1 + recDmaExec8(dmaSIF1, mem, mmreg); + break; + + case 0x1000c801: // dma7 - sif2 + recDmaExec8(dmaSIF2, mem, mmreg); + break; + + case 0x1000d001: // dma8 - fromSPR + recDmaExec8(dmaSPR0, mem, mmreg); + break; + + case 0x1000d401: // dma9 - toSPR + recDmaExec8(dmaSPR1, mem, mmreg); + break; + + case 0x1000f592: // DMAC_ENABLEW + _eeWriteConstMem8( (uptr)&PS2MEM_HW[0xf522], mmreg ); + _eeWriteConstMem8( (uptr)&PS2MEM_HW[0xf592], mmreg ); + break; + + default: + if ((mem & 0xffffff0f) == 0x1000f200) { + u32 at = mem & 0xf0; + switch(at) + { + case 0x00: + _eeWriteConstMem8( (uptr)&PS2MEM_HW[mem&0xffff], mmreg); + break; + case 0x40: + if( IS_EECONSTREG(mmreg) ) { + if( !(g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0] & 0x100) ) { + AND32ItoM( (uptr)&PS2MEM_HW[mem&0xfffc], ~0x100); + } + } + else { + _eeMoveMMREGtoR(EAX, mmreg); + TEST16ItoR(EAX, 0x100); + j8Ptr[5] = JNZ8(0); + AND32ItoM( (uptr)&PS2MEM_HW[mem&0xfffc], ~0x100); + x86SetJ8(j8Ptr[5]); + } + break; + } + return; + } + assert( (mem&0xff0f) != 0xf200 ); + + switch(mem&~3) { + case 0x1000f130: + case 0x1000f410: + case 0x1000f430: + break; + default: + _eeWriteConstMem8((uptr)&PS2MEM_HW[mem&0xffff], mmreg); + } + + break; + } +} + +// Flushcall Note : DMA transfers are almost always "involved" operations +// that use memcpys and/or threading. Freeing all XMM and MMX regs is the +// best option (removes the need for FreezeXMMRegs()). But register +// allocation is such a mess right now that we can't do it (yet). + +static void recDmaExecI16( void (*name)(), u32 mem, int mmreg ) +{ + MOV16ItoM((uptr)&PS2MEM_HW[(mem) & 0xffff], g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]); + if( g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0] & 0x100 ) { + TEST8ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); + j8Ptr[6] = JZ8(0); + CALLFunc((uptr)name); + x86SetJ8( j8Ptr[6] ); + } +} + +static void recDmaExec16(void (*name)(), u32 mem, int mmreg) +{ + iFlushCall(0); + + if( IS_EECONSTREG(mmreg) ) { + recDmaExecI16(name, mem, mmreg); + } + else { + _eeMoveMMREGtoR(EAX, mmreg); + _eeWriteConstMem16((uptr)&PS2MEM_HW[(mem) & 0xffff], mmreg); + + TEST16ItoR(EAX, 0x100); + j8Ptr[5] = JZ8(0); + TEST8ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); + j8Ptr[6] = JZ8(0); + + CALLFunc((uptr)name); + + x86SetJ8( j8Ptr[5] ); + x86SetJ8( j8Ptr[6] ); + } +} + +void hwConstWrite16(u32 mem, int mmreg) +{ + switch(mem) { + + CONSTWRITE_TIMERS(16) + + case 0x10008000: // dma0 - vif0 + recDmaExec16(dmaVIF0, mem, mmreg); + break; + + case 0x10009000: // dma1 - vif1 - chcr + recDmaExec16(dmaVIF1, mem, mmreg); + break; + + case 0x1000a000: // dma2 - gif + recDmaExec16(dmaGIF, mem, mmreg); + break; + case 0x1000b000: // dma3 - fromIPU + recDmaExec16(dmaIPU0, mem, mmreg); + break; + case 0x1000b400: // dma4 - toIPU + recDmaExec16(dmaIPU1, mem, mmreg); + break; + case 0x1000c000: // dma5 - sif0 + //if (value == 0) psxSu32(0x30) = 0x40000; + recDmaExec16(dmaSIF0, mem, mmreg); + break; + case 0x1000c002: + //? + break; + case 0x1000c400: // dma6 - sif1 + recDmaExec16(dmaSIF1, mem, mmreg); + break; + case 0x1000c800: // dma7 - sif2 + recDmaExec16(dmaSIF2, mem, mmreg); + break; + case 0x1000c802: + //? + break; + case 0x1000d000: // dma8 - fromSPR + recDmaExec16(dmaSPR0, mem, mmreg); + break; + case 0x1000d400: // dma9 - toSPR + recDmaExec16(dmaSPR1, mem, mmreg); + break; + case 0x1000f592: // DMAC_ENABLEW + _eeWriteConstMem16((uptr)&PS2MEM_HW[0xf522], mmreg); + _eeWriteConstMem16((uptr)&PS2MEM_HW[0xf592], mmreg); + break; + case 0x1000f130: + case 0x1000f410: + case 0x1000f430: + break; + default: + if ((mem & 0xffffff0f) == 0x1000f200) { + u32 at = mem & 0xf0; + switch(at) + { + case 0x00: + _eeWriteConstMem16((uptr)&PS2MEM_HW[mem&0xffff], mmreg); + break; + case 0x20: + _eeWriteConstMem16OP((uptr)&PS2MEM_HW[mem&0xffff], mmreg, 1); + break; + case 0x30: + if( IS_EECONSTREG(mmreg) ) { + AND16ItoM((uptr)&PS2MEM_HW[mem&0xffff], ~g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]); + } + else { + NOT32R(mmreg&0xf); + AND16RtoM((uptr)&PS2MEM_HW[mem&0xffff], mmreg&0xf); + } + break; + case 0x40: + if( IS_EECONSTREG(mmreg) ) { + if( !(g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0] & 0x100) ) { + AND16ItoM((uptr)&PS2MEM_HW[mem&0xffff], ~0x100); + } + else { + OR16ItoM((uptr)&PS2MEM_HW[mem&0xffff], 0x100); + } + } + else { + _eeMoveMMREGtoR(EAX, mmreg); + TEST16ItoR(EAX, 0x100); + j8Ptr[5] = JZ8(0); + OR16ItoM((uptr)&PS2MEM_HW[mem&0xffff], 0x100); + j8Ptr[6] = JMP8(0); + + x86SetJ8( j8Ptr[5] ); + AND16ItoM((uptr)&PS2MEM_HW[mem&0xffff], ~0x100); + + x86SetJ8( j8Ptr[6] ); + } + + break; + case 0x60: + _eeWriteConstMem16((uptr)&PS2MEM_HW[mem&0xffff], 0); + break; + } + return; + } + + _eeWriteConstMem16((uptr)&PS2MEM_HW[mem&0xffff], mmreg); + } +} + +// when writing an Imm + +static void recDmaExecI( void (*name)(), u32 mem, int mmreg ) +{ + u32 c = g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]; + /* Keep the old tag if in chain mode and hw doesnt set it*/ + if( (c & 0xc) == 0x4 && (c&0xffff0000) == 0 ) { + MOV16ItoM((uptr)&PS2MEM_HW[(mem) & 0xffff], c); + } + else MOV32ItoM((uptr)&PS2MEM_HW[(mem) & 0xffff], c); + if( c & 0x100 ) { + TEST8ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); + j8Ptr[6] = JZ8(0); + CALLFunc((uptr)name); + x86SetJ8( j8Ptr[6] ); + } +} + +static void recDmaExec( void (*name)(), u32 mem, int mmreg ) +{ + + iFlushCall(0); + + if( IS_EECONSTREG(mmreg) ) { + recDmaExecI(name, mem, mmreg); + } + else { + + // fixme: This is a lot of code to be injecting into the recompiler + // for every DMA transfer. It might actually be more efficient to + // set this up as a C function call instead (depends on how often + // the register is written without actually starting a DMA xfer). + + _eeMoveMMREGtoR(EAX, mmreg); + TEST32ItoR(EAX, 0xffff0000); + j8Ptr[6] = JNZ8(0); + MOV32RtoR(ECX, EAX); + AND32ItoR(ECX, 0xc); + CMP32ItoR(ECX, 4); + j8Ptr[7] = JNE8(0); + if( IS_XMMREG(mmreg) || IS_MMXREG(mmreg) ) { + MOV16RtoM((uptr)&PS2MEM_HW[(mem) & 0xffff], EAX); + } + else { + _eeWriteConstMem16((uptr)&PS2MEM_HW[(mem) & 0xffff], mmreg); + } + j8Ptr[8] = JMP8(0); + x86SetJ8(j8Ptr[6]); + x86SetJ8(j8Ptr[7]); + _eeWriteConstMem32((uptr)&PS2MEM_HW[(mem) & 0xffff], mmreg); + x86SetJ8(j8Ptr[8]); + + TEST16ItoR(EAX, 0x100); + j8Ptr[5] = JZ8(0); + TEST32ItoM((uptr)&PS2MEM_HW[DMAC_CTRL&0xffff], 1); + j8Ptr[6] = JZ8(0); + + CALLFunc((uptr)name); + x86SetJ8( j8Ptr[5] ); + x86SetJ8( j8Ptr[6] ); + } +} + + +void hwConstWrite32(u32 mem, int mmreg) +{ + //IPU regs + if ((mem>=0x10002000) && (mem<0x10003000)) { + //psHu32(mem) = value; + ipuConstWrite32(mem, mmreg); + return; + } + + if ((mem>=0x10003800) && (mem<0x10003c00)) { + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(mem); + CALLFunc((uptr)vif0Write32); + ADD32ItoR(ESP, 8); + return; + } + if ((mem>=0x10003c00) && (mem<0x10004000)) { + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(mem); + CALLFunc((uptr)vif1Write32); + ADD32ItoR(ESP, 8); + return; + } + + switch (mem) { + + CONSTWRITE_TIMERS(32) + + case GIF_CTRL: + + _eeMoveMMREGtoR(EAX, mmreg); + + iFlushCall(0); + TEST8ItoR(EAX, 1); + j8Ptr[5] = JZ8(0); + + // reset GS + CALLFunc((uptr)gsGIFReset); + j8Ptr[6] = JMP8(0); + + x86SetJ8( j8Ptr[5] ); + AND32I8toR(EAX, 8); + MOV32RtoM((uptr)&PS2MEM_HW[mem&0xffff], EAX); + + TEST16ItoR(EAX, 8); + j8Ptr[5] = JZ8(0); + OR8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], 8); + j8Ptr[7] = JMP8(0); + + x86SetJ8( j8Ptr[5] ); + AND8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], ~8); + x86SetJ8( j8Ptr[6] ); + x86SetJ8( j8Ptr[7] ); + return; + + case GIF_MODE: + _eeMoveMMREGtoR(EAX, mmreg); + _eeWriteConstMem32((uptr)&PS2MEM_HW[mem&0xffff], mmreg); + AND8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], ~5); + AND8ItoR(EAX, 5); + OR8RtoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], EAX); + return; + + case GIF_STAT: // stat is readonly + return; + + case 0x10008000: // dma0 - vif0 + recDmaExec(dmaVIF0, mem, mmreg); + break; + + case 0x10009000: // dma1 - vif1 - chcr + recDmaExec(dmaVIF1, mem, mmreg); + break; + + case 0x1000a000: // dma2 - gif + recDmaExec(dmaGIF, mem, mmreg); + break; + + case 0x1000b000: // dma3 - fromIPU + recDmaExec(dmaIPU0, mem, mmreg); + break; + case 0x1000b400: // dma4 - toIPU + recDmaExec(dmaIPU1, mem, mmreg); + break; + case 0x1000c000: // dma5 - sif0 + //if (value == 0) psxSu32(0x30) = 0x40000; + recDmaExec(dmaSIF0, mem, mmreg); + break; + + case 0x1000c400: // dma6 - sif1 + recDmaExec(dmaSIF1, mem, mmreg); + break; + + case 0x1000c800: // dma7 - sif2 + recDmaExec(dmaSIF2, mem, mmreg); + break; + + case 0x1000d000: // dma8 - fromSPR + recDmaExec(dmaSPR0, mem, mmreg); + break; + + case 0x1000d400: // dma9 - toSPR + recDmaExec(dmaSPR1, mem, mmreg); + break; + + case 0x1000e010: // DMAC_STAT + _eeMoveMMREGtoR(EAX, mmreg); + iFlushCall(0); + MOV32RtoR(ECX, EAX); + SHR32ItoR(EAX, 16); + NOT32R(ECX); + XOR16RtoM((uptr)&PS2MEM_HW[0xe012], EAX); + AND16RtoM((uptr)&PS2MEM_HW[0xe010], ECX); + + CALLFunc((uptr)cpuTestDMACInts); + break; + + case 0x1000f000: // INTC_STAT + _eeWriteConstMem32OP((uptr)&PS2MEM_HW[0xf000], mmreg, 2); + CALLFunc((uptr)cpuTestINTCInts); + break; + + case 0x1000f010: // INTC_MASK + _eeMoveMMREGtoR(EAX, mmreg); + iFlushCall(0); + XOR16RtoM((uptr)&PS2MEM_HW[0xf010], EAX); + CALLFunc((uptr)cpuTestINTCInts); + break; + + case 0x1000f130: + case 0x1000f410: + break; + + case 0x1000f430://MCH_RICM: x:4|SA:12|x:5|SDEV:1|SOP:4|SBC:1|SDEV:5 + + //if ((((value >> 16) & 0xFFF) == 0x21) && (((value >> 6) & 0xF) == 1) && (((psHu32(0xf440) >> 7) & 1) == 0))//INIT & SRP=0 + // rdram_sdevid = 0 + _eeMoveMMREGtoR(EAX, mmreg); + MOV32RtoR(EDX, EAX); + MOV32RtoR(ECX, EAX); + SHR32ItoR(EAX, 6); + SHR32ItoR(EDX, 16); + AND32ItoR(EAX, 0xf); + AND32ItoR(EDX, 0xfff); + CMP32ItoR(EAX, 1); + j8Ptr[5] = JNE8(0); + CMP32ItoR(EDX, 0x21); + j8Ptr[6] = JNE8(0); + + TEST32ItoM((uptr)&psHu32(0xf440), 0x80); + j8Ptr[7] = JNZ8(0); + + // if SIO repeater is cleared, reset sdevid + MOV32ItoM((uptr)&rdram_sdevid, 0); + + //kill the busy bit + x86SetJ8(j8Ptr[5]); + x86SetJ8(j8Ptr[6]); + x86SetJ8(j8Ptr[7]); + AND32ItoR(ECX, ~0x80000000); + MOV32RtoM((uptr)&psHu32(0xf430), ECX); + break; + + case 0x1000f440://MCH_DRD: + _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf440], mmreg); + break; + + case 0x1000f590: // DMAC_ENABLEW + _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf520], mmreg); + _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf590], mmreg); + return; + + default: + if ((mem & 0xffffff0f) == 0x1000f200) { + u32 at = mem & 0xf0; + switch(at) + { + case 0x00: + _eeWriteConstMem32((uptr)&PS2MEM_HW[mem&0xffff], mmreg); + break; + case 0x20: + _eeWriteConstMem32OP((uptr)&PS2MEM_HW[mem&0xffff], mmreg, 1); + break; + case 0x30: + _eeWriteConstMem32OP((uptr)&PS2MEM_HW[mem&0xffff], mmreg, 2); + break; + case 0x40: + if( IS_EECONSTREG(mmreg) ) { + if( !(g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0] & 0x100) ) { + AND32ItoM( (uptr)&PS2MEM_HW[mem&0xfffc], ~0x100); + } + else { + OR32ItoM((uptr)&PS2MEM_HW[mem&0xffff], 0x100); + } + } + else { + _eeMoveMMREGtoR(EAX, mmreg); + TEST32ItoR(EAX, 0x100); + j8Ptr[5] = JZ8(0); + OR32ItoM((uptr)&PS2MEM_HW[mem&0xffff], 0x100); + j8Ptr[6] = JMP8(0); + + x86SetJ8( j8Ptr[5] ); + AND32ItoM((uptr)&PS2MEM_HW[mem&0xffff], ~0x100); + + x86SetJ8( j8Ptr[6] ); + } + + break; + case 0x60: + MOV32ItoM((uptr)&PS2MEM_HW[mem&0xffff], 0); + break; + } + return; + } + + _eeWriteConstMem32((uptr)&PS2MEM_HW[mem&0xffff], mmreg); + break; + } +} + +void hwConstWrite64(u32 mem, int mmreg) +{ + if ((mem>=0x10002000) && (mem<=0x10002030)) { + ipuConstWrite64(mem, mmreg); + return; + } + + if ((mem>=0x10003800) && (mem<0x10003c00)) { + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(mem); + CALLFunc((uptr)vif0Write32); + ADD32ItoR(ESP, 8); + return; + } + if ((mem>=0x10003c00) && (mem<0x10004000)) { + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(mem); + CALLFunc((uptr)vif1Write32); + ADD32ItoR(ESP, 8); + return; + } + + switch (mem) { + case GIF_CTRL: + _eeMoveMMREGtoR(EAX, mmreg); + + iFlushCall(0); + TEST8ItoR(EAX, 1); + j8Ptr[5] = JZ8(0); + + // reset GS + CALLFunc((uptr)gsGIFReset); + j8Ptr[6] = JMP8(0); + + x86SetJ8( j8Ptr[5] ); + AND32I8toR(EAX, 8); + MOV32RtoM((uptr)&PS2MEM_HW[mem&0xffff], EAX); + + TEST16ItoR(EAX, 8); + j8Ptr[5] = JZ8(0); + OR8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], 8); + j8Ptr[7] = JMP8(0); + + x86SetJ8( j8Ptr[5] ); + AND8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], ~8); + x86SetJ8( j8Ptr[6] ); + x86SetJ8( j8Ptr[7] ); + return; + + case GIF_MODE: + _eeMoveMMREGtoR(EAX, mmreg); + _eeWriteConstMem32((uptr)&PS2MEM_HW[mem&0xffff], mmreg); + + AND8ItoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], ~5); + AND8ItoR(EAX, 5); + OR8RtoM((uptr)&PS2MEM_HW[GIF_STAT&0xffff], EAX); + break; + + case GIF_STAT: // stat is readonly + return; + + case 0x1000a000: // dma2 - gif + recDmaExec(dmaGIF, mem, mmreg); + break; + + case 0x1000e010: // DMAC_STAT + _eeMoveMMREGtoR(EAX, mmreg); + + iFlushCall(0); + MOV32RtoR(ECX, EAX); + SHR32ItoR(EAX, 16); + NOT32R(ECX); + XOR16RtoM((uptr)&PS2MEM_HW[0xe012], EAX); + AND16RtoM((uptr)&PS2MEM_HW[0xe010], ECX); + + CALLFunc((uptr)cpuTestDMACInts); + break; + + case 0x1000f590: // DMAC_ENABLEW + _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf520], mmreg); + _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf590], mmreg); + break; + + case 0x1000f000: // INTC_STAT + _eeWriteConstMem32OP((uptr)&PS2MEM_HW[mem&0xffff], mmreg, 2); + CALLFunc((uptr)cpuTestINTCInts); + break; + + case 0x1000f010: // INTC_MASK + + _eeMoveMMREGtoR(EAX, mmreg); + + iFlushCall(0); + XOR16RtoM((uptr)&PS2MEM_HW[0xf010], EAX); + CALLFunc((uptr)cpuTestINTCInts); + break; + + case 0x1000f130: + case 0x1000f410: + case 0x1000f430: + break; + default: + + _eeWriteConstMem64((uptr)PSM(mem), mmreg); + break; + } +} + +void hwConstWrite128(u32 mem, int mmreg) +{ + if (mem >= 0x10004000 && mem < 0x10008000) { + _eeWriteConstMem128((uptr)&s_TempFIFO[0], mmreg); + iFlushCall(0); + PUSH32I((uptr)&s_TempFIFO[0]); + PUSH32I(mem); + CALLFunc((uptr)WriteFIFO); + ADD32ItoR(ESP, 8); + return; + } + + switch (mem) { + case 0x1000f590: // DMAC_ENABLEW + _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf520], mmreg); + _eeWriteConstMem32((uptr)&PS2MEM_HW[0xf590], mmreg); + break; + case 0x1000f130: + case 0x1000f410: + case 0x1000f430: + break; + + default: + + _eeWriteConstMem128((uptr)&PS2MEM_HW[mem&0xffff], mmreg); + break; + } +} diff --git a/pcsx2/x86/iIPU.cpp b/pcsx2/x86/iIPU.cpp new file mode 100644 index 0000000000..75bca3c25b --- /dev/null +++ b/pcsx2/x86/iIPU.cpp @@ -0,0 +1,203 @@ +/* Pcsx2 - Pc Ps2 Emulator +* Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "iR5900.h" + +#include "IPU.h" + +/////////////////////////////////////////////////////////////////////// +// IPU Register Reads + +int ipuConstRead32(u32 x86reg, u32 mem) +{ + int workingreg, tempreg, tempreg2; + iFlushCall(0); + CALLFunc((u32)IPUProcessInterrupt); + + // if( !(x86reg&(MEM_XMMTAG|MEM_MMXTAG)) ) { + // if( x86reg == EAX ) { + // tempreg = ECX; + // tempreg2 = EDX; + // } + // else if( x86reg == ECX ) { + // tempreg = EAX; + // tempreg2 = EDX; + // } + // else if( x86reg == EDX ) { + // tempreg = EAX; + // tempreg2 = ECX; + // } + // + // workingreg = x86reg; + // } + // else { + workingreg = EAX; + tempreg = ECX; + tempreg2 = EDX; + // } + + switch (mem){ + + case 0x10002010: // IPU_CTRL + + MOV32MtoR(workingreg, (u32)&ipuRegs->ctrl._u32); + AND32ItoR(workingreg, ~0x3f0f); // save OFC + OR8MtoR(workingreg, (u32)&g_BP.IFC); + OR8MtoR(workingreg+4, (u32)&coded_block_pattern); // or ah, mem + + // MOV32MtoR(workingreg, (u32)&ipuRegs->ctrl._u32); + // AND32ItoR(workingreg, ~0x3fff); + // MOV32MtoR(tempreg, (u32)&g_nIPU0Data); + // MOV8MtoR(workingreg, (u32)&g_BP.IFC); + // + // CMP32ItoR(tempreg, 8); + // j8Ptr[5] = JLE8(0); + // MOV32ItoR(tempreg, 8); + // x86SetJ8( j8Ptr[5] ); + // SHL32ItoR(tempreg, 4); + // + // OR8MtoR(workingreg+4, (u32)&coded_block_pattern); // or ah, mem + // OR8RtoR(workingreg, tempreg); + +#ifdef _DEBUG + MOV32RtoM((u32)&ipuRegs->ctrl._u32, workingreg); +#endif + // NOTE: not updating ipuRegs->ctrl + // if( x86reg & MEM_XMMTAG ) SSE2_MOVD_R_to_XMM(x86reg&0xf, workingreg); + // else if( x86reg & MEM_MMXTAG ) MOVD32RtoMMX(x86reg&0xf, workingreg); + return 1; + + case 0x10002020: // IPU_BP + + assert( (u32)&g_BP.FP + 1 == (u32)&g_BP.bufferhasnew ); + + MOVZX32M8toR(workingreg, (u32)&g_BP.BP); + MOVZX32M8toR(tempreg, (u32)&g_BP.FP); + AND8ItoR(workingreg, 0x7f); + ADD8MtoR(tempreg, (u32)&g_BP.bufferhasnew); + MOV8MtoR(workingreg+4, (u32)&g_BP.IFC); + + SHL32ItoR(tempreg, 16); + OR32RtoR(workingreg, tempreg); + +#ifdef _DEBUG + MOV32RtoM((u32)&ipuRegs->ipubp, workingreg); +#endif + // NOTE: not updating ipuRegs->ipubp + // if( x86reg & MEM_XMMTAG ) SSE2_MOVD_R_to_XMM(x86reg&0xf, workingreg); + // else if( x86reg & MEM_MMXTAG ) MOVD32RtoMMX(x86reg&0xf, workingreg); + + return 1; + + default: + // ipu repeats every 0x100 + _eeReadConstMem32(x86reg, (u32)(((u8*)ipuRegs)+(mem&0xff))); + return 0; + } + + return 0; +} + +void ipuConstRead64(u32 mem, int mmreg) +{ + iFlushCall(0); + CALLFunc((u32)IPUProcessInterrupt); + + if( IS_XMMREG(mmreg) ) SSE_MOVLPS_M64_to_XMM(mmreg&0xff, (u32)(((u8*)ipuRegs)+(mem&0xff))); + else { + MOVQMtoR(mmreg, (u32)(((u8*)ipuRegs)+(mem&0xff))); + SetMMXstate(); + } +} + +/////////////////////////////////////////////////////////////////////// +// IPU Register Writes! + +void ipuConstWrite32(u32 mem, int mmreg) +{ + iFlushCall(0); + if( !(mmreg & (MEM_XMMTAG|MEM_MMXTAG|MEM_EECONSTTAG)) ) PUSH32R(mmreg); + CALLFunc((u32)IPUProcessInterrupt); + + switch (mem){ + case 0x10002000: // IPU_CMD + if( (mmreg & (MEM_XMMTAG|MEM_MMXTAG|MEM_EECONSTTAG)) ) _recPushReg(mmreg); + CALLFunc((u32)IPUCMD_WRITE); + ADD32ItoR(ESP, 4); + break; + case 0x10002010: // IPU_CTRL + if( mmreg & MEM_EECONSTTAG ) { + u32 c = g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]&0x47f30000; + + if( c & 0x40000000 ) { + CALLFunc((u32)ipuSoftReset); + } + else { + AND32ItoM((u32)&ipuRegs->ctrl._u32, 0x8000ffff); + OR32ItoM((u32)&ipuRegs->ctrl._u32, c); + } + } + else { + if( mmreg & MEM_XMMTAG ) SSE2_MOVD_XMM_to_R(EAX, mmreg&0xf); + else if( mmreg & MEM_MMXTAG ) MOVD32MMXtoR(EAX, mmreg&0xf); + else POP32R(EAX); + + MOV32MtoR(ECX, (u32)&ipuRegs->ctrl._u32); + AND32ItoR(EAX, 0x47f30000); + AND32ItoR(ECX, 0x8000ffff); + OR32RtoR(EAX, ECX); + MOV32RtoM((u32)&ipuRegs->ctrl._u32, EAX); + + TEST32ItoR(EAX, 0x40000000); + j8Ptr[5] = JZ8(0); + + // reset + CALLFunc((u32)ipuSoftReset); + + x86SetJ8( j8Ptr[5] ); + } + + break; + default: + if( !(mmreg & (MEM_XMMTAG|MEM_MMXTAG|MEM_EECONSTTAG)) ) POP32R(mmreg); + _eeWriteConstMem32((u32)((u8*)ipuRegs + (mem&0xfff)), mmreg); + break; + } +} + +void ipuConstWrite64(u32 mem, int mmreg) +{ + iFlushCall(0); + CALLFunc((u32)IPUProcessInterrupt); + + switch (mem){ + case 0x10002000: + _recPushReg(mmreg); + CALLFunc((u32)IPUCMD_WRITE); + ADD32ItoR(ESP, 4); + break; + + default: + _eeWriteConstMem64( (u32)((u8*)ipuRegs + (mem&0xfff)), mmreg); + break; + } +} diff --git a/pcsx2/x86/iMMI.cpp b/pcsx2/x86/iMMI.cpp new file mode 100644 index 0000000000..82073394b7 --- /dev/null +++ b/pcsx2/x86/iMMI.cpp @@ -0,0 +1,3413 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +/********************************************************* +* cached MMI opcodes * +* * +*********************************************************/ + +#include "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "iMMI.h" + +namespace Interp = R5900::Interpreter::OpcodeImpl::MMI; + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { +namespace MMI +{ + +#ifndef MMI_RECOMPILE + +REC_FUNC_DEL( PLZCW, _Rd_ ); + +REC_FUNC_DEL( PMFHL, _Rd_ ); +REC_FUNC_DEL( PMTHL, _Rd_ ); + +REC_FUNC_DEL( PSRLW, _Rd_ ); +REC_FUNC_DEL( PSRLH, _Rd_ ); + +REC_FUNC_DEL( PSRAH, _Rd_ ); +REC_FUNC_DEL( PSRAW, _Rd_ ); + +REC_FUNC_DEL( PSLLH, _Rd_ ); +REC_FUNC_DEL( PSLLW, _Rd_ ); + +#else + +void recPLZCW() +{ + int regd = -1; + int regs = 0; + + if ( ! _Rd_ ) return; + + if( GPR_IS_CONST1(_Rs_) ) { + _eeOnWriteReg(_Rd_, 0); + _deleteEEreg(_Rd_, 0); + GPR_SET_CONST(_Rd_); + + for(regs = 0; regs < 2; ++regs) { + u32 val = g_cpuConstRegs[_Rs_].UL[regs]; + + if( val != 0 ) { + u32 setbit = val&0x80000000; + g_cpuConstRegs[_Rd_].UL[regs] = 0; + val <<= 1; + + while((val & 0x80000000) == setbit) { + g_cpuConstRegs[_Rd_].UL[regs]++; + val <<= 1; + } + } + else { + g_cpuConstRegs[_Rd_].UL[regs] = 31; + } + } + return; + } + + _eeOnWriteReg(_Rd_, 0); + + if( (regs = _checkXMMreg(XMMTYPE_GPRREG, _Rs_, MODE_READ)) >= 0 ) { + SSE2_MOVD_XMM_to_R(EAX, regs); + regs |= MEM_XMMTAG; + } + else if( (regs = _checkMMXreg(MMX_GPR+_Rs_, MODE_READ)) >= 0 ) { + MOVD32MMXtoR(EAX, regs); + SetMMXstate(); + regs |= MEM_MMXTAG; + } + else { + MOV32MtoR(EAX, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + regs = 0; + } + + if( EEINST_ISLIVE1(_Rd_) ) + _deleteEEreg(_Rd_, 0); + else { + if( (regd = _checkMMXreg(MMX_GPR+_Rd_, MODE_WRITE)) < 0 ) + _deleteEEreg(_Rd_, 0); + } + + // Count the number of leading bits (MSB) that match the sign bit, excluding the sign + // bit itself. + + // Strategy: If the sign bit is set, then negate the value. And that way the same + // bitcompare can be used for either bit status. but be warned! BSR returns undefined + // results if the EAX is zero, so we need to have special checks for zeros before + // using it. + + // --- first word --- + + MOV32ItoR(ECX,31); + TEST32RtoR(EAX, EAX); // TEST sets the sign flag accordingly. + u8* label_notSigned = JNS8(0); + NOT32R(EAX); + x86SetJ8(label_notSigned); + + BSRRtoR(EAX, EAX); + u8* label_Zeroed = JZ8(0); // If BSR sets the ZF, eax is "trash" + SUB32RtoR(ECX, EAX); + DEC32R(ECX); // PS2 doesn't count the first bit + + x86SetJ8(label_Zeroed); + if( EEINST_ISLIVE1(_Rd_) || regd < 0 ) { + MOV32RtoM((uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], ECX); + } + else { + SetMMXstate(); + MOVD32RtoMMX(regd, ECX); + } + + // second word + + if( EEINST_ISLIVE1(_Rd_) ) { + if( regs >= 0 && (regs & MEM_XMMTAG) ) { + SSE2_PSHUFD_XMM_to_XMM(regs&0xf, regs&0xf, 0x4e); + SSE2_MOVD_XMM_to_R(EAX, regs&0xf); + SSE2_PSHUFD_XMM_to_XMM(regs&0xf, regs&0xf, 0x4e); + } + else if( regs >= 0 && (regs & MEM_MMXTAG) ) { + PSHUFWRtoR(regs, regs, 0x4e); + MOVD32MMXtoR(EAX, regs&0xf); + PSHUFWRtoR(regs&0xf, regs&0xf, 0x4e); + SetMMXstate(); + } + else MOV32MtoR(EAX, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ]); + + MOV32ItoR(ECX, 31); + TEST32RtoR(EAX, EAX); // TEST sets the sign flag accordingly. + label_notSigned = JNS8(0); + NOT32R(EAX); + x86SetJ8(label_notSigned); + + BSRRtoR(EAX, EAX); + u8* label_Zeroed = JZ8(0); // If BSR sets the ZF, eax is "trash" + SUB32RtoR(ECX, EAX); + DEC32R(ECX); // PS2 doesn't count the first bit + + x86SetJ8(label_Zeroed); + MOV32RtoM((uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], ECX); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + } + + GPR_DEL_CONST(_Rd_); +} + +static u32 PCSX2_ALIGNED16(s_CmpMasks[]) = { 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff }; + +void recPMFHL() +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_WRITED|XMMINFO_READLO|XMMINFO_READHI) + + int t0reg; + + switch (_Sa_) { + case 0x00: // LW + + t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_PSHUFD_XMM_to_XMM(t0reg, EEREC_HI, 0x88); + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_LO, 0x88); + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_D, t0reg); + + _freeXMMreg(t0reg); + break; + + case 0x01: // UW + t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_PSHUFD_XMM_to_XMM(t0reg, EEREC_HI, 0xdd); + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_LO, 0xdd); + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + break; + + case 0x02: // SLW + // fall to interp + MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (uptr)&cpuRegs.pc, pc ); + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + _deleteEEreg(XMMGPR_LO, 1); + _deleteEEreg(XMMGPR_HI, 1); + iFlushCall(FLUSH_CACHED_REGS); // since calling CALLFunc + CALLFunc( (uptr)R5900::Interpreter::OpcodeImpl::MMI::PMFHL ); + break; + + case 0x03: // LH + t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_PSHUFLW_XMM_to_XMM(t0reg, EEREC_HI, 0x88); + SSE2_PSHUFLW_XMM_to_XMM(EEREC_D, EEREC_LO, 0x88); + SSE2_PSHUFHW_XMM_to_XMM(t0reg, t0reg, 0x88); + SSE2_PSHUFHW_XMM_to_XMM(EEREC_D, EEREC_D, 0x88); + SSE2_PSRLDQ_I8_to_XMM(t0reg, 4); + SSE2_PSRLDQ_I8_to_XMM(EEREC_D, 4); + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + break; + + case 0x04: // SH + if( EEREC_D == EEREC_HI ) { + SSE2_PACKSSDW_XMM_to_XMM(EEREC_D, EEREC_LO); + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_D, 0x72); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_LO); + SSE2_PACKSSDW_XMM_to_XMM(EEREC_D, EEREC_HI); + + // shuffle so a1a0b1b0->a1b1a0b0 + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_D, 0xd8); + } + break; + default: + Console::Error("PMFHL?? *pcsx2 head esplode!*"); + assert(0); + } + +CPU_SSE_XMMCACHE_END + + recCall( Interp::PMFHL, _Rd_ ); +} + +void recPMTHL() +{ + recCall( Interp::PMTHL, 0 ); +} + +// MMX helper routines +#define MMX_ALLOC_TEMP1(code) { \ + int t0reg; \ + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); \ + code; \ + _freeMMXreg(t0reg); \ +} \ + +#define MMX_ALLOC_TEMP2(code) { \ + int t0reg, t1reg; \ + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); \ + t1reg = _allocMMXreg(-1, MMX_TEMP, 0); \ + code; \ + _freeMMXreg(t0reg); \ + _freeMMXreg(t1reg); \ +} \ + +#define MMX_ALLOC_TEMP3(code) { \ + int t0reg, t1reg, t2reg; \ + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); \ + t1reg = _allocMMXreg(-1, MMX_TEMP, 0); \ + t2reg = _allocMMXreg(-1, MMX_TEMP, 0); \ + code; \ + _freeMMXreg(t0reg); \ + _freeMMXreg(t1reg); \ + _freeMMXreg(t2reg); \ +} \ + +#define MMX_ALLOC_TEMP4(code) { \ + int t0reg, t1reg, t2reg, t3reg; \ + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); \ + t1reg = _allocMMXreg(-1, MMX_TEMP, 0); \ + t2reg = _allocMMXreg(-1, MMX_TEMP, 0); \ + t3reg = _allocMMXreg(-1, MMX_TEMP, 0); \ + code; \ + _freeMMXreg(t0reg); \ + _freeMMXreg(t1reg); \ + _freeMMXreg(t2reg); \ + _freeMMXreg(t3reg); \ +} \ + +//////////////////////////////////////////////////// +void recPSRLH( void ) +{ + if ( !_Rd_ ) return; + + CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + if( (_Sa_&0xf) == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + return; + } + + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRLW_I8_to_XMM(EEREC_D,_Sa_&0xf ); + CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSRLWItoR( t0reg, _Sa_&0xf ); + PSRLWItoR( t1reg, _Sa_&0xf ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPSRLW( void ) +{ + if( !_Rd_ ) return; + + CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + if( _Sa_ == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + return; + } + + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRLD_I8_to_XMM(EEREC_D,_Sa_ ); + CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSRLDItoR( t0reg, _Sa_ ); + PSRLDItoR( t1reg, _Sa_ ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPSRAH( void ) +{ + if ( !_Rd_ ) return; + + CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + if( (_Sa_&0xf) == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + return; + } + + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRAW_I8_to_XMM(EEREC_D,_Sa_&0xf ); + CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSRAWItoR( t0reg, _Sa_&0xf ); + PSRAWItoR( t1reg, _Sa_&0xf ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPSRAW( void ) +{ + if ( !_Rd_ ) return; + + CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + if( _Sa_ == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + return; + } + + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRAD_I8_to_XMM(EEREC_D,_Sa_ ); + CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSRADItoR( t0reg, _Sa_ ); + PSRADItoR( t1reg, _Sa_ ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPSLLH( void ) +{ + if ( !_Rd_ ) return; + + CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + if( (_Sa_&0xf) == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + return; + } + + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSLLW_I8_to_XMM(EEREC_D,_Sa_&0xf ); + CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSLLWItoR( t0reg, _Sa_&0xf ); + PSLLWItoR( t1reg, _Sa_&0xf ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPSLLW( void ) +{ + if ( !_Rd_ ) return; + + CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + if( _Sa_ == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + return; + } + + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSLLD_I8_to_XMM(EEREC_D,_Sa_ ); + CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSLLDItoR( t0reg, _Sa_ ); + PSLLDItoR( t1reg, _Sa_ ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +/* +void recMADD( void ) +{ +} + +void recMADDU( void ) +{ +} + +void recPLZCW( void ) +{ +} +*/ + +#endif + +/********************************************************* +* MMI0 opcodes * +* * +*********************************************************/ +#ifndef MMI0_RECOMPILE + +REC_FUNC_DEL( PADDB, _Rd_); +REC_FUNC_DEL( PADDH, _Rd_); +REC_FUNC_DEL( PADDW, _Rd_); +REC_FUNC_DEL( PADDSB, _Rd_); +REC_FUNC_DEL( PADDSH, _Rd_); +REC_FUNC_DEL( PADDSW, _Rd_); +REC_FUNC_DEL( PSUBB, _Rd_); +REC_FUNC_DEL( PSUBH, _Rd_); +REC_FUNC_DEL( PSUBW, _Rd_); +REC_FUNC_DEL( PSUBSB, _Rd_); +REC_FUNC_DEL( PSUBSH, _Rd_); +REC_FUNC_DEL( PSUBSW, _Rd_); + +REC_FUNC_DEL( PMAXW, _Rd_); +REC_FUNC_DEL( PMAXH, _Rd_); + +REC_FUNC_DEL( PCGTW, _Rd_); +REC_FUNC_DEL( PCGTH, _Rd_); +REC_FUNC_DEL( PCGTB, _Rd_); + +REC_FUNC_DEL( PEXTLW, _Rd_); + +REC_FUNC_DEL( PPACW, _Rd_); +REC_FUNC_DEL( PEXTLH, _Rd_); +REC_FUNC_DEL( PPACH, _Rd_); +REC_FUNC_DEL( PEXTLB, _Rd_); +REC_FUNC_DEL( PPACB, _Rd_); +REC_FUNC_DEL( PEXT5, _Rd_); +REC_FUNC_DEL( PPAC5, _Rd_); + +#else + +//////////////////////////////////////////////////// +void recPMAXW() +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + int t0reg; + + if( EEREC_S == EEREC_T ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + return; + } + + t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSE2_PCMPGTD_XMM_to_XMM(t0reg, EEREC_T); + + if( EEREC_D == EEREC_S ) { + SSEX_PAND_XMM_to_XMM(EEREC_D, t0reg); + SSEX_PANDN_XMM_to_XMM(t0reg, EEREC_T); + } + else if( EEREC_D == EEREC_T ) { + int t1reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t1reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSEX_PAND_XMM_to_XMM(EEREC_D, t0reg); + SSEX_PANDN_XMM_to_XMM(t0reg, t1reg); + _freeXMMreg(t1reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSEX_PAND_XMM_to_XMM(EEREC_D, t0reg); + SSEX_PANDN_XMM_to_XMM(t0reg, EEREC_T); + } + + SSEX_POR_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); +CPU_SSE_XMMCACHE_END + + recCall( Interp::PMAXW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPPACW() +{ + if ( ! _Rd_ ) return; + +CPU_SSE_XMMCACHE_START(((_Rs_!=0)?XMMINFO_READS:0)|XMMINFO_READT|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_T, 0x88); + SSE2_PSRLDQ_I8_to_XMM(EEREC_D, 8); + } + else { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + if( EEREC_D == EEREC_T ) { + SSE2_PSHUFD_XMM_to_XMM(t0reg, EEREC_S, 0x88); + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_T, 0x88); + SSE2_PUNPCKLQDQ_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSE2_PSHUFD_XMM_to_XMM(t0reg, EEREC_T, 0x88); + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_S, 0x88); + SSE2_PUNPCKLQDQ_XMM_to_XMM(t0reg, EEREC_D); + + // swap mmx regs.. don't ask + xmmregs[t0reg] = xmmregs[EEREC_D]; + xmmregs[EEREC_D].inuse = 0; + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + //Done - Refraction - Crude but quicker than int + MOV32MtoR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].UL[2]); //Copy this one cos it could get overwritten + + MOV32MtoR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UL[2]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[3], EAX); + MOV32MtoR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UL[0]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[2], EAX); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[1], ECX); //This is where we bring it back + MOV32MtoR( EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UL[0]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[0], EAX); +} + +void recPPACH( void ) +{ + if (!_Rd_) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|XMMINFO_READT|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + SSE2_PSHUFLW_XMM_to_XMM(EEREC_D, EEREC_T, 0x88); + SSE2_PSHUFHW_XMM_to_XMM(EEREC_D, EEREC_D, 0x88); + SSE2_PSLLDQ_I8_to_XMM(EEREC_D, 4); + SSE2_PSRLDQ_I8_to_XMM(EEREC_D, 8); + } + else { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_PSHUFLW_XMM_to_XMM(t0reg, EEREC_S, 0x88); + SSE2_PSHUFLW_XMM_to_XMM(EEREC_D, EEREC_T, 0x88); + SSE2_PSHUFHW_XMM_to_XMM(t0reg, t0reg, 0x88); + SSE2_PSHUFHW_XMM_to_XMM(EEREC_D, EEREC_D, 0x88); + + SSE2_PSRLDQ_I8_to_XMM(t0reg, 4); + SSE2_PSRLDQ_I8_to_XMM(EEREC_D, 4); + SSE2_PUNPCKLQDQ_XMM_to_XMM(EEREC_D, t0reg); + + _freeXMMreg(t0reg); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + //Done - Refraction - Crude but quicker than int + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].US[6]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[7], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].US[6]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[3], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].US[2]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[5], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].US[2]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[1], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].US[4]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[6], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].US[4]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[2], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].US[0]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[4], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].US[0]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[0], EAX); +} + +//////////////////////////////////////////////////// +void recPPACB() +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|XMMINFO_READT|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + if( _hasFreeXMMreg() ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSLLW_I8_to_XMM(EEREC_D, 8); + SSEX_PXOR_XMM_to_XMM(t0reg, t0reg); + SSE2_PSRLW_I8_to_XMM(EEREC_D, 8); + SSE2_PACKUSWB_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSLLW_I8_to_XMM(EEREC_D, 8); + SSE2_PSRLW_I8_to_XMM(EEREC_D, 8); + SSE2_PACKUSWB_XMM_to_XMM(EEREC_D, EEREC_D); + SSE2_PSRLDQ_I8_to_XMM(EEREC_D, 8); + } + } + else { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSLLW_I8_to_XMM(t0reg, 8); + SSE2_PSLLW_I8_to_XMM(EEREC_D, 8); + SSE2_PSRLW_I8_to_XMM(t0reg, 8); + SSE2_PSRLW_I8_to_XMM(EEREC_D, 8); + + SSE2_PACKUSWB_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } +CPU_SSE_XMMCACHE_END + + recCall( Interp::PPACB, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPEXT5() +{ + recCall( Interp::PEXT5, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPPAC5() +{ + recCall( Interp::PPAC5, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPMAXH( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PMAXSW_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PMAXSW_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PMAXSW_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + SSE_PMAXSW_MM_to_MM( t0reg, t1reg ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + SSE_PMAXSW_MM_to_MM( t2reg, t3reg); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t2reg); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPCGTB( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D != EEREC_T ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PCMPGTB_XMM_to_XMM(EEREC_D, EEREC_T); + } + else { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PCMPGTB_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + PCMPGTBRtoR( t0reg, t1reg ); + + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PCMPGTBRtoR( t2reg, t3reg); + + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t2reg); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPCGTH( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D != EEREC_T ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PCMPGTW_XMM_to_XMM(EEREC_D, EEREC_T); + } + else { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PCMPGTW_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + + PCMPGTWRtoR( t0reg, t1reg ); + PCMPGTWRtoR( t2reg, t3reg); + + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t2reg); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPCGTW( void ) +{ + //TODO:optimize RS | RT== 0 + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D != EEREC_T ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PCMPGTD_XMM_to_XMM(EEREC_D, EEREC_T); + } + else { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PCMPGTD_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + + PCMPGTDRtoR( t0reg, t1reg ); + PCMPGTDRtoR( t2reg, t3reg); + + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t2reg); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPADDSB( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PADDSB_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PADDSB_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PADDSB_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PADDSBRtoR( t0reg, t2reg); + PADDSBRtoR( t1reg, t3reg); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPADDSH( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PADDSW_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PADDSW_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PADDSW_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PADDSWRtoR( t0reg, t2reg); + PADDSWRtoR( t1reg, t3reg); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +//NOTE: check kh2 movies if changing this +void recPADDSW( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + int t1reg = _allocTempXMMreg(XMMT_INT, -1); + int t2reg = _allocTempXMMreg(XMMT_INT, -1); + int t3reg = _allocTempXMMreg(XMMT_INT, -1); + + if ( cpucaps.hasStreamingSIMD4Extensions ) { + SSE4_PMOVSXDQ_XMM_to_XMM(t0reg, EEREC_S); + SSE4_PMOVSXDQ_XMM_to_XMM(t1reg, EEREC_T); + SSE2_PADDQ_XMM_to_XMM(t0reg, t1reg); + SSE2_PSHUFD_XMM_to_XMM(t1reg, EEREC_S, 0x0e); + SSE2_PSHUFD_XMM_to_XMM(t2reg, EEREC_T, 0x0e); + SSE4_PMOVSXDQ_XMM_to_XMM(t1reg, t1reg); + SSE4_PMOVSXDQ_XMM_to_XMM(t2reg, t2reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(t1reg, EEREC_T); + SSE2_PXOR_XMM_to_XMM(t2reg, t2reg); + SSE2_PXOR_XMM_to_XMM(t3reg, t3reg); + SSE2_PCMPGTD_XMM_to_XMM(t2reg, t0reg); + SSE2_PCMPGTD_XMM_to_XMM(t3reg, t1reg); + SSE2_PUNPCKLDQ_XMM_to_XMM(t0reg, t2reg); + SSE2_PUNPCKLDQ_XMM_to_XMM(t1reg, t3reg); + SSE2_PADDQ_XMM_to_XMM(t0reg, t1reg); + SSEX_MOVDQA_XMM_to_XMM(t1reg, EEREC_S); + SSE2_PUNPCKHDQ_XMM_to_XMM(t1reg, t2reg); + SSEX_MOVDQA_XMM_to_XMM(t2reg, EEREC_T); + SSE2_PUNPCKHDQ_XMM_to_XMM(t2reg, t3reg); + } + SSE2_PADDQ_XMM_to_XMM(t1reg, t2reg); + /* + t0reg = { Rs[0]+Rt[0], Rs[1]+Rt[1] } + t1reg = { Rs[2]+Rt[2], Rs[3]+Rt[3] } + */ + + SSEX_MOVDQA_XMM_to_XMM(t2reg, t0reg); + SSE_SHUFPS_XMM_to_XMM(t2reg, t1reg, 0xdd); + SSE2_PSRAD_I8_to_XMM(t2reg, 31); + /* + t2reg = { (Rs[0]+Rt[0]) < 0 ? 0xFFFFFFFF : 0, + (Rs[1]+Rt[1]) < 0 ? 0xFFFFFFFF : 0, + (Rs[2]+Rt[2]) < 0 ? 0xFFFFFFFF : 0, + (Rs[3]+Rt[3]) < 0 ? 0xFFFFFFFF : 0 } + */ + + SSE2_PSHUFD_XMM_to_XMM(t3reg, t2reg, 0x50); + SSE2_PXOR_XMM_to_XMM(t0reg, t3reg); + SSE2_PSRLQ_I8_to_XMM(t3reg, 63); + SSE2_PADDQ_XMM_to_XMM(t0reg, t3reg); + /* + t0reg = { abs(Rs[0]+Rt[0]), abs(Rs[1]+Rt[1]) } + */ + SSE2_PSHUFD_XMM_to_XMM(t3reg, t2reg, 0xfa); + SSE2_PXOR_XMM_to_XMM(t1reg, t3reg); + SSE2_PSRLQ_I8_to_XMM(t3reg, 63); + SSE2_PADDQ_XMM_to_XMM(t1reg, t3reg); + /* + t1reg = { abs(Rs[2]+Rt[2]), abs(Rs[3]+Rt[3]) } + */ + SSE2_PSLLQ_I8_to_XMM(t0reg, 1); + SSE2_PSLLQ_I8_to_XMM(t1reg, 1); + SSE2_PCMPEQB_XMM_to_XMM(t3reg, t3reg); + SSE2_PSRLD_I8_to_XMM(t3reg, 1); + SSE2_PXOR_XMM_to_XMM(t2reg, t3reg); + SSE_SHUFPS_XMM_to_XMM(t0reg, t1reg, 0xdd); + SSE2_PXOR_XMM_to_XMM(t1reg, t1reg); + SSE2_PCMPEQD_XMM_to_XMM(t1reg, t0reg); + /* + t1reg = { abs(Rs[0]+Rt[0]) > 0x7FFFFFFF ? 0 : 0xFFFFFFFF, + abs(Rs[1]+Rt[1]) > 0x7FFFFFFF ? 0 : 0xFFFFFFFF, + abs(Rs[2]+Rt[2]) > 0x7FFFFFFF ? 0 : 0xFFFFFFFF, + abs(Rs[3]+Rt[3]) > 0x7FFFFFFF ? 0 : 0xFFFFFFFF } + t2reg = { (Rs[0]+Rt[0]) < 0 ? 0x80000000 : 0x7FFFFFFF, + (Rs[1]+Rt[1]) < 0 ? 0x80000000 : 0x7FFFFFFF, + (Rs[2]+Rt[2]) < 0 ? 0x80000000 : 0x7FFFFFFF, + (Rs[3]+Rt[3]) < 0 ? 0x80000000 : 0x7FFFFFFF } + */ + if( EEREC_D == EEREC_S ) SSE2_PADDD_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PADDD_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PADDD_XMM_to_XMM(EEREC_D, EEREC_T); + } + SSE2_PAND_XMM_to_XMM(EEREC_D, t1reg); + SSE2_PANDN_XMM_to_XMM(t1reg, t2reg); + SSE2_POR_XMM_to_XMM(EEREC_D, t1reg); + /* + Rd = { t1reg[0] ? Rs[0]+Rt[0] : t2reg[0], + t1reg[1] ? Rs[1]+Rt[1] : t2reg[1], + t1reg[2] ? Rs[2]+Rt[2] : t2reg[2], + t1reg[3] ? Rs[3]+Rt[3] : t2reg[3] } + */ + _freeXMMreg(t0reg); + _freeXMMreg(t1reg); + _freeXMMreg(t2reg); + _freeXMMreg(t3reg); +CPU_SSE_XMMCACHE_END + + if( _Rd_ ) _deleteEEreg(_Rd_, 0); + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + _flushConstRegs(); + + MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (uptr)&cpuRegs.pc, pc ); + CALLFunc( (uptr)R5900::Interpreter::OpcodeImpl::MMI::PADDSW ); +} + +//////////////////////////////////////////////////// +void recPSUBSB( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PSUBSB_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBSB_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBSB_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSUBSBRtoR( t0reg, t2reg); + PSUBSBRtoR( t1reg, t3reg); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPSUBSH( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PSUBSW_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBSW_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBSW_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSUBSWRtoR( t0reg, t2reg); + PSUBSWRtoR( t1reg, t3reg); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +//NOTE: check kh2 movies if changing this +void recPSUBSW( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + int t1reg = _allocTempXMMreg(XMMT_INT, -1); + int t2reg = _allocTempXMMreg(XMMT_INT, -1); + int t3reg = _allocTempXMMreg(XMMT_INT, -1); + + if ( cpucaps.hasStreamingSIMD4Extensions ) { + SSE4_PMOVSXDQ_XMM_to_XMM(t0reg, EEREC_S); + SSE4_PMOVSXDQ_XMM_to_XMM(t1reg, EEREC_T); + SSE2_PSUBQ_XMM_to_XMM(t0reg, t1reg); + SSE2_PSHUFD_XMM_to_XMM(t1reg, EEREC_S, 0x0e); + SSE2_PSHUFD_XMM_to_XMM(t2reg, EEREC_T, 0x0e); + SSE4_PMOVSXDQ_XMM_to_XMM(t1reg, t1reg); + SSE4_PMOVSXDQ_XMM_to_XMM(t2reg, t2reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(t1reg, EEREC_T); + SSE2_PXOR_XMM_to_XMM(t2reg, t2reg); + SSE2_PXOR_XMM_to_XMM(t3reg, t3reg); + SSE2_PCMPGTD_XMM_to_XMM(t2reg, t0reg); + SSE2_PCMPGTD_XMM_to_XMM(t3reg, t1reg); + SSE2_PUNPCKLDQ_XMM_to_XMM(t0reg, t2reg); + SSE2_PUNPCKLDQ_XMM_to_XMM(t1reg, t3reg); + SSE2_PSUBQ_XMM_to_XMM(t0reg, t1reg); + SSEX_MOVDQA_XMM_to_XMM(t1reg, EEREC_S); + SSE2_PUNPCKHDQ_XMM_to_XMM(t1reg, t2reg); + SSEX_MOVDQA_XMM_to_XMM(t2reg, EEREC_T); + SSE2_PUNPCKHDQ_XMM_to_XMM(t2reg, t3reg); + } + SSE2_PSUBQ_XMM_to_XMM(t1reg, t2reg); + + SSEX_MOVDQA_XMM_to_XMM(t2reg, t0reg); + SSE_SHUFPS_XMM_to_XMM(t2reg, t1reg, 0xdd); + SSE2_PSRAD_I8_to_XMM(t2reg, 31); + + SSE2_PSHUFD_XMM_to_XMM(t3reg, t2reg, 0x50); + SSE2_PXOR_XMM_to_XMM(t0reg, t3reg); + SSE2_PSRLQ_I8_to_XMM(t3reg, 63); + SSE2_PADDQ_XMM_to_XMM(t0reg, t3reg); + SSE2_PSHUFD_XMM_to_XMM(t3reg, t2reg, 0xfa); + SSE2_PXOR_XMM_to_XMM(t1reg, t3reg); + SSE2_PSRLQ_I8_to_XMM(t3reg, 63); + SSE2_PADDQ_XMM_to_XMM(t1reg, t3reg); + SSE2_PSLLQ_I8_to_XMM(t0reg, 1); + SSE2_PSLLQ_I8_to_XMM(t1reg, 1); + SSE2_PCMPEQB_XMM_to_XMM(t3reg, t3reg); + SSE2_PSRLD_I8_to_XMM(t3reg, 1); + SSE2_PXOR_XMM_to_XMM(t2reg, t3reg); + SSE_SHUFPS_XMM_to_XMM(t0reg, t1reg, 0xdd); + SSE2_PXOR_XMM_to_XMM(t1reg, t1reg); + SSE2_PCMPEQD_XMM_to_XMM(t1reg, t0reg); + if( EEREC_D == EEREC_S ) SSE2_PSUBD_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) { + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBD_XMM_to_XMM(EEREC_D, t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBD_XMM_to_XMM(EEREC_D, EEREC_T); + } + SSE2_PAND_XMM_to_XMM(EEREC_D, t1reg); + SSE2_PANDN_XMM_to_XMM(t1reg, t2reg); + SSE2_POR_XMM_to_XMM(EEREC_D, t1reg); + _freeXMMreg(t0reg); + _freeXMMreg(t1reg); + _freeXMMreg(t2reg); + _freeXMMreg(t3reg); +CPU_SSE_XMMCACHE_END + + if( _Rd_ ) _deleteEEreg(_Rd_, 0); + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + _flushConstRegs(); + + MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (uptr)&cpuRegs.pc, pc ); + CALLFunc( (uptr)R5900::Interpreter::OpcodeImpl::MMI::PSUBSW ); +} + +//////////////////////////////////////////////////// +void recPADDB( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PADDB_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PADDB_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PADDB_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PADDBRtoR( t0reg, t2reg ); + PADDBRtoR( t1reg, t3reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPADDH( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|(_Rt_!=0?XMMINFO_READT:0)|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + if( _Rt_ == 0 ) SSEX_PXOR_XMM_to_XMM(EEREC_D, EEREC_D); + else SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + } + else if( _Rt_ == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + } + else { + if( EEREC_D == EEREC_S ) SSE2_PADDW_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PADDW_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PADDW_XMM_to_XMM(EEREC_D, EEREC_T); + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PADDWRtoR( t0reg, t2reg ); + PADDWRtoR( t1reg, t3reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPADDW( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|(_Rt_!=0?XMMINFO_READT:0)|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + if( _Rt_ == 0 ) SSEX_PXOR_XMM_to_XMM(EEREC_D, EEREC_D); + else SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + } + else if( _Rt_ == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + } + else { + if( EEREC_D == EEREC_S ) SSE2_PADDD_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PADDD_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PADDD_XMM_to_XMM(EEREC_D, EEREC_T); + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PADDDRtoR( t0reg, t2reg ); + PADDDRtoR( t1reg, t3reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPSUBB( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PSUBB_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBB_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBB_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSUBBRtoR( t0reg, t2reg ); + PSUBBRtoR( t1reg, t3reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPSUBH( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PSUBW_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBW_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBW_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSUBWRtoR( t0reg, t2reg ); + PSUBWRtoR( t1reg, t3reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPSUBW( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PSUBD_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBD_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBD_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PSUBDRtoR( t0reg, t2reg); + PSUBDRtoR( t1reg, t3reg); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPEXTLW( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|XMMINFO_READT|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRLQ_I8_to_XMM(EEREC_D, 32); + } + else { + if( EEREC_D == EEREC_T ) SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_D, EEREC_S); + else if( EEREC_D == EEREC_S ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_D, EEREC_S); + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MOV32MtoR( EAX, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + MOV32MtoR( ECX, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 3 ], EAX ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 2 ], ECX ); + + MOV32MtoR( EAX, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + MOV32MtoR( ECX, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EAX ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], ECX ); +} + +void recPEXTLB( void ) +{ + if (!_Rd_) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|XMMINFO_READT|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + SSE2_PUNPCKLBW_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRLW_I8_to_XMM(EEREC_D, 8); + } + else { + if( EEREC_D == EEREC_T ) SSE2_PUNPCKLBW_XMM_to_XMM(EEREC_D, EEREC_S); + else if( EEREC_D == EEREC_S ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKLBW_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKLBW_XMM_to_XMM(EEREC_D, EEREC_S); + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + //Done - Refraction - Crude but quicker than int + //SysPrintf("PEXTLB\n"); + //Rs = cpuRegs.GPR.r[_Rs_]; Rt = cpuRegs.GPR.r[_Rt_]; + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[7]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[15], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[7]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[14], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[6]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[13], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[6]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[12], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[5]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[11], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[5]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[10], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[4]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[9], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[4]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[8], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[3]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[7], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[3]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[6], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[2]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[5], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[2]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[4], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[1]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[3], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[1]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[2], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[0]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[1], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[0]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[0], EAX); +} + +void recPEXTLH( void ) +{ + if (!_Rd_) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|XMMINFO_READT|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + SSE2_PUNPCKLWD_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRLD_I8_to_XMM(EEREC_D, 16); + } + else { + if( EEREC_D == EEREC_T ) SSE2_PUNPCKLWD_XMM_to_XMM(EEREC_D, EEREC_S); + else if( EEREC_D == EEREC_S ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKLWD_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKLWD_XMM_to_XMM(EEREC_D, EEREC_S); + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + //Done - Refraction - Crude but quicker than int + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].US[3]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[7], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].US[3]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[6], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].US[2]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[5], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].US[2]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[4], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].US[1]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[3], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].US[1]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[2], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].US[0]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[1], EAX); + MOV16MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].US[0]); + MOV16RtoM((uptr)&cpuRegs.GPR.r[_Rd_].US[0], EAX); +} + +#endif + +/********************************************************* +* MMI1 opcodes * +* * +*********************************************************/ +#ifndef MMI1_RECOMPILE + +REC_FUNC_DEL( PABSW, _Rd_); +REC_FUNC_DEL( PABSH, _Rd_); + +REC_FUNC_DEL( PMINW, _Rd_); +REC_FUNC_DEL( PADSBH, _Rd_); +REC_FUNC_DEL( PMINH, _Rd_); +REC_FUNC_DEL( PCEQB, _Rd_); +REC_FUNC_DEL( PCEQH, _Rd_); +REC_FUNC_DEL( PCEQW, _Rd_); + +REC_FUNC_DEL( PADDUB, _Rd_); +REC_FUNC_DEL( PADDUH, _Rd_); +REC_FUNC_DEL( PADDUW, _Rd_); + +REC_FUNC_DEL( PSUBUB, _Rd_); +REC_FUNC_DEL( PSUBUH, _Rd_); +REC_FUNC_DEL( PSUBUW, _Rd_); + +REC_FUNC_DEL( PEXTUW, _Rd_); +REC_FUNC_DEL( PEXTUH, _Rd_); +REC_FUNC_DEL( PEXTUB, _Rd_); +REC_FUNC_DEL( QFSRV, _Rd_); + +#else + +//////////////////////////////////////////////////// +PCSX2_ALIGNED16(int s_MaskHighBitD[4]) = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }; +PCSX2_ALIGNED16(int s_MaskHighBitW[4]) = { 0x80008000, 0x80008000, 0x80008000, 0x80008000 }; + +void recPABSW() +{ + if( !_Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRAD_I8_to_XMM(t0reg, 31); + SSEX_PXOR_XMM_to_XMM(EEREC_D, t0reg); + SSE2_PSUBD_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); +CPU_SSE_XMMCACHE_END + + _deleteEEreg(_Rt_, 1); + _deleteEEreg(_Rd_, 0); + _flushConstRegs(); + + MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (uptr)&cpuRegs.pc, pc ); + CALLFunc( (uptr)R5900::Interpreter::OpcodeImpl::MMI::PABSW ); +} + +//////////////////////////////////////////////////// +void recPABSH() +{ +if( !_Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRAW_I8_to_XMM(t0reg, 15); + SSEX_PXOR_XMM_to_XMM(EEREC_D, t0reg); + SSE2_PSUBW_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); +CPU_SSE_XMMCACHE_END + + _deleteEEreg(_Rt_, 1); + _deleteEEreg(_Rd_, 0); + _flushConstRegs(); + + MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (uptr)&cpuRegs.pc, pc ); + CALLFunc( (uptr)R5900::Interpreter::OpcodeImpl::MMI::PABSW ); +} + +//////////////////////////////////////////////////// +void recPMINW() +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + int t0reg; + + if( EEREC_S == EEREC_T ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + return; + } + + t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSE2_PCMPGTD_XMM_to_XMM(t0reg, EEREC_S); + + if( EEREC_D == EEREC_S ) { + SSEX_PAND_XMM_to_XMM(EEREC_D, t0reg); + SSEX_PANDN_XMM_to_XMM(t0reg, EEREC_T); + } + else if( EEREC_D == EEREC_T ) { + int t1reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t1reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSEX_PAND_XMM_to_XMM(EEREC_D, t0reg); + SSEX_PANDN_XMM_to_XMM(t0reg, t1reg); + _freeXMMreg(t1reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSEX_PAND_XMM_to_XMM(EEREC_D, t0reg); + SSEX_PANDN_XMM_to_XMM(t0reg, EEREC_T); + } + + SSEX_POR_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); +CPU_SSE_XMMCACHE_END + + recCall( Interp::PMINW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPADSBH() +{ +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + int t0reg; + + if( EEREC_S == EEREC_T ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PADDW_XMM_to_XMM(EEREC_D, EEREC_D); + // reset lower bits to 0s + SSE2_PSRLDQ_I8_to_XMM(EEREC_D, 8); + SSE2_PSLLDQ_I8_to_XMM(EEREC_D, 8); + return; + } + + t0reg = _allocTempXMMreg(XMMT_INT, -1); + + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + + if( EEREC_D == EEREC_S ) { + SSE2_PADDW_XMM_to_XMM(t0reg, EEREC_S); + SSE2_PSUBW_XMM_to_XMM(EEREC_D, EEREC_T); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBW_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PADDW_XMM_to_XMM(t0reg, EEREC_S); + } + + // t0reg - adds, EEREC_D - subs + SSE2_PSRLDQ_I8_to_XMM(t0reg, 8); + SSE_MOVLHPS_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + +CPU_SSE_XMMCACHE_END + + recCall( Interp::PADSBH, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPADDUW() +{ +CPU_SSE2_XMMCACHE_START((_Rs_?XMMINFO_READS:0)|(_Rt_?XMMINFO_READT:0)|XMMINFO_WRITED) + + if( _Rt_ == 0 ) { + if( _Rs_ == 0 ) { + SSEX_PXOR_XMM_to_XMM(EEREC_D, EEREC_D); + } + else SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + } + else if( _Rs_ == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + } + else { + + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + int t1reg = _allocTempXMMreg(XMMT_INT, -1); + int t2reg = _allocTempXMMreg(XMMT_INT, -1); + + if( _hasFreeXMMreg() ) { + int t3reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_PXOR_XMM_to_XMM(t0reg, t0reg); + SSE2_MOVQ_XMM_to_XMM(t1reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(t2reg, EEREC_S); + if( EEREC_D != EEREC_T ) SSE2_MOVQ_XMM_to_XMM(EEREC_D, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(t3reg, EEREC_T); + SSE2_PUNPCKLDQ_XMM_to_XMM(t1reg, t0reg); + SSE2_PUNPCKHDQ_XMM_to_XMM(t2reg, t0reg); + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_D, t0reg); + SSE2_PUNPCKHDQ_XMM_to_XMM(t3reg, t0reg); + SSE2_PADDQ_XMM_to_XMM(t1reg, EEREC_D); + SSE2_PADDQ_XMM_to_XMM(t2reg, t3reg); + _freeXMMreg(t3reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(t2reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + + SSE2_MOVQ_XMM_to_XMM(t1reg, EEREC_S); + SSE2_PSRLDQ_I8_to_XMM(t2reg, 8); + if( EEREC_D != EEREC_T ) SSE2_MOVQ_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRLDQ_I8_to_XMM(t0reg, 8); + SSE2_PSHUFD_XMM_to_XMM(t1reg, t1reg, 0xE8); + SSE2_PSHUFD_XMM_to_XMM(t2reg, t2reg, 0xE8); + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_D, 0xE8); + SSE2_PSHUFD_XMM_to_XMM(t0reg, t0reg, 0xE8); + SSE2_PADDQ_XMM_to_XMM(t1reg, EEREC_D); + SSE2_PADDQ_XMM_to_XMM(t2reg, t0reg); + SSEX_PXOR_XMM_to_XMM(t0reg, t0reg); + } + + SSE2_PSHUFD_XMM_to_XMM(t1reg, t1reg, 0xd8); + SSE2_PSHUFD_XMM_to_XMM(t2reg, t2reg, 0xd8); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, t1reg); + SSE2_PUNPCKHQDQ_XMM_to_XMM(t1reg, t2reg); + SSE2_PUNPCKLQDQ_XMM_to_XMM(EEREC_D, t2reg); + SSE2_PCMPGTD_XMM_to_XMM(t1reg, t0reg); + SSEX_POR_XMM_to_XMM(EEREC_D, t1reg); + + _freeXMMreg(t0reg); + _freeXMMreg(t1reg); + _freeXMMreg(t2reg); + } + +CPU_SSE_XMMCACHE_END + recCall( Interp::PADDUW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPSUBUB() +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PSUBUSB_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PSUBUSB_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBUSB_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + recCall( Interp::PSUBUB, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPSUBUH() +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PSUBUSW_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PSUBUSW_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSUBUSW_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + recCall( Interp::PSUBUH, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPSUBUW() +{ + recCall( Interp::PSUBUW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPEXTUH() +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|XMMINFO_READT|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + SSE2_PUNPCKHWD_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRLD_I8_to_XMM(EEREC_D, 16); + } + else { + if( EEREC_D == EEREC_T ) SSE2_PUNPCKHWD_XMM_to_XMM(EEREC_D, EEREC_S); + else if( EEREC_D == EEREC_S ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKHWD_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKHWD_XMM_to_XMM(EEREC_D, EEREC_S); + } + } +CPU_SSE_XMMCACHE_END + + recCall( Interp::PEXTUH, _Rd_ ); +} + +//////////////////////////////////////////////////// +// Both Macros are 16 bytes so we can use a shift instead of a Mul instruction +#define QFSRVhelper0() { \ + ajmp[0] = JMP32(0); \ + x86Ptr += 11; \ +} + +#define QFSRVhelper(shift1, shift2) { \ + SSE2_PSRLDQ_I8_to_XMM(EEREC_D, shift1); \ + SSE2_PSLLDQ_I8_to_XMM(t0reg, shift2); \ + ajmp[shift1] = JMP32(0); \ + x86Ptr += 1; \ +} + +void recQFSRV() +{ + if ( !_Rd_ ) return; + //SysPrintf("recQFSRV()\n"); + + CPU_SSE2_XMMCACHE_START( XMMINFO_READS | XMMINFO_READT | XMMINFO_WRITED ) + + u32 *ajmp[16]; + int i, j; + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + + SSE2_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSE2_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + + MOV32MtoR(EAX, (uptr)&cpuRegs.sa); + SHL32ItoR(EAX, 1); // Multiply SA bytes by 16 bytes (the amount of bytes in QFSRVhelper() macros) + AND32I8toR(EAX, 0xf0); // This can possibly be removed but keeping it incase theres garbage in SA (cottonvibes) + ADD32ItoEAX((uptr)x86Ptr + 7); // ADD32 = 5 bytes, JMPR = 2 bytes + JMPR(EAX); // Jumps to a QFSRVhelper() case below (a total of 16 different cases) + + // Case 0: + QFSRVhelper0(); + + // Cases 1 to 15: + for (i = 1, j = 15; i < 16; i++, j--) { + QFSRVhelper(i, j); + } + + // Set jump addresses for the JMP32's in QFSRVhelper() + for (i = 1; i < 16; i++) { + x86SetJ32(ajmp[i]); + } + + // Concatenate the regs after appropriate shifts have been made + SSE2_POR_XMM_to_XMM(EEREC_D, t0reg); + + x86SetJ32(ajmp[0]); // Case 0 jumps to here (to skip the POR) + _freeXMMreg(t0reg); + + CPU_SSE_XMMCACHE_END + //recCall( Interp::QFSRV, _Rd_ ); +} + + +void recPEXTUB( void ) +{ + if (!_Rd_) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|XMMINFO_READT|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + SSE2_PUNPCKHBW_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRLW_I8_to_XMM(EEREC_D, 8); + } + else { + if( EEREC_D == EEREC_T ) SSE2_PUNPCKHBW_XMM_to_XMM(EEREC_D, EEREC_S); + else if( EEREC_D == EEREC_S ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKHBW_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKHBW_XMM_to_XMM(EEREC_D, EEREC_S); + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + //Done - Refraction - Crude but faster than int + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[8]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[0], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[8]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[1], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[9]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[2], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[9]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[3], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[10]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[4], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[10]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[5], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[11]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[6], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[11]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[7], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[12]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[8], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[12]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[9], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[13]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[10], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[13]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[11], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[14]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[12], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[14]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[13], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UC[15]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[14], EAX); + MOV8MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UC[15]); + MOV8RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UC[15], EAX); +} + +//////////////////////////////////////////////////// +void recPEXTUW( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|XMMINFO_READT|XMMINFO_WRITED) + if( _Rs_ == 0 ) { + SSE2_PUNPCKHDQ_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSRLQ_I8_to_XMM(EEREC_D, 32); + } + else { + if( EEREC_D == EEREC_T ) SSE2_PUNPCKHDQ_XMM_to_XMM(EEREC_D, EEREC_S); + else if( EEREC_D == EEREC_S ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKHDQ_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKHDQ_XMM_to_XMM(EEREC_D, EEREC_S); + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MOV32MtoR( EAX, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UL[ 2 ] ); + MOV32MtoR( ECX, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 2 ] ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EAX ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], ECX ); + + MOV32MtoR( EAX, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UL[ 3 ] ); + MOV32MtoR( ECX, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UL[ 3 ] ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 3 ], EAX ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 2 ], ECX ); +} + +//////////////////////////////////////////////////// +void recPMINH( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PMINSW_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PMINSW_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PMINSW_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + SSE_PMINSW_MM_to_MM( t0reg, t2reg ); + SSE_PMINSW_MM_to_MM( t1reg, t3reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPCEQB( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PCMPEQB_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PCMPEQB_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PCMPEQB_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PCMPEQBRtoR( t0reg, t2reg ); + PCMPEQBRtoR( t1reg, t3reg ); + + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPCEQH( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PCMPEQW_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PCMPEQW_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PCMPEQW_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PCMPEQWRtoR( t0reg, t2reg ); + PCMPEQWRtoR( t1reg, t3reg ); + + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPCEQW( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PCMPEQD_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PCMPEQD_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PCMPEQD_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PCMPEQDRtoR( t0reg, t2reg ); + PCMPEQDRtoR( t1reg, t3reg ); + + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPADDUB( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|(_Rt_?XMMINFO_READT:0)|XMMINFO_WRITED) + if( _Rt_ ) { + if( EEREC_D == EEREC_S ) SSE2_PADDUSB_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PADDUSB_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PADDUSB_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + else SSE2_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PADDUSBRtoR( t0reg, t2reg ); + PADDUSBRtoR( t1reg, t3reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPADDUH( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) SSE2_PADDUSW_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PADDUSW_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PADDUSW_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP4( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t2reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQMtoR( t3reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PADDUSWRtoR( t0reg, t2reg ); + PADDUSWRtoR( t1reg, t3reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +#endif +/********************************************************* +* MMI2 opcodes * +* * +*********************************************************/ +#ifndef MMI2_RECOMPILE + +REC_FUNC_DEL( PMFHI, _Rd_); +REC_FUNC_DEL( PMFLO, _Rd_); +REC_FUNC_DEL( PCPYLD, _Rd_); +REC_FUNC_DEL( PAND, _Rd_); +REC_FUNC_DEL( PXOR, _Rd_); + +REC_FUNC_DEL( PMADDW, _Rd_); +REC_FUNC_DEL( PSLLVW, _Rd_); +REC_FUNC_DEL( PSRLVW, _Rd_); +REC_FUNC_DEL( PMSUBW, _Rd_); +REC_FUNC_DEL( PINTH, _Rd_); +REC_FUNC_DEL( PMULTW, _Rd_); +REC_FUNC_DEL( PDIVW, _Rd_); +REC_FUNC_DEL( PMADDH, _Rd_); +REC_FUNC_DEL( PHMADH, _Rd_); +REC_FUNC_DEL( PMSUBH, _Rd_); +REC_FUNC_DEL( PHMSBH, _Rd_); +REC_FUNC_DEL( PEXEH, _Rd_); +REC_FUNC_DEL( PREVH, _Rd_); +REC_FUNC_DEL( PMULTH, _Rd_); +REC_FUNC_DEL( PDIVBW, _Rd_); +REC_FUNC_DEL( PEXEW, _Rd_); +REC_FUNC_DEL( PROT3W, _Rd_ ); + +#else + +//////////////////////////////////////////////////// +void recPMADDW() +{ + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + if( _Rd_ ) EEINST_SETSIGNEXT(_Rd_); + recCall( Interp::PMADDW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPSLLVW() +{ + recCall( Interp::PSLLVW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPSRLVW() +{ + recCall( Interp::PSRLVW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPMSUBW() +{ + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + if( _Rd_ ) EEINST_SETSIGNEXT(_Rd_); +//CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED|XMMINFO_WRITELO|XMMINFO_WRITEHI|XMMINFO_READLO|XMMINFO_READHI) +// int t0reg = _allocTempXMMreg(XMMT_INT, -1); +// +// if( EEREC_D == EEREC_S ) SSE2_PMULUDQ_XMM_to_XMM(EEREC_D, EEREC_T); +// else if( EEREC_D == EEREC_T ) SSE2_PMULUDQ_XMM_to_XMM(EEREC_D, EEREC_S); +// else { +// SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); +// SSE2_PMULUDQ_XMM_to_XMM(EEREC_D, EEREC_T); +// } +// +// // add from LO/HI +// SSE_SHUFPS_XMM_to_XMM(EEREC_LO, EEREC_HI, 0x88); +// SSE2_PSHUFD_XMM_to_XMM(EEREC_LO, EEREC_LO, 0xd8); +// SSE2_PSUBQ_XMM_to_XMM(EEREC_LO, EEREC_D); +// +// // get the signs +// SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_LO); +// SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_LO); +// SSE2_PSRAD_I8_to_XMM(t0reg, 31); +// +// // interleave +// SSE2_PSHUFD_XMM_to_XMM(EEREC_LO, EEREC_LO, 0xd8); +// SSE2_PSHUFD_XMM_to_XMM(t0reg, t0reg, 0xd8); +// SSEX_MOVDQA_XMM_to_XMM(EEREC_HI, EEREC_LO); +// +// SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_LO, t0reg); +// SSE2_PUNPCKHDQ_XMM_to_XMM(EEREC_HI, t0reg); +// +// _freeXMMreg(t0reg); +//CPU_SSE_XMMCACHE_END + + recCall( Interp::PMSUBW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPMULTW() +{ + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + if( _Rd_ ) EEINST_SETSIGNEXT(_Rd_); + recCall( Interp::PMULTW, _Rd_ ); +} +//////////////////////////////////////////////////// +void recPDIVW() +{ + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + recCall( Interp::PDIVW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPDIVBW() +{ + recCall( Interp::PDIVBW, _Rd_ ); //-- +} + +//////////////////////////////////////////////////// +PCSX2_ALIGNED16(int s_mask1[4]) = {~0, 0, ~0, 0}; + +void recPHMADH() +{ +CPU_SSE2_XMMCACHE_START((_Rd_?XMMINFO_WRITED:0)|XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITELO|XMMINFO_WRITEHI) + int t0reg = _Rd_ ? EEREC_D : _allocTempXMMreg(XMMT_INT, -1); + + if( t0reg == EEREC_S ) { + + SSEX_MOVDQA_XMM_to_XMM(EEREC_LO, EEREC_S); + + if( t0reg == EEREC_T ) { + SSE2_PMULHW_XMM_to_XMM(EEREC_LO, EEREC_T); + SSE2_PMULLW_XMM_to_XMM(t0reg, EEREC_T); + } + else { + SSE2_PMULLW_XMM_to_XMM(t0reg, EEREC_T); + SSE2_PMULHW_XMM_to_XMM(EEREC_LO, EEREC_T); + } + SSEX_MOVDQA_XMM_to_XMM(EEREC_HI, t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_LO, EEREC_T); + + SSE2_PMULLW_XMM_to_XMM(t0reg, EEREC_S); + SSE2_PMULHW_XMM_to_XMM(EEREC_LO, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(EEREC_HI, t0reg); + } + + // 0-3 + SSE2_PUNPCKLWD_XMM_to_XMM(t0reg, EEREC_LO); + // 4-7 + SSE2_PUNPCKHWD_XMM_to_XMM(EEREC_HI, EEREC_LO); + + SSE2_PSHUFD_XMM_to_XMM(t0reg, t0reg, 0xd8); // 0,2,1,3, L->H + SSE2_PSHUFD_XMM_to_XMM(EEREC_HI, EEREC_HI, 0xd8); // 4,6,5,7, L->H + SSEX_MOVDQA_XMM_to_XMM(EEREC_LO, t0reg); + + SSE2_PUNPCKLQDQ_XMM_to_XMM(t0reg, EEREC_HI); + SSE2_PUNPCKHQDQ_XMM_to_XMM(EEREC_LO, EEREC_HI); + + SSE2_PADDD_XMM_to_XMM(EEREC_LO, t0reg); + + if( _Rd_ ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_LO); + } + + SSE2_PSHUFD_XMM_to_XMM(EEREC_HI, EEREC_LO, 0xf5); + + SSE2_PAND_M128_to_XMM(EEREC_LO, (uptr)s_mask1); + SSE2_PAND_M128_to_XMM(EEREC_HI, (uptr)s_mask1); + + if( !_Rd_ ) _freeXMMreg(t0reg); + +CPU_SSE_XMMCACHE_END + + recCall( Interp::PHMADH, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPMSUBH() +{ + CPU_SSE2_XMMCACHE_START((_Rd_?XMMINFO_WRITED:0)|XMMINFO_READS|XMMINFO_READT|XMMINFO_READLO|XMMINFO_READHI|XMMINFO_WRITELO|XMMINFO_WRITEHI) + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + int t1reg = _allocTempXMMreg(XMMT_INT, -1); + + if( !_Rd_ ) { + SSE2_PXOR_XMM_to_XMM(t0reg, t0reg); + SSE2_PSHUFD_XMM_to_XMM(t1reg, EEREC_S, 0xd8); //S0, S1, S4, S5, S2, S3, S6, S7 + SSE2_PUNPCKLWD_XMM_to_XMM(t1reg, t0reg); //S0, 0, S1, 0, S4, 0, S5, 0 + SSE2_PSHUFD_XMM_to_XMM(t0reg, EEREC_T, 0xd8); //T0, T1, T4, T5, T2, T3, T6, T7 + SSE2_PUNPCKLWD_XMM_to_XMM(t0reg, t0reg); //T0, T0, T1, T1, T4, T4, T5, T5 + SSE2_PMADDWD_XMM_to_XMM(t0reg, t1reg); //S0*T0+0*T0, S1*T1+0*T1, S4*T4+0*T4, S5*T5+0*T5 + + SSE2_PSUBD_XMM_to_XMM(EEREC_LO, t0reg); + + SSE2_PXOR_XMM_to_XMM(t0reg, t0reg); + SSE2_PSHUFD_XMM_to_XMM(t1reg, EEREC_S, 0xd8); //S0, S1, S4, S5, S2, S3, S6, S7 + SSE2_PUNPCKHWD_XMM_to_XMM(t1reg, t0reg); //S2, 0, S3, 0, S6, 0, S7, 0 + SSE2_PSHUFD_XMM_to_XMM(t0reg, EEREC_T, 0xd8); //T0, T1, T4, T5, T2, T3, T6, T7 + SSE2_PUNPCKHWD_XMM_to_XMM(t0reg, t0reg); //T2, T2, T3, T3, T6, T6, T7, T7 + SSE2_PMADDWD_XMM_to_XMM(t0reg, t1reg); //S2*T2+0*T2, S3*T3+0*T3, S6*T6+0*T6, S7*T7+0*T7 + + SSE2_PSUBD_XMM_to_XMM(EEREC_HI, t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(t1reg, EEREC_S); + + SSE2_PMULLW_XMM_to_XMM(t0reg, EEREC_T); + SSE2_PMULHW_XMM_to_XMM(t1reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, t0reg); + + // 0-3 + SSE2_PUNPCKLWD_XMM_to_XMM(t0reg, t1reg); + // 4-7 + SSE2_PUNPCKHWD_XMM_to_XMM(EEREC_D, t1reg); + SSEX_MOVDQA_XMM_to_XMM(t1reg, t0reg); + + // 0,1,4,5, L->H + SSE2_PUNPCKLQDQ_XMM_to_XMM(t0reg, EEREC_D); + // 2,3,6,7, L->H + SSE2_PUNPCKHQDQ_XMM_to_XMM(t1reg, EEREC_D); + + SSE2_PSUBD_XMM_to_XMM(EEREC_LO, t0reg); + SSE2_PSUBD_XMM_to_XMM(EEREC_HI, t1reg); + + // 0,2,4,6, L->H + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_LO, 0x88); + SSE2_PSHUFD_XMM_to_XMM(t0reg, EEREC_HI, 0x88); + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_D, t0reg); + } + + _freeXMMreg(t0reg); + _freeXMMreg(t1reg); + +CPU_SSE_XMMCACHE_END + recCall( Interp::PMSUBH, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPHMSBH() +{ +CPU_SSE2_XMMCACHE_START((_Rd_?XMMINFO_WRITED:0)|XMMINFO_READS|XMMINFO_READT|XMMINFO_READLO|XMMINFO_READHI|XMMINFO_WRITELO|XMMINFO_WRITEHI) + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(EEREC_LO, EEREC_S); + + SSE2_PMULLW_XMM_to_XMM(t0reg, EEREC_T); + SSE2_PMULHW_XMM_to_XMM(EEREC_LO, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_HI, t0reg); + + // 0-3 + SSE2_PUNPCKLWD_XMM_to_XMM(t0reg, EEREC_LO); + // 4-7 + SSE2_PUNPCKHWD_XMM_to_XMM(EEREC_HI, EEREC_LO); + + SSE2_PSHUFD_XMM_to_XMM(t0reg, t0reg, 0xd8); // 0,2,1,3, L->H + SSE2_PSHUFD_XMM_to_XMM(EEREC_HI, EEREC_HI, 0xd8); // 4,6,5,7, L->H + SSEX_MOVDQA_XMM_to_XMM(EEREC_LO, t0reg); + + SSE2_PUNPCKLDQ_XMM_to_XMM(t0reg, EEREC_HI); + SSE2_PUNPCKHDQ_XMM_to_XMM(EEREC_LO, EEREC_HI); + + SSE2_PSUBD_XMM_to_XMM(EEREC_LO, t0reg); + + if( _Rd_ ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_LO); + } + + SSE2_PSHUFD_XMM_to_XMM(EEREC_HI, EEREC_LO, 0xf5); + + _freeXMMreg(t0reg); + +CPU_SSE_XMMCACHE_END + + recCall( Interp::PHMSBH, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPEXEH( void ) +{ + if (!_Rd_) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + SSE2_PSHUFLW_XMM_to_XMM(EEREC_D, EEREC_T, 0xc6); + SSE2_PSHUFHW_XMM_to_XMM(EEREC_D, EEREC_D, 0xc6); +CPU_SSE_XMMCACHE_END + + recCall( Interp::PEXEH, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPREVH( void ) +{ + if (!_Rd_) return; + + +CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + SSE2_PSHUFLW_XMM_to_XMM(EEREC_D, EEREC_T, 0x1B); + SSE2_PSHUFHW_XMM_to_XMM(EEREC_D, EEREC_D, 0x1B); +CPU_SSE_XMMCACHE_END + + recCall( Interp::PREVH, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPINTH( void ) +{ + if (!_Rd_) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + if( EEREC_D == EEREC_S ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE_MOVHLPS_XMM_to_XMM(t0reg, EEREC_S); + if( EEREC_D != EEREC_T ) SSE2_MOVQ_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKLWD_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSE_MOVLHPS_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKHWD_XMM_to_XMM(EEREC_D, EEREC_S); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + //Done - Refraction + MOV16MtoR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].US[4]); + MOV16MtoR( EBX, (uptr)&cpuRegs.GPR.r[_Rt_].US[1]); + MOV16MtoR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].US[2]); + MOV16MtoR( EDX, (uptr)&cpuRegs.GPR.r[_Rt_].US[0]); + + MOV16RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].US[1], EAX); + MOV16RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].US[2], EBX); + MOV16RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].US[4], ECX); + MOV16RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].US[0], EDX); + + MOV16MtoR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].US[5]); + MOV16MtoR( EBX, (uptr)&cpuRegs.GPR.r[_Rs_].US[6]); + MOV16MtoR( ECX, (uptr)&cpuRegs.GPR.r[_Rs_].US[7]); + MOV16MtoR( EDX, (uptr)&cpuRegs.GPR.r[_Rt_].US[3]); + + MOV16RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].US[3], EAX); + MOV16RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].US[5], EBX); + MOV16RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].US[7], ECX); + MOV16RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].US[6], EDX); +} + +void recPEXEW( void ) +{ + if (!_Rd_) return; + +CPU_SSE_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_T, 0xc6); +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MOV32MtoR( EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UL[2]); + MOV32MtoR( EBX, (uptr)&cpuRegs.GPR.r[_Rt_].UL[1]); + MOV32MtoR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].UL[0]); + MOV32MtoR( EDX, (uptr)&cpuRegs.GPR.r[_Rt_].UL[3]); + + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[0], EAX); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[1], EBX); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[2], ECX); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[3], EDX); +} + +void recPROT3W( void ) +{ + if (!_Rd_) return; + +CPU_SSE_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED) + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_T, 0xc9); +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MOV32MtoR( EAX, (uptr)&cpuRegs.GPR.r[_Rt_].UL[1]); + MOV32MtoR( EBX, (uptr)&cpuRegs.GPR.r[_Rt_].UL[2]); + MOV32MtoR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].UL[0]); + MOV32MtoR( EDX, (uptr)&cpuRegs.GPR.r[_Rt_].UL[3]); + + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[0], EAX); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[1], EBX); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[2], ECX); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[3], EDX); +} + +void recPMULTH( void ) +{ +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|(_Rd_?XMMINFO_WRITED:0)|XMMINFO_WRITELO|XMMINFO_WRITEHI) + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + + SSEX_MOVDQA_XMM_to_XMM(EEREC_LO, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(EEREC_HI, EEREC_S); + + SSE2_PMULLW_XMM_to_XMM(EEREC_LO, EEREC_T); + SSE2_PMULHW_XMM_to_XMM(EEREC_HI, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_LO); + + // 0-3 + SSE2_PUNPCKLWD_XMM_to_XMM(EEREC_LO, EEREC_HI); + // 4-7 + SSE2_PUNPCKHWD_XMM_to_XMM(t0reg, EEREC_HI); + + if( _Rd_ ) { + // 0,2,4,6, L->H + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_LO, 0x88); + SSE2_PSHUFD_XMM_to_XMM(EEREC_HI, t0reg, 0x88); + SSE2_PUNPCKLQDQ_XMM_to_XMM(EEREC_D, EEREC_HI); + } + + SSEX_MOVDQA_XMM_to_XMM(EEREC_HI, EEREC_LO); + + // 0,1,4,5, L->H + SSE2_PUNPCKLQDQ_XMM_to_XMM(EEREC_LO, t0reg); + // 2,3,6,7, L->H + SSE2_PUNPCKHQDQ_XMM_to_XMM(EEREC_HI, t0reg); + + _freeXMMreg(t0reg); +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + _deleteEEreg(XMMGPR_LO, 0); + _deleteEEreg(XMMGPR_HI, 0); + _deleteGPRtoXMMreg(_Rs_, 1); + _deleteGPRtoXMMreg(_Rt_, 1); + + if(!_Rt_ || !_Rs_) { + MOV32ItoM( (uptr)&cpuRegs.LO.UL[0], 0); + MOV32ItoM( (uptr)&cpuRegs.LO.UL[1], 0); + MOV32ItoM( (uptr)&cpuRegs.LO.UL[2], 0); + MOV32ItoM( (uptr)&cpuRegs.LO.UL[3], 0); + MOV32ItoM( (uptr)&cpuRegs.HI.UL[0], 0); + MOV32ItoM( (uptr)&cpuRegs.HI.UL[1], 0); + MOV32ItoM( (uptr)&cpuRegs.HI.UL[2], 0); + MOV32ItoM( (uptr)&cpuRegs.HI.UL[3], 0); + + if( _Rd_ ) { + MOV32ItoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[0], 0); + MOV32ItoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[1], 0); + MOV32ItoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[2], 0); + MOV32ItoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[3], 0); + } + return; + } + + //Done - Refraction + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[0]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[0]); + IMUL32RtoR( EAX, ECX); + MOV32RtoM( (uptr)&cpuRegs.LO.UL[0], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[1]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[1]); + IMUL32RtoR( EAX, ECX); + MOV32RtoM( (uptr)&cpuRegs.LO.UL[1], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[2]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[2]); + IMUL32RtoR( EAX, ECX); + MOV32RtoM( (uptr)&cpuRegs.HI.UL[0], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[3]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[3]); + IMUL32RtoR( EAX, ECX); + MOV32RtoM( (uptr)&cpuRegs.HI.UL[1], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[4]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[4]); + IMUL32RtoR( EAX, ECX); + MOV32RtoM( (uptr)&cpuRegs.LO.UL[2], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[5]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[5]); + IMUL32RtoR( EAX, ECX); + MOV32RtoM( (uptr)&cpuRegs.LO.UL[3], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[6]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[6]); + IMUL32RtoR( EAX, ECX); + MOV32RtoM( (uptr)&cpuRegs.HI.UL[2], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[7]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[7]); + IMUL32RtoR( EAX, ECX); + MOV32RtoM( (uptr)&cpuRegs.HI.UL[3], EAX); + + if (_Rd_) { + MOV32MtoR( EAX, (uptr)&cpuRegs.LO.UL[0]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[0], EAX); + MOV32MtoR( EAX, (uptr)&cpuRegs.HI.UL[0]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[1], EAX); + MOV32MtoR( EAX, (uptr)&cpuRegs.LO.UL[2]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[2], EAX); + MOV32MtoR( EAX, (uptr)&cpuRegs.HI.UL[2]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[3], EAX); + } +} + +void recPMFHI( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE_XMMCACHE_START(XMMINFO_WRITED|XMMINFO_READHI) + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_HI); +CPU_SSE_XMMCACHE_END + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.HI.UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.HI.UD[ 1 ] ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPMFLO( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE_XMMCACHE_START(XMMINFO_WRITED|XMMINFO_READLO) + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_LO); +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.LO.UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.LO.UD[ 1 ] ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPAND( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE_XMMCACHE_START(XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT) + if( EEREC_D == EEREC_T ) { + SSEX_PAND_XMM_to_XMM(EEREC_D, EEREC_S); + } + else if( EEREC_D == EEREC_S ) { + SSEX_PAND_XMM_to_XMM(EEREC_D, EEREC_T); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSEX_PAND_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + PANDMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + PANDMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPXOR( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE_XMMCACHE_START(XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT) + if( EEREC_D == EEREC_T ) { + SSEX_PXOR_XMM_to_XMM(EEREC_D, EEREC_S); + } + else if( EEREC_D == EEREC_S ) { + SSEX_PXOR_XMM_to_XMM(EEREC_D, EEREC_T); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSEX_PXOR_XMM_to_XMM(EEREC_D, EEREC_T); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + PXORMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + PXORMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPCPYLD( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE_XMMCACHE_START(XMMINFO_WRITED|(( _Rs_== 0) ? 0:XMMINFO_READS)|XMMINFO_READT) + if( _Rs_ == 0 ) { + SSE2_MOVQ_XMM_to_XMM(EEREC_D, EEREC_T); + } + else { + if( EEREC_D == EEREC_T ) SSE2_PUNPCKLQDQ_XMM_to_XMM(EEREC_D, EEREC_S); + else if( EEREC_S == EEREC_T ) SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_S, 0x44); + else if( EEREC_D == EEREC_S ) { + SSE2_PUNPCKLQDQ_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_D, 0x4e); + } + else { + SSE2_MOVQ_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PUNPCKLQDQ_XMM_to_XMM(EEREC_D, EEREC_S); + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t1reg ); + SetMMXstate(); + ) +} + +void recPMADDH( void ) +{ + CPU_SSE2_XMMCACHE_START((_Rd_?XMMINFO_WRITED:0)|XMMINFO_READS|XMMINFO_READT|XMMINFO_READLO|XMMINFO_READHI|XMMINFO_WRITELO|XMMINFO_WRITEHI) + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + int t1reg = _allocTempXMMreg(XMMT_INT, -1); + + if( !_Rd_ ) { + SSE2_PXOR_XMM_to_XMM(t0reg, t0reg); + SSE2_PSHUFD_XMM_to_XMM(t1reg, EEREC_S, 0xd8); //S0, S1, S4, S5, S2, S3, S6, S7 + SSE2_PUNPCKLWD_XMM_to_XMM(t1reg, t0reg); //S0, 0, S1, 0, S4, 0, S5, 0 + SSE2_PSHUFD_XMM_to_XMM(t0reg, EEREC_T, 0xd8); //T0, T1, T4, T5, T2, T3, T6, T7 + SSE2_PUNPCKLWD_XMM_to_XMM(t0reg, t0reg); //T0, T0, T1, T1, T4, T4, T5, T5 + SSE2_PMADDWD_XMM_to_XMM(t0reg, t1reg); //S0*T0+0*T0, S1*T1+0*T1, S4*T4+0*T4, S5*T5+0*T5 + + SSE2_PADDD_XMM_to_XMM(EEREC_LO, t0reg); + + SSE2_PXOR_XMM_to_XMM(t0reg, t0reg); + SSE2_PSHUFD_XMM_to_XMM(t1reg, EEREC_S, 0xd8); //S0, S1, S4, S5, S2, S3, S6, S7 + SSE2_PUNPCKHWD_XMM_to_XMM(t1reg, t0reg); //S2, 0, S3, 0, S6, 0, S7, 0 + SSE2_PSHUFD_XMM_to_XMM(t0reg, EEREC_T, 0xd8); //T0, T1, T4, T5, T2, T3, T6, T7 + SSE2_PUNPCKHWD_XMM_to_XMM(t0reg, t0reg); //T2, T2, T3, T3, T6, T6, T7, T7 + SSE2_PMADDWD_XMM_to_XMM(t0reg, t1reg); //S2*T2+0*T2, S3*T3+0*T3, S6*T6+0*T6, S7*T7+0*T7 + + SSE2_PADDD_XMM_to_XMM(EEREC_HI, t0reg); + } + else { + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSEX_MOVDQA_XMM_to_XMM(t1reg, EEREC_S); + + SSE2_PMULLW_XMM_to_XMM(t0reg, EEREC_T); + SSE2_PMULHW_XMM_to_XMM(t1reg, EEREC_T); + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, t0reg); + + // 0-3 + SSE2_PUNPCKLWD_XMM_to_XMM(t0reg, t1reg); + // 4-7 + SSE2_PUNPCKHWD_XMM_to_XMM(EEREC_D, t1reg); + SSEX_MOVDQA_XMM_to_XMM(t1reg, t0reg); + + // 0,1,4,5, L->H + SSE2_PUNPCKLQDQ_XMM_to_XMM(t0reg, EEREC_D); + // 2,3,6,7, L->H + SSE2_PUNPCKHQDQ_XMM_to_XMM(t1reg, EEREC_D); + + SSE2_PADDD_XMM_to_XMM(EEREC_LO, t0reg); + SSE2_PADDD_XMM_to_XMM(EEREC_HI, t1reg); + + // 0,2,4,6, L->H + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_LO, 0x88); + SSE2_PSHUFD_XMM_to_XMM(t0reg, EEREC_HI, 0x88); + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_D, t0reg); + } + + _freeXMMreg(t0reg); + _freeXMMreg(t1reg); + +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + _deleteEEreg(XMMGPR_LO, 1); + _deleteEEreg(XMMGPR_HI, 1); + _deleteGPRtoXMMreg(_Rs_, 1); + _deleteGPRtoXMMreg(_Rt_, 1); + + if(_Rt_ && _Rs_){ + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[0]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[0]); + IMUL32RtoR( EAX, ECX); + ADD32RtoM( (uptr)&cpuRegs.LO.UL[0], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[1]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[1]); + IMUL32RtoR( EAX, ECX); + ADD32RtoM( (uptr)&cpuRegs.LO.UL[1], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[2]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[2]); + IMUL32RtoR( EAX, ECX); + ADD32RtoM( (uptr)&cpuRegs.HI.UL[0], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[3]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[3]); + IMUL32RtoR( EAX, ECX); + ADD32RtoM( (uptr)&cpuRegs.HI.UL[1], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[4]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[4]); + IMUL32RtoR( EAX, ECX); + ADD32RtoM( (uptr)&cpuRegs.LO.UL[2], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[5]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[5]); + IMUL32RtoR( EAX, ECX); + ADD32RtoM( (uptr)&cpuRegs.LO.UL[3], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[6]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[6]); + IMUL32RtoR( EAX, ECX); + ADD32RtoM( (uptr)&cpuRegs.HI.UL[2], EAX); + + MOVSX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[_Rs_].SS[7]); + MOVSX32M16toR( ECX, (uptr)&cpuRegs.GPR.r[_Rt_].SS[7]); + IMUL32RtoR( EAX, ECX); + ADD32RtoM( (uptr)&cpuRegs.HI.UL[3], EAX); + + } + + if (_Rd_) { + MOV32MtoR( EAX, (uptr)&cpuRegs.LO.UL[0]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[0], EAX); + MOV32MtoR( EAX, (uptr)&cpuRegs.HI.UL[0]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[1], EAX); + MOV32MtoR( EAX, (uptr)&cpuRegs.LO.UL[2]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[2], EAX); + MOV32MtoR( EAX, (uptr)&cpuRegs.HI.UL[2]); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[_Rd_].UL[3], EAX); + } +} + +#endif +/********************************************************* +* MMI3 opcodes * +* * +*********************************************************/ +#ifndef MMI3_RECOMPILE + +REC_FUNC_DEL( PMADDUW, _Rd_); +REC_FUNC_DEL( PSRAVW, _Rd_); +REC_FUNC_DEL( PMTHI, _Rd_); +REC_FUNC_DEL( PMTLO, _Rd_); +REC_FUNC_DEL( PINTEH, _Rd_); +REC_FUNC_DEL( PMULTUW, _Rd_); +REC_FUNC_DEL( PDIVUW, _Rd_); +REC_FUNC_DEL( PCPYUD, _Rd_); +REC_FUNC_DEL( POR, _Rd_); +REC_FUNC_DEL( PNOR, _Rd_); +REC_FUNC_DEL( PCPYH, _Rd_); +REC_FUNC_DEL( PEXCW, _Rd_); +REC_FUNC_DEL( PEXCH, _Rd_); + +#else + +//////////////////////////////////////////////////// +//REC_FUNC( PSRAVW, _Rd_ ); + +void recPSRAVW( void ) +{ + MOV32ItoM( (uptr)&cpuRegs.code, (u32)cpuRegs.code ); + MOV32ItoM( (uptr)&cpuRegs.pc, (u32)pc ); + iFlushCall(FLUSH_EVERYTHING); + if( _Rd_ > 0 ) _deleteEEreg(_Rd_, 0); + CALLFunc( (uptr)R5900::Interpreter::OpcodeImpl::MMI::PSRAVW ); +} + + +//////////////////////////////////////////////////// +PCSX2_ALIGNED16(u32 s_tempPINTEH[4]) = {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff }; + +void recPINTEH() +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START((_Rs_?XMMINFO_READS:0)|(_Rt_?XMMINFO_READT:0)|XMMINFO_WRITED) + + int t0reg = -1; + + if( _Rs_ == 0 ) { + if( _Rt_ == 0 ) { + SSEX_PXOR_XMM_to_XMM(EEREC_D, EEREC_D); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + SSE2_PAND_M128_to_XMM(EEREC_D, (uptr)s_tempPINTEH); + } + } + else if( _Rt_ == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSLLD_I8_to_XMM(EEREC_D, 16); + } + else { + if( EEREC_S == EEREC_T ) { + SSE2_PSHUFLW_XMM_to_XMM(EEREC_D, EEREC_S, 0xa0); + SSE2_PSHUFHW_XMM_to_XMM(EEREC_D, EEREC_D, 0xa0); + } + else if( EEREC_D == EEREC_T ) { + assert( EEREC_D != EEREC_S ); + t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_PSLLD_I8_to_XMM(EEREC_D, 16); + SSE2_MOVDQA_XMM_to_XMM(t0reg, EEREC_S); + SSE2_PSRLD_I8_to_XMM(EEREC_D, 16); + SSE2_PSLLD_I8_to_XMM(t0reg, 16); + SSE2_POR_XMM_to_XMM(EEREC_D, t0reg); + } + else { + t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_MOVDQA_XMM_to_XMM(t0reg, EEREC_T); + SSE2_PSLLD_I8_to_XMM(t0reg, 16); + SSE2_PSLLD_I8_to_XMM(EEREC_D, 16); + SSE2_PSRLD_I8_to_XMM(t0reg, 16); + SSE2_POR_XMM_to_XMM(EEREC_D, t0reg); + } + } + + if( t0reg >= 0 ) _freeXMMreg(t0reg); +CPU_SSE_XMMCACHE_END + + recCall( Interp::PINTEH, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPMULTUW() +{ +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED|XMMINFO_WRITELO|XMMINFO_WRITEHI) + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + if( _Rd_ ) EEINST_SETSIGNEXT(_Rd_); + + if( EEREC_D == EEREC_S ) SSE2_PMULUDQ_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PMULUDQ_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PMULUDQ_XMM_to_XMM(EEREC_D, EEREC_T); + } + + // get the signs + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_D); + SSE2_PSRAD_I8_to_XMM(t0reg, 31); + + // interleave + SSE2_PSHUFD_XMM_to_XMM(EEREC_LO, EEREC_D, 0xd8); + SSE2_PSHUFD_XMM_to_XMM(t0reg, t0reg, 0xd8); + SSEX_MOVDQA_XMM_to_XMM(EEREC_HI, EEREC_LO); + + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_LO, t0reg); + SSE2_PUNPCKHDQ_XMM_to_XMM(EEREC_HI, t0reg); + + _freeXMMreg(t0reg); +CPU_SSE_XMMCACHE_END + recCall( Interp::PMULTUW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPMADDUW() +{ +CPU_SSE2_XMMCACHE_START(XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED|XMMINFO_WRITELO|XMMINFO_WRITEHI|XMMINFO_READLO|XMMINFO_READHI) + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + + if( EEREC_D == EEREC_S ) SSE2_PMULUDQ_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE2_PMULUDQ_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PMULUDQ_XMM_to_XMM(EEREC_D, EEREC_T); + } + + // add from LO/HI + SSE2_PSHUFD_XMM_to_XMM(EEREC_LO, EEREC_LO, 0x88); + SSE2_PSHUFD_XMM_to_XMM(EEREC_HI, EEREC_HI, 0x88); + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_LO, EEREC_HI); + SSE2_PADDQ_XMM_to_XMM(EEREC_D, EEREC_LO); + + // get the signs + SSEX_MOVDQA_XMM_to_XMM(t0reg, EEREC_D); + SSE2_PSRAD_I8_to_XMM(t0reg, 31); + + // interleave + SSE2_PSHUFD_XMM_to_XMM(EEREC_LO, EEREC_D, 0xd8); + SSE2_PSHUFD_XMM_to_XMM(t0reg, t0reg, 0xd8); + SSEX_MOVDQA_XMM_to_XMM(EEREC_HI, EEREC_LO); + + SSE2_PUNPCKLDQ_XMM_to_XMM(EEREC_LO, t0reg); + SSE2_PUNPCKHDQ_XMM_to_XMM(EEREC_HI, t0reg); + + _freeXMMreg(t0reg); +CPU_SSE_XMMCACHE_END + + recCall( Interp::PMADDUW, _Rd_ ); +} + +//////////////////////////////////////////////////// +//do EEINST_SETSIGNEXT +void recPDIVUW() +{ + recCall( Interp::PDIVUW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPEXCW() +{ + if (!_Rd_) return; + +CPU_SSE_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_T, 0xd8); +CPU_SSE_XMMCACHE_END + +recCall( Interp::PEXCW, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPEXCH( void ) +{ + if (!_Rd_) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + SSE2_PSHUFLW_XMM_to_XMM(EEREC_D, EEREC_T, 0xd8); + SSE2_PSHUFHW_XMM_to_XMM(EEREC_D, EEREC_D, 0xd8); +CPU_SSE_XMMCACHE_END + + recCall( Interp::PEXCH, _Rd_ ); +} + +//////////////////////////////////////////////////// +void recPNOR( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|(_Rt_!=0?XMMINFO_READT:0)|XMMINFO_WRITED) + + if( _Rs_ == 0 ) { + if( _Rt_ == 0 ) { + SSE2_PCMPEQD_XMM_to_XMM( EEREC_D, EEREC_D ); + } + else { + if( EEREC_D == EEREC_T ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_PCMPEQD_XMM_to_XMM( t0reg, t0reg); + SSEX_PXOR_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSE2_PCMPEQD_XMM_to_XMM( EEREC_D, EEREC_D ); + if( _Rt_ != 0 ) SSEX_PXOR_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + } + else if( _Rt_ == 0 ) { + if( EEREC_D == EEREC_S ) { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_PCMPEQD_XMM_to_XMM( t0reg, t0reg); + SSEX_PXOR_XMM_to_XMM(EEREC_D, t0reg); + _freeXMMreg(t0reg); + } + else { + SSE2_PCMPEQD_XMM_to_XMM( EEREC_D, EEREC_D ); + SSEX_PXOR_XMM_to_XMM(EEREC_D, EEREC_S); + } + } + else { + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + + if( EEREC_D == EEREC_S ) SSEX_POR_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSEX_POR_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + if( EEREC_S != EEREC_T ) SSEX_POR_XMM_to_XMM(EEREC_D, EEREC_T); + } + + SSE2_PCMPEQD_XMM_to_XMM( t0reg, t0reg ); + SSEX_PXOR_XMM_to_XMM( EEREC_D, t0reg ); + _freeXMMreg(t0reg); + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP3( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + PORMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + PORMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + PCMPEQDRtoR( t2reg, t2reg ); + PXORRtoR( t0reg, t2reg ); + PXORRtoR( t1reg, t2reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPMTHI( void ) +{ +CPU_SSE_XMMCACHE_START(XMMINFO_READS|XMMINFO_WRITEHI) + SSEX_MOVDQA_XMM_to_XMM(EEREC_HI, EEREC_S); +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(XMMGPR_HI, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQRtoM( (uptr)&cpuRegs.HI.UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.HI.UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPMTLO( void ) +{ +CPU_SSE_XMMCACHE_START(XMMINFO_READS|XMMINFO_WRITELO) + SSEX_MOVDQA_XMM_to_XMM(EEREC_LO, EEREC_S); +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(XMMGPR_LO, 0); + _deleteGPRtoXMMreg(_Rs_, 1); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQRtoM( (uptr)&cpuRegs.LO.UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.LO.UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPCPYUD( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE_XMMCACHE_START(XMMINFO_READS|(( _Rt_ == 0) ? 0:XMMINFO_READT)|XMMINFO_WRITED) + + if( _Rt_ == 0 ) { + if( EEREC_D == EEREC_S ) { + SSE2_PUNPCKHQDQ_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_MOVQ_XMM_to_XMM(EEREC_D, EEREC_D); + } + else { + SSE_MOVHLPS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_MOVQ_XMM_to_XMM(EEREC_D, EEREC_D); + } + } + else { + if( EEREC_D == EEREC_S ) SSE2_PUNPCKHQDQ_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) { + //TODO + SSE2_PUNPCKHQDQ_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_D, 0x4e); + } + else { + if( EEREC_S == EEREC_T ) { + SSE2_PSHUFD_XMM_to_XMM(EEREC_D, EEREC_S, 0xee); + } + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + SSE2_PUNPCKHQDQ_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPOR( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE_XMMCACHE_START((_Rs_!=0?XMMINFO_READS:0)|(_Rt_!=0?XMMINFO_READT:0)|XMMINFO_WRITED) + + if( _Rs_ == 0 ) { + if( _Rt_ == 0 ) { + SSEX_PXOR_XMM_to_XMM(EEREC_D, EEREC_D); + } + else SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + } + else if( _Rt_ == 0 ) { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + } + else { + if( EEREC_D == EEREC_S ) { + SSEX_POR_XMM_to_XMM(EEREC_D, EEREC_T); + } + else if( EEREC_D == EEREC_T ) { + SSEX_POR_XMM_to_XMM(EEREC_D, EEREC_S); + } + else { + if( _Rs_ == 0 ) SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + else if( _Rt_ == 0 ) SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSEX_MOVDQA_XMM_to_XMM(EEREC_D, EEREC_T); + if( EEREC_S != EEREC_T ) { + SSEX_POR_XMM_to_XMM(EEREC_D, EEREC_S); + } + } + } + } +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + MMX_ALLOC_TEMP2( + MOVQMtoR( t0reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQMtoR( t1reg, (uptr)&cpuRegs.GPR.r[ _Rs_ ].UD[ 1 ] ); + if ( _Rt_ != 0 ) + { + PORMtoR ( t0reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + PORMtoR ( t1reg, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + } + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], t0reg ); + MOVQRtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UD[ 1 ], t1reg ); + SetMMXstate(); + ) +} + +//////////////////////////////////////////////////// +void recPCPYH( void ) +{ + if ( ! _Rd_ ) return; + +CPU_SSE2_XMMCACHE_START(XMMINFO_READT|XMMINFO_WRITED) + SSE2_PSHUFLW_XMM_to_XMM(EEREC_D, EEREC_T, 0); + SSE2_PSHUFHW_XMM_to_XMM(EEREC_D, EEREC_D, 0); +CPU_SSE_XMMCACHE_END + + _flushCachedRegs(); + _deleteEEreg(_Rd_, 0); + + //PUSH32R( EBX ); + MOVZX32M16toR( EAX, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + MOV32RtoR( ECX, EAX ); + SHL32ItoR( ECX, 16 ); + OR32RtoR( EAX, ECX ); + MOVZX32M16toR( EDX, (uptr)&cpuRegs.GPR.r[ _Rt_ ].UD[ 1 ] ); + MOV32RtoR( ECX, EDX ); + SHL32ItoR( ECX, 16 ); + OR32RtoR( EDX, ECX ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EAX ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 2 ], EDX ); + MOV32RtoM( (uptr)&cpuRegs.GPR.r[ _Rd_ ].UL[ 3 ], EDX ); + //POP32R( EBX ); +} + +#endif // else MMI3_RECOMPILE + +} } } } \ No newline at end of file diff --git a/pcsx2/x86/iMMI.h b/pcsx2/x86/iMMI.h new file mode 100644 index 0000000000..b57fe1d9f9 --- /dev/null +++ b/pcsx2/x86/iMMI.h @@ -0,0 +1,147 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +//btw, nice box ya got there ! +/********************************************************* +* MMI opcodes * +* * +*********************************************************/ +#ifndef __IMMI_H__ +#define __IMMI_H__ + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + + // These are instructions contained the MMI "opcode space" but are not + // actually MMI instructions. They are just specialized versions of standard + // instructions that "fit" into the second pipeline of the EE. + + void recMADD1(); + void recMADDU1(); + void recMADD(); + void recMADDU(); + + void recMTHI1(); + void recMTLO1(); + void recMFHI1(); + void recMFLO1(); + void recMULT1(); + void recMULTU1(); + void recDIV1(); + void recDIVU1(); + +namespace MMI +{ + void recPLZCW(); + void recMMI0(); + void recMMI1(); + void recMMI2(); + void recMMI3(); + void recPMFHL(); + void recPMTHL(); + void recPMAXW(); + void recPMINW(); + void recPPACW(); + void recPEXTLH(); + void recPPACH(); + void recPEXTLB(); + void recPPACB(); + void recPEXT5(); + void recPPAC5(); + void recPABSW(); + void recPADSBH(); + void recPABSH(); + void recPADDUW(); + void recPSUBUW(); + void recPSUBUH(); + void recPEXTUH(); + void recPSUBUB(); + void recPEXTUB(); + void recQFSRV(); + void recPMADDW(); + void recPSLLVW(); + void recPSRLVW(); + void recPMSUBW(); + void recPINTH(); + void recPMULTW(); + void recPDIVW(); + void recPMADDH(); + void recPHMADH(); + void recPMSUBH(); + void recPHMSBH(); + void recPEXEH(); + void recPREVH(); + void recPMULTH(); + void recPDIVBW(); + void recPEXEW(); + void recPROT3W(); + void recPMADDUW(); + void recPSRAVW(); + void recPINTEH(); + void recPMULTUW(); + void recPDIVUW(); + void recPEXCH(); + void recPEXCW(); + + void recPSRLH(); + void recPSRLW(); + void recPSRAH(); + void recPSRAW(); + void recPSLLH(); + void recPSLLW(); + void recPMAXH(); + void recPCGTB(); + void recPCGTH(); + void recPCGTW(); + void recPADDSB(); + void recPADDSH(); + void recPADDSW(); + void recPSUBSB(); + void recPSUBSH(); + void recPSUBSW(); + void recPADDB(); + void recPADDH(); + void recPADDW(); + void recPSUBB(); + void recPSUBH(); + void recPSUBW(); + void recPEXTLW(); + void recPEXTUW(); + void recPMINH(); + void recPCEQB(); + void recPCEQH(); + void recPCEQW(); + void recPADDUB(); + void recPADDUH(); + void recPMFHI(); + void recPMFLO(); + void recPAND(); + void recPXOR(); + void recPCPYLD(); + void recPNOR(); + void recPMTHI(); + void recPMTLO(); + void recPCPYUD(); + void recPOR(); + void recPCPYH(); + +} } } } + +#endif + diff --git a/pcsx2/x86/iPsxHw.cpp b/pcsx2/x86/iPsxHw.cpp new file mode 100644 index 0000000000..7ffcf6ad63 --- /dev/null +++ b/pcsx2/x86/iPsxHw.cpp @@ -0,0 +1,1179 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" +#include "iR5900.h" + +extern int g_pbufi; +extern s8 g_pbuf[1024]; + +#define CONSTREAD8_CALL(name) { \ + iFlushCall(0); \ + CALLFunc((uptr)name); \ + if( sign ) MOVSX32R8toR(EAX, EAX); \ + else MOVZX32R8toR(EAX, EAX); \ +} \ + +static u32 s_16 = 0x10; + +int psxHwConstRead8(u32 x86reg, u32 add, u32 sign) { + + if (add >= 0x1f801600 && add < 0x1f801700) { + PUSH32I(add); + CONSTREAD8_CALL(USBread8); + // since calling from different dll, esp already changed + return 1; + } + + switch (add) { + case 0x1f801040: + CONSTREAD8_CALL(sioRead8); + return 1; + // case 0x1f801050: hard = serial_read8(); break;//for use of serial port ignore for now + +#ifdef PCSX2_DEVBUILD + case 0x1f801100: + case 0x1f801104: + case 0x1f801108: + case 0x1f801110: + case 0x1f801114: + case 0x1f801118: + case 0x1f801120: + case 0x1f801124: + case 0x1f801128: + case 0x1f801480: + case 0x1f801484: + case 0x1f801488: + case 0x1f801490: + case 0x1f801494: + case 0x1f801498: + case 0x1f8014a0: + case 0x1f8014a4: + case 0x1f8014a8: + SysPrintf("8bit counter read %x\n", add); + _eeReadConstMem8(x86reg, (uptr)&psxH[(add) & 0xffff], sign); + return 0; +#endif + + case 0x1f80146e: // DEV9_R_REV + PUSH32I(add); + CONSTREAD8_CALL(DEV9read8); + return 1; + + case 0x1f801800: CONSTREAD8_CALL(cdrRead0); return 1; + case 0x1f801801: CONSTREAD8_CALL(cdrRead1); return 1; + case 0x1f801802: CONSTREAD8_CALL(cdrRead2); return 1; + case 0x1f801803: CONSTREAD8_CALL(cdrRead3); return 1; + + case 0x1f803100: // PS/EE/IOP conf related + if( IS_XMMREG(x86reg) ) SSEX_MOVD_M32_to_XMM(x86reg&0xf, (uptr)&s_16); + MMXONLY(else if( IS_MMXREG(x86reg) ) MOVDMtoMMX(x86reg&0xf, (uptr)&s_16);) + else MOV32ItoR(x86reg, 0x10); + return 0; + + case 0x1F808264: //sio2 serial data feed/fifo_out + CONSTREAD8_CALL(sio2_fifoOut); + return 1; + + default: + _eeReadConstMem8(x86reg, (uptr)&psxH[(add) & 0xffff], sign); + return 0; + } +} + +#define CONSTREAD16_CALL(name) { \ + iFlushCall(0); \ + CALLFunc((uptr)name); \ + if( sign ) MOVSX32R16toR(EAX, EAX); \ + else MOVZX32R16toR(EAX, EAX); \ +} \ + +void psxConstReadCounterMode16(int x86reg, int index, int sign) +{ + if( IS_MMXREG(x86reg) ) { + MMXONLY(MOV16MtoR(ECX, (uptr)&psxCounters[index].mode); + MOVDMtoMMX(x86reg&0xf, (uptr)&psxCounters[index].mode - 2);) + } + else { + if( sign ) MOVSX32M16toR(ECX, (uptr)&psxCounters[index].mode); + else MOVZX32M16toR(ECX, (uptr)&psxCounters[index].mode); + + MOV32RtoR(x86reg, ECX); + } + + AND16ItoR(ECX, ~0x1800); + OR16ItoR(ECX, 0x400); + MOV16RtoM((uptr)&psxCounters[index].mode, ECX); +} + +int psxHwConstRead16(u32 x86reg, u32 add, u32 sign) { + if (add >= 0x1f801600 && add < 0x1f801700) { + PUSH32I(add); + CONSTREAD16_CALL(USBread16); + return 1; + } + + switch (add) { + + case 0x1f801040: + iFlushCall(0); + CALLFunc((uptr)sioRead8); + PUSHR(EAX); + CALLFunc((uptr)sioRead8); + POPR(ECX); + AND32ItoR(ECX, 0xff); + SHL32ItoR(EAX, 8); + OR32RtoR(EAX, ECX); + if( sign ) MOVSX32R16toR(EAX, EAX); + else MOVZX32R16toR(EAX, EAX); + return 1; + + case 0x1f801044: + _eeReadConstMem16(x86reg, (uptr)&sio.StatReg, sign); + return 0; + + case 0x1f801048: + _eeReadConstMem16(x86reg, (uptr)&sio.ModeReg, sign); + return 0; + + case 0x1f80104a: + _eeReadConstMem16(x86reg, (uptr)&sio.CtrlReg, sign); + return 0; + + case 0x1f80104e: + _eeReadConstMem16(x86reg, (uptr)&sio.BaudReg, sign); + return 0; + + // counters[0] + case 0x1f801100: + PUSH32I(0); + CONSTREAD16_CALL(psxRcntRcount16); + ADD32ItoR(ESP, 4); + return 1; + case 0x1f801104: + psxConstReadCounterMode16(x86reg, 0, sign); + return 0; + + case 0x1f801108: + _eeReadConstMem16(x86reg, (uptr)&psxCounters[0].target, sign); + return 0; + + // counters[1] + case 0x1f801110: + PUSH32I(1); + CONSTREAD16_CALL(psxRcntRcount16); + ADD32ItoR(ESP, 4); + return 1; + case 0x1f801114: + psxConstReadCounterMode16(x86reg, 1, sign); + return 0; + + case 0x1f801118: + _eeReadConstMem16(x86reg, (uptr)&psxCounters[1].target, sign); + return 0; + + // counters[2] + case 0x1f801120: + PUSH32I(2); + CONSTREAD16_CALL(psxRcntRcount16); + ADD32ItoR(ESP, 4); + return 1; + case 0x1f801124: + psxConstReadCounterMode16(x86reg, 2, sign); + return 0; + + case 0x1f801128: + _eeReadConstMem16(x86reg, (uptr)&psxCounters[2].target, sign); + return 0; + + case 0x1f80146e: // DEV9_R_REV + PUSH32I(add); + CONSTREAD16_CALL(DEV9read16); + return 1; + + // counters[3] + case 0x1f801480: + PUSH32I(3); + CONSTREAD16_CALL(psxRcntRcount32); + ADD32ItoR(ESP, 4); + return 1; + + case 0x1f801484: + psxConstReadCounterMode16(x86reg, 3, sign); + return 0; + + case 0x1f801488: + _eeReadConstMem16(x86reg, (uptr)&psxCounters[3].target, sign); + return 0; + + // counters[4] + case 0x1f801490: + PUSH32I(4); + CONSTREAD16_CALL(psxRcntRcount32); + ADD32ItoR(ESP, 4); + return 1; + + case 0x1f801494: + psxConstReadCounterMode16(x86reg, 4, sign); + return 0; + + case 0x1f801498: + _eeReadConstMem16(x86reg, (uptr)&psxCounters[4].target, sign); + return 0; + + // counters[5] + case 0x1f8014a0: + PUSH32I(5); + CONSTREAD16_CALL(psxRcntRcount32); + ADD32ItoR(ESP, 4); + return 1; + + case 0x1f8014a4: + psxConstReadCounterMode16(x86reg, 5, sign); + return 0; + + case 0x1f8014a8: + _eeReadConstMem16(x86reg, (uptr)&psxCounters[5].target, sign); + return 0; + + default: + if (add>=0x1f801c00 && add<0x1f801e00) { + + PUSH32I(add); + CONSTREAD16_CALL(SPU2read); + return 1; + } else { + _eeReadConstMem16(x86reg, (uptr)&psxH[(add) & 0xffff], sign); + return 0; + } + } +} + +void psxConstReadCounterMode32(int x86reg, int index) +{ + if( IS_MMXREG(x86reg) ) { + MMXONLY(MOV16MtoR(ECX, (uptr)&psxCounters[index].mode); + MOVDMtoMMX(x86reg&0xf, (uptr)&psxCounters[index].mode);) + } + else { + MOVZX32M16toR(ECX, (uptr)&psxCounters[index].mode); + MOV32RtoR(x86reg, ECX); + } + + //AND16ItoR(ECX, ~0x1800); + //OR16ItoR(ECX, 0x400); + //MOV16RtoM((uptr)&psxCounters[index].mode, ECX); +} + +static u32 s_tempsio; +int psxHwConstRead32(u32 x86reg, u32 add) { + if (add >= 0x1f801600 && add < 0x1f801700) { + iFlushCall(0); + PUSH32I(add); + CALLFunc((uptr)USBread32); + return 1; + } + if (add >= 0x1f808400 && add <= 0x1f808550) {//the size is a complete guess.. + iFlushCall(0); + PUSH32I(add); + CALLFunc((uptr)FWread32); + return 1; + } + + switch (add) { + case 0x1f801040: + iFlushCall(0); + CALLFunc((uptr)sioRead8); + AND32ItoR(EAX, 0xff); + MOV32RtoM((uptr)&s_tempsio, EAX); + CALLFunc((uptr)sioRead8); + AND32ItoR(EAX, 0xff); + SHL32ItoR(EAX, 8); + OR32RtoM((uptr)&s_tempsio, EAX); + + // 3rd + CALLFunc((uptr)sioRead8); + AND32ItoR(EAX, 0xff); + SHL32ItoR(EAX, 16); + OR32RtoM((uptr)&s_tempsio, EAX); + + // 4th + CALLFunc((uptr)sioRead8); + SHL32ItoR(EAX, 24); + OR32MtoR(EAX, (uptr)&s_tempsio); + return 1; + + //case 0x1f801050: hard = serial_read32(); break;//serial port + case 0x1f801078: + PSXHW_LOG("ICTRL 32bit read %x\n", psxHu32(0x1078)); + _eeReadConstMem32(x86reg, (uptr)&psxH[add&0xffff]); + MOV32ItoM((uptr)&psxH[add&0xffff], 0); + return 0; + + // counters[0] + case 0x1f801100: + iFlushCall(0); + PUSH32I(0); + CALLFunc((uptr)psxRcntRcount16); + ADD32ItoR(ESP, 4); + return 1; + case 0x1f801104: + psxConstReadCounterMode32(x86reg, 0); + return 0; + + case 0x1f801108: + _eeReadConstMem32(x86reg, (uptr)&psxCounters[0].target); + return 0; + + // counters[1] + case 0x1f801110: + iFlushCall(0); + PUSH32I(1); + CALLFunc((uptr)psxRcntRcount16); + ADD32ItoR(ESP, 4); + return 1; + case 0x1f801114: + psxConstReadCounterMode32(x86reg, 1); + return 0; + + case 0x1f801118: + _eeReadConstMem32(x86reg, (uptr)&psxCounters[1].target); + return 0; + + // counters[2] + case 0x1f801120: + iFlushCall(0); + PUSH32I(2); + CALLFunc((uptr)psxRcntRcount16); + ADD32ItoR(ESP, 4); + return 1; + case 0x1f801124: + psxConstReadCounterMode32(x86reg, 2); + return 0; + + case 0x1f801128: + _eeReadConstMem32(x86reg, (uptr)&psxCounters[2].target); + return 0; + + // counters[3] + case 0x1f801480: + iFlushCall(0); + PUSH32I(3); + CALLFunc((uptr)psxRcntRcount32); + ADD32ItoR(ESP, 4); + return 1; + + case 0x1f801484: + psxConstReadCounterMode32(x86reg, 3); + return 0; + + case 0x1f801488: + _eeReadConstMem32(x86reg, (uptr)&psxCounters[3].target); + return 0; + + // counters[4] + case 0x1f801490: + iFlushCall(0); + PUSH32I(4); + CALLFunc((uptr)psxRcntRcount32); + ADD32ItoR(ESP, 4); + return 1; + + case 0x1f801494: + psxConstReadCounterMode32(x86reg, 4); + return 0; + + case 0x1f801498: + _eeReadConstMem32(x86reg, (uptr)&psxCounters[4].target); + return 0; + + // counters[5] + case 0x1f8014a0: + iFlushCall(0); + PUSH32I(5); + CALLFunc((uptr)psxRcntRcount32); + ADD32ItoR(ESP, 4); + return 1; + + case 0x1f8014a4: + psxConstReadCounterMode32(x86reg, 5); + return 0; + + case 0x1f8014a8: + _eeReadConstMem32(x86reg, (uptr)&psxCounters[5].target); + return 0; + + case 0x1F808200: + case 0x1F808204: + case 0x1F808208: + case 0x1F80820C: + case 0x1F808210: + case 0x1F808214: + case 0x1F808218: + case 0x1F80821C: + case 0x1F808220: + case 0x1F808224: + case 0x1F808228: + case 0x1F80822C: + case 0x1F808230: + case 0x1F808234: + case 0x1F808238: + case 0x1F80823C: + iFlushCall(0); + PUSH32I((add-0x1F808200)/4); + CALLFunc((uptr)sio2_getSend3); + ADD32ItoR(ESP, 4); + return 1; + + case 0x1F808240: + case 0x1F808248: + case 0x1F808250: + case 0x1F80825C: + iFlushCall(0); + PUSH32I((add-0x1F808240)/8); + CALLFunc((uptr)sio2_getSend1); + ADD32ItoR(ESP, 4); + return 1; + + case 0x1F808244: + case 0x1F80824C: + case 0x1F808254: + case 0x1F808258: + iFlushCall(0); + PUSH32I((add-0x1F808244)/8); + CALLFunc((uptr)sio2_getSend2); + ADD32ItoR(ESP, 4); + return 1; + + case 0x1F808268: + iFlushCall(0); + CALLFunc((uptr)sio2_getCtrl); + return 1; + + case 0x1F80826C: + iFlushCall(0); + CALLFunc((uptr)sio2_getRecv1); + return 1; + + case 0x1F808270: + iFlushCall(0); + CALLFunc((uptr)sio2_getRecv2); + return 1; + + case 0x1F808274: + iFlushCall(0); + CALLFunc((uptr)sio2_getRecv3); + return 1; + + case 0x1F808278: + iFlushCall(0); + CALLFunc((uptr)sio2_get8278); + return 1; + + case 0x1F80827C: + iFlushCall(0); + CALLFunc((uptr)sio2_get827C); + return 1; + + case 0x1F808280: + iFlushCall(0); + CALLFunc((uptr)sio2_getIntr); + return 1; + + case 0x1F801C00: + iFlushCall(0); + PUSH32I(0); + CALLFunc((uptr)SPU2ReadMemAddr); + return 1; + + case 0x1F801500: + iFlushCall(0); + PUSH32I(1); + CALLFunc((uptr)SPU2ReadMemAddr); + return 1; + + default: + _eeReadConstMem32(x86reg, (uptr)&psxH[(add) & 0xffff]); + return 0; + } +} + +#define CONSTWRITE_CALL(name) { \ + _recPushReg(mmreg); \ + iFlushCall(0); \ + CALLFunc((uptr)name); \ + ADD32ItoR(ESP, 4); \ +} \ + +void Write8PrintBuffer(u8 value) +{ + if (value == '\r') return; + if (value == '\n' || g_pbufi >= 1023) { + g_pbuf[g_pbufi++] = 0; g_pbufi = 0; + SysPrintf("%s\n", g_pbuf); return; + } + g_pbuf[g_pbufi++] = value; +} + +void psxHwConstWrite8(u32 add, int mmreg) +{ + if (add >= 0x1f801600 && add < 0x1f801700) { + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(add); + CALLFunc((uptr)USBwrite8); + return; + } + + switch (add) { + case 0x1f801040: + CONSTWRITE_CALL(sioWrite8); break; + //case 0x1f801050: serial_write8(value); break;//serial port + case 0x1f801100: + case 0x1f801104: + case 0x1f801108: + case 0x1f801110: + case 0x1f801114: + case 0x1f801118: + case 0x1f801120: + case 0x1f801124: + case 0x1f801128: + case 0x1f801480: + case 0x1f801484: + case 0x1f801488: + case 0x1f801490: + case 0x1f801494: + case 0x1f801498: + case 0x1f8014a0: + case 0x1f8014a4: + case 0x1f8014a8: + SysPrintf("8bit counter write %x\n", add); + _eeWriteConstMem8((uptr)&psxH[(add) & 0xffff], mmreg); + return; + case 0x1f801800: CONSTWRITE_CALL(cdrWrite0); break; + case 0x1f801801: CONSTWRITE_CALL(cdrWrite1); break; + case 0x1f801802: CONSTWRITE_CALL(cdrWrite2); break; + case 0x1f801803: CONSTWRITE_CALL(cdrWrite3); break; + case 0x1f80380c: CONSTWRITE_CALL(Write8PrintBuffer); break; + case 0x1F808260: CONSTWRITE_CALL(sio2_serialIn); break; + + default: + _eeWriteConstMem8((uptr)&psxH[(add) & 0xffff], mmreg); + return; + } +} + +void psxHwConstWrite16(u32 add, int mmreg) { + if (add >= 0x1f801600 && add < 0x1f801700) { + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(add); + CALLFunc((uptr)USBwrite16); + return; + } + + switch (add) { + case 0x1f801040: + _recPushReg(mmreg); + iFlushCall(0); + CALLFunc((uptr)sioWrite8); + ADD32ItoR(ESP, 1); + CALLFunc((uptr)sioWrite8); + ADD32ItoR(ESP, 3); + return; + case 0x1f801044: + return; + case 0x1f801048: + _eeWriteConstMem16((uptr)&sio.ModeReg, mmreg); + return; + case 0x1f80104a: // control register + CONSTWRITE_CALL(sioWriteCtrl16); + return; + case 0x1f80104e: // baudrate register + _eeWriteConstMem16((uptr)&sio.BaudReg, mmreg); + return; + + case 0x1f801070: + _eeWriteConstMem16OP((uptr)&psxHu32(0x1070), mmreg, 0); // AND operation + return; + + case 0x1f801074: + _eeWriteConstMem16((uptr)&psxHu32(0x1074), mmreg); + iFlushCall(0); + CALLFunc( (uptr)&iopTestIntc ); + return; + + case 0x1f801078: + //According to pSXAuthor this allways becomes 1 on write, but MHPB won't boot if value is not writen ;p + _eeWriteConstMem16((uptr)&psxHu32(0x1078), mmreg); + iFlushCall(0); + CALLFunc( (uptr)&iopTestIntc ); + return; + + // counters[0] + case 0x1f801100: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(0); + CALLFunc((uptr)psxRcntWcount16); + ADD32ItoR(ESP, 8); + return; + case 0x1f801104: + CONSTWRITE_CALL(psxRcnt0Wmode); + return; + case 0x1f801108: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(0); + CALLFunc((uptr)psxRcntWtarget16); + ADD32ItoR(ESP, 8); + return; + + // counters[1] + case 0x1f801110: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(1); + CALLFunc((uptr)psxRcntWcount16); + ADD32ItoR(ESP, 8); + return; + + case 0x1f801114: + CONSTWRITE_CALL(psxRcnt1Wmode); + return; + + case 0x1f801118: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(1); + CALLFunc((uptr)psxRcntWtarget16); + ADD32ItoR(ESP, 8); + return; + + // counters[2] + case 0x1f801120: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(2); + CALLFunc((uptr)psxRcntWcount16); + ADD32ItoR(ESP, 8); + return; + + case 0x1f801124: + CONSTWRITE_CALL(psxRcnt2Wmode); + return; + + case 0x1f801128: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(2); + CALLFunc((uptr)psxRcntWtarget16); + ADD32ItoR(ESP, 8); + return; + + // counters[3] + case 0x1f801480: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(3); + CALLFunc((uptr)psxRcntWcount32); + ADD32ItoR(ESP, 8); + return; + + case 0x1f801484: + CONSTWRITE_CALL(psxRcnt3Wmode); + return; + + case 0x1f801488: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(3); + CALLFunc((uptr)psxRcntWtarget32); + ADD32ItoR(ESP, 8); + return; + + // counters[4] + case 0x1f801490: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(4); + CALLFunc((uptr)psxRcntWcount32); + ADD32ItoR(ESP, 8); + return; + + case 0x1f801494: + CONSTWRITE_CALL(psxRcnt4Wmode); + return; + + case 0x1f801498: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(4); + CALLFunc((uptr)psxRcntWtarget32); + ADD32ItoR(ESP, 8); + return; + + // counters[5] + case 0x1f8014a0: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(5); + CALLFunc((uptr)psxRcntWcount32); + ADD32ItoR(ESP, 8); + return; + + case 0x1f8014a4: + CONSTWRITE_CALL(psxRcnt5Wmode); + return; + + case 0x1f8014a8: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(5); + CALLFunc((uptr)psxRcntWtarget32); + ADD32ItoR(ESP, 8); + return; + + default: + if (add>=0x1f801c00 && add<0x1f801e00) { + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(add); + CALLFunc((uptr)SPU2write); + // leave esp alone + return; + } + + _eeWriteConstMem16((uptr)&psxH[(add) & 0xffff], mmreg); + return; + } +} + +#define recDmaExec(n) { \ + iFlushCall(0); \ + if( n > 6 ) TEST32ItoM((uptr)&HW_DMA_PCR2, 8 << (((n<<2)-28)&0x1f)); \ + else TEST32ItoM((uptr)&HW_DMA_PCR, 8 << (((n<<2))&0x1f)); \ + j8Ptr[5] = JZ8(0); \ + MOV32MtoR(EAX, (uptr)&HW_DMA##n##_CHCR); \ + TEST32ItoR(EAX, 0x01000000); \ + j8Ptr[6] = JZ8(0); \ + \ + _callFunctionArg3((uptr)psxDma##n, MEM_MEMORYTAG, MEM_MEMORYTAG, MEM_X86TAG, (uptr)&HW_DMA##n##_MADR, (uptr)&HW_DMA##n##_BCR, EAX); \ + \ + x86SetJ8( j8Ptr[5] ); \ + x86SetJ8( j8Ptr[6] ); \ +} \ + +#define CONSTWRITE_CALL32(name) { \ + iFlushCall(0); \ + _recPushReg(mmreg); \ + CALLFunc((uptr)name); \ + ADD32ItoR(ESP, 4); \ +} \ + +void psxHwConstWrite32(u32 add, int mmreg) +{ + if (add >= 0x1f801600 && add < 0x1f801700) { + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(add); + CALLFunc((uptr)USBwrite32); + return; + } + if (add >= 0x1f808400 && add <= 0x1f808550) { + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(add); + CALLFunc((uptr)FWwrite32); + return; + } + + switch (add) { + case 0x1f801040: + _recPushReg(mmreg); + iFlushCall(0); + CALLFunc((uptr)sioWrite8); + ADD32ItoR(ESP, 1); + CALLFunc((uptr)sioWrite8); + ADD32ItoR(ESP, 1); + CALLFunc((uptr)sioWrite8); + ADD32ItoR(ESP, 1); + CALLFunc((uptr)sioWrite8); + ADD32ItoR(ESP, 1); + return; + + case 0x1f801070: + _eeWriteConstMem32OP((uptr)&psxHu32(0x1070), mmreg, 0); // and + return; + + case 0x1f801074: + _eeWriteConstMem32((uptr)&psxHu32(0x1074), mmreg); + iFlushCall(0); + CALLFunc( (uptr)&iopTestIntc ); + return; + + case 0x1f801078: + //According to pSXAuthor this allways becomes 1 on write, but MHPB won't boot if value is not writen ;p + _eeWriteConstMem32((uptr)&psxHu32(0x1078), mmreg); + iFlushCall(0); + CALLFunc( (uptr)&iopTestIntc ); + return; + +// case 0x1f801088: +// HW_DMA0_CHCR = value; // DMA0 chcr (MDEC in DMA) +//// DmaExec(0); +// return; + +// case 0x1f801098: +// HW_DMA1_CHCR = value; // DMA1 chcr (MDEC out DMA) +//// DmaExec(1); +// return; + + case 0x1f8010a8: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + recDmaExec(2); + return; + + case 0x1f8010b8: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + recDmaExec(3); + return; + + case 0x1f8010c8: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + recDmaExec(4); + return; + + case 0x1f8010e8: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + recDmaExec(6); + return; + + case 0x1f801508: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + recDmaExec(7); + return; + + case 0x1f801518: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + recDmaExec(8); + return; + + case 0x1f801528: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + recDmaExec(9); + return; + + case 0x1f801538: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + recDmaExec(10); + return; + + case 0x1f801548: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + recDmaExec(11); + return; + + case 0x1f801558: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + recDmaExec(12); + return; + + case 0x1f8010f4: + case 0x1f801574: + { + // u32 tmp = (~value) & HW_DMA_ICR; + _eeMoveMMREGtoR(EAX, mmreg); + MOV32RtoR(ECX, EAX); + NOT32R(ECX); + AND32MtoR(ECX, (uptr)&psxH[(add) & 0xffff]); + + // HW_DMA_ICR = ((tmp ^ value) & 0xffffff) ^ tmp; + XOR32RtoR(EAX, ECX); + AND32ItoR(EAX, 0xffffff); + XOR32RtoR(EAX, ECX); + MOV32RtoM((uptr)&psxH[(add) & 0xffff], EAX); + return; + } + + // counters[0] + case 0x1f801100: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(0); + CALLFunc((uptr)psxRcntWcount16); + ADD32ItoR(ESP, 8); + return; + case 0x1f801104: + CONSTWRITE_CALL32(psxRcnt0Wmode); + return; + case 0x1f801108: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(0); + CALLFunc((uptr)psxRcntWtarget16); + ADD32ItoR(ESP, 8); + return; + + // counters[1] + case 0x1f801110: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(1); + CALLFunc((uptr)psxRcntWcount16); + ADD32ItoR(ESP, 8); + return; + + case 0x1f801114: + CONSTWRITE_CALL32(psxRcnt1Wmode); + return; + + case 0x1f801118: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(1); + CALLFunc((uptr)psxRcntWtarget16); + ADD32ItoR(ESP, 8); + return; + + // counters[2] + case 0x1f801120: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(2); + CALLFunc((uptr)psxRcntWcount16); + ADD32ItoR(ESP, 8); + return; + + case 0x1f801124: + CONSTWRITE_CALL32(psxRcnt2Wmode); + return; + + case 0x1f801128: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(2); + CALLFunc((uptr)psxRcntWtarget16); + ADD32ItoR(ESP, 8); + return; + + // counters[3] + case 0x1f801480: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(3); + CALLFunc((uptr)psxRcntWcount32); + ADD32ItoR(ESP, 8); + return; + + case 0x1f801484: + CONSTWRITE_CALL32(psxRcnt3Wmode); + return; + + case 0x1f801488: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(3); + CALLFunc((uptr)psxRcntWtarget32); + ADD32ItoR(ESP, 8); + return; + + // counters[4] + case 0x1f801490: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(4); + CALLFunc((uptr)psxRcntWcount32); + ADD32ItoR(ESP, 8); + return; + + case 0x1f801494: + CONSTWRITE_CALL32(psxRcnt4Wmode); + return; + + case 0x1f801498: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(4); + CALLFunc((uptr)psxRcntWtarget32); + ADD32ItoR(ESP, 8); + return; + + // counters[5] + case 0x1f8014a0: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(5); + CALLFunc((uptr)psxRcntWcount32); + ADD32ItoR(ESP, 8); + return; + + case 0x1f8014a4: + CONSTWRITE_CALL32(psxRcnt5Wmode); + return; + + case 0x1f8014a8: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(5); + CALLFunc((uptr)psxRcntWtarget32); + ADD32ItoR(ESP, 8); + return; + + case 0x1f8014c0: + SysPrintf("RTC_HOLDMODE 32bit write\n"); + break; + + case 0x1F808200: + case 0x1F808204: + case 0x1F808208: + case 0x1F80820C: + case 0x1F808210: + case 0x1F808214: + case 0x1F808218: + case 0x1F80821C: + case 0x1F808220: + case 0x1F808224: + case 0x1F808228: + case 0x1F80822C: + case 0x1F808230: + case 0x1F808234: + case 0x1F808238: + case 0x1F80823C: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I((add-0x1F808200)/4); + CALLFunc((uptr)sio2_setSend3); + ADD32ItoR(ESP, 8); + return; + + case 0x1F808240: + case 0x1F808248: + case 0x1F808250: + case 0x1F808258: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I((add-0x1F808240)/8); + CALLFunc((uptr)sio2_setSend1); + ADD32ItoR(ESP, 8); + return; + + case 0x1F808244: + case 0x1F80824C: + case 0x1F808254: + case 0x1F80825C: + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I((add-0x1F808244)/8); + CALLFunc((uptr)sio2_setSend2); + ADD32ItoR(ESP, 8); + return; + + case 0x1F808268: CONSTWRITE_CALL32(sio2_setCtrl); return; + case 0x1F808278: CONSTWRITE_CALL32(sio2_set8278); return; + case 0x1F80827C: CONSTWRITE_CALL32(sio2_set827C); return; + case 0x1F808280: CONSTWRITE_CALL32(sio2_setIntr); return; + + case 0x1F8010C0: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(0); + CALLFunc((uptr)SPU2WriteMemAddr); + return; + + case 0x1F801500: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + _recPushReg(mmreg); + iFlushCall(0); + PUSH32I(1); + CALLFunc((uptr)SPU2WriteMemAddr); + return; + default: + _eeWriteConstMem32((uptr)&psxH[(add) & 0xffff], mmreg); + return; + } +} + +int psxHw4ConstRead8(u32 x86reg, u32 add, u32 sign) { + switch (add) { + case 0x1f402004: CONSTREAD8_CALL((uptr)cdvdRead04); return 1; + case 0x1f402005: CONSTREAD8_CALL((uptr)cdvdRead05); return 1; + case 0x1f402006: CONSTREAD8_CALL((uptr)cdvdRead06); return 1; + case 0x1f402007: CONSTREAD8_CALL((uptr)cdvdRead07); return 1; + case 0x1f402008: CONSTREAD8_CALL((uptr)cdvdRead08); return 1; + case 0x1f40200A: CONSTREAD8_CALL((uptr)cdvdRead0A); return 1; + case 0x1f40200B: CONSTREAD8_CALL((uptr)cdvdRead0B); return 1; + case 0x1f40200C: CONSTREAD8_CALL((uptr)cdvdRead0C); return 1; + case 0x1f40200D: CONSTREAD8_CALL((uptr)cdvdRead0D); return 1; + case 0x1f40200E: CONSTREAD8_CALL((uptr)cdvdRead0E); return 1; + case 0x1f40200F: CONSTREAD8_CALL((uptr)cdvdRead0F); return 1; + case 0x1f402013: CONSTREAD8_CALL((uptr)cdvdRead13); return 1; + case 0x1f402015: CONSTREAD8_CALL((uptr)cdvdRead15); return 1; + case 0x1f402016: CONSTREAD8_CALL((uptr)cdvdRead16); return 1; + case 0x1f402017: CONSTREAD8_CALL((uptr)cdvdRead17); return 1; + case 0x1f402018: CONSTREAD8_CALL((uptr)cdvdRead18); return 1; + case 0x1f402020: CONSTREAD8_CALL((uptr)cdvdRead20); return 1; + case 0x1f402021: CONSTREAD8_CALL((uptr)cdvdRead21); return 1; + case 0x1f402022: CONSTREAD8_CALL((uptr)cdvdRead22); return 1; + case 0x1f402023: CONSTREAD8_CALL((uptr)cdvdRead23); return 1; + case 0x1f402024: CONSTREAD8_CALL((uptr)cdvdRead24); return 1; + case 0x1f402028: CONSTREAD8_CALL((uptr)cdvdRead28); return 1; + case 0x1f402029: CONSTREAD8_CALL((uptr)cdvdRead29); return 1; + case 0x1f40202A: CONSTREAD8_CALL((uptr)cdvdRead2A); return 1; + case 0x1f40202B: CONSTREAD8_CALL((uptr)cdvdRead2B); return 1; + case 0x1f40202C: CONSTREAD8_CALL((uptr)cdvdRead2C); return 1; + case 0x1f402030: CONSTREAD8_CALL((uptr)cdvdRead30); return 1; + case 0x1f402031: CONSTREAD8_CALL((uptr)cdvdRead31); return 1; + case 0x1f402032: CONSTREAD8_CALL((uptr)cdvdRead32); return 1; + case 0x1f402033: CONSTREAD8_CALL((uptr)cdvdRead33); return 1; + case 0x1f402034: CONSTREAD8_CALL((uptr)cdvdRead34); return 1; + case 0x1f402038: CONSTREAD8_CALL((uptr)cdvdRead38); return 1; + case 0x1f402039: CONSTREAD8_CALL((uptr)cdvdRead39); return 1; + case 0x1f40203A: CONSTREAD8_CALL((uptr)cdvdRead3A); return 1; + default: + Console::Notice("*Unknown 8bit read at address %lx", params add); + XOR32RtoR(x86reg, x86reg); + return 0; + } +} + +void psxHw4ConstWrite8(u32 add, int mmreg) { + switch (add) { + case 0x1f402004: CONSTWRITE_CALL(cdvdWrite04); return; + case 0x1f402005: CONSTWRITE_CALL(cdvdWrite05); return; + case 0x1f402006: CONSTWRITE_CALL(cdvdWrite06); return; + case 0x1f402007: CONSTWRITE_CALL(cdvdWrite07); return; + case 0x1f402008: CONSTWRITE_CALL(cdvdWrite08); return; + case 0x1f40200A: CONSTWRITE_CALL(cdvdWrite0A); return; + case 0x1f40200F: CONSTWRITE_CALL(cdvdWrite0F); return; + case 0x1f402014: CONSTWRITE_CALL(cdvdWrite14); return; + case 0x1f402016: + MMXONLY(_freeMMXregs();) + CONSTWRITE_CALL(cdvdWrite16); + return; + case 0x1f402017: CONSTWRITE_CALL(cdvdWrite17); return; + case 0x1f402018: CONSTWRITE_CALL(cdvdWrite18); return; + case 0x1f40203A: CONSTWRITE_CALL(cdvdWrite3A); return; + default: + Console::Notice("*Unknown 8bit write at address %lx", params add); + return; + } +} diff --git a/pcsx2/x86/iPsxMem.cpp b/pcsx2/x86/iPsxMem.cpp new file mode 100644 index 0000000000..f6201a2164 --- /dev/null +++ b/pcsx2/x86/iPsxMem.cpp @@ -0,0 +1,871 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "PsxCommon.h" +#include "iR3000A.h" +#include "VU.h" + + +extern int g_psxWriteOk; +extern u32 g_psxMaxRecMem; +static u32 writectrl; + +#ifdef PCSX2_VIRTUAL_MEM + +#ifdef _DEBUG + +#define ASSERT_WRITEOK \ +{ \ + __asm cmp g_psxWriteOk, 1 \ + __asm je WriteOk \ + __asm int 10 \ +} \ +WriteOk: \ + +#else +#define ASSERT_WRITEOK +#endif + +__declspec(naked) void psxRecMemRead8() +{ + __asm { + mov edx, ecx + shr edx, 16 + cmp dx, 0x1f80 + je hwread + cmp dx, 0x1f40 + je hw4read + cmp dx, 0x1000 + je devread + cmp dx, 0x1f00 + je spuread + } + + ASSERT_WRITEOK + + __asm { +memread: + // rom reads, has to be PS2MEM_BASE_ + mov eax, dword ptr [ecx+PS2MEM_BASE_] + ret + +hwread: + cmp cx, 0x1000 + jb memread + + push ecx + call psxHwRead8 + add esp, 4 + ret + +hw4read: + push ecx + call psxHw4Read8 + add esp, 4 + ret + +devread: + push ecx + call DEV9read8 + // stack already incremented + ret + +spuread: + push ecx + call SPU2read + // stack already incremented + ret + } +} + +int psxRecMemConstRead8(u32 x86reg, u32 mem, u32 sign) +{ + u32 t = (mem >> 16) & 0x1fff; + + switch(t) { + case 0x1f80: + return psxHwConstRead8(x86reg, mem&0x1fffffff, sign); + +#ifdef _DEBUG + case 0x1d00: assert(0); +#endif + + case 0x1f40: + return psxHw4ConstRead8(x86reg, mem&0x1fffffff, sign); + + case 0x1000: + PUSH32I(mem&0x1fffffff); + CALLFunc((uptr)DEV9read8); + if( sign ) MOVSX32R8toR(x86reg, EAX); + else MOVZX32R8toR(x86reg, EAX); + return 0; + + default: + _eeReadConstMem8(x86reg, (u32)PSXM(mem), sign); + return 0; + } +} + +__declspec(naked) void psxRecMemRead16() +{ + __asm { + mov edx, ecx + shr edx, 16 + cmp dx, 0x1f80 + je hwread + cmp dx, 0x1f90 + je spuread + cmp dx, 0x1d00 + je sifread + cmp dx, 0x1000 + je devread + } + + ASSERT_WRITEOK + + __asm { +memread: + // rom reads, has to be PS2MEM_BASE_ + mov eax, dword ptr [ecx+PS2MEM_BASE_] + ret + +hwread: + cmp cx, 0x1000 + jb memread + + push ecx + call psxHwRead16 + add esp, 4 + ret + +sifread: + mov edx, ecx + and edx, 0xf0 + cmp dl, 0x60 + je Sif60 + + + mov eax, dword ptr [edx+PS2MEM_BASE_+0x1000f200] + + cmp dl, 0x40 + jne End + + // 0x40 + or eax, 2 + jmp End +Sif60: + xor eax, eax + jmp End + +spuread: + push ecx + call SPU2read + // stack already incremented + +End: + ret + +devread: + push ecx + call DEV9read16 + // stack already incremented + ret + } +} + +int psxRecMemConstRead16(u32 x86reg, u32 mem, u32 sign) +{ + u32 t = (mem >> 16) & 0x1fff; + + switch(t) { + case 0x1f80: return psxHwConstRead16(x86reg, mem&0x1fffffff, sign); + + case 0x1d00: + + switch(mem & 0xF0) + { + case 0x40: + _eeReadConstMem16(x86reg, (u32)PS2MEM_HW+0xF240, sign); + OR32ItoR(x86reg, 0x0002); + break; + case 0x60: + XOR32RtoR(x86reg, x86reg); + break; + default: + _eeReadConstMem16(x86reg, (u32)PS2MEM_HW+0xf200+(mem&0xf0), sign); + break; + } + return 0; + + case 0x1f90: + PUSH32I(mem&0x1fffffff); + CALLFunc((uptr)SPU2read); + if( sign ) MOVSX32R16toR(x86reg, EAX); + else MOVZX32R16toR(x86reg, EAX); + return 0; + + case 0x1000: + PUSH32I(mem&0x1fffffff); + CALLFunc((uptr)DEV9read16); + if( sign ) MOVSX32R16toR(x86reg, EAX); + else MOVZX32R16toR(x86reg, EAX); + return 0; + + default: + assert( g_psxWriteOk ); + _eeReadConstMem16(x86reg, (u32)PSXM(mem), sign); + return 0; + } + + return 0; +} + +__declspec(naked) void psxRecMemRead32() +{ + __asm { + mov edx, ecx + shr edx, 16 + cmp dx, 0x1f80 + je hwread + cmp dx, 0x1d00 + je sifread + cmp dx, 0x1000 + je devread + cmp ecx, 0x1ffe0130 + je WriteCtrlRead + } + + ASSERT_WRITEOK + + __asm { +memread: + // rom reads, has to be PS2MEM_BASE_ + mov eax, dword ptr [ecx+PS2MEM_BASE_] + ret + +hwread: + cmp cx, 0x1000 + jb memread + + push ecx + call psxHwRead32 + add esp, 4 + ret + +sifread: + mov edx, ecx + and edx, 0xf0 + cmp dl, 0x60 + je Sif60 + + // do the read from ps2 mem + mov eax, dword ptr [edx+PS2MEM_BASE_+0x1000f200] + + cmp dl, 0x40 + jne End + + // 0x40 + or eax, 0xf0000002 + jmp End +Sif60: + xor eax, eax +End: + ret + +devread: + push ecx + call DEV9read32 + // stack already incremented + ret + +WriteCtrlRead: + mov eax, writectrl + ret + } +} + +int psxRecMemConstRead32(u32 x86reg, u32 mem) +{ + u32 t = (mem >> 16) & 0x1fff; + + switch(t) { + case 0x1f80: return psxHwConstRead32(x86reg, mem&0x1fffffff); + + case 0x1d00: + switch(mem & 0xF0) + { + case 0x40: + _eeReadConstMem32(x86reg, (u32)PS2MEM_HW+0xF240); + OR32ItoR(x86reg, 0xf0000002); + break; + case 0x60: + XOR32RtoR(x86reg, x86reg); + break; + default: + _eeReadConstMem32(x86reg, (u32)PS2MEM_HW+0xf200+(mem&0xf0)); + break; + } + return 0; + + case 0x1000: + PUSH32I(mem&0x1fffffff); + CALLFunc((uptr)DEV9read32); + return 1; + + default: + if( mem == 0xfffe0130 ) + MOV32MtoR(x86reg, (uptr)&writectrl); + else { + XOR32RtoR(x86reg, x86reg); + CMP32ItoM((uptr)&g_psxWriteOk, 0); + CMOVNE32MtoR(x86reg, (u32)PSXM(mem)); + } + + return 0; + } +} + +__declspec(naked) void psxRecMemWrite8() +{ + __asm { + mov edx, ecx + shr edx, 16 + cmp dx, 0x1f80 + je hwwrite + cmp dx, 0x1f40 + je hw4write + cmp dx, 0x1000 + je devwrite + } + + ASSERT_WRITEOK + + __asm { +memwrite: + // rom writes, has to be PS2MEM_BASE_ + mov byte ptr [ecx+PS2MEM_BASE_], al + ret + +hwwrite: + cmp cx, 0x1000 + jb memwrite + + push eax + push ecx + call psxHwWrite8 + add esp, 8 + ret + +hw4write: + push eax + push ecx + call psxHw4Write8 + add esp, 8 + ret + +devwrite: + push eax + push ecx + call DEV9write8 + // stack alwritey incremented + ret + } +} + +int psxRecMemConstWrite8(u32 mem, int mmreg) +{ + u32 t = (mem >> 16) & 0x1fff; + + switch(t) { + case 0x1f80: + psxHwConstWrite8(mem&0x1fffffff, mmreg); + return 0; + case 0x1f40: + psxHw4ConstWrite8(mem&0x1fffffff, mmreg); + return 0; + + case 0x1d00: + assert(0); + _eeWriteConstMem8((u32)(PS2MEM_HW+0xf200+(mem&0xff)), mmreg); + return 0; + + case 0x1000: + _recPushReg(mmreg); + PUSH32I(mem&0x1fffffff); + CALLFunc((uptr)DEV9write8); + return 0; + + default: + _eeWriteConstMem8((u32)PSXM(mem), mmreg); + return 1; + } +} + +__declspec(naked) void psxRecMemWrite16() +{ + __asm { + mov edx, ecx + shr edx, 16 + cmp dx, 0x1f80 + je hwwrite + cmp dx, 0x1f90 + je spuwrite + cmp dx, 0x1d00 + je sifwrite + cmp dx, 0x1000 + je devwrite + cmp dx, 0x1600 + je ignorewrite + } + + ASSERT_WRITEOK + + __asm { +memwrite: + // rom writes, has to be PS2MEM_BASE_ + mov word ptr [ecx+PS2MEM_BASE_], ax + ret + +hwwrite: + cmp cx, 0x1000 + jb memwrite + + push eax + push ecx + call psxHwWrite16 + add esp, 8 + ret + +sifwrite: + mov edx, ecx + and edx, 0xf0 + cmp dl, 0x60 + je Sif60 + cmp dl, 0x40 + je Sif40 + + mov word ptr [edx+PS2MEM_BASE_+0x1000f200], ax + ret + +Sif40: + mov bx, word ptr [edx+PS2MEM_BASE_+0x1000f200] + test ax, 0xa0 + jz Sif40_2 + // psHu16(0x1000F240) &= ~0xF000; + // psHu16(0x1000F240) |= 0x2000; + and bx, 0x0fff + or bx, 0x2000 + +Sif40_2: + // if(psHu16(0x1000F240) & temp) psHu16(0x1000F240) &= ~temp; + // else psHu16(0x1000F240) |= temp; + and ax, 0xf0 + test bx, ax + jz Sif40_3 + + not ax + and bx, ax + jmp Sif40_4 +Sif40_3: + or bx, ax +Sif40_4: + mov word ptr [edx+PS2MEM_BASE_+0x1000f200], bx + ret + +Sif60: + mov word ptr [edx+PS2MEM_BASE_+0x1000f200], 0 + ret + +spuwrite: + push eax + push ecx + call SPU2write + // stack alwritey incremented + ret + +devwrite: + push eax + push ecx + call DEV9write16 + // stack alwritey incremented + ret + +ignorewrite: + ret + } +} + +int psxRecMemConstWrite16(u32 mem, int mmreg) +{ + u32 t = (mem >> 16) & 0x1fff; + switch(t) { + case 0x1600: + //HACK: DEV9 VM crash fix + return 0; + case 0x1f80: + psxHwConstWrite16(mem&0x1fffffff, mmreg); + return 0; + + case 0x1d00: + switch (mem & 0xf0) { + case 0x10: + // write to ps2 mem + _eeWriteConstMem16((u32)(PS2MEM_HW+0xf210), mmreg); + return 0; + case 0x40: + { + // delete x86reg + _eeMoveMMREGtoR(EAX, mmreg); + + assert( mmreg != EBX ); + MOV16MtoR(EBX, (u32)PS2MEM_HW+0xf240); + TEST16ItoR(EAX, 0xa0); + j8Ptr[0] = JZ8(0); + + AND16ItoR(EBX, 0x0fff); + OR16ItoR(EBX, 0x2000); + + x86SetJ8(j8Ptr[0]); + + AND16ItoR(EAX, 0xf0); + TEST16RtoR(EAX, 0xf0); + j8Ptr[0] = JZ8(0); + + NOT32R(EAX); + AND16RtoR(EBX, EAX); + j8Ptr[1] = JMP8(0); + + x86SetJ8(j8Ptr[0]); + OR16RtoR(EBX, EAX); + + x86SetJ8(j8Ptr[1]); + + MOV16RtoM((u32)PS2MEM_HW+0xf240, EBX); + + return 0; + } + case 0x60: + MOV32ItoM((u32)(PS2MEM_HW+0xf260), 0); + return 0; + default: + assert(0); + } + return 0; + + case 0x1f90: + _recPushReg(mmreg); + PUSH32I(mem&0x1fffffff); + CALLFunc((uptr)SPU2write); + return 0; + + case 0x1000: + _recPushReg(mmreg); + PUSH32I(mem&0x1fffffff); + CALLFunc((uptr)DEV9write16); + return 0; + + default: + _eeWriteConstMem16((u32)PSXM(mem), mmreg); + return 1; + } +} + +__declspec(naked) void psxRecMemWrite32() +{ + __asm { + mov edx, ecx + shr edx, 16 + cmp dx, 0x1f80 + je hwwrite + cmp dx, 0x1d00 + je sifwrite + cmp dx, 0x1000 + je devwrite + cmp dx, 0x1ffe + je WriteCtrl + } + + __asm { + // rom writes, has to be PS2MEM_BASE_ + test g_psxWriteOk, 1 + jz endwrite + +memwrite: + mov dword ptr [ecx+PS2MEM_BASE_], eax +endwrite: + ret + +hwwrite: + cmp cx, 0x1000 + jb memwrite + + push eax + push ecx + call psxHwWrite32 + add esp, 8 + ret + +sifwrite: + mov edx, ecx + and edx, 0xf0 + cmp dl, 0x60 + je Sif60 + cmp dl, 0x40 + je Sif40 + cmp dl, 0x30 + je Sif30 + cmp dl, 0x20 + je Sif20 + + mov dword ptr [edx+PS2MEM_BASE_+0x1000f200], eax + ret + +Sif40: + mov bx, word ptr [edx+PS2MEM_BASE_+0x1000f200] + test ax, 0xa0 + jz Sif40_2 + // psHu16(0x1000F240) &= ~0xF000; + // psHu16(0x1000F240) |= 0x2000; + and bx, 0x0fff + or bx, 0x2000 + +Sif40_2: + // if(psHu16(0x1000F240) & temp) psHu16(0x1000F240) &= ~temp; + // else psHu16(0x1000F240) |= temp; + and ax, 0xf0 + test bx, ax + jz Sif40_3 + + not ax + and bx, ax + jmp Sif40_4 +Sif40_3: + or bx, ax +Sif40_4: + mov word ptr [edx+PS2MEM_BASE_+0x1000f200], bx + ret + +Sif30: + or dword ptr [edx+PS2MEM_BASE_+0x1000f200], eax + ret +Sif20: + not eax + and dword ptr [edx+PS2MEM_BASE_+0x1000f200], eax + ret +Sif60: + mov dword ptr [edx+PS2MEM_BASE_+0x1000f200], 0 + ret + +devwrite: + push eax + push ecx + call DEV9write32 + // stack alwritey incremented + ret + +WriteCtrl: + cmp ecx, 0x1ffe0130 + jne End + + mov writectrl, eax + + cmp eax, 0x800 + je SetWriteNotOk + cmp eax, 0x804 + je SetWriteNotOk + cmp eax, 0xc00 + je SetWriteNotOk + cmp eax, 0xc04 + je SetWriteNotOk + cmp eax, 0xcc0 + je SetWriteNotOk + cmp eax, 0xcc4 + je SetWriteNotOk + cmp eax, 0x0c4 + je SetWriteNotOk + + // test ok + cmp eax, 0x1e988 + je SetWriteOk + cmp eax, 0x1edd8 + je SetWriteOk + +End: + ret + +SetWriteNotOk: + mov g_psxWriteOk, 0 + ret +SetWriteOk: + mov g_psxWriteOk, 1 + ret + } +} + +int psxRecMemConstWrite32(u32 mem, int mmreg) +{ + u32 t = (mem >> 16) & 0x1fff; + switch(t) { + case 0x1f80: + psxHwConstWrite32(mem&0x1fffffff, mmreg); + return 0; + + case 0x1d00: + switch (mem & 0xf0) { + case 0x10: + // write to ps2 mem + _eeWriteConstMem32((u32)PS2MEM_HW+0xf210, mmreg); + return 0; + case 0x20: + // write to ps2 mem + // delete x86reg + if( IS_PSXCONSTREG(mmreg) ) { + AND32ItoM((u32)PS2MEM_HW+0xf220, ~g_psxConstRegs[(mmreg>>16)&0x1f]); + } + else { + NOT32R(mmreg); + AND32RtoM((u32)PS2MEM_HW+0xf220, mmreg); + } + return 0; + case 0x30: + // write to ps2 mem + _eeWriteConstMem32OP((u32)PS2MEM_HW+0xf230, mmreg, 1); + return 0; + case 0x40: + { + // delete x86reg + assert( mmreg != EBX ); + + _eeMoveMMREGtoR(EAX, mmreg); + + MOV16MtoR(EBX, (u32)PS2MEM_HW+0xf240); + TEST16ItoR(EAX, 0xa0); + j8Ptr[0] = JZ8(0); + + AND16ItoR(EBX, 0x0fff); + OR16ItoR(EBX, 0x2000); + + x86SetJ8(j8Ptr[0]); + + AND16ItoR(EAX, 0xf0); + TEST16RtoR(EAX, 0xf0); + j8Ptr[0] = JZ8(0); + + NOT32R(EAX); + AND16RtoR(EBX, EAX); + j8Ptr[1] = JMP8(0); + + x86SetJ8(j8Ptr[0]); + OR16RtoR(EBX, EAX); + + x86SetJ8(j8Ptr[1]); + + MOV16RtoM((u32)PS2MEM_HW+0xf240, EBX); + + return 0; + } + case 0x60: + MOV32ItoM((u32)(PS2MEM_HW+0xf260), 0); + return 0; + default: + assert(0); + } + return 0; + + case 0x1000: + _recPushReg(mmreg); + PUSH32I(mem&0x1fffffff); + CALLFunc((uptr)DEV9write32); + return 0; + + case 0x1ffe: + if( mem == 0xfffe0130 ) { + u8* ptrs[9]; + + _eeWriteConstMem32((uptr)&writectrl, mmreg); + + if( IS_PSXCONSTREG(mmreg) ) { + switch (g_psxConstRegs[(mmreg>>16)&0x1f]) { + case 0x800: case 0x804: + case 0xc00: case 0xc04: + case 0xcc0: case 0xcc4: + case 0x0c4: + MOV32ItoM((uptr)&g_psxWriteOk, 0); + break; + case 0x1e988: + case 0x1edd8: + MOV32ItoM((uptr)&g_psxWriteOk, 1); + break; + default: + assert(0); + } + } + else { + // not ok + CMP32ItoR(mmreg, 0x800); + ptrs[0] = JE8(0); + CMP32ItoR(mmreg, 0x804); + ptrs[1] = JE8(0); + CMP32ItoR(mmreg, 0xc00); + ptrs[2] = JE8(0); + CMP32ItoR(mmreg, 0xc04); + ptrs[3] = JE8(0); + CMP32ItoR(mmreg, 0xcc0); + ptrs[4] = JE8(0); + CMP32ItoR(mmreg, 0xcc4); + ptrs[5] = JE8(0); + CMP32ItoR(mmreg, 0x0c4); + ptrs[6] = JE8(0); + + // ok + CMP32ItoR(mmreg, 0x1e988); + ptrs[7] = JE8(0); + CMP32ItoR(mmreg, 0x1edd8); + ptrs[8] = JE8(0); + + x86SetJ8(ptrs[0]); + x86SetJ8(ptrs[1]); + x86SetJ8(ptrs[2]); + x86SetJ8(ptrs[3]); + x86SetJ8(ptrs[4]); + x86SetJ8(ptrs[5]); + x86SetJ8(ptrs[6]); + MOV32ItoM((uptr)&g_psxWriteOk, 0); + ptrs[0] = JMP8(0); + + x86SetJ8(ptrs[7]); + x86SetJ8(ptrs[8]); + MOV32ItoM((uptr)&g_psxWriteOk, 1); + + x86SetJ8(ptrs[0]); + } + } + return 0; + + default: + TEST8ItoM((uptr)&g_psxWriteOk, 1); + j8Ptr[0] = JZ8(0); + _eeWriteConstMem32((u32)PSXM(mem), mmreg); + x86SetJ8(j8Ptr[0]); + return 1; + } +} + +#endif diff --git a/pcsx2/x86/iR3000A.cpp b/pcsx2/x86/iR3000A.cpp new file mode 100644 index 0000000000..84a8b5330e --- /dev/null +++ b/pcsx2/x86/iR3000A.cpp @@ -0,0 +1,1541 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// recompiler reworked to add dynamic linking Jan06 +// and added reg caching, const propagation, block analysis Jun06 +// zerofrog(@gmail.com) + + +#include "PrecompiledHeader.h" + +#include + +#ifndef _WIN32 +#include +#endif + +#include "PsxCommon.h" +#include "VU.h" + +#include "ix86/ix86.h" +#include "iCore.h" +#include "iR3000A.h" + +#include "SamplProf.h" + +extern u32 g_psxNextBranchCycle; +extern void psxBREAK(); +extern void zeroEx(); + +u32 g_psxMaxRecMem = 0; +u32 s_psxrecblocks[] = {0}; + +//Using assembly code from an external file. +#ifdef __LINUX__ +extern "C" { +#endif +void psxRecRecompile(u32 startpc); +#ifdef __LINUX__ +} +#endif + +uptr *psxRecLUT; + +#define PSX_NUMBLOCKS (1<<12) +#define MAPBASE 0x48000000 +#define RECMEM_SIZE (8*1024*1024) + +#define PSX_MEMMASK 0x5fffffff // mask when comparing two pcs + +// R3000A statics +int psxreclog = 0; + +static u8 *recMem = NULL; // the recompiled blocks will be here +static BASEBLOCK *recRAM = NULL; // and the ptr to the blocks here +static BASEBLOCK *recROM = NULL; // and here +static BASEBLOCK *recROM1 = NULL; // also here +static BASEBLOCKEX *recBlocks = NULL; +static u8 *recPtr = NULL; +u32 psxpc; // recompiler psxpc +int psxbranch; // set for branch +u32 g_iopCyclePenalty; + +static EEINST* s_pInstCache = NULL; +static u32 s_nInstCacheSize = 0; + +static BASEBLOCK* s_pCurBlock = NULL; +static BASEBLOCKEX* s_pCurBlockEx = NULL; + +#if defined(_MSC_VER) && !defined(__x86_64__) +static BASEBLOCK* s_pDispatchBlock = NULL; +#endif + +static u32 s_nEndBlock = 0; // what psxpc the current block ends + +static u32 s_nNextBlock = 0; // next free block in recBlocks + +static u32 s_ConstGPRreg; +static u32 s_saveConstGPRreg = 0, s_saveHasConstReg = 0, s_saveFlushedConstReg = 0; +static EEINST* s_psaveInstInfo = NULL; + +u32 s_psxBlockCycles = 0; // cycles of current block recompiling +static u32 s_savenBlockCycles = 0; + +static void iPsxBranchTest(u32 newpc, u32 cpuBranch); +void psxRecompileNextInstruction(int delayslot); + +extern void (*rpsxBSC[64])(); +extern void (*rpsxBSC_co[64])(); +void rpsxpropBSC(EEINST* prev, EEINST* pinst); + +#ifdef _DEBUG +u32 psxdump = 0; +#else +#define psxdump 0 +#endif + +#define PSX_GETBLOCK(x) PC_GETBLOCK_(x, psxRecLUT) + +#define PSXREC_CLEARM(mem) { \ + if ((mem) < g_psxMaxRecMem && psxRecLUT[(mem) >> 16]) { \ + BASEBLOCK* p = PSX_GETBLOCK(mem); \ + if( *(u32*)p ) psxRecClearMem(p); \ + } \ +} \ + +BASEBLOCKEX* PSX_GETBLOCKEX(BASEBLOCK* p) +{ +// BASEBLOCKEX* pex = *(BASEBLOCKEX**)(p+1); +// if( pex >= recBlocks && pex < recBlocks+PSX_NUMBLOCKS ) +// return pex; + + // otherwise, use the sorted list + return GetBaseBlockEx(p->startpc, 1); +} + +//////////////////////////////////////////////////// +#ifdef _DEBUG +using namespace R3000A; +static void iIopDumpBlock( int startpc, u8 * ptr ) +{ + FILE *f; + char filename[ g_MaxPath ]; +#ifdef __LINUX__ + char command[256]; +#endif + u32 i, j; + EEINST* pcur; + u8 used[34]; + int numused, count; + + SysPrintf( "dump1 %x:%x, %x\n", startpc, psxpc, psxRegs.cycle ); +#ifdef _WIN32 + CreateDirectory("dumps", NULL); + sprintf_s( filename, g_MaxPath, "dumps\\psxdump%.8X.txt", startpc); +#else + mkdir("dumps", 0755); + sprintf( filename, "dumps/psxdump%.8X.txt", startpc); +#endif + + fflush( stdout ); + + f = fopen( filename, "w" ); + assert( f != NULL ); + for ( i = startpc; i < s_nEndBlock; i += 4 ) { + fprintf( f, "%s\n", disR3000Fasm( *(u32*)PSXM( i ), i ) ); + } + + // write the instruction info + fprintf(f, "\n\nlive0 - %x, lastuse - %x used - %x\n", EEINST_LIVE0, EEINST_LASTUSE, EEINST_USED); + + memzero_obj(used); + numused = 0; + for(i = 0; i < ARRAYSIZE(s_pInstCache->regs); ++i) { + if( s_pInstCache->regs[i] & EEINST_USED ) { + used[i] = 1; + numused++; + } + } + + fprintf(f, " "); + for(i = 0; i < ARRAYSIZE(s_pInstCache->regs); ++i) { + if( used[i] ) fprintf(f, "%2d ", i); + } + fprintf(f, "\n"); + + fprintf(f, " "); + for(i = 0; i < ARRAYSIZE(s_pInstCache->regs); ++i) { + if( used[i] ) fprintf(f, "%s ", disRNameGPR[i]); + } + fprintf(f, "\n"); + + pcur = s_pInstCache+1; + for( i = 0; i < (s_nEndBlock-startpc)/4; ++i, ++pcur) { + fprintf(f, "%2d: %2.2x ", i+1, pcur->info); + + count = 1; + for(j = 0; j < ARRAYSIZE(s_pInstCache->regs); j++) { + if( used[j] ) { + fprintf(f, "%2.2x%s", pcur->regs[j], ((count%8)&&count tempdump", filename ); + system( command ); + sprintf(command, "mv tempdump %s", filename); + system(command); + f = fopen( filename, "a+" ); +#endif +} +#endif + +u8 _psxLoadWritesRs(u32 tempcode) +{ + switch(tempcode>>26) { + case 32: case 33: case 34: case 35: case 36: case 37: case 38: + return ((tempcode>>21)&0x1f)==((tempcode>>16)&0x1f); // rs==rt + } + return 0; +} + +u8 _psxIsLoadStore(u32 tempcode) +{ + switch(tempcode>>26) { + case 32: case 33: case 34: case 35: case 36: case 37: case 38: + // 4 byte stores + case 40: case 41: case 42: case 43: case 46: + return 1; + } + return 0; +} + +void _psxFlushAllUnused() +{ + int i; + for(i = 0; i < 34; ++i) { + if( psxpc < s_nEndBlock ) { + if( (g_pCurInstInfo[1].regs[i]&EEINST_USED) ) + continue; + } + else if( (g_pCurInstInfo[0].regs[i]&EEINST_USED) ) + continue; + + if( i < 32 && PSX_IS_CONST1(i) ) _psxFlushConstReg(i); + else { + _deleteX86reg(X86TYPE_PSX, i, 1); + } + } +} + +int _psxFlushUnusedConstReg() +{ + int i; + for(i = 1; i < 32; ++i) { + if( (g_psxHasConstReg & (1< 0 ); + + // make sure right GPR was saved + assert( g_psxHasConstReg == s_saveHasConstReg || (g_psxHasConstReg ^ s_saveHasConstReg) == (1<>26) == 9 ) { + //ADDIU, call bios +#ifdef _DEBUG + MOV32ItoM( (uptr)&psxRegs.code, psxRegs.code ); + MOV32ItoM( (uptr)&psxRegs.pc, psxpc ); + _psxFlushCall(FLUSH_NODESTROY); + CALLFunc((uptr)zeroEx); +#endif + // Bios Call: Force the IOP to do a Branch Test ASAP. + // Important! This helps prevent game freeze-ups during boot-up and stage loads. + // Note: Fixes to cdvd have removed the need for this code. + //MOV32MtoR( EAX, (uptr)&psxRegs.cycle ); + //MOV32RtoM( (uptr)&g_psxNextBranchCycle, EAX ); + } + return; + } + + // for now, don't support xmm + PSX_CHECK_SAVE_REG(_Rt_); + + _deleteX86reg(X86TYPE_PSX, _Rs_, 1); + _deleteX86reg(X86TYPE_PSX, _Rt_, 0); + + if( PSX_IS_CONST1(_Rs_) ) { + PSX_SET_CONST(_Rt_); + constcode(); + return; + } + + noconstcode(0); + PSX_DEL_CONST(_Rt_); +} + +// rd = rt op sa +void psxRecompileCodeConst2(R3000AFNPTR constcode, R3000AFNPTR_INFO noconstcode) +{ + if ( ! _Rd_ ) return; + + // for now, don't support xmm + PSX_CHECK_SAVE_REG(_Rd_); + + _deleteX86reg(X86TYPE_PSX, _Rt_, 1); + _deleteX86reg(X86TYPE_PSX, _Rd_, 0); + + if( PSX_IS_CONST1(_Rt_) ) { + PSX_SET_CONST(_Rd_); + constcode(); + return; + } + + noconstcode(0); + PSX_DEL_CONST(_Rd_); +} + +// rd = rt MULT rs (SPECIAL) +void psxRecompileCodeConst3(R3000AFNPTR constcode, R3000AFNPTR_INFO constscode, R3000AFNPTR_INFO consttcode, R3000AFNPTR_INFO noconstcode, int LOHI) +{ + _deleteX86reg(X86TYPE_PSX, _Rs_, 1); + _deleteX86reg(X86TYPE_PSX, _Rt_, 1); + + if( LOHI ) { + _deleteX86reg(X86TYPE_PSX, PSX_HI, 1); + _deleteX86reg(X86TYPE_PSX, PSX_LO, 1); + } + + if( PSX_IS_CONST2(_Rs_, _Rt_) ) { + constcode(); + return; + } + + if( PSX_IS_CONST1(_Rs_) ) { + constscode(0); + return; + } + + if( PSX_IS_CONST1(_Rt_) ) { + consttcode(0); + return; + } + + noconstcode(0); +} + +static u8* m_recBlockAlloc = NULL; + +static const uint m_recBlockAllocSize = + (((Ps2MemSize::IopRam + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4) * sizeof(BASEBLOCK)) + + (PSX_NUMBLOCKS*sizeof(BASEBLOCKEX)); // recBlocks + +static void recAlloc() +{ + // Note: the VUrec depends on being able to grab an allocation below the 0x10000000 line, + // so we give the EErec an address above that to try first as it's basemem address, hence + // the 0x28000000 pick (0x20000000 is picked by the EE) + + if( recMem == NULL ) + recMem = (u8*)SysMmapEx( 0x28000000, RECMEM_SIZE, 0, "recAlloc(R3000a)" ); + + if( recMem == NULL ) + throw Exception::OutOfMemory( "R3000a Init > failed to allocate memory for the recompiler." ); + + if( psxRecLUT == NULL ) + psxRecLUT = (uptr*) malloc(0x010000 * sizeof(uptr)); + + // Goal: Allocate BASEBLOCKs for every possible branch target in IOP memory. + // Any 4-byte aligned address makes a valid branch target as per MIPS design (all instructions are + // always 4 bytes long). + + if( m_recBlockAlloc == NULL ) + m_recBlockAlloc = (u8*)_aligned_malloc( m_recBlockAllocSize, 4096 ); + + if( m_recBlockAlloc == NULL ) + throw Exception::OutOfMemory( "R3000a Init > Failed to allocate memory for baseblock lookup tables." ); + + u8* curpos = m_recBlockAlloc; + recRAM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::IopRam / 4) * sizeof(BASEBLOCK); + recROM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom / 4) * sizeof(BASEBLOCK); + recROM1 = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom1 / 4) * sizeof(BASEBLOCK); + recBlocks = (BASEBLOCKEX*)curpos; // curpos += sizeof(BASEBLOCKEX)*EE_NUMBLOCKS; + + if( s_pInstCache == NULL ) + { + s_nInstCacheSize = 128; + s_pInstCache = (EEINST*)malloc( sizeof(EEINST) * s_nInstCacheSize ); + } + + if( s_pInstCache == NULL ) + throw Exception::OutOfMemory( "R3000a Init > Failed to allocate memory for pInstCache." ); + + ProfilerRegisterSource( "IOPRec", recMem, RECMEM_SIZE ); +} + +void recResetIOP() +{ + // calling recResetIOP without first calling recInit is bad mojo. + jASSUME( psxRecLUT != NULL ); + jASSUME( recMem != NULL ); + jASSUME( m_recBlockAlloc != NULL ); + + DbgCon::Status( "iR3000A > Resetting recompiler memory and structures!" ); + + memzero_ptr<0x010000 * sizeof(uptr)>( psxRecLUT ); + memset_8<0xcd,RECMEM_SIZE>( recMem ); + memzero_ptr( m_recBlockAlloc ); + + // We're only mapping 20 pages here in 4 places. + // 0x80 comes from : (Ps2MemSize::IopRam / 0x10000) * 4 + for (int i=0; i<0x80; i++) + { + psxRecLUT[i + 0x0000] = (uptr)&recRAM[(i & 0x1f) << 14]; + psxRecLUT[i + 0x8000] = (uptr)&recRAM[(i & 0x1f) << 14]; + psxRecLUT[i + 0xa000] = (uptr)&recRAM[(i & 0x1f) << 14]; + } + + for (int i=0; i<(Ps2MemSize::Rom / 0x10000); i++) + { + psxRecLUT[i + 0x1fc0] = (uptr)&recROM[i << 14]; + psxRecLUT[i + 0x9fc0] = (uptr)&recROM[i << 14]; + psxRecLUT[i + 0xbfc0] = (uptr)&recROM[i << 14]; + } + + for (int i=0; i<(Ps2MemSize::Rom1 / 0x10000); i++) + { + psxRecLUT[i + 0x1e00] = (uptr)&recROM1[i << 14]; + psxRecLUT[i + 0x9e00] = (uptr)&recROM1[i << 14]; + psxRecLUT[i + 0xbe00] = (uptr)&recROM1[i << 14]; + } + + if( s_pInstCache ) + memset( s_pInstCache, 0, sizeof(EEINST)*s_nInstCacheSize ); + + ResetBaseBlockEx(1); + g_psxMaxRecMem = 0; + + recPtr = recMem; + psxbranch = 0; +} + +static void recShutdown() +{ + ProfilerTerminateSource( "IOPRec" ); + + SafeSysMunmap(recMem, RECMEM_SIZE); + safe_aligned_free( m_recBlockAlloc ); + + safe_free(psxRecLUT); + safe_free( s_pInstCache ); + s_nInstCacheSize = 0; + + x86Shutdown(); +} + +#pragma warning(disable:4731) // frame pointer register 'ebp' modified by inline assembly code + +static u32 s_uSaveESP = 0; + +static __forceinline void R3000AExecute() +{ +#ifdef _DEBUG + u8* fnptr; + u32 oldesi; +/*#else + R3000AFNPTR pfn;*/ +#endif + + BASEBLOCK* pblock; + + pblock = PSX_GETBLOCK(psxRegs.pc); + + if ( !pblock->GetFnptr() || (pblock->startpc&PSX_MEMMASK) != (psxRegs.pc&PSX_MEMMASK) ) { + psxRecRecompile(psxRegs.pc); + } + + assert( pblock->GetFnptr() != 0 ); + +#ifdef _DEBUG + + fnptr = (u8*)pblock->GetFnptr(); + +#ifdef _MSC_VER + + __asm { + // save data + mov oldesi, esi; + mov s_uSaveESP, esp; + sub s_uSaveESP, 8; + push ebp; + + call fnptr; // jump into function + // restore data + pop ebp; + mov esi, oldesi; + } + +#else // linux + + __asm__("movl %%esi, %0\n" + "movl %%esp, %1\n" + "sub $8, %%esp\n" + "push %%ebp\n" + "call *%2\n" + "pop %%ebp\n" + "movl %0, %%esi\n" : "=m"(oldesi), "=m"(s_uSaveESP) : "c"(fnptr) : ); +#endif // _MSC_VER + +#else + ((R3000AFNPTR)pblock->GetFnptr())(); +#endif +} + +u32 g_psxlastpc = 0; + +#if defined(_MSC_VER) + +static u32 g_temp; + +// jumped to when invalid psxpc address +__declspec(naked,noreturn) void psxDispatcher() +{ + // EDX contains the current psxpc to jump to, stack contains the jump addr to modify + __asm push edx + + // calc PSX_GETBLOCK + s_pDispatchBlock = PSX_GETBLOCK(psxRegs.pc); + + __asm { + mov eax, s_pDispatchBlock + + // check if startpc&PSX_MEMMASK == psxRegs.pc&PSX_MEMMASK + mov ecx, psxRegs.pc + mov edx, [eax+BLOCKTYPE_STARTPC]; + and ecx, PSX_MEMMASK // remove higher bits + and edx, PSX_MEMMASK + cmp ecx, edx + je CheckPtr + + // recompile + push psxRegs.pc // psxpc + call psxRecRecompile + add esp, 4 // pop old param + mov eax, s_pDispatchBlock +CheckPtr: + mov eax, dword ptr [eax] + } + +#ifdef _DEBUG + __asm mov g_temp, eax + assert( g_temp ); +#endif + + __asm { + //and eax, 0x0fffffff + shl eax,4 + mov edx, eax + pop ecx // x86Ptr to mod + sub edx, ecx + sub edx, 4 + mov dword ptr [ecx], edx + + jmp eax + } +} + +__declspec(naked,noreturn) void psxDispatcherClear() +{ + // EDX contains the current psxpc + __asm mov psxRegs.pc, edx + __asm push edx + + // calc PSX_GETBLOCK + s_pDispatchBlock = PSX_GETBLOCK(psxRegs.pc); + + if( (s_pDispatchBlock->startpc&PSX_MEMMASK) == (psxRegs.pc&PSX_MEMMASK) ) { + assert( s_pDispatchBlock->GetFnptr() != 0 ); + + // already modded the code, jump to the new place + __asm { + pop edx + add esp, 4 // ignore stack + mov eax, s_pDispatchBlock + mov eax, dword ptr [eax] + //and eax, 0x0fffffff + shl eax,4 + jmp eax + } + } + + __asm { + call psxRecRecompile + add esp, 4 // pop old param + mov eax, s_pDispatchBlock + mov eax, dword ptr [eax] + + pop ecx // old fnptr + + //and eax, 0x0fffffff + shl eax,4 + mov byte ptr [ecx], 0xe9 // jmp32 + mov edx, eax + sub edx, ecx + sub edx, 5 + mov dword ptr [ecx+1], edx + + jmp eax + } +} + +// called when jumping to variable psxpc address +__declspec(naked,noreturn) void psxDispatcherReg() +{ + __asm { + //s_pDispatchBlock = PSX_GETBLOCK(psxRegs.pc); + mov edx, psxRegs.pc + mov ecx, edx + } + + __asm { + shr edx, 14 + and edx, 0xfffffffc + add edx, psxRecLUT + mov edx, dword ptr [edx] + + mov eax, ecx + and eax, 0xfffc + // edx += 2*eax + shl eax, 1 + add edx, eax + + // check if startpc == psxRegs.pc + mov eax, ecx + cmp eax, dword ptr [edx+BLOCKTYPE_STARTPC] + jne recomp + + mov eax, dword ptr [edx] + } + +#ifdef _DEBUG + __asm mov g_temp, eax + assert( g_temp ); +#endif + + __asm { + //and eax, 0x0fffffff + shl eax,4 + jmp eax // fnptr + +recomp: + sub esp, 8 + mov dword ptr [esp+4], edx + mov dword ptr [esp], ecx + call psxRecRecompile + mov edx, dword ptr [esp+4] + add esp, 8 + + mov eax, dword ptr [edx] + //and eax, 0x0fffffff + shl eax,4 + jmp eax // fnptr + } +} + +#else // _MSC_VER +// Linux uses an assembly version of these routines. +#ifdef __LINUX__ +extern "C" { +#endif +void psxDispatcher(); +void psxDispatcherClear(); +void psxDispatcherReg(); +#ifdef __LINUX__ +} +#endif + +#endif // _MSC_VER + +static void recClear(u32 Addr, u32 Size) +{ + u32 i; + for(i = 0; i < Size; ++i, Addr+=4) { + PSXREC_CLEARM(Addr); + } +} + +#define IOP_MIN_BLOCK_BYTES 15 + +void rpsxMemConstClear(u32 mem) +{ + // NOTE! This assumes recLUT never changes its mapping + if( !psxRecLUT[mem>>16] ) + return; + + CMP32ItoM((uptr)PSX_GETBLOCK(mem), 0); + j8Ptr[6] = JE8(0); + + _callFunctionArg1((uptr)psxRecClearMem, MEM_CONSTTAG, (uptr)PSX_GETBLOCK(mem)); + x86SetJ8(j8Ptr[6]); +} + +void psxRecClearMem(BASEBLOCK* p) +{ + BASEBLOCKEX* pexblock; + BASEBLOCK* pstart; + int lastdelay; + + assert( p != NULL ); + + if( p->uType & BLOCKTYPE_DELAYSLOT ) { + psxRecClearMem(p-1); + if( p->GetFnptr() == 0 ) + return; + } + + assert( p->GetFnptr() != 0 ); + assert( p->startpc ); + + x86Ptr = (u8*)p->GetFnptr(); + + // there is a small problem: mem can be ored with 0xa<<28 or 0x8<<28, and don't know which + MOV32ItoR(EDX, p->startpc); + assert( (uptr)x86Ptr <= 0xffffffff ); + PUSH32I((uptr)x86Ptr); + JMP32((uptr)psxDispatcherClear - ( (uptr)x86Ptr + 5 )); + assert( x86Ptr == (u8*)p->GetFnptr() + IOP_MIN_BLOCK_BYTES ); + + pstart = PSX_GETBLOCK(p->startpc); + pexblock = PSX_GETBLOCKEX(pstart); + assert( pexblock->startpc == pstart->startpc ); + + // don't delete if last is delay + lastdelay = pexblock->size; + if( pstart[pexblock->size-1].uType & BLOCKTYPE_DELAYSLOT ) { + assert( pstart[pexblock->size-1].GetFnptr() != pstart->GetFnptr() ); + if( pstart[pexblock->size-1].GetFnptr() != 0 ) { + pstart[pexblock->size-1].uType = 0; + --lastdelay; + } + } + + memset(pstart, 0, lastdelay*sizeof(BASEBLOCK)); + + RemoveBaseBlockEx(pexblock, 1); + pexblock->size = 0; + pexblock->startpc = 0; +} + +void psxSetBranchReg(u32 reg) +{ + psxbranch = 1; + + if( reg != 0xffffffff ) { + _allocX86reg(ESI, X86TYPE_PCWRITEBACK, 0, MODE_WRITE); + _psxMoveGPRtoR(ESI, reg); + + psxRecompileNextInstruction(1); + + if( x86regs[ESI].inuse ) { + assert( x86regs[ESI].type == X86TYPE_PCWRITEBACK ); + MOV32RtoM((uptr)&psxRegs.pc, ESI); + x86regs[ESI].inuse = 0; + } + else { + MOV32MtoR(EAX, (uptr)&g_recWriteback); + MOV32RtoM((uptr)&psxRegs.pc, EAX); + } + } + + _psxFlushCall(FLUSH_EVERYTHING); + iPsxBranchTest(0xffffffff, 1); + + JMP32((uptr)psxDispatcherReg - ( (uptr)x86Ptr + 5 )); +} + +void psxSetBranchImm( u32 imm ) +{ + u32* ptr; + psxbranch = 1; + assert( imm ); + + // end the current block + MOV32ItoM( (uptr)&psxRegs.pc, imm ); + _psxFlushCall(FLUSH_EVERYTHING); + iPsxBranchTest(imm, imm <= psxpc); + + MOV32ItoR(EDX, 0); + ptr = (u32*)(x86Ptr-4); + *ptr = (uptr)JMP32((uptr)psxDispatcher - ( (uptr)x86Ptr + 5 )); +} + +//fixme : this is all a huge hack, we base the counter advancements on the average an opcode should take (wtf?) +// If that wasn't bad enough we have default values like 9/8 which will get cast to int later +// (yeah, that means all sync code couldn't have worked to beginn with) +// So for now these are new settings that work. +// (rama) + +static u32 psxScaleBlockCycles() +{ + return s_psxBlockCycles * (CHECK_IOP_CYCLERATE ? 2 : 1); +} + +static void iPsxBranchTest(u32 newpc, u32 cpuBranch) +{ + u32 blockCycles = psxScaleBlockCycles(); + + MOV32MtoR(ECX, (uptr)&psxRegs.cycle); + MOV32MtoR(EAX, (uptr)&psxCycleEE); + ADD32ItoR(ECX, blockCycles); + SUB32ItoR(EAX, blockCycles*8); + MOV32RtoM((uptr)&psxRegs.cycle, ECX); // update cycles + MOV32RtoM((uptr)&psxCycleEE, EAX); + + j8Ptr[2] = JG8( 0 ); // jump if psxCycleEE > 0 + + RET2(); // returns control to the EE + + // Continue onward with branching here: + x86SetJ8( j8Ptr[2] ); + + // check if should branch + SUB32MtoR(ECX, (uptr)&g_psxNextBranchCycle); + j8Ptr[0] = JS8( 0 ); + + CALLFunc((uptr)psxBranchTest); + + if( newpc != 0xffffffff ) + { + CMP32ItoM((uptr)&psxRegs.pc, newpc); + JNE32((uptr)psxDispatcherReg - ( (uptr)x86Ptr + 6 )); + } + + // Skip branch jump target here: + x86SetJ8( j8Ptr[0] ); +} + +static int *s_pCode; + +#if !defined(_MSC_VER) +static void checkcodefn() +{ + int pctemp; + +#ifdef _MSC_VER + __asm mov pctemp, eax; +#else + __asm__("movl %%eax, %0" : : "m"(pctemp) ); +#endif + SysPrintf("iop code changed! %x\n", pctemp); +} +#endif + +void rpsxSYSCALL() +{ + MOV32ItoM( (uptr)&psxRegs.code, psxRegs.code ); + MOV32ItoM((uptr)&psxRegs.pc, psxpc - 4); + _psxFlushCall(FLUSH_NODESTROY); + + _callFunctionArg2((uptr)psxException, MEM_CONSTTAG, MEM_CONSTTAG, 0x20, psxbranch==1); + + CMP32ItoM((uptr)&psxRegs.pc, psxpc-4); + j8Ptr[0] = JE8(0); + + ADD32ItoM((uptr)&psxRegs.cycle, psxScaleBlockCycles() ); + SUB32ItoM((uptr)&psxCycleEE, psxScaleBlockCycles()*8 ); + JMP32((uptr)psxDispatcherReg - ( (uptr)x86Ptr + 5 )); + + // jump target for skipping blockCycle updates + x86SetJ8(j8Ptr[0]); + + //if (!psxbranch) psxbranch = 2; +} + +void rpsxBREAK() +{ + MOV32ItoM( (uptr)&psxRegs.code, psxRegs.code ); + MOV32ItoM((uptr)&psxRegs.pc, psxpc - 4); + _psxFlushCall(FLUSH_NODESTROY); + + _callFunctionArg2((uptr)psxBREAK, MEM_CONSTTAG, MEM_CONSTTAG, 0x24, psxbranch==1); + + CMP32ItoM((uptr)&psxRegs.pc, psxpc-4); + j8Ptr[0] = JE8(0); + ADD32ItoM((uptr)&psxRegs.cycle, psxScaleBlockCycles() ); + SUB32ItoM((uptr)&psxCycleEE, psxScaleBlockCycles()*8 ); + JMP32((uptr)psxDispatcherReg - ( (uptr)x86Ptr + 5 )); + x86SetJ8(j8Ptr[0]); + + //if (!psxbranch) psxbranch = 2; +} + +u32 psxRecompileCodeSafe(u32 temppc) +{ + BASEBLOCK* pblock = PSX_GETBLOCK(temppc); + + if( pblock->GetFnptr() != 0 && pblock->startpc != s_pCurBlock->startpc ) { + if( psxpc == pblock->startpc ) + return 0; + } + + return 1; +} + +void psxRecompileNextInstruction(int delayslot) +{ + static u8 s_bFlushReg = 1; + + BASEBLOCK* pblock = PSX_GETBLOCK(psxpc); + + // need *ppblock != s_pCurBlock because of branches + if( pblock->GetFnptr() != 0 && pblock->startpc != s_pCurBlock->startpc ) { + + if( !delayslot && psxpc == pblock->startpc ) { + // code already in place, so jump to it and exit recomp + assert( PSX_GETBLOCKEX(pblock)->startpc == pblock->startpc ); + + _psxFlushCall(FLUSH_EVERYTHING); + MOV32ItoM((uptr)&psxRegs.pc, psxpc); + +// if( pexblock->pOldFnptr ) { +// // code already in place, so jump to it and exit recomp +// JMP32((uptr)pexblock->pOldFnptr - ((uptr)x86Ptr + 5)); +// branch = 3; +// return; +// } + + JMP32((uptr)pblock->GetFnptr() - ((uptr)x86Ptr + 5)); + psxbranch = 3; + return; + } + else { + + if( !(delayslot && pblock->startpc == psxpc) ) { + u8* oldX86 = x86Ptr; + //__Log("clear block %x\n", pblock->startpc); + psxRecClearMem(pblock); + x86Ptr = oldX86; + if( delayslot ) + SysPrintf("delay slot %x\n", psxpc); + } + } + } + + if( delayslot ) + pblock->uType = BLOCKTYPE_DELAYSLOT; + +#ifdef _DEBUG + MOV32ItoR(EAX, psxpc); +#endif + + s_pCode = (int *)PSXM( psxpc ); + assert(s_pCode); + + psxRegs.code = *(int *)s_pCode; + s_psxBlockCycles++; + psxpc += 4; + +//#ifdef _DEBUG +// CMP32ItoM((uptr)s_pCode, psxRegs.code); +// j8Ptr[0] = JE8(0); +// MOV32ItoR(EAX, psxpc); +// CALLFunc((uptr)checkcodefn); +// x86SetJ8( j8Ptr[ 0 ] ); +//#endif + + + g_pCurInstInfo++; + +#ifdef PCSX2_VM_COISSUE + assert( g_pCurInstInfo->info & EEINSTINFO_COREC ); +#endif + + g_iopCyclePenalty = 0; + rpsxBSC[ psxRegs.code >> 26 ](); + s_psxBlockCycles += g_iopCyclePenalty; + + if( !delayslot ) { + if( s_bFlushReg ) { + //_psxFlushUnusedConstReg(); + } + else s_bFlushReg = 1; + } + else s_bFlushReg = 1; + + _clearNeededX86regs(); +} + +static void recExecute() { + for (;;) R3000AExecute(); +} + +static s32 recExecuteBlock( s32 eeCycles ) +{ + psxBreak = 0; + psxCycleEE = eeCycles; + R3000AExecute(); + return psxBreak + psxCycleEE; +} + +#include "IopHw.h" + +void iDumpPsxRegisters(u32 startpc, u32 temp) +{ +// [TODO] fixme : thie code is broken and has no labels. Needs a rewrite to be useful. + +#if 0 + int i; + const char* pstr = temp ? "t" : ""; + + __Log("%spsxreg: %x %x ra:%x k0: %x %x\n", pstr, startpc, psxRegs.cycle, psxRegs.GPR.n.ra, psxRegs.GPR.n.k0, *(int*)PSXM(0x13c128)); + for(i = 0; i < 34; i+=2) __Log("%spsx%s: %x %x\n", pstr, disRNameGPR[i], psxRegs.GPR.r[i], psxRegs.GPR.r[i+1]); + __Log("%scycle: %x %x %x; counters %x %x\n", pstr, psxRegs.cycle, g_psxNextBranchCycle, EEsCycle, + psxNextsCounter, psxNextCounter); + + __Log("psxdma%d c%x b%x m%x t%x\n", 2, HW_DMA2_CHCR, HW_DMA2_BCR, HW_DMA2_MADR, HW_DMA2_TADR); + __Log("psxdma%d c%x b%x m%x\n", 3, HW_DMA3_CHCR, HW_DMA3_BCR, HW_DMA3_MADR); + __Log("psxdma%d c%x b%x m%x t%x\n", 4, HW_DMA4_CHCR, HW_DMA4_BCR, HW_DMA4_MADR, HW_DMA4_TADR); + __Log("psxdma%d c%x b%x m%x\n", 6, HW_DMA6_CHCR, HW_DMA6_BCR, HW_DMA6_MADR); + __Log("psxdma%d c%x b%x m%x\n", 7, HW_DMA7_CHCR, HW_DMA7_BCR, HW_DMA7_MADR); + __Log("psxdma%d c%x b%x m%x\n", 8, HW_DMA8_CHCR, HW_DMA8_BCR, HW_DMA8_MADR); + __Log("psxdma%d c%x b%x m%x t%x\n", 9, HW_DMA9_CHCR, HW_DMA9_BCR, HW_DMA9_MADR, HW_DMA9_TADR); + __Log("psxdma%d c%x b%x m%x\n", 10, HW_DMA10_CHCR, HW_DMA10_BCR, HW_DMA10_MADR); + __Log("psxdma%d c%x b%x m%x\n", 11, HW_DMA11_CHCR, HW_DMA11_BCR, HW_DMA11_MADR); + __Log("psxdma%d c%x b%x m%x\n", 12, HW_DMA12_CHCR, HW_DMA12_BCR, HW_DMA12_MADR); + for(i = 0; i < 7; ++i) + __Log("%scounter%d: mode %x count %I64x rate %x scycle %x target %I64x\n", pstr, i, psxCounters[i].mode, psxCounters[i].count, psxCounters[i].rate, psxCounters[i].sCycleT, psxCounters[i].target); +#endif +} + +void iDumpPsxRegisters(u32 startpc); + +#ifdef _DEBUG +static void printfn() +{ + static int lastrec = 0; + static int curcount = 0; + const int skip = 0; + + //*(int*)PSXM(0x27990) = 1; // enables cdvd bios output for scph10000 + + if( psxRegs.cycle == 0x113a1be5 ) { +// FILE* tempf = fopen("tempdmciop.txt", "wb"); +// fwrite(PSXM(0), 0x200000, 1, tempf); +// fclose(tempf); + //psxdump |= 2; + } + +// if( psxRegs.cycle == 0x114152d8 ) { +// psxRegs.GPR.n.s0 = 0x55000; +// } + + if( (psxdump&2) && lastrec != g_psxlastpc ) { + curcount++; + + if( curcount > skip ) { + iDumpPsxRegisters(g_psxlastpc, 1); + curcount = 0; + } + + lastrec = g_psxlastpc; + } +} +#endif + +void psxRecRecompile(u32 startpc) +{ + u32 i; + u32 branchTo; + u32 willbranch3 = 0; + u32* ptr; + +#ifdef _DEBUG + //psxdump |= 4; + if( psxdump & 4 ) + iDumpPsxRegisters(startpc, 0); +#endif + + assert( startpc ); + + // if recPtr reached the mem limit reset whole mem + if (((uptr)recPtr - (uptr)recMem) >= (RECMEM_SIZE - 0x10000)) { + DevCon::WriteLn("IOP Recompiler data reset"); + recResetIOP(); + } + + s_pCurBlock = PSX_GETBLOCK(startpc); + + if( s_pCurBlock->GetFnptr() ) { + // clear if already taken + assert( s_pCurBlock->startpc < startpc ); + psxRecClearMem(s_pCurBlock); + } + + if( s_pCurBlock->startpc == startpc ) { + s_pCurBlockEx = PSX_GETBLOCKEX(s_pCurBlock); + assert( s_pCurBlockEx->startpc == startpc ); + } + else { + s_pCurBlockEx = NULL; + for(i = 0; i < PSX_NUMBLOCKS; ++i) { + if( recBlocks[(i+s_nNextBlock)%PSX_NUMBLOCKS].size == 0 ) { + s_pCurBlockEx = recBlocks+(i+s_nNextBlock)%PSX_NUMBLOCKS; + s_nNextBlock = (i+s_nNextBlock+1)%PSX_NUMBLOCKS; + break; + } + } + + if( s_pCurBlockEx == NULL ) { + DevCon::WriteLn("IOP Recompiler data reset"); + recResetIOP(); + s_nNextBlock = 0; + s_pCurBlockEx = recBlocks; + } + + s_pCurBlockEx->startpc = startpc; + } + + x86SetPtr( recPtr ); + x86Align(16); + recPtr = x86Ptr; + + psxbranch = 0; + + s_pCurBlock->startpc = startpc; + s_pCurBlock->SetFnptr( (uptr)x86Ptr ); + s_psxBlockCycles = 0; + + // reset recomp state variables + psxpc = startpc; + s_saveConstGPRreg = 0; + g_psxHasConstReg = g_psxFlushedConstReg = 1; + + _initX86regs(); + +#ifdef _DEBUG + // for debugging purposes + MOV32ItoM((uptr)&g_psxlastpc, psxpc); + CALLFunc((uptr)printfn); +#endif + + // go until the next branch + i = startpc; + s_nEndBlock = 0xffffffff; + + while(1) { + BASEBLOCK* pblock = PSX_GETBLOCK(i); + if( pblock->GetFnptr() != 0 && pblock->startpc != s_pCurBlock->startpc ) { + + if( i == pblock->startpc ) { + // branch = 3 + willbranch3 = 1; + s_nEndBlock = i; + break; + } + } + + psxRegs.code = *(int *)PSXM(i); + + switch(psxRegs.code >> 26) { + case 0: // special + + if( _Funct_ == 8 || _Funct_ == 9 ) { // JR, JALR + s_nEndBlock = i + 8; + goto StartRecomp; + } + + break; + case 1: // regimm + + if( _Rt_ == 0 || _Rt_ == 1 || _Rt_ == 16 || _Rt_ == 17 ) { + + branchTo = _Imm_ * 4 + i + 4; + if( branchTo > startpc && branchTo < i ) s_nEndBlock = branchTo; + else s_nEndBlock = i+8; + + goto StartRecomp; + } + + break; + + case 2: // J + case 3: // JAL + s_nEndBlock = i + 8; + goto StartRecomp; + + // branches + case 4: case 5: case 6: case 7: + + branchTo = _Imm_ * 4 + i + 4; + if( branchTo > startpc && branchTo < i ) s_nEndBlock = branchTo; + else s_nEndBlock = i+8; + + goto StartRecomp; + } + + i += 4; + } + +StartRecomp: + + // rec info // + { + EEINST* pcur; + + if( s_nInstCacheSize < (s_nEndBlock-startpc)/4+1 ) { + free(s_pInstCache); + s_nInstCacheSize = (s_nEndBlock-startpc)/4+10; + s_pInstCache = (EEINST*)malloc(sizeof(EEINST)*s_nInstCacheSize); + assert( s_pInstCache != NULL ); + } + + pcur = s_pInstCache + (s_nEndBlock-startpc)/4; + _recClearInst(pcur); + pcur->info = 0; + + for(i = s_nEndBlock; i > startpc; i -= 4 ) { + psxRegs.code = *(int *)PSXM(i-4); + pcur[-1] = pcur[0]; + rpsxpropBSC(pcur-1, pcur); + pcur--; + } + } + +#ifdef _DEBUG + // dump code + for(i = 0; i < ARRAYSIZE(s_psxrecblocks); ++i) { + if( startpc == s_psxrecblocks[i] ) { + iIopDumpBlock(startpc, recPtr); + } + } + + if( (psxdump & 1) ) + iIopDumpBlock(startpc, recPtr); +#endif + + g_pCurInstInfo = s_pInstCache; + while (!psxbranch && psxpc < s_nEndBlock) { + psxRecompileNextInstruction(0); + } + +#ifdef _DEBUG + if( (psxdump & 1) ) + iIopDumpBlock(startpc, recPtr); +#endif + + assert( (psxpc-startpc)>>2 <= 0xffff ); + s_pCurBlockEx->size = (psxpc-startpc)>>2; + + for(i = 1; i < (u32)s_pCurBlockEx->size-1; ++i) { + s_pCurBlock[i].SetFnptr( s_pCurBlock->GetFnptr() ); + s_pCurBlock[i].startpc = s_pCurBlock->startpc; + } + + // don't overwrite if delay slot + if( i < (u32)s_pCurBlockEx->size && !(s_pCurBlock[i].uType & BLOCKTYPE_DELAYSLOT) ) { + s_pCurBlock[i].SetFnptr( s_pCurBlock->GetFnptr() ); + s_pCurBlock[i].startpc = s_pCurBlock->startpc; + } + + // set the block ptr + AddBaseBlockEx(s_pCurBlockEx, 1); + + if( !(psxpc&0x10000000) ) + g_psxMaxRecMem = std::max( (psxpc&~0xa0000000), g_psxMaxRecMem ); + + if( psxbranch == 2 ) { + _psxFlushCall(FLUSH_EVERYTHING); + + iPsxBranchTest(0xffffffff, 1); + + JMP32((uptr)psxDispatcherReg - ( (uptr)x86Ptr + 5 )); + } + else { + assert( psxbranch != 3 ); + if( psxbranch ) assert( !willbranch3 ); + else + { + ADD32ItoM((uptr)&psxRegs.cycle, psxScaleBlockCycles() ); + SUB32ItoM((uptr)&psxCycleEE, psxScaleBlockCycles()*8 ); + } + + if( willbranch3 ) { + BASEBLOCK* pblock = PSX_GETBLOCK(s_nEndBlock); + assert( psxpc == s_nEndBlock ); + _psxFlushCall(FLUSH_EVERYTHING); + MOV32ItoM((uptr)&psxRegs.pc, psxpc); + JMP32((uptr)pblock->GetFnptr() - ((uptr)x86Ptr + 5)); + psxbranch = 3; + } + else if( !psxbranch ) { + // didn't branch, but had to stop + MOV32ItoM( (uptr)&psxRegs.pc, psxpc ); + + _psxFlushCall(FLUSH_EVERYTHING); + + ptr = JMP32(0); + //JMP32((uptr)psxDispatcherReg - ( (uptr)x86Ptr + 5 )); + } + } + + assert( x86Ptr >= (u8*)s_pCurBlock->GetFnptr() + IOP_MIN_BLOCK_BYTES ); + assert( x86Ptr < recMem+RECMEM_SIZE ); + + recPtr = x86Ptr; + + assert( (g_psxHasConstReg&g_psxFlushedConstReg) == g_psxHasConstReg ); + + if( !psxbranch ) { + BASEBLOCK* pcurblock = s_pCurBlock; + u32 nEndBlock = s_nEndBlock; + s_pCurBlock = PSX_GETBLOCK(psxpc); + assert( ptr != NULL ); + + if( s_pCurBlock->startpc != psxpc ){ + psxRecRecompile(psxpc); + } + + // could have reset + if( pcurblock->startpc == startpc ) { + assert( pcurblock->GetFnptr() ); + assert( s_pCurBlock->startpc == nEndBlock ); + *ptr = (u32)((uptr)s_pCurBlock->GetFnptr() - ( (uptr)ptr + 4 )); + } + else { + psxRecRecompile(startpc); + assert( pcurblock->GetFnptr() != 0 ); + } + } + else + assert( s_pCurBlock->GetFnptr() != 0 ); +} + +R3000Acpu psxRec = { + recAlloc, + recResetIOP, + recExecute, + recExecuteBlock, + recClear, + recShutdown +}; + diff --git a/pcsx2/x86/iR3000A.h b/pcsx2/x86/iR3000A.h new file mode 100644 index 0000000000..5fe1a61afb --- /dev/null +++ b/pcsx2/x86/iR3000A.h @@ -0,0 +1,141 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +#ifndef _R3000A_SUPERREC_ +#define _R3000A_SUPERREC_ + +#include "R3000A.h" +#include "iCore.h" +#include "BaseblockEx.h" + +// Cycle penalties for particularly slow instructions. +static const int psxInstCycles_Mult = 7; +static const int psxInstCycles_Div = 40; + +// Currently unused (iop mod incomplete) +static const int psxInstCycles_Peephole_Store = 0; +static const int psxInstCycles_Store = 0; +static const int psxInstCycles_Load = 0; + +// to be consistent with EE +#define PSX_HI XMMGPR_HI +#define PSX_LO XMMGPR_LO + +extern uptr *psxRecLUT; + +u8 _psxLoadWritesRs(u32 tempcode); +u8 _psxIsLoadStore(u32 tempcode); + +void _psxFlushAllUnused(); +int _psxFlushUnusedConstReg(); +void _psxFlushCachedRegs(); +void _psxFlushConstReg(int reg); +void _psxFlushConstRegs(); + +void _psxDeleteReg(int reg, int flush); +void _psxFlushCall(int flushtype); + +void _psxOnWriteReg(int reg); + +void _psxMoveGPRtoR(x86IntRegType to, int fromgpr); +void _psxMoveGPRtoM(u32 to, int fromgpr); +void _psxMoveGPRtoRm(x86IntRegType to, int fromgpr); + +void PSX_CHECK_SAVE_REG(int reg); + +extern u32 psxpc; // recompiler pc +extern int psxbranch; // set for branch +extern u32 g_iopCyclePenalty; + +void psxSaveBranchState(); +void psxLoadBranchState(); + +void psxSetBranchReg(u32 reg); +void psxSetBranchImm( u32 imm ); +void psxRecompileNextInstruction(int delayslot); +void psxRecClearMem(BASEBLOCK* p); + +//////////////////////////////////////////////////////////////////// +// IOP Constant Propagation Defines, Vars, and API - From here down! + +#define PSX_IS_CONST1(reg) ((reg)<32 && (g_psxHasConstReg&(1<<(reg)))) +#define PSX_IS_CONST2(reg1, reg2) ((g_psxHasConstReg&(1<<(reg1)))&&(g_psxHasConstReg&(1<<(reg2)))) +#define PSX_SET_CONST(reg) { \ + if( (reg) < 32 ) { \ + g_psxHasConstReg |= (1<<(reg)); \ + g_psxFlushedConstReg &= ~(1<<(reg)); \ + } \ +} + +#define PSX_DEL_CONST(reg) { \ + if( (reg) < 32 ) g_psxHasConstReg &= ~(1<<(reg)); \ +} + +extern u32 g_psxConstRegs[32]; +extern u32 g_psxHasConstReg, g_psxFlushedConstReg; + +typedef void (*R3000AFNPTR)(); +typedef void (*R3000AFNPTR_INFO)(int info); + +// +// non mmx/xmm version, slower +// +// rd = rs op rt +#define PSXRECOMPILE_CONSTCODE0(fn) \ +void rpsx##fn(void) \ +{ \ + psxRecompileCodeConst0(rpsx##fn##_const, rpsx##fn##_consts, rpsx##fn##_constt, rpsx##fn##_); \ +} + +// rt = rs op imm16 +#define PSXRECOMPILE_CONSTCODE1(fn) \ +void rpsx##fn(void) \ +{ \ + psxRecompileCodeConst1(rpsx##fn##_const, rpsx##fn##_); \ +} + +// rd = rt op sa +#define PSXRECOMPILE_CONSTCODE2(fn) \ +void rpsx##fn(void) \ +{ \ + psxRecompileCodeConst2(rpsx##fn##_const, rpsx##fn##_); \ +} + +// [lo,hi] = rt op rs +#define PSXRECOMPILE_CONSTCODE3(fn, LOHI) \ +void rpsx##fn(void) \ +{ \ + psxRecompileCodeConst3(rpsx##fn##_const, rpsx##fn##_consts, rpsx##fn##_constt, rpsx##fn##_, LOHI); \ +} + +#define PSXRECOMPILE_CONSTCODE3_PENALTY(fn, LOHI, cycles) \ +void rpsx##fn(void) \ +{ \ + psxRecompileCodeConst3(rpsx##fn##_const, rpsx##fn##_consts, rpsx##fn##_constt, rpsx##fn##_, LOHI); \ + g_iopCyclePenalty = cycles; \ +} + +// rd = rs op rt +void psxRecompileCodeConst0(R3000AFNPTR constcode, R3000AFNPTR_INFO constscode, R3000AFNPTR_INFO consttcode, R3000AFNPTR_INFO noconstcode); +// rt = rs op imm16 +void psxRecompileCodeConst1(R3000AFNPTR constcode, R3000AFNPTR_INFO noconstcode); +// rd = rt op sa +void psxRecompileCodeConst2(R3000AFNPTR constcode, R3000AFNPTR_INFO noconstcode); +// [lo,hi] = rt op rs +void psxRecompileCodeConst3(R3000AFNPTR constcode, R3000AFNPTR_INFO constscode, R3000AFNPTR_INFO consttcode, R3000AFNPTR_INFO noconstcode, int LOHI); + +#endif diff --git a/pcsx2/x86/iR3000Atables.cpp b/pcsx2/x86/iR3000Atables.cpp new file mode 100644 index 0000000000..d6f49b4ae2 --- /dev/null +++ b/pcsx2/x86/iR3000Atables.cpp @@ -0,0 +1,2030 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include + +#include "PsxCommon.h" +#include "ix86/ix86.h" +#include "iR3000A.h" +#include "IopMem.h" +#include "IopDma.h" + +extern int g_psxWriteOk; +extern u32 g_psxMaxRecMem; + +// R3000A instruction implementation +#define REC_FUNC(f) \ +static void rpsx##f() { \ + MOV32ItoM((uptr)&psxRegs.code, (u32)psxRegs.code); \ + _psxFlushCall(FLUSH_EVERYTHING); \ + CALLFunc((uptr)psx##f); \ + PSX_DEL_CONST(_Rt_); \ +/* branch = 2; */\ +} + +extern void psxLWL(); +extern void psxLWR(); +extern void psxSWL(); +extern void psxSWR(); + +//// +void rpsxADDIU_const() +{ + g_psxConstRegs[_Rt_] = g_psxConstRegs[_Rs_] + _Imm_; +} + +// adds a constant to sreg and puts into dreg +void rpsxADDconst(int dreg, int sreg, u32 off, int info) +{ + if (sreg) { + if (sreg == dreg) { + ADD32ItoM((uptr)&psxRegs.GPR.r[dreg], off); + } else { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[sreg]); + if (off) ADD32ItoR(EAX, off); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], EAX); + } + } + else { + MOV32ItoM((uptr)&psxRegs.GPR.r[dreg], off); + } +} + +void rpsxADDIU_(int info) +{ + // Rt = Rs + Im + if (!_Rt_) return; + rpsxADDconst(_Rt_, _Rs_, _Imm_, info); +} + +PSXRECOMPILE_CONSTCODE1(ADDIU); + +void rpsxADDI() { rpsxADDIU(); } + +//// SLTI +void rpsxSLTI_const() +{ + g_psxConstRegs[_Rt_] = *(int*)&g_psxConstRegs[_Rs_] < _Imm_; +} + +void rpsxSLTconst(int info, int dreg, int sreg, int imm) +{ + XOR32RtoR(EAX, EAX); + CMP32ItoM((uptr)&psxRegs.GPR.r[sreg], imm); + SETL8R(EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], EAX); +} + +void rpsxSLTI_(int info) { rpsxSLTconst(info, _Rt_, _Rs_, _Imm_); } + +PSXRECOMPILE_CONSTCODE1(SLTI); + +//// SLTIU +void rpsxSLTIU_const() +{ + g_psxConstRegs[_Rt_] = g_psxConstRegs[_Rs_] < _ImmU_; +} + +void rpsxSLTUconst(int info, int dreg, int sreg, int imm) +{ + XOR32RtoR(EAX, EAX); + CMP32ItoM((uptr)&psxRegs.GPR.r[sreg], imm); + SETB8R(EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], EAX); +} + +void rpsxSLTIU_(int info) { rpsxSLTUconst(info, _Rt_, _Rs_, (s32)_Imm_); } + +PSXRECOMPILE_CONSTCODE1(SLTIU); + +//// ANDI +void rpsxANDI_const() +{ + g_psxConstRegs[_Rt_] = g_psxConstRegs[_Rs_] & _ImmU_; +} + +void rpsxANDconst(int info, int dreg, int sreg, u32 imm) +{ + if (imm) { + if (sreg == dreg) { + AND32ItoM((uptr)&psxRegs.GPR.r[dreg], imm); + } else { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[sreg]); + AND32ItoR(EAX, imm); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], EAX); + } + } else { + MOV32ItoM((uptr)&psxRegs.GPR.r[dreg], 0); + } +} + +void rpsxANDI_(int info) { rpsxANDconst(info, _Rt_, _Rs_, _ImmU_); } + +PSXRECOMPILE_CONSTCODE1(ANDI); + +//// ORI +void rpsxORI_const() +{ + g_psxConstRegs[_Rt_] = g_psxConstRegs[_Rs_] | _ImmU_; +} + +void rpsxORconst(int info, int dreg, int sreg, u32 imm) +{ + if (imm) { + if (sreg == dreg) { + OR32ItoM((uptr)&psxRegs.GPR.r[dreg], imm); + } + else { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[sreg]); + OR32ItoR (EAX, imm); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], EAX); + } + } + else { + if( dreg != sreg ) { + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[sreg]); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], ECX); + } + } +} + +void rpsxORI_(int info) { rpsxORconst(info, _Rt_, _Rs_, _ImmU_); } + +PSXRECOMPILE_CONSTCODE1(ORI); + +void rpsxXORI_const() +{ + g_psxConstRegs[_Rt_] = g_psxConstRegs[_Rs_] ^ _ImmU_; +} + +void rpsxXORconst(int info, int dreg, int sreg, u32 imm) +{ + if( imm == 0xffffffff ) { + if( dreg == sreg ) { + NOT32M((uptr)&psxRegs.GPR.r[dreg]); + } + else { + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[sreg]); + NOT32R(ECX); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], ECX); + } + } + else if (imm) { + + if (sreg == dreg) { + XOR32ItoM((uptr)&psxRegs.GPR.r[dreg], imm); + } + else { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[sreg]); + XOR32ItoR(EAX, imm); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], EAX); + } + } + else { + if( dreg != sreg ) { + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[sreg]); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], ECX); + } + } +} + +void rpsxXORI_(int info) { rpsxXORconst(info, _Rt_, _Rs_, _ImmU_); } + +PSXRECOMPILE_CONSTCODE1(XORI); + +void rpsxLUI() +{ + if(!_Rt_) return; + _psxOnWriteReg(_Rt_); + _psxDeleteReg(_Rt_, 0); + PSX_SET_CONST(_Rt_); + g_psxConstRegs[_Rt_] = psxRegs.code << 16; +} + +void rpsxADDU_const() +{ + g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] + g_psxConstRegs[_Rt_]; +} + +void rpsxADDU_consts(int info) { rpsxADDconst(_Rd_, _Rt_, g_psxConstRegs[_Rs_], info); } +void rpsxADDU_constt(int info) +{ + info |= PROCESS_EE_SET_S(EEREC_T); + rpsxADDconst(_Rd_, _Rs_, g_psxConstRegs[_Rt_], info); +} + +void rpsxADDU_(int info) +{ + if (_Rs_ && _Rt_) { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]); + ADD32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + } else if (_Rs_) { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]); + } else if (_Rt_) { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + } else { + XOR32RtoR(EAX, EAX); + } + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +PSXRECOMPILE_CONSTCODE0(ADDU); + +void rpsxADD() { rpsxADDU(); } + + +void rpsxSUBU_const() +{ + g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] - g_psxConstRegs[_Rt_]; +} + +void rpsxSUBU_consts(int info) +{ + MOV32ItoR(EAX, g_psxConstRegs[_Rs_]); + SUB32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +void rpsxSUBU_constt(int info) { rpsxADDconst(_Rd_, _Rs_, -(int)g_psxConstRegs[_Rt_], info); } + +void rpsxSUBU_(int info) +{ + // Rd = Rs - Rt + if (!_Rd_) return; + + if( _Rd_ == _Rs_ ) { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + SUB32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); + } + else { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]); + SUB32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); + } +} + +PSXRECOMPILE_CONSTCODE0(SUBU); + +void rpsxSUB() { rpsxSUBU(); } + +void rpsxLogicalOp(int info, int op) +{ + if( _Rd_ == _Rs_ || _Rd_ == _Rt_ ) { + int vreg = _Rd_ == _Rs_ ? _Rt_ : _Rs_; + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[vreg]); + LogicalOp32RtoM((uptr)&psxRegs.GPR.r[_Rd_], ECX, op); + if( op == 3 ) + NOT32M((uptr)&psxRegs.GPR.r[_Rd_]); + } + else { + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[_Rs_]); + LogicalOp32MtoR(ECX, (uptr)&psxRegs.GPR.r[_Rt_], op); + if( op == 3 ) + NOT32R(ECX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], ECX); + } +} + +void rpsxAND_const() +{ + g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] & g_psxConstRegs[_Rt_]; +} + +void rpsxAND_consts(int info) { rpsxANDconst(info, _Rd_, _Rt_, g_psxConstRegs[_Rs_]); } +void rpsxAND_constt(int info) { rpsxANDconst(info, _Rd_, _Rs_, g_psxConstRegs[_Rt_]); } +void rpsxAND_(int info) { rpsxLogicalOp(info, 0); } + +PSXRECOMPILE_CONSTCODE0(AND); + +void rpsxOR_const() +{ + g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] | g_psxConstRegs[_Rt_]; +} + +void rpsxOR_consts(int info) { rpsxORconst(info, _Rd_, _Rt_, g_psxConstRegs[_Rs_]); } +void rpsxOR_constt(int info) { rpsxORconst(info, _Rd_, _Rs_, g_psxConstRegs[_Rt_]); } +void rpsxOR_(int info) { rpsxLogicalOp(info, 1); } + +PSXRECOMPILE_CONSTCODE0(OR); + +//// XOR +void rpsxXOR_const() +{ + g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] ^ g_psxConstRegs[_Rt_]; +} + +void rpsxXOR_consts(int info) { rpsxXORconst(info, _Rd_, _Rt_, g_psxConstRegs[_Rs_]); } +void rpsxXOR_constt(int info) { rpsxXORconst(info, _Rd_, _Rs_, g_psxConstRegs[_Rt_]); } +void rpsxXOR_(int info) { rpsxLogicalOp(info, 2); } + +PSXRECOMPILE_CONSTCODE0(XOR); + +//// NOR +void rpsxNOR_const() +{ + g_psxConstRegs[_Rd_] = ~(g_psxConstRegs[_Rs_] | g_psxConstRegs[_Rt_]); +} + +void rpsxNORconst(int info, int dreg, int sreg, u32 imm) +{ + if( imm ) { + if( dreg == sreg ) { + OR32ItoM((uptr)&psxRegs.GPR.r[dreg], imm); + NOT32M((uptr)&psxRegs.GPR.r[dreg]); + } + else { + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[sreg]); + OR32ItoR(ECX, imm); + NOT32R(ECX); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], ECX); + } + } + else { + if( dreg == sreg ) { + NOT32M((uptr)&psxRegs.GPR.r[dreg]); + } + else { + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[sreg]); + NOT32R(ECX); + MOV32RtoM((uptr)&psxRegs.GPR.r[dreg], ECX); + } + } +} + +void rpsxNOR_consts(int info) { rpsxNORconst(info, _Rd_, _Rt_, g_psxConstRegs[_Rs_]); } +void rpsxNOR_constt(int info) { rpsxNORconst(info, _Rd_, _Rs_, g_psxConstRegs[_Rt_]); } +void rpsxNOR_(int info) { rpsxLogicalOp(info, 3); } + +PSXRECOMPILE_CONSTCODE0(NOR); + +//// SLT +void rpsxSLT_const() +{ + g_psxConstRegs[_Rd_] = *(int*)&g_psxConstRegs[_Rs_] < *(int*)&g_psxConstRegs[_Rt_]; +} + +void rpsxSLT_consts(int info) +{ + XOR32RtoR(EAX, EAX); + CMP32ItoM((uptr)&psxRegs.GPR.r[_Rt_], g_psxConstRegs[_Rs_]); + SETG8R(EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +void rpsxSLT_constt(int info) { rpsxSLTconst(info, _Rd_, _Rs_, g_psxConstRegs[_Rt_]); } +void rpsxSLT_(int info) +{ + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]); + CMP32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + SETL8R (EAX); + AND32ItoR(EAX, 0xff); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +PSXRECOMPILE_CONSTCODE0(SLT); + +//// SLTU +void rpsxSLTU_const() +{ + g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] < g_psxConstRegs[_Rt_]; +} + +void rpsxSLTU_consts(int info) +{ + XOR32RtoR(EAX, EAX); + CMP32ItoM((uptr)&psxRegs.GPR.r[_Rt_], g_psxConstRegs[_Rs_]); + SETA8R(EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +void rpsxSLTU_constt(int info) { rpsxSLTUconst(info, _Rd_, _Rs_, g_psxConstRegs[_Rt_]); } +void rpsxSLTU_(int info) +{ + // Rd = Rs < Rt (unsigned) + if (!_Rd_) return; + + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]); + CMP32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + SBB32RtoR(EAX, EAX); + NEG32R (EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +PSXRECOMPILE_CONSTCODE0(SLTU); + +//// MULT +void rpsxMULT_const() +{ + u64 res = (s64)((s64)*(int*)&g_psxConstRegs[_Rs_] * (s64)*(int*)&g_psxConstRegs[_Rt_]); + + MOV32ItoM((uptr)&psxRegs.GPR.n.hi, (u32)((res >> 32) & 0xffffffff)); + MOV32ItoM((uptr)&psxRegs.GPR.n.lo, (u32)(res & 0xffffffff)); +} + +void rpsxMULTsuperconst(int info, int sreg, int imm, int sign) +{ + // Lo/Hi = Rs * Rt (signed) + MOV32ItoR(EAX, imm); + if( sign ) IMUL32M ((uptr)&psxRegs.GPR.r[sreg]); + else MUL32M ((uptr)&psxRegs.GPR.r[sreg]); + MOV32RtoM((uptr)&psxRegs.GPR.n.lo, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.n.hi, EDX); +} + +void rpsxMULTsuper(int info, int sign) +{ + // Lo/Hi = Rs * Rt (signed) + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]); + if( sign ) IMUL32M ((uptr)&psxRegs.GPR.r[_Rt_]); + else MUL32M ((uptr)&psxRegs.GPR.r[_Rt_]); + MOV32RtoM((uptr)&psxRegs.GPR.n.lo, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.n.hi, EDX); +} + +void rpsxMULT_consts(int info) { rpsxMULTsuperconst(info, _Rt_, g_psxConstRegs[_Rs_], 1); } +void rpsxMULT_constt(int info) { rpsxMULTsuperconst(info, _Rs_, g_psxConstRegs[_Rt_], 1); } +void rpsxMULT_(int info) { rpsxMULTsuper(info, 1); } + +PSXRECOMPILE_CONSTCODE3_PENALTY(MULT, 1, psxInstCycles_Mult); + +//// MULTU +void rpsxMULTU_const() +{ + u64 res = (u64)((u64)g_psxConstRegs[_Rs_] * (u64)g_psxConstRegs[_Rt_]); + + MOV32ItoM((uptr)&psxRegs.GPR.n.hi, (u32)((res >> 32) & 0xffffffff)); + MOV32ItoM((uptr)&psxRegs.GPR.n.lo, (u32)(res & 0xffffffff)); +} + +void rpsxMULTU_consts(int info) { rpsxMULTsuperconst(info, _Rt_, g_psxConstRegs[_Rs_], 0); } +void rpsxMULTU_constt(int info) { rpsxMULTsuperconst(info, _Rs_, g_psxConstRegs[_Rt_], 0); } +void rpsxMULTU_(int info) { rpsxMULTsuper(info, 0); } + +PSXRECOMPILE_CONSTCODE3_PENALTY(MULTU, 1, psxInstCycles_Mult); + +//// DIV +void rpsxDIV_const() +{ + u32 lo, hi; + + if (g_psxConstRegs[_Rt_] != 0) { + lo = *(int*)&g_psxConstRegs[_Rs_] / *(int*)&g_psxConstRegs[_Rt_]; + hi = *(int*)&g_psxConstRegs[_Rs_] % *(int*)&g_psxConstRegs[_Rt_]; + MOV32ItoM((uptr)&psxRegs.GPR.n.hi, hi); + MOV32ItoM((uptr)&psxRegs.GPR.n.lo, lo); + } +} + +void rpsxDIVsuperconsts(int info, int sign) +{ + u32 imm = g_psxConstRegs[_Rs_]; + + if( imm ) { + // Lo/Hi = Rs / Rt (signed) + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[_Rt_]); + CMP32ItoR(ECX, 0); + j8Ptr[0] = JE8(0); + MOV32ItoR(EAX, imm); + + + if( sign ) { + CDQ(); + IDIV32R (ECX); + } + else { + XOR32RtoR( EDX, EDX ); + DIV32R(ECX); + } + + + MOV32RtoM((uptr)&psxRegs.GPR.n.lo, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.n.hi, EDX); + x86SetJ8(j8Ptr[0]); + } + else { + XOR32RtoR(EAX, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.n.hi, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.n.lo, EAX); + } +} + +void rpsxDIVsuperconstt(int info, int sign) +{ + u32 imm = g_psxConstRegs[_Rt_]; + + if( imm ) { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]); + MOV32ItoR(ECX, imm); + //CDQ(); + + if( sign ) { + CDQ(); + IDIV32R (ECX); + } + else { + XOR32RtoR( EDX, EDX ); + DIV32R(ECX); + } + + + MOV32RtoM((uptr)&psxRegs.GPR.n.lo, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.n.hi, EDX); + } +} + +void rpsxDIVsuper(int info, int sign) +{ + // Lo/Hi = Rs / Rt (signed) + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[_Rt_]); + CMP32ItoR(ECX, 0); + j8Ptr[0] = JE8(0); + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]); + + if( sign ) { + CDQ(); + IDIV32R (ECX); + } + else { + XOR32RtoR( EDX, EDX ); + DIV32R(ECX); + } + + MOV32RtoM((uptr)&psxRegs.GPR.n.lo, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.n.hi, EDX); + x86SetJ8(j8Ptr[0]); +} + +void rpsxDIV_consts(int info) { rpsxDIVsuperconsts(info, 1); } +void rpsxDIV_constt(int info) { rpsxDIVsuperconstt(info, 1); } +void rpsxDIV_(int info) { rpsxDIVsuper(info, 1); } + +PSXRECOMPILE_CONSTCODE3_PENALTY(DIV, 1, psxInstCycles_Div); + +//// DIVU +void rpsxDIVU_const() +{ + u32 lo, hi; + + if (g_psxConstRegs[_Rt_] != 0) { + lo = g_psxConstRegs[_Rs_] / g_psxConstRegs[_Rt_]; + hi = g_psxConstRegs[_Rs_] % g_psxConstRegs[_Rt_]; + MOV32ItoM((uptr)&psxRegs.GPR.n.hi, hi); + MOV32ItoM((uptr)&psxRegs.GPR.n.lo, lo); + } +} + +void rpsxDIVU_consts(int info) { rpsxDIVsuperconsts(info, 0); } +void rpsxDIVU_constt(int info) { rpsxDIVsuperconstt(info, 0); } +void rpsxDIVU_(int info) { rpsxDIVsuper(info, 0); } + +PSXRECOMPILE_CONSTCODE3_PENALTY(DIVU, 1, psxInstCycles_Div); + +//// LoadStores +#ifdef PCSX2_VIRTUAL_MEM + +// VM load store functions (fastest) + +//#define REC_SLOWREAD +//#define REC_SLOWWRITE + +int _psxPrepareReg(int gprreg) +{ + return 0; +} + +static u32 s_nAddMemOffset = 0; + +static __forceinline void SET_HWLOC_R3000A() { + x86SetJ8(j8Ptr[0]); + SHR32ItoR(ECX, 3); + if( s_nAddMemOffset ) ADD32ItoR(ECX, s_nAddMemOffset); +} + +int rpsxSetMemLocation(int regs, int mmreg) +{ + s_nAddMemOffset = 0; + MOV32MtoR( ECX, (int)&psxRegs.GPR.r[ regs ] ); + + if ( _Imm_ != 0 ) ADD32ItoR( ECX, _Imm_ ); + + SHL32ItoR(ECX, 3); + j8Ptr[0] = JS8(0); + SHR32ItoR(ECX, 3); + AND32ItoR(ECX, 0x1fffff); // 2Mb + return 1; +} + +void recLoad32(u32 bit, u32 sign) +{ + int mmreg = -1; + +#ifdef REC_SLOWREAD + _psxFlushConstReg(_Rs_); +#else + if( PSX_IS_CONST1( _Rs_ ) ) { + // do const processing + int ineax = 0; + + _psxOnWriteReg(_Rt_); + mmreg = EAX; + + switch(bit) { + case 8: ineax = psxRecMemConstRead8(mmreg, g_psxConstRegs[_Rs_]+_Imm_, sign); break; + case 16: + assert( (g_psxConstRegs[_Rs_]+_Imm_) % 2 == 0 ); + ineax = psxRecMemConstRead16(mmreg, g_psxConstRegs[_Rs_]+_Imm_, sign); + break; + case 32: + assert( (g_psxConstRegs[_Rs_]+_Imm_) % 4 == 0 ); + ineax = psxRecMemConstRead32(mmreg, g_psxConstRegs[_Rs_]+_Imm_); + break; + } + + if( _Rt_ ) MOV32RtoM( (int)&psxRegs.GPR.r[ _Rt_ ], EAX ); + } + else +#endif + { + int dohw; + int mmregs = _psxPrepareReg(_Rs_); + + _psxOnWriteReg(_Rt_); + _psxDeleteReg(_Rt_, 0); + + dohw = rpsxSetMemLocation(_Rs_, mmregs); + + switch(bit) { + case 8: + if( sign ) MOVSX32Rm8toROffset(EAX, ECX, PS2MEM_PSX_+s_nAddMemOffset); + else MOVZX32Rm8toROffset(EAX, ECX, PS2MEM_PSX_+s_nAddMemOffset); + break; + case 16: + if( sign ) MOVSX32Rm16toROffset(EAX, ECX, PS2MEM_PSX_+s_nAddMemOffset); + else MOVZX32Rm16toROffset(EAX, ECX, PS2MEM_PSX_+s_nAddMemOffset); + break; + case 32: + MOV32RmtoROffset(EAX, ECX, PS2MEM_PSX_+s_nAddMemOffset); + break; + } + + if( dohw ) { + j8Ptr[1] = JMP8(0); + + SET_HWLOC_R3000A(); + + switch(bit) { + case 8: + CALLFunc( (int)psxRecMemRead8 ); + if( sign ) MOVSX32R8toR(EAX, EAX); + else MOVZX32R8toR(EAX, EAX); + break; + case 16: + CALLFunc( (int)psxRecMemRead16 ); + if( sign ) MOVSX32R16toR(EAX, EAX); + else MOVZX32R16toR(EAX, EAX); + break; + case 32: + CALLFunc( (int)psxRecMemRead32 ); + break; + } + + x86SetJ8(j8Ptr[1]); + } + + if( _Rt_ ) + MOV32RtoM( (int)&psxRegs.GPR.r[ _Rt_ ], EAX ); + } +} + +void rpsxLB() { recLoad32(8, 1); } +void rpsxLBU() { recLoad32(8, 0); } +void rpsxLH() { recLoad32(16, 1); } +void rpsxLHU() { recLoad32(16, 0); } +void rpsxLW() { recLoad32(32, 0); } + +extern void rpsxMemConstClear(u32 mem); + +// check if mem is executable, and clear it +__declspec(naked) void rpsxWriteMemClear() +{ + _asm { + mov edx, ecx + shr edx, 14 + and dl, 0xfc + add edx, psxRecLUT + test dword ptr [edx], 0xffffffff + jnz Clear32 + ret +Clear32: + // recLUT[mem>>16] + (mem&0xfffc) + mov edx, dword ptr [edx] + mov eax, ecx + and eax, 0xfffc + // edx += 2*eax + shl eax, 1 + add edx, eax + cmp dword ptr [edx], 0 + je ClearRet + sub esp, 4 + mov dword ptr [esp], edx + call psxRecClearMem + add esp, 4 +ClearRet: + ret + } +} + +extern u32 s_psxBlockCycles; +void recStore(int bit) +{ +#ifdef REC_SLOWWRITE + _psxFlushConstReg(_Rs_); +#else + if( PSX_IS_CONST1( _Rs_ ) ) { + u8* pjmpok; + u32 addr = g_psxConstRegs[_Rs_]+_Imm_; + int doclear = 0; + + if( !(addr & 0x10000000) ) { + // check g_psxWriteOk + CMP32ItoM((uptr)&g_psxWriteOk, 0); + pjmpok = JE8(0); + } + + switch(bit) { + case 8: + if( PSX_IS_CONST1(_Rt_) ) doclear = psxRecMemConstWrite8(addr, MEM_PSXCONSTTAG|(_Rt_<<16)); + else { + _psxMoveGPRtoR(EAX, _Rt_); + doclear = psxRecMemConstWrite8(addr, EAX); + } + + break; + + case 16: + assert( (addr)%2 == 0 ); + if( PSX_IS_CONST1(_Rt_) ) doclear = psxRecMemConstWrite16(addr, MEM_PSXCONSTTAG|(_Rt_<<16)); + else { + _psxMoveGPRtoR(EAX, _Rt_); + doclear = psxRecMemConstWrite16(addr, EAX); + } + + break; + + case 32: + assert( (addr)%4 == 0 ); + if( PSX_IS_CONST1(_Rt_) ) doclear = psxRecMemConstWrite32(addr, MEM_PSXCONSTTAG|(_Rt_<<16)); + else { + _psxMoveGPRtoR(EAX, _Rt_); + doclear = psxRecMemConstWrite32(addr, EAX); + } + + break; + } + + if( !(addr & 0x10000000) ) { + if( doclear ) rpsxMemConstClear((addr)&~3); + x86SetJ8(pjmpok); + } + } + else +#endif + { + int dohw; + int mmregs = _psxPrepareReg(_Rs_); + dohw = rpsxSetMemLocation(_Rs_, mmregs); + + CMP32ItoM((uptr)&g_psxWriteOk, 0); + u8* pjmpok = JE8(0); + + if( PSX_IS_CONST1( _Rt_ ) ) { + switch(bit) { + case 8: MOV8ItoRmOffset(ECX, g_psxConstRegs[_Rt_], PS2MEM_PSX_+s_nAddMemOffset); break; + case 16: MOV16ItoRmOffset(ECX, g_psxConstRegs[_Rt_], PS2MEM_PSX_+s_nAddMemOffset); break; + case 32: MOV32ItoRmOffset(ECX, g_psxConstRegs[_Rt_], PS2MEM_PSX_+s_nAddMemOffset); break; + } + } + else { + switch(bit) { + case 8: + MOV8MtoR(EAX, (int)&psxRegs.GPR.r[ _Rt_ ]); + MOV8RtoRmOffset(ECX, EAX, PS2MEM_PSX_+s_nAddMemOffset); + break; + + case 16: + MOV16MtoR(EAX, (int)&psxRegs.GPR.r[ _Rt_ ]); + MOV16RtoRmOffset(ECX, EAX, PS2MEM_PSX_+s_nAddMemOffset); + break; + + case 32: + MOV32MtoR(EAX, (int)&psxRegs.GPR.r[ _Rt_ ]); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_PSX_+s_nAddMemOffset); + break; + } + } + + if( s_nAddMemOffset ) ADD32ItoR(ECX, s_nAddMemOffset); + CMP32MtoR(ECX, (uptr)&g_psxMaxRecMem); + + j8Ptr[1] = JAE8(0); + + if( bit < 32 ) AND8ItoR(ECX, 0xfc); + CALLFunc((u32)rpsxWriteMemClear); + + if( dohw ) { + j8Ptr[2] = JMP8(0); + + SET_HWLOC_R3000A(); + + if( PSX_IS_CONST1(_Rt_) ) { + switch(bit) { + case 8: MOV8ItoR(EAX, g_psxConstRegs[_Rt_]); break; + case 16: MOV16ItoR(EAX, g_psxConstRegs[_Rt_]); break; + case 32: MOV32ItoR(EAX, g_psxConstRegs[_Rt_]); break; + } + } + else { + switch(bit) { + case 8: MOV8MtoR(EAX, (int)&psxRegs.GPR.r[ _Rt_ ]); break; + case 16: MOV16MtoR(EAX, (int)&psxRegs.GPR.r[ _Rt_ ]); break; + case 32: MOV32MtoR(EAX, (int)&psxRegs.GPR.r[ _Rt_ ]); break; + } + } + + if( s_nAddMemOffset != 0 ) ADD32ItoR(ECX, s_nAddMemOffset); + + // some type of hardware write + switch(bit) { + case 8: CALLFunc( (int)psxRecMemWrite8 ); break; + case 16: CALLFunc( (int)psxRecMemWrite16 ); break; + case 32: CALLFunc( (int)psxRecMemWrite32 ); break; + } + + x86SetJ8(j8Ptr[2]); + } + + x86SetJ8(j8Ptr[1]); + x86SetJ8(pjmpok); + } +} + +void rpsxSB() { recStore(8); } +void rpsxSH() { recStore(16); } +void rpsxSW() { recStore(32); } + +REC_FUNC(LWL); +REC_FUNC(LWR); +REC_FUNC(SWL); +REC_FUNC(SWR); + +#else + +// TLB loadstore functions (slower +REC_FUNC(LWL); +REC_FUNC(LWR); +REC_FUNC(SWL); +REC_FUNC(SWR); + +static void rpsxLB() +{ + _psxDeleteReg(_Rs_, 1); + _psxOnWriteReg(_Rt_); + _psxDeleteReg(_Rt_, 0); + + MOV32MtoR(X86ARG1, (uptr)&psxRegs.GPR.r[_Rs_]); + if (_Imm_) ADD32ItoR(X86ARG1, _Imm_); + _callFunctionArg1((uptr)psxMemRead8, X86ARG1|MEM_X86TAG, 0); + if (_Rt_) { + MOVSX32R8toR(EAX, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rt_], EAX); + } + PSX_DEL_CONST(_Rt_); +} + +static void rpsxLBU() +{ + _psxDeleteReg(_Rs_, 1); + _psxOnWriteReg(_Rt_); + _psxDeleteReg(_Rt_, 0); + + MOV32MtoR(X86ARG1, (uptr)&psxRegs.GPR.r[_Rs_]); + if (_Imm_) ADD32ItoR(X86ARG1, _Imm_); + _callFunctionArg1((uptr)psxMemRead8, X86ARG1|MEM_X86TAG, 0); + if (_Rt_) { + MOVZX32R8toR(EAX, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rt_], EAX); + } + PSX_DEL_CONST(_Rt_); +} + +static void rpsxLH() +{ + _psxDeleteReg(_Rs_, 1); + _psxOnWriteReg(_Rt_); + _psxDeleteReg(_Rt_, 0); + + MOV32MtoR(X86ARG1, (uptr)&psxRegs.GPR.r[_Rs_]); + if (_Imm_) ADD32ItoR(X86ARG1, _Imm_); + _callFunctionArg1((uptr)psxMemRead16, X86ARG1|MEM_X86TAG, 0); + if (_Rt_) { + MOVSX32R16toR(EAX, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rt_], EAX); + } + PSX_DEL_CONST(_Rt_); +} + +static void rpsxLHU() +{ + _psxDeleteReg(_Rs_, 1); + _psxOnWriteReg(_Rt_); + _psxDeleteReg(_Rt_, 0); + + MOV32MtoR(X86ARG1, (uptr)&psxRegs.GPR.r[_Rs_]); + if (_Imm_) ADD32ItoR(X86ARG1, _Imm_); + _callFunctionArg1((uptr)psxMemRead16, X86ARG1|MEM_X86TAG, 0); + if (_Rt_) { + MOVZX32R16toR(EAX, EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rt_], EAX); + } + PSX_DEL_CONST(_Rt_); +} + +static void rpsxLW() +{ + _psxDeleteReg(_Rs_, 1); + _psxOnWriteReg(_Rt_); + _psxDeleteReg(_Rt_, 0); + + _psxFlushCall(FLUSH_EVERYTHING); + MOV32MtoR(X86ARG1, (uptr)&psxRegs.GPR.r[_Rs_]); + if (_Imm_) ADD32ItoR(X86ARG1, _Imm_); + +#ifndef TLB_DEBUG_MEM + TEST32ItoR(X86ARG1, 0x10000000); + j8Ptr[0] = JZ8(0); +#endif + + _callFunctionArg1((uptr)psxMemRead32, X86ARG1|MEM_X86TAG, 0); + if (_Rt_) { + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rt_], EAX); + } +#ifndef TLB_DEBUG_MEM + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + + // read from psM directly + AND32ItoR(X86ARG1, 0x1fffff); + ADD32ItoR(X86ARG1, (uptr)psxM); + + MOV32RmtoR( X86ARG1, X86ARG1 ); + MOV32RtoM( (uptr)&psxRegs.GPR.r[_Rt_], X86ARG1); + + x86SetJ8(j8Ptr[1]); +#endif + PSX_DEL_CONST(_Rt_); +} + +static void rpsxSB() +{ + _psxDeleteReg(_Rs_, 1); + _psxDeleteReg(_Rt_, 1); + + MOV32MtoR(X86ARG1, (uptr)&psxRegs.GPR.r[_Rs_]); + if (_Imm_) ADD32ItoR(X86ARG1, _Imm_); + _callFunctionArg2((uptr)psxMemWrite8, X86ARG1|MEM_X86TAG, MEM_MEMORYTAG, 0, (uptr)&psxRegs.GPR.r[_Rt_]); +} + +static void rpsxSH() +{ + _psxDeleteReg(_Rs_, 1); + _psxDeleteReg(_Rt_, 1); + + MOV32MtoR(X86ARG1, (uptr)&psxRegs.GPR.r[_Rs_]); + if (_Imm_) ADD32ItoR(X86ARG1, _Imm_); + _callFunctionArg2((uptr)psxMemWrite16, X86ARG1|MEM_X86TAG, MEM_MEMORYTAG, 0, (uptr)&psxRegs.GPR.r[_Rt_]); +} + +static void rpsxSW() +{ + _psxDeleteReg(_Rs_, 1); + _psxDeleteReg(_Rt_, 1); + + MOV32MtoR(X86ARG1, (uptr)&psxRegs.GPR.r[_Rs_]); + if (_Imm_) ADD32ItoR(X86ARG1, _Imm_); + _callFunctionArg2((uptr)psxMemWrite32, X86ARG1|MEM_X86TAG, MEM_MEMORYTAG, 0, (uptr)&psxRegs.GPR.r[_Rt_]); +} + +#endif // end load store + +//// SLL +void rpsxSLL_const() +{ + g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rt_] << _Sa_; +} + +// shifttype: 0 - sll, 1 - srl, 2 - sra +void rpsxShiftConst(int info, int rdreg, int rtreg, int imm, int shifttype) +{ + imm &= 0x1f; + if (imm) { + if( rdreg == rtreg ) { + switch(shifttype) { + case 0: SHL32ItoM((uptr)&psxRegs.GPR.r[rdreg], imm); break; + case 1: SHR32ItoM((uptr)&psxRegs.GPR.r[rdreg], imm); break; + case 2: SAR32ItoM((uptr)&psxRegs.GPR.r[rdreg], imm); break; + } + } + else { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[rtreg]); + switch(shifttype) { + case 0: SHL32ItoR(EAX, imm); break; + case 1: SHR32ItoR(EAX, imm); break; + case 2: SAR32ItoR(EAX, imm); break; + } + MOV32RtoM((uptr)&psxRegs.GPR.r[rdreg], EAX); + } + } + else { + if( rdreg != rtreg ) { + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[rtreg]); + MOV32RtoM((uptr)&psxRegs.GPR.r[rdreg], EAX); + } + } +} + +void rpsxSLL_(int info) { rpsxShiftConst(info, _Rd_, _Rt_, _Sa_, 0); } +PSXRECOMPILE_CONSTCODE2(SLL); + +//// SRL +void rpsxSRL_const() +{ + g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rt_] >> _Sa_; +} + +void rpsxSRL_(int info) { rpsxShiftConst(info, _Rd_, _Rt_, _Sa_, 1); } +PSXRECOMPILE_CONSTCODE2(SRL); + +//// SRA +void rpsxSRA_const() +{ + g_psxConstRegs[_Rd_] = *(int*)&g_psxConstRegs[_Rt_] >> _Sa_; +} + +void rpsxSRA_(int info) { rpsxShiftConst(info, _Rd_, _Rt_, _Sa_, 2); } +PSXRECOMPILE_CONSTCODE2(SRA); + +//// SLLV +void rpsxSLLV_const() +{ + g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rt_] << (g_psxConstRegs[_Rs_]&0x1f); +} + +void rpsxShiftVconsts(int info, int shifttype) +{ + rpsxShiftConst(info, _Rd_, _Rt_, g_psxConstRegs[_Rs_], shifttype); +} + +void rpsxShiftVconstt(int info, int shifttype) +{ + MOV32ItoR(EAX, g_psxConstRegs[_Rt_]); + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[_Rs_]); + switch(shifttype) { + case 0: SHL32CLtoR(EAX); break; + case 1: SHR32CLtoR(EAX); break; + case 2: SAR32CLtoR(EAX); break; + } + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +void rpsxSLLV_consts(int info) { rpsxShiftVconsts(info, 0); } +void rpsxSLLV_constt(int info) { rpsxShiftVconstt(info, 0); } +void rpsxSLLV_(int info) +{ + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[_Rs_]); + SHL32CLtoR(EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +PSXRECOMPILE_CONSTCODE0(SLLV); + +//// SRLV +void rpsxSRLV_const() +{ + g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rt_] >> (g_psxConstRegs[_Rs_]&0x1f); +} + +void rpsxSRLV_consts(int info) { rpsxShiftVconsts(info, 1); } +void rpsxSRLV_constt(int info) { rpsxShiftVconstt(info, 1); } +void rpsxSRLV_(int info) +{ + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[_Rs_]); + SHR32CLtoR(EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +PSXRECOMPILE_CONSTCODE0(SRLV); + +//// SRAV +void rpsxSRAV_const() +{ + g_psxConstRegs[_Rd_] = *(int*)&g_psxConstRegs[_Rt_] >> (g_psxConstRegs[_Rs_]&0x1f); +} + +void rpsxSRAV_consts(int info) { rpsxShiftVconsts(info, 2); } +void rpsxSRAV_constt(int info) { rpsxShiftVconstt(info, 2); } +void rpsxSRAV_(int info) +{ + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + MOV32MtoR(ECX, (uptr)&psxRegs.GPR.r[_Rs_]); + SAR32CLtoR(EAX); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +PSXRECOMPILE_CONSTCODE0(SRAV); + +extern void rpsxSYSCALL(); +extern void rpsxBREAK(); + +void rpsxMFHI() +{ + if (!_Rd_) return; + + _psxOnWriteReg(_Rd_); + _psxDeleteReg(_Rd_, 0); + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.n.hi); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +void rpsxMTHI() +{ + if( PSX_IS_CONST1(_Rs_) ) { + MOV32ItoM((uptr)&psxRegs.GPR.n.hi, g_psxConstRegs[_Rs_]); + } + else { + _psxDeleteReg(_Rs_, 1); + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]); + MOV32RtoM((uptr)&psxRegs.GPR.n.hi, EAX); + } +} + +void rpsxMFLO() +{ + if (!_Rd_) return; + + _psxOnWriteReg(_Rd_); + _psxDeleteReg(_Rd_, 0); + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.n.lo); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rd_], EAX); +} + +void rpsxMTLO() +{ + if( PSX_IS_CONST1(_Rs_) ) { + MOV32ItoM((uptr)&psxRegs.GPR.n.hi, g_psxConstRegs[_Rs_]); + } + else { + _psxDeleteReg(_Rs_, 1); + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]); + MOV32RtoM((uptr)&psxRegs.GPR.n.lo, EAX); + } +} + +void rpsxJ() +{ + // j target + u32 newpc = _Target_ * 4 + (psxpc & 0xf0000000); + psxRecompileNextInstruction(1); + psxSetBranchImm(newpc); +} + +void rpsxJAL() +{ + u32 newpc = (_Target_ << 2) + ( psxpc & 0xf0000000 ); + _psxDeleteReg(31, 0); + PSX_SET_CONST(31); + g_psxConstRegs[31] = psxpc + 4; + + psxRecompileNextInstruction(1); + psxSetBranchImm(newpc); +} + +void rpsxJR() +{ + psxSetBranchReg(_Rs_); +} + +void rpsxJALR() +{ + // jalr Rs + _allocX86reg(ESI, X86TYPE_PCWRITEBACK, 0, MODE_WRITE); + _psxMoveGPRtoR(ESI, _Rs_); + + if ( _Rd_ ) + { + _psxDeleteReg(_Rd_, 0); + PSX_SET_CONST(_Rd_); + g_psxConstRegs[_Rd_] = psxpc + 4; + } + + psxRecompileNextInstruction(1); + + if( x86regs[ESI].inuse ) { + assert( x86regs[ESI].type == X86TYPE_PCWRITEBACK ); + MOV32RtoM((uptr)&psxRegs.pc, ESI); + x86regs[ESI].inuse = 0; + } + else { + MOV32MtoR(EAX, (uptr)&g_recWriteback); + MOV32RtoM((uptr)&psxRegs.pc, EAX); + } + + psxSetBranchReg(0xffffffff); +} + +//// BEQ +static void* s_pbranchjmp; +static u32 s_do32 = 0; + +#define JUMPVALID(pjmp) (( x86Ptr - (u8*)pjmp ) <= 0x80) + +void rpsxSetBranchEQ(int info, int process) +{ + if( process & PROCESS_CONSTS ) { + CMP32ItoM( (uptr)&psxRegs.GPR.r[ _Rt_ ], g_psxConstRegs[_Rs_] ); + if( s_do32 ) s_pbranchjmp = JNE32( 0 ); + else s_pbranchjmp = JNE8( 0 ); + } + else if( process & PROCESS_CONSTT ) { + CMP32ItoM( (uptr)&psxRegs.GPR.r[ _Rs_ ], g_psxConstRegs[_Rt_] ); + if( s_do32 ) s_pbranchjmp = JNE32( 0 ); + else s_pbranchjmp = JNE8( 0 ); + } + else { + MOV32MtoR( EAX, (uptr)&psxRegs.GPR.r[ _Rs_ ] ); + CMP32MtoR( EAX, (uptr)&psxRegs.GPR.r[ _Rt_ ] ); + if( s_do32 ) s_pbranchjmp = JNE32( 0 ); + else s_pbranchjmp = JNE8( 0 ); + } +} + +void rpsxBEQ_const() +{ + u32 branchTo; + + if( g_psxConstRegs[_Rs_] == g_psxConstRegs[_Rt_] ) + branchTo = ((s32)_Imm_ * 4) + psxpc; + else + branchTo = psxpc+4; + + psxRecompileNextInstruction(1); + psxSetBranchImm( branchTo ); +} + +void rpsxBEQ_process(int info, int process) +{ + u32 branchTo = ((s32)_Imm_ * 4) + psxpc; + + if ( _Rs_ == _Rt_ ) + { + psxRecompileNextInstruction(1); + psxSetBranchImm( branchTo ); + } + else + { + _psxFlushAllUnused(); + u8* prevx86 = x86Ptr; + s_do32 = 0; + psxSaveBranchState(); + + rpsxSetBranchEQ(info, process); + + psxRecompileNextInstruction(1); + psxSetBranchImm(branchTo); + + if( JUMPVALID(s_pbranchjmp) ) { + x86SetJ8A( (u8*)s_pbranchjmp ); + } + else { + x86Ptr = prevx86; + s_do32 = 1; + psxpc -= 4; + psxRegs.code = *(u32*)PSXM( psxpc - 4 ); + psxLoadBranchState(); + rpsxSetBranchEQ(info, process); + psxRecompileNextInstruction(1); + psxSetBranchImm(branchTo); + x86SetJ32A( (u32*)s_pbranchjmp ); + } + + // recopy the next inst + psxpc -= 4; + psxLoadBranchState(); + psxRecompileNextInstruction(1); + + psxSetBranchImm(psxpc); + } +} + +void rpsxBEQ_(int info) { rpsxBEQ_process(info, 0); } +void rpsxBEQ_consts(int info) { rpsxBEQ_process(info, PROCESS_CONSTS); } +void rpsxBEQ_constt(int info) { rpsxBEQ_process(info, PROCESS_CONSTT); } +PSXRECOMPILE_CONSTCODE3(BEQ, 0); + +//// BNE +void rpsxBNE_const() +{ + u32 branchTo; + + if( g_psxConstRegs[_Rs_] != g_psxConstRegs[_Rt_] ) + branchTo = ((s32)_Imm_ * 4) + psxpc; + else + branchTo = psxpc+4; + + psxRecompileNextInstruction(1); + psxSetBranchImm( branchTo ); +} + +void rpsxBNE_process(int info, int process) +{ + u32 branchTo = ((s32)_Imm_ * 4) + psxpc; + + if ( _Rs_ == _Rt_ ) + { + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + return; + } + + _psxFlushAllUnused(); + u8* prevx86 = x86Ptr; + s_do32 = 0; + rpsxSetBranchEQ(info, process); + + psxSaveBranchState(); + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + + if( JUMPVALID(s_pbranchjmp) ) { + x86SetJ8A( (u8*)s_pbranchjmp ); + } + else { + x86Ptr = prevx86; + s_do32 = 1; + psxpc -= 4; + psxRegs.code = *(u32*)PSXM( psxpc - 4 ); + psxLoadBranchState(); + rpsxSetBranchEQ(info, process); + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + x86SetJ32A( (u32*)s_pbranchjmp ); + } + + // recopy the next inst + psxpc -= 4; + psxLoadBranchState(); + psxRecompileNextInstruction(1); + + psxSetBranchImm(branchTo); +} + +void rpsxBNE_(int info) { rpsxBNE_process(info, 0); } +void rpsxBNE_consts(int info) { rpsxBNE_process(info, PROCESS_CONSTS); } +void rpsxBNE_constt(int info) { rpsxBNE_process(info, PROCESS_CONSTT); } +PSXRECOMPILE_CONSTCODE3(BNE, 0); + +//// BLTZ +void rpsxBLTZ() +{ + // Branch if Rs < 0 + u32 branchTo = (s32)_Imm_ * 4 + psxpc; + + _psxFlushAllUnused(); + + if( PSX_IS_CONST1(_Rs_) ) { + if( (int)g_psxConstRegs[_Rs_] >= 0 ) + branchTo = psxpc+4; + + psxRecompileNextInstruction(1); + psxSetBranchImm( branchTo ); + return; + } + + CMP32ItoM((uptr)&psxRegs.GPR.r[_Rs_], 0); + u8* prevx86 = x86Ptr; + u8* pjmp = JL8(0); + + psxSaveBranchState(); + psxRecompileNextInstruction(1); + + psxSetBranchImm(psxpc); + + if( JUMPVALID(pjmp) ) { + x86SetJ8A( pjmp ); + } + else { + x86Ptr = prevx86; + psxpc -= 4; + psxRegs.code = *(u32*)PSXM( psxpc - 4 ); + psxLoadBranchState(); + u32* pjmp32 = JL32(0); + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + x86SetJ32A( pjmp32 ); + } + + // recopy the next inst + psxpc -= 4; + psxLoadBranchState(); + psxRecompileNextInstruction(1); + + psxSetBranchImm(branchTo); +} + +//// BGEZ +void rpsxBGEZ() +{ + u32 branchTo = ((s32)_Imm_ * 4) + psxpc; + + _psxFlushAllUnused(); + + if( PSX_IS_CONST1(_Rs_) ) { + if ( (int)g_psxConstRegs[_Rs_] < 0 ) + branchTo = psxpc+4; + + psxRecompileNextInstruction(1); + psxSetBranchImm( branchTo ); + return; + } + + CMP32ItoM((uptr)&psxRegs.GPR.r[_Rs_], 0); + u8* prevx86 = x86Ptr; + u8* pjmp = JGE8(0); + + psxSaveBranchState(); + psxRecompileNextInstruction(1); + + psxSetBranchImm(psxpc); + + if( JUMPVALID(pjmp) ) { + x86SetJ8A( pjmp ); + } + else { + x86Ptr = prevx86; + psxpc -= 4; + psxRegs.code = *(u32*)PSXM( psxpc - 4 ); + psxLoadBranchState(); + u32* pjmp32 = JGE32(0); + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + x86SetJ32A( pjmp32 ); + } + + // recopy the next inst + psxpc -= 4; + psxLoadBranchState(); + psxRecompileNextInstruction(1); + + psxSetBranchImm(branchTo); +} + +//// BLTZAL +void rpsxBLTZAL() +{ + // Branch if Rs < 0 + u32 branchTo = (s32)_Imm_ * 4 + psxpc; + + _psxFlushConstReg(31); + _psxDeleteReg(31, 0); + _psxFlushAllUnused(); + + if( PSX_IS_CONST1(_Rs_) ) { + if( (int)g_psxConstRegs[_Rs_] >= 0 ) + branchTo = psxpc+4; + else { + PSX_SET_CONST(_Rt_); + g_psxConstRegs[31] = psxpc+4; + } + + psxRecompileNextInstruction(1); + psxSetBranchImm( branchTo ); + return; + } + + CMP32ItoM((uptr)&psxRegs.GPR.r[_Rs_], 0); + u8* prevx86 = x86Ptr; + u8* pjmp = JL8(0); + + psxSaveBranchState(); + + MOV32ItoM((uptr)&psxRegs.GPR.r[31], psxpc+4); + psxRecompileNextInstruction(1); + + psxSetBranchImm(psxpc); + + if( JUMPVALID(pjmp) ) { + x86SetJ8A( pjmp ); + } + else { + x86Ptr = prevx86; + psxpc -= 4; + psxRegs.code = *(u32*)PSXM( psxpc - 4 ); + psxLoadBranchState(); + u32* pjmp32 = JL32(0); + MOV32ItoM((uptr)&psxRegs.GPR.r[31], psxpc+4); + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + x86SetJ32A( pjmp32 ); + } + + // recopy the next inst + psxpc -= 4; + psxLoadBranchState(); + psxRecompileNextInstruction(1); + + psxSetBranchImm(branchTo); +} + +//// BGEZAL +void rpsxBGEZAL() +{ + u32 branchTo = ((s32)_Imm_ * 4) + psxpc; + + _psxFlushConstReg(31); + _psxDeleteReg(31, 0); + _psxFlushAllUnused(); + + if( PSX_IS_CONST1(_Rs_) ) { + if( g_psxConstRegs[_Rs_] < 0 ) + branchTo = psxpc+4; + else MOV32ItoM((uptr)&psxRegs.GPR.r[31], psxpc+4); + + psxRecompileNextInstruction(1); + psxSetBranchImm( branchTo ); + return; + } + + CMP32ItoM((uptr)&psxRegs.GPR.r[_Rs_], 0); + u8* prevx86 = x86Ptr; + u8* pjmp = JGE8(0); + + MOV32ItoM((uptr)&psxRegs.GPR.r[31], psxpc+4); + + psxSaveBranchState(); + psxRecompileNextInstruction(1); + + psxSetBranchImm(psxpc); + + if( JUMPVALID(pjmp) ) { + x86SetJ8A( pjmp ); + } + else { + x86Ptr = prevx86; + psxpc -= 4; + psxRegs.code = *(u32*)PSXM( psxpc - 4 ); + psxLoadBranchState(); + u32* pjmp32 = JGE32(0); + MOV32ItoM((uptr)&psxRegs.GPR.r[31], psxpc+4); + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + x86SetJ32A( pjmp32 ); + } + + // recopy the next inst + psxpc -= 4; + psxLoadBranchState(); + psxRecompileNextInstruction(1); + + psxSetBranchImm(branchTo); +} + +//// BLEZ +void rpsxBLEZ() +{ + // Branch if Rs <= 0 + u32 branchTo = (s32)_Imm_ * 4 + psxpc; + + _psxFlushAllUnused(); + + if( PSX_IS_CONST1(_Rs_) ) { + if( (int)g_psxConstRegs[_Rs_] > 0 ) + branchTo = psxpc+4; + + psxRecompileNextInstruction(1); + psxSetBranchImm( branchTo ); + return; + } + + _psxDeleteReg(_Rs_, 1); + _clearNeededX86regs(); + + CMP32ItoM((uptr)&psxRegs.GPR.r[_Rs_], 0); + u8* prevx86 = x86Ptr; + u8* pjmp = JLE8(0); + + psxSaveBranchState(); + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + + if( JUMPVALID(pjmp) ) { + x86SetJ8A( pjmp ); + } + else { + x86Ptr = prevx86; + psxpc -= 4; + psxRegs.code = *(u32*)PSXM( psxpc - 4 ); + psxLoadBranchState(); + u32* pjmp32 = JLE32(0); + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + x86SetJ32A( pjmp32 ); + } + + psxpc -= 4; + psxLoadBranchState(); + psxRecompileNextInstruction(1); + psxSetBranchImm(branchTo); +} + +//// BGTZ +void rpsxBGTZ() +{ + // Branch if Rs > 0 + u32 branchTo = (s32)_Imm_ * 4 + psxpc; + + _psxFlushAllUnused(); + + if( PSX_IS_CONST1(_Rs_) ) { + if( (int)g_psxConstRegs[_Rs_] <= 0 ) + branchTo = psxpc+4; + + psxRecompileNextInstruction(1); + psxSetBranchImm( branchTo ); + return; + } + + _psxDeleteReg(_Rs_, 1); + _clearNeededX86regs(); + + CMP32ItoM((uptr)&psxRegs.GPR.r[_Rs_], 0); + u8* prevx86 = x86Ptr; + u8* pjmp = JG8(0); + + psxSaveBranchState(); + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + + if( JUMPVALID(pjmp) ) { + x86SetJ8A( pjmp ); + } + else { + x86Ptr = prevx86; + psxpc -= 4; + psxRegs.code = *(u32*)PSXM( psxpc - 4 ); + psxLoadBranchState(); + u32* pjmp32 = JG32(0); + psxRecompileNextInstruction(1); + psxSetBranchImm(psxpc); + x86SetJ32A( pjmp32 ); + } + + psxpc -= 4; + psxLoadBranchState(); + psxRecompileNextInstruction(1); + psxSetBranchImm(branchTo); +} + +void rpsxMFC0() +{ + // Rt = Cop0->Rd + if (!_Rt_) return; + + _psxOnWriteReg(_Rt_); + MOV32MtoR(EAX, (uptr)&psxRegs.CP0.r[_Rd_]); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rt_], EAX); +} + +void rpsxCFC0() +{ + // Rt = Cop0->Rd + if (!_Rt_) return; + + _psxOnWriteReg(_Rt_); + MOV32MtoR(EAX, (uptr)&psxRegs.CP0.r[_Rd_]); + MOV32RtoM((uptr)&psxRegs.GPR.r[_Rt_], EAX); +} + +void rpsxMTC0() +{ + // Cop0->Rd = Rt + if( PSX_IS_CONST1(_Rt_) ) { + MOV32ItoM((uptr)&psxRegs.CP0.r[_Rd_], g_psxConstRegs[_Rt_]); + } + else { + _psxDeleteReg(_Rt_, 1); + MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rt_]); + MOV32RtoM((uptr)&psxRegs.CP0.r[_Rd_], EAX); + } +} + +void rpsxCTC0() +{ + // Cop0->Rd = Rt + rpsxMTC0(); +} + +void rpsxRFE() +{ + MOV32MtoR(EAX, (uptr)&psxRegs.CP0.n.Status); + MOV32RtoR(ECX, EAX); + AND32ItoR(EAX, 0xfffffff0); + AND32ItoR(ECX, 0x3c); + SHR32ItoR(ECX, 2); + OR32RtoR (EAX, ECX); + MOV32RtoM((uptr)&psxRegs.CP0.n.Status, EAX); + + // Test the IOP's INTC status, so that any pending ints get raised. + + _psxFlushCall(0); + CALLFunc( (uptr)&iopTestIntc ); +} + +// R3000A tables +extern void (*rpsxBSC[64])(); +extern void (*rpsxSPC[64])(); +extern void (*rpsxREG[32])(); +extern void (*rpsxCP0[32])(); +extern void (*rpsxCP2[64])(); +extern void (*rpsxCP2BSC[32])(); + +static void rpsxSPECIAL() { rpsxSPC[_Funct_](); } +static void rpsxREGIMM() { rpsxREG[_Rt_](); } +static void rpsxCOP0() { rpsxCP0[_Rs_](); } +//static void rpsxBASIC() { rpsxCP2BSC[_Rs_](); } + +static void rpsxNULL() { + SysPrintf("psxUNK: %8.8x\n", psxRegs.code); +} + +void (*rpsxBSC[64])() = { + rpsxSPECIAL, rpsxREGIMM, rpsxJ , rpsxJAL , rpsxBEQ , rpsxBNE , rpsxBLEZ, rpsxBGTZ, + rpsxADDI , rpsxADDIU , rpsxSLTI, rpsxSLTIU, rpsxANDI, rpsxORI , rpsxXORI, rpsxLUI , + rpsxCOP0 , rpsxNULL , rpsxNULL, rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxLB , rpsxLH , rpsxLWL , rpsxLW , rpsxLBU , rpsxLHU , rpsxLWR , rpsxNULL, + rpsxSB , rpsxSH , rpsxSWL , rpsxSW , rpsxNULL, rpsxNULL, rpsxSWR , rpsxNULL, + rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL +}; + +void (*rpsxSPC[64])() = { + rpsxSLL , rpsxNULL, rpsxSRL , rpsxSRA , rpsxSLLV , rpsxNULL , rpsxSRLV, rpsxSRAV, + rpsxJR , rpsxJALR, rpsxNULL, rpsxNULL, rpsxSYSCALL, rpsxBREAK, rpsxNULL, rpsxNULL, + rpsxMFHI, rpsxMTHI, rpsxMFLO, rpsxMTLO, rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, + rpsxMULT, rpsxMULTU, rpsxDIV, rpsxDIVU, rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, + rpsxADD , rpsxADDU, rpsxSUB , rpsxSUBU, rpsxAND , rpsxOR , rpsxXOR , rpsxNOR , + rpsxNULL, rpsxNULL, rpsxSLT , rpsxSLTU, rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL +}; + +void (*rpsxREG[32])() = { + rpsxBLTZ , rpsxBGEZ , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxBLTZAL, rpsxBGEZAL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL +}; + +void (*rpsxCP0[32])() = { + rpsxMFC0, rpsxNULL, rpsxCFC0, rpsxNULL, rpsxMTC0, rpsxNULL, rpsxCTC0, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxRFE , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL +}; + +// coissued insts +void (*rpsxBSC_co[64] )() = { + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, + rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, +}; + +//////////////////////////////////////////////// +// Back-Prob Function Tables - Gathering Info // +//////////////////////////////////////////////// +#define rpsxpropSetRead(reg) { \ + if( !(pinst->regs[reg] & EEINST_USED) ) \ + pinst->regs[reg] |= EEINST_LASTUSE; \ + prev->regs[reg] |= EEINST_LIVE0|EEINST_USED; \ + pinst->regs[reg] |= EEINST_USED; \ + _recFillRegister(*pinst, XMMTYPE_GPRREG, reg, 0); \ +} \ + +#define rpsxpropSetWrite(reg) { \ + prev->regs[reg] &= ~EEINST_LIVE0; \ + if( !(pinst->regs[reg] & EEINST_USED) ) \ + pinst->regs[reg] |= EEINST_LASTUSE; \ + pinst->regs[reg] |= EEINST_USED; \ + prev->regs[reg] |= EEINST_USED; \ + _recFillRegister(*pinst, XMMTYPE_GPRREG, reg, 1); \ +} + +void rpsxpropBSC(EEINST* prev, EEINST* pinst); +void rpsxpropSPECIAL(EEINST* prev, EEINST* pinst); +void rpsxpropREGIMM(EEINST* prev, EEINST* pinst); +void rpsxpropCP0(EEINST* prev, EEINST* pinst); +void rpsxpropCP2(EEINST* prev, EEINST* pinst); + +//SPECIAL, REGIMM, J , JAL , BEQ , BNE , BLEZ, BGTZ, +//ADDI , ADDIU , SLTI, SLTIU, ANDI, ORI , XORI, LUI , +//COP0 , NULL , COP2, NULL , NULL, NULL, NULL, NULL, +//NULL , NULL , NULL, NULL , NULL, NULL, NULL, NULL, +//LB , LH , LWL , LW , LBU , LHU , LWR , NULL, +//SB , SH , SWL , SW , NULL, NULL, SWR , NULL, +//NULL , NULL , NULL, NULL , NULL, NULL, NULL, NULL, +//NULL , NULL , NULL, NULL , NULL, NULL, NULL, NULL +void rpsxpropBSC(EEINST* prev, EEINST* pinst) +{ + switch(psxRegs.code >> 26) { + case 0: rpsxpropSPECIAL(prev, pinst); break; + case 1: rpsxpropREGIMM(prev, pinst); break; + case 2: // j + break; + case 3: // jal + rpsxpropSetWrite(31); + break; + case 4: // beq + case 5: // bne + rpsxpropSetRead(_Rs_); + rpsxpropSetRead(_Rt_); + break; + + case 6: // blez + case 7: // bgtz + rpsxpropSetRead(_Rs_); + break; + + case 15: // lui + rpsxpropSetWrite(_Rt_); + break; + + case 16: rpsxpropCP0(prev, pinst); break; + case 18: assert(0); break; + + // stores + case 40: case 41: case 42: case 43: case 46: + rpsxpropSetRead(_Rt_); + rpsxpropSetRead(_Rs_); + break; + + default: + rpsxpropSetWrite(_Rt_); + rpsxpropSetRead(_Rs_); + break; + } +} + +//SLL , NULL, SRL , SRA , SLLV , NULL , SRLV, SRAV, +//JR , JALR, NULL, NULL, SYSCALL, BREAK, NULL, NULL, +//MFHI, MTHI, MFLO, MTLO, NULL , NULL , NULL, NULL, +//MULT, MULTU, DIV, DIVU, NULL , NULL , NULL, NULL, +//ADD , ADDU, SUB , SUBU, AND , OR , XOR , NOR , +//NULL, NULL, SLT , SLTU, NULL , NULL , NULL, NULL, +//NULL, NULL, NULL, NULL, NULL , NULL , NULL, NULL, +//NULL, NULL, NULL, NULL, NULL , NULL , NULL, NULL +void rpsxpropSPECIAL(EEINST* prev, EEINST* pinst) +{ + switch(_Funct_) { + case 0: // SLL + case 2: // SRL + case 3: // SRA + rpsxpropSetWrite(_Rd_); + rpsxpropSetRead(_Rt_); + break; + + case 8: // JR + rpsxpropSetRead(_Rs_); + break; + case 9: // JALR + rpsxpropSetWrite(_Rd_); + rpsxpropSetRead(_Rs_); + break; + + case 12: // syscall + case 13: // break + _recClearInst(prev); + prev->info = 0; + break; + case 15: // sync + break; + + case 16: // mfhi + rpsxpropSetWrite(_Rd_); + rpsxpropSetRead(PSX_HI); + break; + case 17: // mthi + rpsxpropSetWrite(PSX_HI); + rpsxpropSetRead(_Rs_); + break; + case 18: // mflo + rpsxpropSetWrite(_Rd_); + rpsxpropSetRead(PSX_LO); + break; + case 19: // mtlo + rpsxpropSetWrite(PSX_LO); + rpsxpropSetRead(_Rs_); + break; + + case 24: // mult + case 25: // multu + case 26: // div + case 27: // divu + rpsxpropSetWrite(PSX_LO); + rpsxpropSetWrite(PSX_HI); + rpsxpropSetRead(_Rs_); + rpsxpropSetRead(_Rt_); + break; + + case 32: // add + case 33: // addu + case 34: // sub + case 35: // subu + rpsxpropSetWrite(_Rd_); + if( _Rs_ ) rpsxpropSetRead(_Rs_); + if( _Rt_ ) rpsxpropSetRead(_Rt_); + break; + + default: + rpsxpropSetWrite(_Rd_); + rpsxpropSetRead(_Rs_); + rpsxpropSetRead(_Rt_); + break; + } +} + +//BLTZ , BGEZ , NULL, NULL, NULL, NULL, NULL, NULL, +//NULL , NULL , NULL, NULL, NULL, NULL, NULL, NULL, +//BLTZAL, BGEZAL, NULL, NULL, NULL, NULL, NULL, NULL, +//NULL , NULL , NULL, NULL, NULL, NULL, NULL, NULL +void rpsxpropREGIMM(EEINST* prev, EEINST* pinst) +{ + switch(_Rt_) { + case 0: // bltz + case 1: // bgez + rpsxpropSetRead(_Rs_); + break; + + case 16: // bltzal + case 17: // bgezal + // do not write 31 + rpsxpropSetRead(_Rs_); + break; + + jNO_DEFAULT + } +} + +//MFC0, NULL, CFC0, NULL, MTC0, NULL, CTC0, NULL, +//NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +//RFE , NULL, NULL, NULL, NULL, NULL, NULL, NULL, +//NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +void rpsxpropCP0(EEINST* prev, EEINST* pinst) +{ + switch(_Rs_) { + case 0: // mfc0 + case 2: // cfc0 + rpsxpropSetWrite(_Rt_); + break; + + case 4: // mtc0 + case 6: // ctc0 + rpsxpropSetRead(_Rt_); + break; + case 16: // rfe + break; + + jNO_DEFAULT + } +} diff --git a/pcsx2/x86/iR5900.h b/pcsx2/x86/iR5900.h new file mode 100644 index 0000000000..a33e1765f6 --- /dev/null +++ b/pcsx2/x86/iR5900.h @@ -0,0 +1,316 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IR5900_H__ +#define __IR5900_H__ + +#include "R5900.h" +#include "VU.h" +#include "iCore.h" +#include "BaseblockEx.h" // needed for recClear and stuff + +// Yay! These work now! (air) +#define ARITHMETICIMM_RECOMPILE +#define ARITHMETIC_RECOMPILE +#define MULTDIV_RECOMPILE +#define SHIFT_RECOMPILE +#define BRANCH_RECOMPILE +#define JUMP_RECOMPILE +#define LOADSTORE_RECOMPILE +#define MOVE_RECOMPILE +#define MMI_RECOMPILE +#define MMI0_RECOMPILE +#define MMI1_RECOMPILE +#define MMI2_RECOMPILE +#define MMI3_RECOMPILE +#define FPU_RECOMPILE +#define CP0_RECOMPILE +#define CP2_RECOMPILE + +#define EE_CONST_PROP // rec2 - enables constant propagation (faster) + +#define PC_GETBLOCK(x) PC_GETBLOCK_(x, recLUT) + +extern u32 pc; +extern int branch; +extern uptr* recLUT; + +extern u32 maxrecmem; +extern u32 pc; // recompiler pc (also used by the SuperVU! .. why? (air)) +extern int branch; // set for branch (also used by the SuperVU! .. why? (air)) +extern u32 target; // branch target +extern u32 s_nBlockCycles; // cycles of current block recompiling +extern u32 s_saveConstGPRreg; +extern GPR_reg64 s_ConstGPRreg; + +#define REC_FUNC( f ) \ + void rec##f( void ) \ + { \ + MOV32ItoM( (uptr)&cpuRegs.code, (u32)cpuRegs.code ); \ + MOV32ItoM( (uptr)&cpuRegs.pc, (u32)pc ); \ + iFlushCall(FLUSH_EVERYTHING); \ + CALLFunc( (uptr)Interp::f ); \ + } + +#define REC_FUNC_DEL( f, delreg ) \ + void rec##f( void ) \ +{ \ + MOV32ItoM( (uptr)&cpuRegs.code, (u32)cpuRegs.code ); \ + MOV32ItoM( (uptr)&cpuRegs.pc, (u32)pc ); \ + iFlushCall(FLUSH_EVERYTHING); \ + if( (delreg) > 0 ) _deleteEEreg(delreg, 0); \ + CALLFunc( (uptr)Interp::f ); \ +} + +#define REC_SYS( f ) \ + void rec##f( void ) \ + { \ + MOV32ItoM( (uptr)&cpuRegs.code, (u32)cpuRegs.code ); \ + MOV32ItoM( (uptr)&cpuRegs.pc, (u32)pc ); \ + iFlushCall(FLUSH_EVERYTHING); \ + CALLFunc( (uptr)Interp::f ); \ + branch = 2; \ + } + + +// Used to clear recompiled code blocks during memory/dma write operations. +void recClearMem(BASEBLOCK* p); +void REC_CLEARM( u32 mem ); + +// used when processing branches +void SaveBranchState(); +void LoadBranchState(); + +void recompileNextInstruction(int delayslot); +void SetBranchReg( u32 reg ); +void SetBranchImm( u32 imm ); +u32 eeScaleBlockCycles(); + +void iFlushCall(int flushtype); +void recBranchCall( void (*func)() ); +void recCall( void (*func)(), int delreg ); + +// these are defined in iFPU.cpp +void LoadCW(); +void SaveCW(int type); + +extern void recExecute(); // same as recCpu.Execute(), but faster (can be inline'd) + +namespace R5900{ +namespace Dynarec { +extern void recDoBranchImm( u32* jmpSkip, bool isLikely = false ); +extern void recDoBranchImm_Likely( u32* jmpSkip ); +} } + +//////////////////////////////////////////////////////////////////// +// Constant Propagation - From here to the end of the header! + +#define GPR_IS_CONST1(reg) ((reg)<32 && (g_cpuHasConstReg&(1<<(reg)))) +#define GPR_IS_CONST2(reg1, reg2) ((g_cpuHasConstReg&(1<<(reg1)))&&(g_cpuHasConstReg&(1<<(reg2)))) +#define GPR_SET_CONST(reg) { \ + if( (reg) < 32 ) { \ + g_cpuHasConstReg |= (1<<(reg)); \ + g_cpuFlushedConstReg &= ~(1<<(reg)); \ + } \ +} + +#define GPR_DEL_CONST(reg) { \ + if( (reg) < 32 ) g_cpuHasConstReg &= ~(1<<(reg)); \ +} + +extern void (*recBSC_co[64])(); +extern PCSX2_ALIGNED16_DECL(GPR_reg64 g_cpuConstRegs[32]); +extern u32 g_cpuHasConstReg, g_cpuFlushedConstReg; + +// gets a memory pointer to the constant reg +u32* _eeGetConstReg(int reg); + +// finds where the GPR is stored and moves lower 32 bits to EAX +void _eeMoveGPRtoR(x86IntRegType to, int fromgpr); +void _eeMoveGPRtoM(u32 to, int fromgpr); +void _eeMoveGPRtoRm(x86IntRegType to, int fromgpr); + +void _eeFlushAllUnused(); +void _eeOnWriteReg(int reg, int signext); + +// totally deletes from const, xmm, and mmx entries +// if flush is 1, also flushes to memory +// if 0, only flushes if not an xmm reg (used when overwriting lower 64bits of reg) +void _deleteEEreg(int reg, int flush); + +// allocates memory on the instruction size and returns the pointer +u32* recAllocStackMem(int size, int align); + +void _vuRegsCOP22(VURegs * VU, _VURegsNum *VUregsn); + +////////////////////////////////////// +// Templates for code recompilation // +////////////////////////////////////// + +typedef void (*R5900FNPTR)(); +typedef void (*R5900FNPTR_INFO)(int info); + +#define EERECOMPILE_CODE0(fn, xmminfo) \ +void rec##fn(void) \ +{ \ + eeRecompileCode0(rec##fn##_const, rec##fn##_consts, rec##fn##_constt, rec##fn##_, xmminfo); \ +} + +#define EERECOMPILE_CODEX(codename, fn) \ +void rec##fn(void) \ +{ \ + codename(rec##fn##_const, rec##fn##_); \ +} + +// +// MMX/XMM caching helpers +// + +// rd = rs op rt +void eeRecompileCode0(R5900FNPTR constcode, R5900FNPTR_INFO constscode, R5900FNPTR_INFO consttcode, R5900FNPTR_INFO noconstcode, int xmminfo); +// rt = rs op imm16 +void eeRecompileCode1(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode); +// rd = rt op sa +void eeRecompileCode2(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode); +// rt op rs (SPECIAL) +void eeRecompileCode3(R5900FNPTR constcode, R5900FNPTR_INFO multicode); + +// +// non mmx/xmm version, slower +// +// rd = rs op rt +#define EERECOMPILE_CONSTCODE0(fn) \ +void rec##fn(void) \ +{ \ + eeRecompileCodeConst0(rec##fn##_const, rec##fn##_consts, rec##fn##_constt, rec##fn##_); \ +} \ + +// rt = rs op imm16 +#define EERECOMPILE_CONSTCODE1(fn) \ +void rec##fn(void) \ +{ \ + eeRecompileCodeConst1(rec##fn##_const, rec##fn##_); \ +} \ + +// rd = rt op sa +#define EERECOMPILE_CONSTCODE2(fn) \ +void rec##fn(void) \ +{ \ + eeRecompileCodeConst2(rec##fn##_const, rec##fn##_); \ +} \ + +// rd = rt op rs +#define EERECOMPILE_CONSTCODESPECIAL(fn, mult) \ +void rec##fn(void) \ +{ \ + eeRecompileCodeConstSPECIAL(rec##fn##_const, rec##fn##_, mult); \ +} \ + +// rd = rs op rt +void eeRecompileCodeConst0(R5900FNPTR constcode, R5900FNPTR_INFO constscode, R5900FNPTR_INFO consttcode, R5900FNPTR_INFO noconstcode); +// rt = rs op imm16 +void eeRecompileCodeConst1(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode); +// rd = rt op sa +void eeRecompileCodeConst2(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode); +// rd = rt MULT rs (SPECIAL) +void eeRecompileCodeConstSPECIAL(R5900FNPTR constcode, R5900FNPTR_INFO multicode, int MULT); + +// XMM caching helpers +#define XMMINFO_READLO 0x01 +#define XMMINFO_READHI 0x02 +#define XMMINFO_WRITELO 0x04 +#define XMMINFO_WRITEHI 0x08 +#define XMMINFO_WRITED 0x10 +#define XMMINFO_READD 0x20 +#define XMMINFO_READS 0x40 +#define XMMINFO_READT 0x80 +#define XMMINFO_READD_LO 0x100 // if set and XMMINFO_READD is set, reads only low 64 bits of D +#define XMMINFO_READACC 0x200 +#define XMMINFO_WRITEACC 0x400 + +#define CPU_SSE_XMMCACHE_START(xmminfo) \ + { \ + int info = eeRecompileCodeXMM(xmminfo); \ + +#define CPU_SSE2_XMMCACHE_START(xmminfo) \ + { \ + int info = eeRecompileCodeXMM(xmminfo); \ + +#define CPU_SSE_XMMCACHE_END \ + _clearNeededXMMregs(); \ + return; \ + } \ + +#define FPURECOMPILE_CONSTCODE(fn, xmminfo) \ +void rec##fn(void) \ +{ \ + eeFPURecompileCode(rec##fn##_xmm, R5900::Interpreter::OpcodeImpl::COP1::fn, xmminfo); \ +} + +// rd = rs op rt (all regs need to be in xmm) +int eeRecompileCodeXMM(int xmminfo); +void eeFPURecompileCode(R5900FNPTR_INFO xmmcode, R5900FNPTR fpucode, int xmminfo); + + +// For propagation of BSC stuff. +// Code implementations in ir5900tables.c +class BSCPropagate +{ +protected: + EEINST& prev; + EEINST& pinst; + +public: + BSCPropagate( EEINST& previous, EEINST& pinstance ); + + void rprop(); + +protected: + void rpropSPECIAL(); + void rpropREGIMM(); + void rpropCP0(); + void rpropCP1(); + void rpropCP2(); + void rpropMMI(); + void rpropMMI0(); + void rpropMMI1(); + void rpropMMI2(); + void rpropMMI3(); + + void rpropSetRead( int reg, int mask ); + void rpropSetFPURead( int reg, int mask ); + void rpropSetWrite( int reg, int mask ); + void rpropSetFPUWrite( int reg, int mask ); + + template< int mask > + void rpropSetRead( int reg ); + + template< int live > + void rpropSetWrite0( int reg, int mask ); + + void rpropSetFast( int write1, int read1, int read2, int mask ); + + template< int low, int hi > + void rpropSetLOHI( int write1, int read1, int read2, int mask ); + + template< int live > + void rpropSetFPUWrite0( int reg, int mask ); + +}; + +#endif // __IR5900_H__ diff --git a/pcsx2/x86/iR5900Arit.h b/pcsx2/x86/iR5900Arit.h new file mode 100644 index 0000000000..e39d9bd460 --- /dev/null +++ b/pcsx2/x86/iR5900Arit.h @@ -0,0 +1,46 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IR5900ARIT_H__ +#define __IR5900ARIT_H__ + +/********************************************************* +* Register arithmetic * +* Format: OP rd, rs, rt * +*********************************************************/ + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl +{ + void recADD( void ); + void recADDU( void ); + void recDADD( void ); + void recDADDU( void ); + void recSUB( void ); + void recSUBU( void ); + void recDSUB( void ); + void recDSUBU( void ); + void recAND( void ); + void recOR( void ); + void recXOR( void ); + void recNOR( void ); + void recSLT( void ); + void recSLTU( void ); +} } } +#endif diff --git a/pcsx2/x86/iR5900AritImm.h b/pcsx2/x86/iR5900AritImm.h new file mode 100644 index 0000000000..0590cca572 --- /dev/null +++ b/pcsx2/x86/iR5900AritImm.h @@ -0,0 +1,42 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IR5900ARITIMM_H__ +#define __IR5900ARITIMM_H__ + +/********************************************************* +* Arithmetic with immediate operand * +* Format: OP rt, rs, immediate * +*********************************************************/ +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + + void recADDI( void ); + void recADDIU( void ); + void recDADDI( void ); + void recDADDIU( void ); + void recANDI( void ); + void recORI( void ); + void recXORI( void ); + + void recSLTI( void ); + void recSLTIU( void ); +} } } + +#endif diff --git a/pcsx2/x86/iR5900Branch.h b/pcsx2/x86/iR5900Branch.h new file mode 100644 index 0000000000..32a2d0c669 --- /dev/null +++ b/pcsx2/x86/iR5900Branch.h @@ -0,0 +1,49 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IR5900BRANCH_H__ +#define __IR5900BRANCH_H__ + +/********************************************************* +* Shift arithmetic with constant shift * +* Format: OP rd, rt, sa * +*********************************************************/ + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + + void recBEQ( void ); + void recBEQL( void ); + void recBNE( void ); + void recBNEL( void ); + void recBLTZ( void ); + void recBLTZL( void ); + void recBLTZAL( void ); + void recBLTZALL( void ); + void recBGTZ( void ); + void recBGTZL( void ); + void recBLEZ( void ); + void recBLEZL( void ); + void recBGEZ( void ); + void recBGEZL( void ); + void recBGEZAL( void ); + void recBGEZALL( void ); +} } } + +#endif diff --git a/pcsx2/x86/iR5900CoissuedLoadStore.cpp b/pcsx2/x86/iR5900CoissuedLoadStore.cpp new file mode 100644 index 0000000000..a86800bb2b --- /dev/null +++ b/pcsx2/x86/iR5900CoissuedLoadStore.cpp @@ -0,0 +1,1739 @@ +/* Pcsx2 - Pc Ps2 Emulator +* Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#ifdef PCSX2_VM_COISSUE + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "iR5900LoadStore.h" +#include "ix86/ix86.h" +#include "iR5900.h" + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + +#define _Imm_co_ (*(s16*)PSM(pc)) + +int _eePrepareReg_coX(int gprreg, int num) +{ + int mmreg = _eePrepareReg(gprreg); + + if( (mmreg&MEM_MMXTAG) && num == 7 ) { + if( mmxregs[mmreg&0xf].mode & MODE_WRITE ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[gprreg], mmreg&0xf); + mmxregs[mmreg&0xf].mode &= ~MODE_WRITE; + mmxregs[mmreg&0xf].needed = 0; + } + } + + return mmreg; +} + +void recLoad32_co(u32 bit, u32 sign) +{ + int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); + int mmreg1 = -1, mmreg2 = -1; + +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + int ineax = 0; + u32 written = 0; + + _eeOnLoadWrite(_Rt_); + _eeOnLoadWrite(nextrt); + + if( bit == 32 ) { + mmreg1 = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); + if( mmreg1 >= 0 ) mmreg1 |= MEM_MMXTAG; + else mmreg1 = EBX; + + mmreg2 = _allocCheckGPRtoMMX(g_pCurInstInfo+1, nextrt, MODE_WRITE); + if( mmreg2 >= 0 ) mmreg2 |= MEM_MMXTAG; + else mmreg2 = EAX; + } + else { + _deleteEEreg(_Rt_, 0); + _deleteEEreg(nextrt, 0); + mmreg1 = EBX; + mmreg2 = EAX; + } + + // do const processing + switch(bit) { + case 8: + if( recMemConstRead8(mmreg1, g_cpuConstRegs[_Rs_].UL[0]+_Imm_, sign) ) { + if( mmreg1&MEM_MMXTAG ) mmxregs[mmreg1&0xf].inuse = 0; + if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); + written = 1; + } + ineax = recMemConstRead8(mmreg2, g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_, sign); + break; + case 16: + if( recMemConstRead16(mmreg1, g_cpuConstRegs[_Rs_].UL[0]+_Imm_, sign) ) { + if( mmreg1&MEM_MMXTAG ) mmxregs[mmreg1&0xf].inuse = 0; + if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); + written = 1; + } + ineax = recMemConstRead16(mmreg2, g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_, sign); + break; + case 32: + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); + if( recMemConstRead32(mmreg1, g_cpuConstRegs[_Rs_].UL[0]+_Imm_) ) { + if( mmreg1&MEM_MMXTAG ) mmxregs[mmreg1&0xf].inuse = 0; + if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); + written = 1; + } + ineax = recMemConstRead32(mmreg2, g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_); + break; + } + + if( !written && _Rt_ ) { + if( mmreg1&MEM_MMXTAG ) { + assert( mmxregs[mmreg1&0xf].mode & MODE_WRITE ); + if( sign ) _signExtendGPRtoMMX(mmreg1&0xf, _Rt_, 32-bit); + else if( bit < 32 ) PSRLDItoR(mmreg1&0xf, 32-bit); + } + else recTransferX86ToReg(mmreg1, _Rt_, sign); + } + if( nextrt ) { + g_pCurInstInfo++; + if( !ineax && (mmreg2 & MEM_MMXTAG) ) { + assert( mmxregs[mmreg2&0xf].mode & MODE_WRITE ); + if( sign ) _signExtendGPRtoMMX(mmreg2&0xf, nextrt, 32-bit); + else if( bit < 32 ) PSRLDItoR(mmreg2&0xf, 32-bit); + } + else { + if( mmreg2&MEM_MMXTAG ) mmxregs[mmreg2&0xf].inuse = 0; + recTransferX86ToReg(mmreg2, nextrt, sign); + } + g_pCurInstInfo--; + } + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + assert( !REC_FORCEMMX ); + + _eeOnLoadWrite(_Rt_); + _eeOnLoadWrite(nextrt); + _deleteEEreg(_Rt_, 0); + _deleteEEreg(nextrt, 0); + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); + + switch(bit) { + case 8: + if( sign ) { + MOVSX32Rm8toROffset(EBX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOVSX32Rm8toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + else { + MOVZX32Rm8toROffset(EBX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOVZX32Rm8toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + break; + case 16: + if( sign ) { + MOVSX32Rm16toROffset(EBX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOVSX32Rm16toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + else { + MOVZX32Rm16toROffset(EBX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOVZX32Rm16toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + break; + case 32: + MOV32RmtoROffset(EBX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + break; + } + + if ( _Rt_ ) recTransferX86ToReg(EBX, _Rt_, sign); + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + switch(bit) { + case 8: + MOV32RtoM((u32)&s_tempaddr, ECX); + CALLFunc( (int)recMemRead8 ); + if( sign ) MOVSX32R8toR(EAX, EAX); + MOV32MtoR(ECX, (u32)&s_tempaddr); + if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); + + ADD32ItoR(ECX, _Imm_co_-_Imm_); + CALLFunc( (int)recMemRead8 ); + if( sign ) MOVSX32R8toR(EAX, EAX); + break; + case 16: + MOV32RtoM((u32)&s_tempaddr, ECX); + CALLFunc( (int)recMemRead16 ); + if( sign ) MOVSX32R16toR(EAX, EAX); + MOV32MtoR(ECX, (u32)&s_tempaddr); + if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); + + ADD32ItoR(ECX, _Imm_co_-_Imm_); + CALLFunc( (int)recMemRead16 ); + if( sign ) MOVSX32R16toR(EAX, EAX); + break; + case 32: + MOV32RtoM((u32)&s_tempaddr, ECX); + iMemRead32Check(); + CALLFunc( (int)recMemRead32 ); + MOV32MtoR(ECX, (u32)&s_tempaddr); + if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); + + ADD32ItoR(ECX, _Imm_co_-_Imm_); + iMemRead32Check(); + CALLFunc( (int)recMemRead32 ); + break; + } + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[2]); + } + + if( nextrt ) { + g_pCurInstInfo++; + recTransferX86ToReg(EAX, nextrt, sign); + g_pCurInstInfo--; + } + } +} + +void recLB_co( void ) { recLoad32_co(8, 1); } +void recLBU_co( void ) { recLoad32_co(8, 0); } +void recLH_co( void ) { recLoad32_co(16, 1); } +void recLHU_co( void ) { recLoad32_co(16, 0); } +void recLW_co( void ) { recLoad32_co(32, 1); } +void recLWU_co( void ) { recLoad32_co(32, 0); } +void recLDL_co(void) { recLoad64(_Imm_-7, 0); } +void recLDR_co(void) { recLoad64(_Imm_, 0); } +void recLWL_co(void) { recLoad32(32, _Imm_-3, 1); } // paired with LWR +void recLWR_co(void) { recLoad32(32, _Imm_, 1); } // paired with LWL + +void recLD_co( void ) +{ +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + recLD(); + g_pCurInstInfo++; + cpuRegs.code = *(u32*)PSM(pc); + recLD(); + g_pCurInstInfo--; // incremented later + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + int mmreg1 = -1, mmreg2 = -1, t0reg = -1, t1reg = -1; + int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); + + if( _Rt_ ) _eeOnWriteReg(_Rt_, 0); + if( nextrt ) _eeOnWriteReg(nextrt, 0); + + if( _Rt_ ) { + mmreg1 = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); + if( mmreg1 < 0 ) { + mmreg1 = _allocCheckGPRtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE|MODE_READ); + if( mmreg1 >= 0 ) mmreg1 |= 0x8000; + } + + if( mmreg1 < 0 && _hasFreeMMXreg() ) mmreg1 = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_WRITE); + } + + if( nextrt ) { + mmreg2 = _allocCheckGPRtoMMX(g_pCurInstInfo+1, nextrt, MODE_WRITE); + if( mmreg2 < 0 ) { + mmreg2 = _allocCheckGPRtoXMM(g_pCurInstInfo+1, nextrt, MODE_WRITE|MODE_READ); + if( mmreg2 >= 0 ) mmreg2 |= 0x8000; + } + + if( mmreg2 < 0 && _hasFreeMMXreg() ) mmreg2 = _allocMMXreg(-1, MMX_GPR+nextrt, MODE_WRITE); + } + + if( mmreg1 < 0 || mmreg2 < 0 ) { + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + if( mmreg1 < 0 && mmreg2 < 0 && _hasFreeMMXreg() ) { + t1reg = _allocMMXreg(-1, MMX_TEMP, 0); + } + else t1reg = t0reg; + } + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 1, 0); + + if( mmreg1 >= 0 ) { + if( mmreg1 & 0x8000 ) SSE_MOVLPSRmtoROffset(mmreg1&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); + else MOVQRmtoROffset(mmreg1, ECX, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + if( _Rt_ ) { + MOVQRmtoROffset(t0reg, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], t0reg); + } + } + + if( mmreg2 >= 0 ) { + if( mmreg2 & 0x8000 ) SSE_MOVLPSRmtoROffset(mmreg2&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + else MOVQRmtoROffset(mmreg2, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + else { + if( nextrt ) { + MOVQRmtoROffset(t1reg, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + MOVQRtoM((int)&cpuRegs.GPR.r[ nextrt ].UL[ 0 ], t1reg); + } + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + MOV32RtoM((u32)&s_tempaddr, ECX); + + if( mmreg1 >= 0 ) { + PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + CALLFunc( (int)recMemRead64 ); + + if( mmreg1 & 0x8000 ) SSE_MOVLPS_M64_to_XMM(mmreg1&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); + else MOVQMtoR(mmreg1, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); + } + else { + if( _Rt_ ) { + _deleteEEreg(_Rt_, 0); + PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + } + else PUSH32I( (int)&retValues[0] ); + + CALLFunc( (int)recMemRead64 ); + } + + MOV32MtoR(ECX, (u32)&s_tempaddr); + + if( mmreg2 >= 0 ) { + MOV32ItoRmOffset(ESP, (int)&cpuRegs.GPR.r[ nextrt ].UD[ 0 ], 0 ); + ADD32ItoR(ECX, _Imm_co_-_Imm_); + CALLFunc( (int)recMemRead64 ); + + if( mmreg2 & 0x8000 ) SSE_MOVLPS_M64_to_XMM(mmreg2&0xf, (int)&cpuRegs.GPR.r[ nextrt ].UD[ 0 ]); + else MOVQMtoR(mmreg2, (int)&cpuRegs.GPR.r[ nextrt ].UD[ 0 ]); + } + else { + if( nextrt ) { + _deleteEEreg(nextrt, 0); + MOV32ItoRmOffset(ESP, (int)&cpuRegs.GPR.r[ nextrt ].UD[ 0 ], 0 ); + } + else MOV32ItoRmOffset(ESP, (int)&retValues[0], 0 ); + + ADD32ItoR(ECX, _Imm_co_-_Imm_); + CALLFunc( (int)recMemRead64 ); + } + + ADD32ItoR(ESP, 4); + + if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); + else x86SetJ8A(j8Ptr[2]); + } + + if( mmreg1 < 0 || mmreg2 < 0 || !(mmreg1&0x8000) || !(mmreg2&0x8000) ) SetMMXstate(); + + if( t0reg >= 0 ) _freeMMXreg(t0reg); + if( t0reg != t1reg && t1reg >= 0 ) _freeMMXreg(t1reg); + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + } +} + +void recLD_coX( int num ) +{ + int i; + int mmreg = -1; + int mmregs[XMMREGS]; + int nextrts[XMMREGS]; + + assert( num > 1 && num < XMMREGS ); + +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + recLD(); + + for(i = 0; i < num; ++i) { + g_pCurInstInfo++; + cpuRegs.code = *(u32*)PSM(pc+i*4); + recLD(); + } + + g_pCurInstInfo -= num; // incremented later + } + else +#endif + { + int dohw; + int mmregS = _eePrepareReg_coX(_Rs_, num); + + if( _Rt_ ) _eeOnWriteReg(_Rt_, 0); + for(i = 0; i < num; ++i) { + nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); + _eeOnWriteReg(nextrts[i], 0); + } + + if( _Rt_ ) { + mmreg = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); + if( mmreg < 0 ) { + mmreg = _allocCheckGPRtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE|MODE_READ); + if( mmreg >= 0 ) mmreg |= MEM_XMMTAG; + else mmreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_WRITE)|MEM_MMXTAG; + } + else mmreg |= MEM_MMXTAG; + } + + for(i = 0; i < num; ++i) { + mmregs[i] = _allocCheckGPRtoMMX(g_pCurInstInfo+i+1, nextrts[i], MODE_WRITE); + if( mmregs[i] < 0 ) { + mmregs[i] = _allocCheckGPRtoXMM(g_pCurInstInfo+i+1, nextrts[i], MODE_WRITE|MODE_READ); + if( mmregs[i] >= 0 ) mmregs[i] |= MEM_XMMTAG; + else mmregs[i] = _allocMMXreg(-1, MMX_GPR+nextrts[i], MODE_WRITE)|MEM_MMXTAG; + } + else mmregs[i] |= MEM_MMXTAG; + } + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregS, 1, 0); + + if( mmreg >= 0 ) { + if( mmreg & MEM_XMMTAG ) SSE_MOVLPSRmtoROffset(mmreg&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); + else MOVQRmtoROffset(mmreg&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); + } + + for(i = 0; i < num; ++i) { + u32 off = PS2MEM_BASE_+s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_; + + if( mmregs[i] >= 0 ) { + if( mmregs[i] & MEM_XMMTAG ) SSE_MOVLPSRmtoROffset(mmregs[i]&0xf, ECX, off); + else MOVQRmtoROffset(mmregs[i]&0xf, ECX, off); + } + } + + if( dohw ) { + if( (s_bCachingMem & 2) || num > 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + MOV32RtoM((u32)&s_tempaddr, ECX); + + if( mmreg >= 0 ) { + PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + CALLFunc( (int)recMemRead64 ); + + if( mmreg & MEM_XMMTAG ) SSE_MOVLPS_M64_to_XMM(mmreg&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); + else MOVQMtoR(mmreg&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); + } + else { + PUSH32I( (int)&retValues[0] ); + CALLFunc( (int)recMemRead64 ); + } + + for(i = 0; i < num; ++i ) { + MOV32MtoR(ECX, (u32)&s_tempaddr); + ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-_Imm_); + + if( mmregs[i] >= 0 ) { + MOV32ItoRmOffset(ESP, (int)&cpuRegs.GPR.r[nextrts[i]].UL[0], 0); + CALLFunc( (int)recMemRead64 ); + + if( mmregs[i] & MEM_XMMTAG ) SSE_MOVLPS_M64_to_XMM(mmregs[i]&0xf, (int)&cpuRegs.GPR.r[ nextrts[i] ].UD[ 0 ]); + else MOVQMtoR(mmregs[i]&0xf, (int)&cpuRegs.GPR.r[ nextrts[i] ].UD[ 0 ]); + } + else { + MOV32ItoRmOffset(ESP, (int)&retValues[0], 0); + CALLFunc( (int)recMemRead64 ); + } + } + + ADD32ItoR(ESP, 4); + + if( (s_bCachingMem & 2) || num > 2 ) x86SetJ32A(j32Ptr[4]); + else x86SetJ8A(j8Ptr[2]); + } + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + } +} + +void recLQ_co( void ) +{ +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + recLQ(); + g_pCurInstInfo++; + cpuRegs.code = *(u32*)PSM(pc); + recLQ(); + g_pCurInstInfo--; // incremented later + } + else +#endif + { + int dohw; + int t0reg = -1; + int mmregs = _eePrepareReg(_Rs_); + + int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); + int mmreg1 = -1, mmreg2 = -1; + + if( _Rt_ ) { + _eeOnWriteReg(_Rt_, 0); + + if( _hasFreeMMXreg() ) { + mmreg1 = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); + if( mmreg1 >= 0 ) { + mmreg1 |= MEM_MMXTAG; + if( t0reg < 0 ) t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + } + } + else _deleteMMXreg(MMX_GPR+_Rt_, 2); + + if( mmreg1 < 0 ) { + mmreg1 = _allocGPRtoXMMreg(-1, _Rt_, MODE_WRITE); + if( mmreg1 >= 0 ) mmreg1 |= MEM_XMMTAG; + } + } + + if( nextrt ) { + _eeOnWriteReg(nextrt, 0); + + if( _hasFreeMMXreg() ) { + mmreg2 = _allocCheckGPRtoMMX(g_pCurInstInfo+1, nextrt, MODE_WRITE); + if( mmreg2 >= 0 ) { + mmreg2 |= MEM_MMXTAG; + if( t0reg < 0 ) t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + } + } + else _deleteMMXreg(MMX_GPR+nextrt, 2); + + if( mmreg2 < 0 ) { + mmreg2 = _allocGPRtoXMMreg(-1, nextrt, MODE_WRITE); + if( mmreg2 >= 0 ) mmreg2 |= MEM_XMMTAG; + } + } + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 0); + + if( _Rt_ ) { + if( mmreg1 >= 0 && (mmreg1 & MEM_MMXTAG) ) { + MOVQRmtoROffset(t0reg, ECX, PS2MEM_BASE_+s_nAddMemOffset+8); + MOVQRmtoROffset(mmreg1&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rt_].UL[2], t0reg); + } + else if( mmreg1 >= 0 && (mmreg1 & MEM_XMMTAG) ) { + SSEX_MOVDQARmtoROffset(mmreg1&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + _recMove128RmOffsettoM((u32)&cpuRegs.GPR.r[_Rt_].UL[0], PS2MEM_BASE_+s_nAddMemOffset); + } + } + + if( nextrt ) { + if( mmreg2 >= 0 && (mmreg2 & MEM_MMXTAG) ) { + MOVQRmtoROffset(t0reg, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_+8); + MOVQRmtoROffset(mmreg2&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + MOVQRtoM((u32)&cpuRegs.GPR.r[nextrt].UL[2], t0reg); + } + else if( mmreg2 >= 0 && (mmreg2 & MEM_XMMTAG) ) { + SSEX_MOVDQARmtoROffset(mmreg2&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + else { + _recMove128RmOffsettoM((u32)&cpuRegs.GPR.r[nextrt].UL[0], PS2MEM_BASE_+s_nAddMemOffset); + } + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + MOV32RtoM((u32)&s_tempaddr, ECX); + if( _Rt_ ) PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + else PUSH32I( (int)&retValues[0] ); + CALLFunc( (int)recMemRead128 ); + + MOV32MtoR(ECX, (u32)&s_tempaddr); + if( nextrt ) MOV32ItoRmOffset(ESP, (int)&cpuRegs.GPR.r[nextrt].UL[0], 0); + else MOV32ItoRmOffset(ESP, (int)&retValues[0], 0); + ADD32ItoR(ECX, _Imm_co_-_Imm_); + CALLFunc( (int)recMemRead128 ); + + if( _Rt_) { + if( mmreg1 >= 0 && (mmreg1 & MEM_MMXTAG) ) MOVQMtoR(mmreg1&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + else if( mmreg1 >= 0 && (mmreg1 & MEM_XMMTAG) ) SSEX_MOVDQA_M128_to_XMM(mmreg1&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + } + if( nextrt ) { + if( mmreg2 >= 0 && (mmreg2 & MEM_MMXTAG) ) MOVQMtoR(mmreg2&0xf, (int)&cpuRegs.GPR.r[ nextrt ].UL[ 0 ]); + else if( mmreg2 >= 0 && (mmreg2 & MEM_XMMTAG) ) SSEX_MOVDQA_M128_to_XMM(mmreg2&0xf, (int)&cpuRegs.GPR.r[ nextrt ].UL[ 0 ] ); + } + ADD32ItoR(ESP, 4); + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[2]); + } + + if( t0reg >= 0 ) _freeMMXreg(t0reg); + } +} + +// coissues more than 2 LQs +void recLQ_coX(int num) +{ + int i; + int mmreg = -1; + int mmregs[XMMREGS]; + int nextrts[XMMREGS]; + + assert( num > 1 && num < XMMREGS ); + +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + recLQ(); + + for(i = 0; i < num; ++i) { + g_pCurInstInfo++; + cpuRegs.code = *(u32*)PSM(pc+i*4); + recLQ(); + } + + g_pCurInstInfo -= num; // incremented later + } + else +#endif + { + int dohw; + int mmregS = _eePrepareReg_coX(_Rs_, num); + + + if( _Rt_ ) _deleteMMXreg(MMX_GPR+_Rt_, 2); + for(i = 0; i < num; ++i) { + nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); + if( nextrts[i] ) _deleteMMXreg(MMX_GPR+nextrts[i], 2); + } + + if( _Rt_ ) { + _eeOnWriteReg(_Rt_, 0); + mmreg = _allocGPRtoXMMreg(-1, _Rt_, MODE_WRITE); + } + + for(i = 0; i < num; ++i) { + if( nextrts[i] ) { + _eeOnWriteReg(nextrts[i], 0); + mmregs[i] = _allocGPRtoXMMreg(-1, nextrts[i], MODE_WRITE); + } + else mmregs[i] = -1; + } + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregS, 2, 1); + + if( _Rt_ ) SSEX_MOVDQARmtoROffset(mmreg, ECX, PS2MEM_BASE_+s_nAddMemOffset); + + for(i = 0; i < num; ++i) { + u32 off = s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_; + if( nextrts[i] ) SSEX_MOVDQARmtoROffset(mmregs[i], ECX, PS2MEM_BASE_+off&~0xf); + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + MOV32RtoM((u32)&s_tempaddr, ECX); + + if( _Rt_ ) PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + else PUSH32I( (int)&retValues[0] ); + CALLFunc( (int)recMemRead128 ); + if( _Rt_) SSEX_MOVDQA_M128_to_XMM(mmreg, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + + for(i = 0; i < num; ++i) { + MOV32MtoR(ECX, (u32)&s_tempaddr); + ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-_Imm_); + + if( nextrts[i] ) MOV32ItoRmOffset(ESP, (int)&cpuRegs.GPR.r[nextrts[i]].UL[0], 0); + else MOV32ItoRmOffset(ESP, (int)&retValues[0], 0); + CALLFunc( (int)recMemRead128 ); + if( nextrts[i] ) SSEX_MOVDQA_M128_to_XMM(mmregs[i], (int)&cpuRegs.GPR.r[ nextrts[i] ].UL[ 0 ] ); + } + + ADD32ItoR(ESP, 4); + + if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); + else x86SetJ8A(j8Ptr[2]); + } + } +} + +void recStore_co(int bit, int align) +{ + int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); + +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + u32 addr = g_cpuConstRegs[_Rs_].UL[0]+_Imm_; + u32 coaddr = g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_; + int mmreg, t0reg = -1, mmreg2; + int doclear = 0; + + switch(bit) { + case 8: + if( GPR_IS_CONST1(_Rt_) ) doclear |= recMemConstWrite8(addr, MEM_EECONSTTAG|(_Rt_<<16)); + else { + _eeMoveGPRtoR(EAX, _Rt_); + doclear |= recMemConstWrite8(addr, EAX); + } + if( GPR_IS_CONST1(nextrt) ) doclear |= recMemConstWrite8(coaddr, MEM_EECONSTTAG|(nextrt<<16)); + else { + _eeMoveGPRtoR(EAX, nextrt); + doclear |= recMemConstWrite8(coaddr, EAX); + } + break; + case 16: + assert( (addr)%2 == 0 ); + assert( (coaddr)%2 == 0 ); + + if( GPR_IS_CONST1(_Rt_) ) doclear |= recMemConstWrite16(addr, MEM_EECONSTTAG|(_Rt_<<16)); + else { + _eeMoveGPRtoR(EAX, _Rt_); + doclear |= recMemConstWrite16(addr, EAX); + } + + if( GPR_IS_CONST1(nextrt) ) doclear |= recMemConstWrite16(coaddr, MEM_EECONSTTAG|(nextrt<<16)); + else { + _eeMoveGPRtoR(EAX, nextrt); + doclear |= recMemConstWrite16(coaddr, EAX); + } + break; + case 32: + assert( (addr)%4 == 0 ); + if( GPR_IS_CONST1(_Rt_) ) doclear = recMemConstWrite32(addr, MEM_EECONSTTAG|(_Rt_<<16)); + else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ)) >= 0 ) { + doclear = recMemConstWrite32(addr, mmreg|MEM_XMMTAG|(_Rt_<<16)); + } + else if( (mmreg = _checkMMXreg(MMX_GPR+_Rt_, MODE_READ)) >= 0 ) { + doclear = recMemConstWrite32(addr, mmreg|MEM_MMXTAG|(_Rt_<<16)); + } + else { + _eeMoveGPRtoR(EAX, _Rt_); + doclear = recMemConstWrite32(addr, EAX); + } + + if( GPR_IS_CONST1(nextrt) ) doclear |= recMemConstWrite32(coaddr, MEM_EECONSTTAG|(nextrt<<16)); + else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, nextrt, MODE_READ)) >= 0 ) { + doclear |= recMemConstWrite32(coaddr, mmreg|MEM_XMMTAG|(nextrt<<16)); + } + else if( (mmreg = _checkMMXreg(MMX_GPR+nextrt, MODE_READ)) >= 0 ) { + doclear |= recMemConstWrite32(coaddr, mmreg|MEM_MMXTAG|(nextrt<<16)); + } + else { + _eeMoveGPRtoR(EAX, nextrt); + doclear |= recMemConstWrite32(coaddr, EAX); + } + + break; + case 64: + { + int mask = align ? ~7 : ~0; + //assert( (addr)%8 == 0 ); + + if( GPR_IS_CONST1(_Rt_) ) mmreg = MEM_EECONSTTAG|(_Rt_<<16); + else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ)) >= 0 ) { + mmreg |= MEM_XMMTAG|(_Rt_<<16); + } + else mmreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ)|MEM_MMXTAG|(_Rt_<<16); + + if( GPR_IS_CONST1(nextrt) ) mmreg2 = MEM_EECONSTTAG|(nextrt<<16); + else if( (mmreg2 = _checkXMMreg(XMMTYPE_GPRREG, nextrt, MODE_READ)) >= 0 ) { + mmreg2 |= MEM_XMMTAG|(nextrt<<16); + } + else mmreg2 = _allocMMXreg(-1, MMX_GPR+nextrt, MODE_READ)|MEM_MMXTAG|(nextrt<<16); + + doclear = recMemConstWrite64((addr)&mask, mmreg); + doclear |= recMemConstWrite64((coaddr)&mask, mmreg2); + doclear <<= 1; + break; + } + case 128: + assert( (addr)%16 == 0 ); + + mmreg = _eePrepConstWrite128(_Rt_); + mmreg2 = _eePrepConstWrite128(nextrt); + doclear = recMemConstWrite128((addr)&~15, mmreg); + doclear |= recMemConstWrite128((coaddr)&~15, mmreg2); + doclear <<= 2; + break; + } + + if( doclear ) { + u8* ptr; + CMP32ItoM((u32)&maxrecmem, g_cpuConstRegs[_Rs_].UL[0]+(_Imm_ < _Imm_co_ ? _Imm_ : _Imm_co_)); + ptr = JB8(0); + recMemConstClear((addr)&~(doclear*4-1), doclear); + recMemConstClear((coaddr)&~(doclear*4-1), doclear); + x86SetJ8A(ptr); + } + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + int off = _Imm_co_-_Imm_; + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, align ? bit/64 : 0, bit==128); + + recStore_raw(g_pCurInstInfo, bit, EAX, _Rt_, s_nAddMemOffset); + recStore_raw(g_pCurInstInfo+1, bit, EBX, nextrt, s_nAddMemOffset+off); + + // clear the writes, do only one camera (with the lowest addr) + if( off < 0 ) ADD32ItoR(ECX, s_nAddMemOffset+off); + else if( s_nAddMemOffset ) ADD32ItoR(ECX, s_nAddMemOffset); + CMP32MtoR(ECX, (u32)&maxrecmem); + + if( s_bCachingMem & 2 ) j32Ptr[5] = JAE32(0); + else j8Ptr[1] = JAE8(0); + + MOV32RtoM((u32)&s_tempaddr, ECX); + + if( bit < 32 ) AND8ItoR(ECX, 0xfc); + if( bit <= 32 ) CALLFunc((uptr)recWriteMemClear32); + else if( bit == 64 ) CALLFunc((uptr)recWriteMemClear64); + else CALLFunc((uptr)recWriteMemClear128); + + MOV32MtoR(ECX, (u32)&s_tempaddr); + if( off < 0 ) ADD32ItoR(ECX, -off); + else ADD32ItoR(ECX, off); + + if( bit < 32 ) AND8ItoR(ECX, 0xfc); + if( bit <= 32 ) CALLFunc((uptr)recWriteMemClear32); + else if( bit == 64 ) CALLFunc((uptr)recWriteMemClear64); + else CALLFunc((uptr)recWriteMemClear128); + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + MOV32RtoM((u32)&s_tempaddr, ECX); + recStore_call(bit, _Rt_, s_nAddMemOffset); + MOV32MtoR(ECX, (u32)&s_tempaddr); + recStore_call(bit, nextrt, s_nAddMemOffset+_Imm_co_-_Imm_); + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[2]); + } + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[5]); + else x86SetJ8(j8Ptr[1]); + } + + _clearNeededMMXregs(); // needed since allocing + _clearNeededXMMregs(); // needed since allocing +} + +void recSB_co( void ) { recStore_co(8, 1); } +void recSH_co( void ) { recStore_co(16, 1); } +void recSW_co( void ) { recStore_co(32, 1); } +void recSD_co( void ) { recStore_co(64, 1); } +void recSQ_co( void ) { recStore_co(128, 1); } + +void recSWL_co(void) { recStore(32, _Imm_-3, 0); } +void recSWR_co(void) { recStore(32, _Imm_, 0); } +void recSDL_co(void) { recStore(64, _Imm_-7, 0); } +void recSDR_co(void) { recStore(64, _Imm_, 0); } + +// coissues more than 2 SDs +void recSD_coX(int num, int align) +{ + int i; + int mmreg = -1; + int nextrts[XMMREGS]; + u32 mask = align ? ~7 : ~0; + + assert( num > 1 && num < XMMREGS ); + + for(i = 0; i < num; ++i) { + nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); + } + +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + int minimm = _Imm_; + int t0reg = -1; + int doclear = 0; + + if( GPR_IS_CONST1(_Rt_) ) mmreg = MEM_EECONSTTAG|(_Rt_<<16); + else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ)) >= 0 ) { + mmreg |= MEM_XMMTAG|(_Rt_<<16); + } + else mmreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ)|MEM_MMXTAG|(_Rt_<<16); + doclear |= recMemConstWrite64((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&mask, mmreg); + + for(i = 0; i < num; ++i) { + int imm = (*(s16*)PSM(pc+i*4)); + if( minimm > imm ) minimm = imm; + + if( GPR_IS_CONST1(nextrts[i]) ) mmreg = MEM_EECONSTTAG|(nextrts[i]<<16); + else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, nextrts[i], MODE_READ)) >= 0 ) { + mmreg |= MEM_XMMTAG|(nextrts[i]<<16); + } + else mmreg = _allocMMXreg(-1, MMX_GPR+nextrts[i], MODE_READ)|MEM_MMXTAG|(nextrts[i]<<16); + doclear |= recMemConstWrite64((g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4)))&mask, mmreg); + } + + if( doclear ) { + u32* ptr; + CMP32ItoM((u32)&maxrecmem, g_cpuConstRegs[_Rs_].UL[0]+minimm); + ptr = JB32(0); + recMemConstClear((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~7, 4); + + for(i = 0; i < num; ++i) { + recMemConstClear((g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4)))&~7, 2); + } + x86SetJ32A(ptr); + } + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg_coX(_Rs_, num); + int minoff = 0; + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, align, 1); + + recStore_raw(g_pCurInstInfo, 64, EAX, _Rt_, s_nAddMemOffset); + + for(i = 0; i < num; ++i) { + recStore_raw(g_pCurInstInfo+i+1, 64, EAX, nextrts[i], s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); + } + + // clear the writes + minoff = _Imm_; + for(i = 0; i < num; ++i) { + if( minoff > (*(s16*)PSM(pc+i*4)) ) minoff = (*(s16*)PSM(pc+i*4)); + } + + if( s_nAddMemOffset || minoff != _Imm_ ) ADD32ItoR(ECX, s_nAddMemOffset+minoff-_Imm_); + CMP32MtoR(ECX, (u32)&maxrecmem); + if( s_bCachingMem & 2 ) j32Ptr[5] = JAE32(0); + else j8Ptr[1] = JAE8(0); + + MOV32RtoM((u32)&s_tempaddr, ECX); + if( minoff != _Imm_ ) ADD32ItoR(ECX, _Imm_-minoff); + CALLFunc((uptr)recWriteMemClear64); + + for(i = 0; i < num; ++i) { + MOV32MtoR(ECX, (u32)&s_tempaddr); + if( minoff != (*(s16*)PSM(pc+i*4)) ) ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-minoff); + CALLFunc((uptr)recWriteMemClear64); + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + MOV32RtoM((u32)&s_tempaddr, ECX); + recStore_call(64, _Rt_, s_nAddMemOffset); + for(i = 0; i < num; ++i) { + MOV32MtoR(ECX, (u32)&s_tempaddr); + recStore_call(64, nextrts[i], s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); + } + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[2]); + } + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[5]); + else x86SetJ8(j8Ptr[1]); + + _clearNeededMMXregs(); // needed since allocing + _clearNeededXMMregs(); // needed since allocing + } +} + +// coissues more than 2 SQs +void recSQ_coX(int num) +{ + int i; + int mmreg = -1; + int nextrts[XMMREGS]; + + assert( num > 1 && num < XMMREGS ); + + for(i = 0; i < num; ++i) { + nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); + } + +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + int minimm = _Imm_; + int t0reg = -1; + int doclear = 0; + + mmreg = _eePrepConstWrite128(_Rt_); + doclear |= recMemConstWrite128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, mmreg); + + for(i = 0; i < num; ++i) { + int imm = (*(s16*)PSM(pc+i*4)); + if( minimm > imm ) minimm = imm; + + mmreg = _eePrepConstWrite128(nextrts[i]); + doclear |= recMemConstWrite128((g_cpuConstRegs[_Rs_].UL[0]+imm)&~15, mmreg); + } + + if( doclear ) { + u32* ptr; + CMP32ItoM((u32)&maxrecmem, g_cpuConstRegs[_Rs_].UL[0]+minimm); + ptr = JB32(0); + recMemConstClear((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, 4); + + for(i = 0; i < num; ++i) { + recMemConstClear((g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4)))&~15, 4); + } + x86SetJ32A(ptr); + } + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg_coX(_Rs_, num); + int minoff = 0; + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 1); + + recStore_raw(g_pCurInstInfo, 128, EAX, _Rt_, s_nAddMemOffset); + + for(i = 0; i < num; ++i) { + recStore_raw(g_pCurInstInfo+i+1, 128, EAX, nextrts[i], s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); + } + + // clear the writes + minoff = _Imm_; + for(i = 0; i < num; ++i) { + if( minoff > (*(s16*)PSM(pc+i*4)) ) minoff = (*(s16*)PSM(pc+i*4)); + } + + if( s_nAddMemOffset || minoff != _Imm_ ) ADD32ItoR(ECX, s_nAddMemOffset+minoff-_Imm_); + CMP32MtoR(ECX, (u32)&maxrecmem); + if( s_bCachingMem & 2 ) j32Ptr[5] = JAE32(0); + else j8Ptr[1] = JAE8(0); + + MOV32RtoM((u32)&s_tempaddr, ECX); + if( minoff != _Imm_ ) ADD32ItoR(ECX, _Imm_-minoff); + CALLFunc((uptr)recWriteMemClear128); + + for(i = 0; i < num; ++i) { + MOV32MtoR(ECX, (u32)&s_tempaddr); + if( minoff != (*(s16*)PSM(pc+i*4)) ) ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-minoff); + CALLFunc((uptr)recWriteMemClear128); + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + MOV32RtoM((u32)&s_tempaddr, ECX); + recStore_call(128, _Rt_, s_nAddMemOffset); + for(i = 0; i < num; ++i) { + MOV32MtoR(ECX, (u32)&s_tempaddr); + recStore_call(128, nextrts[i], s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); + } + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[2]); + } + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[5]); + else x86SetJ8(j8Ptr[1]); + + _clearNeededMMXregs(); // needed since allocing + _clearNeededXMMregs(); // needed since allocing + } +} + +void recLWC1_co( void ) +{ + int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); + +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + u32 written = 0; + int ineax, mmreg1, mmreg2; + + mmreg1 = _allocCheckFPUtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE); + if( mmreg1 >= 0 ) mmreg1 |= MEM_XMMTAG; + else mmreg1 = EBX; + + mmreg2 = _allocCheckFPUtoXMM(g_pCurInstInfo+1, nextrt, MODE_WRITE); + if( mmreg2 >= 0 ) mmreg2 |= MEM_XMMTAG; + else mmreg2 = EAX; + + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); + if( recMemConstRead32(mmreg1, g_cpuConstRegs[_Rs_].UL[0]+_Imm_) ) { + if( mmreg1&MEM_XMMTAG ) xmmregs[mmreg1&0xf].inuse = 0; + MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EBX ); + written = 1; + } + ineax = recMemConstRead32(mmreg2, g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_); + + if( !written ) { + if( !(mmreg1&MEM_XMMTAG) ) MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EBX ); + } + + if( ineax || !(mmreg2 & MEM_XMMTAG) ) { + if( mmreg2&MEM_XMMTAG ) xmmregs[mmreg2&0xf].inuse = 0; + MOV32RtoM( (int)&fpuRegs.fpr[ nextrt ].UL, EAX ); + } + } + else +#endif + { + int dohw; + int regt, regtnext; + int mmregs = _eePrepareReg(_Rs_); + + _deleteMMXreg(MMX_FPU+_Rt_, 2); + regt = _allocCheckFPUtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE); + regtnext = _allocCheckFPUtoXMM(g_pCurInstInfo+1, nextrt, MODE_WRITE); + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); + + if( regt >= 0 ) { + SSEX_MOVD_RmOffset_to_XMM(regt, ECX, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); + } + + if( regtnext >= 0 ) { + SSEX_MOVD_RmOffset_to_XMM(regtnext, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + else { + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + MOV32RtoM( (int)&fpuRegs.fpr[ nextrt ].UL, EAX ); + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + PUSH32R(ECX); + CALLFunc( (int)recMemRead32 ); + POP32R(ECX); + + if( regt >= 0 ) { + SSE2_MOVD_R_to_XMM(regt, EAX); + } + else { + MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); + } + + ADD32ItoR(ECX, _Imm_co_-_Imm_); + CALLFunc( (int)recMemRead32 ); + + if( regtnext >= 0 ) { + SSE2_MOVD_R_to_XMM(regtnext, EAX); + } + else { + MOV32RtoM( (int)&fpuRegs.fpr[ nextrt ].UL, EAX ); + } + + if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); + else x86SetJ8A(j8Ptr[2]); + } + } +} + +void recLWC1_coX(int num) +{ + int i; + int mmreg = -1; + int mmregs[XMMREGS]; + int nextrts[XMMREGS]; + + assert( num > 1 && num < XMMREGS ); + +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + int ineax; + u32 written = 0; + mmreg = _allocCheckFPUtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE); + if( mmreg >= 0 ) mmreg |= MEM_XMMTAG; + else mmreg = EAX; + + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); + if( recMemConstRead32(mmreg, g_cpuConstRegs[_Rs_].UL[0]+_Imm_) ) { + if( mmreg&MEM_XMMTAG ) xmmregs[mmreg&0xf].inuse = 0; + MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EBX ); + written = 1; + } + else if( !IS_XMMREG(mmreg) ) MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); + + // recompile two at a time + for(i = 0; i < num-1; i += 2) { + nextrts[0] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); + nextrts[1] = ((*(u32*)(PSM(pc+i*4+4)) >> 16) & 0x1F); + + written = 0; + mmregs[0] = _allocCheckFPUtoXMM(g_pCurInstInfo+i+1, nextrts[0], MODE_WRITE); + if( mmregs[0] >= 0 ) mmregs[0] |= MEM_XMMTAG; + else mmregs[0] = EBX; + + mmregs[1] = _allocCheckFPUtoXMM(g_pCurInstInfo+i+2, nextrts[1], MODE_WRITE); + if( mmregs[1] >= 0 ) mmregs[1] |= MEM_XMMTAG; + else mmregs[1] = EAX; + + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); + if( recMemConstRead32(mmregs[0], g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4))) ) { + if( mmregs[0]&MEM_XMMTAG ) xmmregs[mmregs[0]&0xf].inuse = 0; + MOV32RtoM( (int)&fpuRegs.fpr[ nextrts[0] ].UL, EBX ); + written = 1; + } + ineax = recMemConstRead32(mmregs[1], g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4+4))); + + if( !written ) { + if( !(mmregs[0]&MEM_XMMTAG) ) MOV32RtoM( (int)&fpuRegs.fpr[ nextrts[0] ].UL, EBX ); + } + + if( ineax || !(mmregs[1] & MEM_XMMTAG) ) { + if( mmregs[1]&MEM_XMMTAG ) xmmregs[mmregs[1]&0xf].inuse = 0; + MOV32RtoM( (int)&fpuRegs.fpr[ nextrts[1] ].UL, EAX ); + } + } + + if( i < num ) { + // one left + int nextrt = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); + + mmreg = _allocCheckFPUtoXMM(g_pCurInstInfo+i+1, nextrt, MODE_WRITE); + if( mmreg >= 0 ) mmreg |= MEM_XMMTAG; + else mmreg = EAX; + + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); + if( recMemConstRead32(mmreg, g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4))) ) { + if( mmreg&MEM_XMMTAG ) xmmregs[mmreg&0xf].inuse = 0; + MOV32RtoM( (int)&fpuRegs.fpr[ nextrt ].UL, EBX ); + written = 1; + } + else if( !IS_XMMREG(mmreg) ) MOV32RtoM( (int)&fpuRegs.fpr[ nextrt ].UL, EAX ); + } + } + else +#endif + { + int dohw; + int mmregS = _eePrepareReg_coX(_Rs_, num); + + + _deleteMMXreg(MMX_FPU+_Rt_, 2); + for(i = 0; i < num; ++i) { + nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); + _deleteMMXreg(MMX_FPU+nextrts[i], 2); + } + + mmreg = _allocCheckFPUtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE); + + for(i = 0; i < num; ++i) { + mmregs[i] = _allocCheckFPUtoXMM(g_pCurInstInfo+i+1, nextrts[i], MODE_WRITE); + } + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregS, 0, 1); + + if( mmreg >= 0 ) { + SSEX_MOVD_RmOffset_to_XMM(mmreg, ECX, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); + } + + for(i = 0; i < num; ++i) { + if( mmregs[i] >= 0 ) { + SSEX_MOVD_RmOffset_to_XMM(mmregs[i], ECX, PS2MEM_BASE_+s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); + } + else { + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); + MOV32RtoM( (int)&fpuRegs.fpr[ nextrts[i] ].UL, EAX ); + } + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + MOV32RtoM((u32)&s_tempaddr, ECX); + CALLFunc( (int)recMemRead32 ); + + if( mmreg >= 0 ) SSE2_MOVD_R_to_XMM(mmreg, EAX); + else MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); + + for(i = 0; i < num; ++i) { + MOV32MtoR(ECX, (u32)&s_tempaddr); + ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-_Imm_); + CALLFunc( (int)recMemRead32 ); + + if( mmregs[i] >= 0 ) SSE2_MOVD_R_to_XMM(mmregs[i], EAX); + else MOV32RtoM( (int)&fpuRegs.fpr[ nextrts[i] ].UL, EAX ); + } + + if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); + else x86SetJ8A(j8Ptr[2]); + } + } +} + +void recSWC1_co( void ) +{ + int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); + +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + int mmreg; + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)%4 == 0 ); + + mmreg = _checkXMMreg(XMMTYPE_FPREG, _Rt_, MODE_READ); + if( mmreg >= 0 ) mmreg |= MEM_XMMTAG|(_Rt_<<16); + else { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); + mmreg = EAX; + } + recMemConstWrite32(g_cpuConstRegs[_Rs_].UL[0]+_Imm_, mmreg); + + mmreg = _checkXMMreg(XMMTYPE_FPREG, nextrt, MODE_READ); + if( mmreg >= 0 ) mmreg |= MEM_XMMTAG|(nextrt<<16); + else { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrt ].UL); + mmreg = EAX; + } + recMemConstWrite32(g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_, mmreg); + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + + int mmreg1, mmreg2; + assert( _checkMMXreg(MMX_FPU+_Rt_, MODE_READ) == -1 ); + assert( _checkMMXreg(MMX_FPU+nextrt, MODE_READ) == -1 ); + + mmreg1 = _checkXMMreg(XMMTYPE_FPREG, _Rt_, MODE_READ); + mmreg2 = _checkXMMreg(XMMTYPE_FPREG, nextrt, MODE_READ); + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); + + if(mmreg1 >= 0 ) { + if( mmreg2 >= 0 ) { + SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset); + SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + else { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrt ].UL); + SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + } + else { + if( mmreg2 >= 0 ) { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); + SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); + MOV32MtoR(EDX, (int)&fpuRegs.fpr[ nextrt ].UL); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + MOV32RtoM((u32)&s_tempaddr, ECX); + + // some type of hardware write + if( mmreg1 >= 0) SSE2_MOVD_XMM_to_R(EAX, mmreg1); + else MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); + CALLFunc( (int)recMemWrite32 ); + + MOV32MtoR(ECX, (u32)&s_tempaddr); + ADD32ItoR(ECX, _Imm_co_-_Imm_); + if( mmreg2 >= 0) SSE2_MOVD_XMM_to_R(EAX, mmreg2); + else MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrt ].UL); + CALLFunc( (int)recMemWrite32 ); + + if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); + else x86SetJ8A(j8Ptr[2]); + } + } +} + +void recSWC1_coX(int num) +{ + int i; + int mmreg = -1; + int mmregs[XMMREGS]; + int nextrts[XMMREGS]; + + assert( num > 1 && num < XMMREGS ); + + for(i = 0; i < num; ++i) { + nextrts[i] = ((*(u32*)(PSM(pc+i*4)) >> 16) & 0x1F); + } + +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)%4 == 0 ); + + mmreg = _checkXMMreg(XMMTYPE_FPREG, _Rt_, MODE_READ); + if( mmreg >= 0 ) mmreg |= MEM_XMMTAG|(_Rt_<<16); + else { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); + mmreg = EAX; + } + recMemConstWrite32(g_cpuConstRegs[_Rs_].UL[0]+_Imm_, mmreg); + + for(i = 0; i < num; ++i) { + mmreg = _checkXMMreg(XMMTYPE_FPREG, nextrts[i], MODE_READ); + if( mmreg >= 0 ) mmreg |= MEM_XMMTAG|(nextrts[i]<<16); + else { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrts[i] ].UL); + mmreg = EAX; + } + recMemConstWrite32(g_cpuConstRegs[_Rs_].UL[0]+(*(s16*)PSM(pc+i*4)), mmreg); + } + } + else +#endif + { + int dohw; + int mmregS = _eePrepareReg_coX(_Rs_, num); + + assert( _checkMMXreg(MMX_FPU+_Rt_, MODE_READ) == -1 ); + mmreg = _checkXMMreg(XMMTYPE_FPREG, _Rt_, MODE_READ); + + for(i = 0; i < num; ++i) { + assert( _checkMMXreg(MMX_FPU+nextrts[i], MODE_READ) == -1 ); + mmregs[i] = _checkXMMreg(XMMTYPE_FPREG, nextrts[i], MODE_READ); + } + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregS, 0, 1); + + if( mmreg >= 0) { + SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); + } + + for(i = 0; i < num; ++i) { + if( mmregs[i] >= 0) { + SSEX_MOVD_XMM_to_RmOffset(ECX, mmregs[i], PS2MEM_BASE_+s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); + } + else { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrts[i] ].UL); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+(*(s16*)PSM(pc+i*4))-_Imm_); + } + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + MOV32RtoM((u32)&s_tempaddr, ECX); + + // some type of hardware write + if( mmreg >= 0) SSE2_MOVD_XMM_to_R(EAX, mmreg); + else MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); + CALLFunc( (int)recMemWrite32 ); + + for(i = 0; i < num; ++i) { + MOV32MtoR(ECX, (u32)&s_tempaddr); + ADD32ItoR(ECX, (*(s16*)PSM(pc+i*4))-_Imm_); + if( mmregs[i] >= 0 && (xmmregs[mmregs[i]].mode&MODE_WRITE) ) SSE2_MOVD_XMM_to_R(EAX, mmregs[i]); + else MOV32MtoR(EAX, (int)&fpuRegs.fpr[ nextrts[i] ].UL); + CALLFunc( (int)recMemWrite32 ); + } + + if( s_bCachingMem & 2 ) x86SetJ32A(j32Ptr[4]); + else x86SetJ8A(j8Ptr[2]); + } + } +} + +void recLQC2_co( void ) +{ + int mmreg1 = -1, mmreg2 = -1, t0reg = -1; + int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); + +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 16 == 0 ); + + if( _Ft_ ) mmreg1 = _allocVFtoXMMreg(&VU0, -1, _Ft_, MODE_WRITE); + else t0reg = _allocTempXMMreg(XMMT_FPS, -1); + recMemConstRead128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, mmreg1 >= 0 ? mmreg1 : t0reg); + + if( nextrt ) mmreg2 = _allocVFtoXMMreg(&VU0, -1, nextrt, MODE_WRITE); + else if( t0reg < 0 ) t0reg = _allocTempXMMreg(XMMT_FPS, -1); + recMemConstRead128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_)&~15, mmreg2 >= 0 ? mmreg2 : t0reg); + + if( t0reg >= 0 ) _freeXMMreg(t0reg); + } + else +#endif + { + u8* rawreadptr; + int dohw; + int mmregs = _eePrepareReg(_Rs_); + + if( _Ft_ ) mmreg1 = _allocVFtoXMMreg(&VU0, -1, _Ft_, MODE_WRITE); + if( nextrt ) mmreg2 = _allocVFtoXMMreg(&VU0, -1, nextrt, MODE_WRITE); + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 0); + + rawreadptr = x86Ptr; + if( mmreg1 >= 0 ) SSEX_MOVDQARmtoROffset(mmreg1, ECX, PS2MEM_BASE_+s_nAddMemOffset); + if( mmreg2 >= 0 ) SSEX_MOVDQARmtoROffset(mmreg2, ECX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + + if( dohw ) { + j8Ptr[1] = JMP8(0); + SET_HWLOC_R5900(); + + // check if writing to VUs + CMP32ItoR(ECX, 0x11000000); + JAE8(rawreadptr - (x86Ptr+2)); + + MOV32RtoM((u32)&s_tempaddr, ECX); + + if( _Ft_ ) PUSH32I( (int)&VU0.VF[_Ft_].UD[0] ); + else PUSH32I( (int)&retValues[0] ); + CALLFunc( (int)recMemRead128 ); + + if( mmreg1 >= 0 ) SSEX_MOVDQA_M128_to_XMM(mmreg1, (int)&VU0.VF[_Ft_].UD[0] ); + + MOV32MtoR(ECX, (u32)&s_tempaddr); + ADD32ItoR(ECX, _Imm_co_-_Imm_); + + if( nextrt ) MOV32ItoRmOffset(ESP, (int)&VU0.VF[nextrt].UD[0], 0 ); + else MOV32ItoRmOffset(ESP, (int)&retValues[0], 0 ); + CALLFunc( (int)recMemRead128 ); + + if( mmreg2 >= 0 ) SSEX_MOVDQA_M128_to_XMM(mmreg2, (int)&VU0.VF[nextrt].UD[0] ); + + ADD32ItoR(ESP, 4); + x86SetJ8(j8Ptr[1]); + } + } + + _clearNeededXMMregs(); // needed since allocing +} + +void recSQC2_co( void ) +{ + int nextrt = ((*(u32*)(PSM(pc)) >> 16) & 0x1F); + int mmreg1, mmreg2; + +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)%16 == 0 ); + + mmreg1 = _allocVFtoXMMreg(&VU0, -1, _Ft_, MODE_READ)|MEM_XMMTAG; + mmreg2 = _allocVFtoXMMreg(&VU0, -1, nextrt, MODE_READ)|MEM_XMMTAG; + recMemConstWrite128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, mmreg1); + recMemConstWrite128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_co_)&~15, mmreg2); + } + else +#endif + { + u8* rawreadptr; + int dohw; + int mmregs = _eePrepareReg(_Rs_); + + mmreg1 = _checkXMMreg(XMMTYPE_VFREG, _Ft_, MODE_READ); + mmreg2 = _checkXMMreg(XMMTYPE_VFREG, nextrt, MODE_READ); + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 0); + + rawreadptr = x86Ptr; + + if( mmreg1 >= 0 ) { + SSEX_MOVDQARtoRmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + if( _hasFreeXMMreg() ) { + mmreg1 = _allocTempXMMreg(XMMT_FPS, -1); + SSEX_MOVDQA_M128_to_XMM(mmreg1, (int)&VU0.VF[_Ft_].UD[0]); + SSEX_MOVDQARtoRmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset); + _freeXMMreg(mmreg1); + } + else if( _hasFreeMMXreg() ) { + mmreg1 = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(mmreg1, (int)&VU0.VF[_Ft_].UD[0]); + MOVQRtoRmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset); + MOVQMtoR(mmreg1, (int)&VU0.VF[_Ft_].UL[2]); + MOVQRtoRmOffset(ECX, mmreg1, PS2MEM_BASE_+s_nAddMemOffset+8); + SetMMXstate(); + _freeMMXreg(mmreg1); + } + else { + MOV32MtoR(EAX, (int)&VU0.VF[_Ft_].UL[0]); + MOV32MtoR(EDX, (int)&VU0.VF[_Ft_].UL[1]); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+4); + MOV32MtoR(EAX, (int)&VU0.VF[_Ft_].UL[2]); + MOV32MtoR(EDX, (int)&VU0.VF[_Ft_].UL[3]); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+8); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+12); + } + } + + if( mmreg2 >= 0 ) { + SSEX_MOVDQARtoRmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + } + else { + if( _hasFreeXMMreg() ) { + mmreg2 = _allocTempXMMreg(XMMT_FPS, -1); + SSEX_MOVDQA_M128_to_XMM(mmreg2, (int)&VU0.VF[nextrt].UD[0]); + SSEX_MOVDQARtoRmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + _freeXMMreg(mmreg2); + } + else if( _hasFreeMMXreg() ) { + mmreg2 = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(mmreg2, (int)&VU0.VF[nextrt].UD[0]); + MOVQRtoRmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + MOVQMtoR(mmreg2, (int)&VU0.VF[nextrt].UL[2]); + MOVQRtoRmOffset(ECX, mmreg2, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_+8); + SetMMXstate(); + _freeMMXreg(mmreg2); + } + else { + MOV32MtoR(EAX, (int)&VU0.VF[nextrt].UL[0]); + MOV32MtoR(EDX, (int)&VU0.VF[nextrt].UL[1]); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_+4); + MOV32MtoR(EAX, (int)&VU0.VF[nextrt].UL[2]); + MOV32MtoR(EDX, (int)&VU0.VF[nextrt].UL[3]); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_+8); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+_Imm_co_-_Imm_+12); + } + } + + if( dohw ) { + j8Ptr[1] = JMP8(0); + + SET_HWLOC_R5900(); + + // check if writing to VUs + CMP32ItoR(ECX, 0x11000000); + JAE8(rawreadptr - (x86Ptr+2)); + + // some type of hardware write + if( mmreg1 >= 0) { + if( xmmregs[mmreg1].mode & MODE_WRITE ) { + SSEX_MOVDQA_XMM_to_M128((int)&VU0.VF[_Ft_].UD[0], mmreg1); + } + } + + MOV32RtoM((u32)&s_tempaddr, ECX); + + MOV32ItoR(EAX, (int)&VU0.VF[_Ft_].UD[0]); + CALLFunc( (int)recMemWrite128 ); + + if( mmreg2 >= 0) { + if( xmmregs[mmreg2].mode & MODE_WRITE ) { + SSEX_MOVDQA_XMM_to_M128((int)&VU0.VF[nextrt].UD[0], mmreg2); + } + } + + MOV32MtoR(ECX, (u32)&s_tempaddr); + ADD32ItoR(ECX, _Imm_co_-_Imm_); + + MOV32ItoR(EAX, (int)&VU0.VF[nextrt].UD[0]); + CALLFunc( (int)recMemWrite128 ); + + x86SetJ8A(j8Ptr[1]); + } + } +} + +#endif // PCSX2_VM_COISSUE diff --git a/pcsx2/x86/iR5900Jump.h b/pcsx2/x86/iR5900Jump.h new file mode 100644 index 0000000000..a7f2472206 --- /dev/null +++ b/pcsx2/x86/iR5900Jump.h @@ -0,0 +1,37 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IR5900JUMP_H__ +#define __IR5900JUMP_H__ + +/********************************************************* +* Jump to target * +* Format: OP target * +*********************************************************/ + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + + void recJ( void ); + void recJAL( void ); + void recJR( void ); + void recJALR( void ); +} } } + +#endif diff --git a/pcsx2/x86/iR5900LoadStore.h b/pcsx2/x86/iR5900LoadStore.h new file mode 100644 index 0000000000..ed15fd2446 --- /dev/null +++ b/pcsx2/x86/iR5900LoadStore.h @@ -0,0 +1,95 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IR5900LOADSTORE_H__ +#define __IR5900LOADSTORE_H__ +/********************************************************* +* Load and store for GPR * +* Format: OP rt, offset(base) * +*********************************************************/ + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + + void recLB( void ); + void recLBU( void ); + void recLH( void ); + void recLHU( void ); + void recLW( void ); + void recLWU( void ); + void recLWL( void ); + void recLWR( void ); + void recLD( void ); + void recLDR( void ); + void recLDL( void ); + void recLQ( void ); + void recSB( void ); + void recSH( void ); + void recSW( void ); + void recSWL( void ); + void recSWR( void ); + void recSD( void ); + void recSDL( void ); + void recSDR( void ); + void recSQ( void ); + void recLWC1( void ); + void recSWC1( void ); + void recLQC2( void ); + void recSQC2( void ); + +// coissues +#ifdef PCSX2_VM_COISSUE + void recLB_co( void ); + void recLBU_co( void ); + void recLH_co( void ); + void recLHU_co( void ); + void recLW_co( void ); + void recLWU_co( void ); + void recLWL_co( void ); + void recLWR_co( void ); + void recLD_co( void ); + void recLDR_co( void ); + void recLDL_co( void ); + void recLQ_co( void ); + void recSB_co( void ); + void recSH_co( void ); + void recSW_co( void ); + void recSWL_co( void ); + void recSWR_co( void ); + void recSD_co( void ); + void recSDL_co( void ); + void recSDR_co( void ); + void recSQ_co( void ); + void recLWC1_co( void ); + void recSWC1_co( void ); + void recLQC2_co( void ); + void recSQC2_co( void ); + + // coissue-X + void recLD_coX(int num); + void recLQ_coX(int num); + void recLWC1_coX(int num); + void recSD_coX(int num, int align); + void recSQ_coX(int num); + void recSWC1_coX(int num); +#endif + +} } } + +#endif diff --git a/pcsx2/x86/iR5900Misc.cpp b/pcsx2/x86/iR5900Misc.cpp new file mode 100644 index 0000000000..8972efbdaa --- /dev/null +++ b/pcsx2/x86/iR5900Misc.cpp @@ -0,0 +1,252 @@ +#include "PrecompiledHeader.h" + +#include "Common.h" +#include "iR5900.h" +#include "R5900OpcodeTables.h" + + +namespace R5900 { +namespace Dynarec { + +// R5900 branch hepler! +// Recompiles code for a branch test and/or skip, complete with delay slot +// handling. Note, for "likely" branches use iDoBranchImm_Likely instead, which +// handles delay slots differently. +// Parameters: +// jmpSkip - This parameter is the result of the appropriate J32 instruction +// (usually JZ32 or JNZ32). +void recDoBranchImm( u32* jmpSkip, bool isLikely ) +{ + // All R5900 branches use this format: + const u32 branchTo = ((s32)_Imm_ * 4) + pc; + + // First up is the Branch Taken Path : Save the recompiler's state, compile the + // DelaySlot, and issue a BranchTest insertion. The state is reloaded below for + // the "did not branch" path (maintains consts, register allocations, and other optimizations). + + SaveBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); + + // Jump target when the branch is *not* taken, skips the branchtest code + // insertion above. + x86SetJ32(jmpSkip); + + // if it's a likely branch then we'll need to skip the delay slot here, since + // MIPS cancels the delay slot instruction when branches aren't taken. + LoadBranchState(); + if( !isLikely ) + { + pc -= 4; // instruction rewinder for delay slot, if non-likely. + recompileNextInstruction(1); + } + SetBranchImm(pc); +} + +void recDoBranchImm_Likely( u32* jmpSkip ) +{ + recDoBranchImm( jmpSkip, true ); +} + +namespace OpcodeImpl { + +//////////////////////////////////////////////////// +//static void recCACHE( void ) { +// MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code ); +// MOV32ItoM( (uptr)&cpuRegs.pc, pc ); +// iFlushCall(FLUSH_EVERYTHING); +// CALLFunc( (uptr)CACHE ); +// //branch = 2; +// +// CMP32ItoM((int)&cpuRegs.pc, pc); +// j8Ptr[0] = JE8(0); +// RET(); +// x86SetJ8(j8Ptr[0]); +//} + + +void recPREF( void ) +{ +} + +void recSYNC( void ) +{ +} + +void recMFSA( void ) +{ + int mmreg; + if (!_Rd_) return; + + mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, MODE_WRITE); + if( mmreg >= 0 ) { + SSE_MOVLPS_M64_to_XMM(mmreg, (uptr)&cpuRegs.sa); + } + else if( (mmreg = _checkMMXreg(MMX_GPR+_Rd_, MODE_WRITE)) >= 0 ) { + MOVDMtoMMX(mmreg, (uptr)&cpuRegs.sa); + SetMMXstate(); + } + else { + MOV32MtoR(EAX, (u32)&cpuRegs.sa); + _deleteEEreg(_Rd_, 0); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[0], EAX); + MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[1], 0); + } +} + +void recMTSA( void ) +{ + if( GPR_IS_CONST1(_Rs_) ) { + MOV32ItoM((uptr)&cpuRegs.sa, g_cpuConstRegs[_Rs_].UL[0] ); + } + else { + int mmreg; + + if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rs_, MODE_READ)) >= 0 ) { + SSE_MOVSS_XMM_to_M32((uptr)&cpuRegs.sa, mmreg); + } + else if( (mmreg = _checkMMXreg(MMX_GPR+_Rs_, MODE_READ)) >= 0 ) { + MOVDMMXtoM((uptr)&cpuRegs.sa, mmreg); + SetMMXstate(); + } + else { + MOV32MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UL[0]); + MOV32RtoM((uptr)&cpuRegs.sa, EAX); + } + } +} + +void recMTSAB( void ) +{ + if( GPR_IS_CONST1(_Rs_) ) { + MOV32ItoM((uptr)&cpuRegs.sa, ((g_cpuConstRegs[_Rs_].UL[0] & 0xF) ^ (_Imm_ & 0xF)) << 3); + } + else { + _eeMoveGPRtoR(EAX, _Rs_); + AND32ItoR(EAX, 0xF); + XOR32ItoR(EAX, _Imm_&0xf); + SHL32ItoR(EAX, 3); + MOV32RtoM((uptr)&cpuRegs.sa, EAX); + } +} + +void recMTSAH( void ) +{ + if( GPR_IS_CONST1(_Rs_) ) { + MOV32ItoM((uptr)&cpuRegs.sa, ((g_cpuConstRegs[_Rs_].UL[0] & 0x7) ^ (_Imm_ & 0x7)) << 4); + } + else { + _eeMoveGPRtoR(EAX, _Rs_); + AND32ItoR(EAX, 0x7); + XOR32ItoR(EAX, _Imm_&0x7); + SHL32ItoR(EAX, 4); + MOV32RtoM((uptr)&cpuRegs.sa, EAX); + } +} + + //////////////////////////////////////////////////// + void recNULL( void ) + { + Console::Error("EE: Unimplemented op %x", params cpuRegs.code); + } + + //////////////////////////////////////////////////// + void recUnknown() + { + // TODO : Unknown ops should throw an exception. + Console::Error("EE: Unrecognized op %x", params cpuRegs.code); + } + + void recMMI_Unknown() + { + // TODO : Unknown ops should throw an exception. + Console::Error("EE: Unrecognized MMI op %x", params cpuRegs.code); + } + + void recCOP0_Unknown() + { + // TODO : Unknown ops should throw an exception. + Console::Error("EE: Unrecognized COP0 op %x", params cpuRegs.code); + } + + void recCOP1_Unknown() + { + // TODO : Unknown ops should throw an exception. + Console::Error("EE: Unrecognized FPU/COP1 op %x", params cpuRegs.code); + } + + /********************************************************** + * UNHANDLED YET OPCODES + * + **********************************************************/ + + void recCACHE() + { + MOV32ItoM( (uptr)&cpuRegs.code, (u32)cpuRegs.code ); + MOV32ItoM( (uptr)&cpuRegs.pc, (u32)pc ); + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (uptr)R5900::Interpreter::OpcodeImpl::CACHE ); + branch = 2; + } + + void recTGE( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TGE ); + } + + void recTGEU( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TGEU ); + } + + void recTLT( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TLT ); + } + + void recTLTU( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TLTU ); + } + + void recTEQ( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TEQ ); + } + + void recTNE( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TNE ); + } + + void recTGEI( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TGEI ); + } + + void recTGEIU( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TGEIU ); + } + + void recTLTI( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TLTI ); + } + + void recTLTIU( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TLTIU ); + } + + void recTEQI( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TEQI ); + } + + void recTNEI( void ) + { + recBranchCall( R5900::Interpreter::OpcodeImpl::TNEI ); + } + +} }} // end Namespace R5900::Dynarec::OpcodeImpl diff --git a/pcsx2/x86/iR5900Move.h b/pcsx2/x86/iR5900Move.h new file mode 100644 index 0000000000..9129edcc72 --- /dev/null +++ b/pcsx2/x86/iR5900Move.h @@ -0,0 +1,35 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IR5900MOVE_H__ +#define __IR5900MOVE_H__ + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + + void recLUI( void ); + void recMFLO( void ); + void recMFHI( void ); + void recMTLO( void ); + void recMTHI( void ); + void recMOVN( void ); + void recMOVZ( void ); +} } } + +#endif diff --git a/pcsx2/x86/iR5900MultDiv.h b/pcsx2/x86/iR5900MultDiv.h new file mode 100644 index 0000000000..361dcafa8a --- /dev/null +++ b/pcsx2/x86/iR5900MultDiv.h @@ -0,0 +1,37 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IR5900MULTDIV_H__ +#define __IR5900MULTDIV_H__ + +/********************************************************* +* Register mult/div & Register trap logic * +* Format: OP rs, rt * +*********************************************************/ + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + + void recMULT( void ); + void recMULTU( void ); + void recDIV( void ); + void recDIVU( void ); +} } } + +#endif diff --git a/pcsx2/x86/iR5900Shift.h b/pcsx2/x86/iR5900Shift.h new file mode 100644 index 0000000000..e688d2d1f4 --- /dev/null +++ b/pcsx2/x86/iR5900Shift.h @@ -0,0 +1,49 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#ifndef __IR5900SHIFT_H__ +#define __IR5900SHIFT_H__ + +/********************************************************* +* Shift arithmetic with constant shift * +* Format: OP rd, rt, sa * +*********************************************************/ + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + + void recSLL( void ); + void recSRL( void ); + void recSRA( void ); + void recDSLL( void ); + void recDSRL( void ); + void recDSRA( void ); + void recDSLL32( void ); + void recDSRL32( void ); + void recDSRA32( void ); + + void recSLLV( void ); + void recSRLV( void ); + void recSRAV( void ); + void recDSLLV( void ); + void recDSRLV( void ); + void recDSRAV( void ); +} } } + +#endif diff --git a/pcsx2/x86/iVU0micro.cpp b/pcsx2/x86/iVU0micro.cpp new file mode 100644 index 0000000000..baf28fee47 --- /dev/null +++ b/pcsx2/x86/iVU0micro.cpp @@ -0,0 +1,76 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "VUmicro.h" +#include "iVUzerorec.h" + +namespace VU0micro +{ + void recAlloc() + { + SuperVUAlloc(0); + } + + void __fastcall recClear(u32 Addr, u32 Size) + { + SuperVUClear(Addr, Size, 0); // Size should be a multiple of 8 bytes! + } + + void recShutdown() + { + SuperVUDestroy( 0 ); + } + + static void recReset() + { + SuperVUReset(0); + + // these shouldn't be needed, but shouldn't hurt anything either. + x86FpuState = FPU_STATE; + iCWstate = 0; + } + + static void recStep() + { + } + + static void recExecuteBlock() + { + if((VU0.VI[REG_VPU_STAT].UL & 1) == 0) + return; + + FreezeXMMRegs(1); + SuperVUExecuteProgram(VU0.VI[ REG_TPC ].UL & 0xfff, 0); + FreezeXMMRegs(0); + } +} + +using namespace VU0micro; + +const VUmicroCpu recVU0 = +{ + recReset +, recStep +, recExecuteBlock +, recClear +}; diff --git a/pcsx2/x86/iVU1micro.cpp b/pcsx2/x86/iVU1micro.cpp new file mode 100644 index 0000000000..13768673d7 --- /dev/null +++ b/pcsx2/x86/iVU1micro.cpp @@ -0,0 +1,135 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "VUmicro.h" +#include "iVUzerorec.h" + +#ifdef _DEBUG +extern u32 vudump; +#endif + +namespace VU1micro +{ + void recAlloc() + { + SuperVUAlloc(1); + } + + void __fastcall recClear( u32 Addr, u32 Size ) + { + assert( (Addr&7) == 0 ); + SuperVUClear(Addr, Size, 1); // Size should be a multiple of 8 bytes! + } + + void recShutdown() + { + SuperVUDestroy( 1 ); + } + + // commented out because I'm not sure it actually works anymore with SuperVU (air) + /*static void iVU1DumpBlock() + { + FILE *f; + char filename[ g_MaxPath ]; + u32 *mem; + u32 i; + + #ifdef _WIN32 + CreateDirectory("dumps", NULL); + sprintf_s( filename, g_MaxPath, "dumps\\vu%.4X.txt", VU1.VI[ REG_TPC ].UL ); + #else + mkdir("dumps", 0755); + sprintf( filename, "dumps/vu%.4X.txt", VU1.VI[ REG_TPC ].UL ); + #endif + SysPrintf( "dump1 %x => %x (%s)\n", VU1.VI[ REG_TPC ].UL, pc, filename ); + + f = fopen( filename, "wb" ); + for ( i = VU1.VI[REG_TPC].UL; i < pc; i += 8 ) { + char* pstr; + mem = (u32*)&VU1.Micro[i]; + + pstr = disVU1MicroUF( mem[1], i+4 ); + fprintf(f, "%x: %-40s ", i, pstr); + + pstr = disVU1MicroLF( mem[0], i ); + fprintf(f, "%s\n", pstr); + } + fclose( f ); + }*/ + + static void recReset() + { + SuperVUReset(1); + + // these shouldn't be needed, but shouldn't hurt anything either. + x86FpuState = FPU_STATE; + iCWstate = 0; + } + + static void recStep() + { + } + + static void recExecuteBlock(void) + { + #ifdef _DEBUG + static u32 vuprogcount = 0; + vuprogcount++; + if( vudump & 8 ) __Log("start vu1: %x %x\n", VU1.VI[ REG_TPC ].UL, vuprogcount); + #endif + + if((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0){ + //SysPrintf("Execute block VU1, VU1 not busy\n"); + return; + } + + if (VU1.VI[REG_TPC].UL >= VU1.maxmicro) + { + Console::Error("VU1 memory overflow!!: %x", params VU1.VI[REG_TPC].UL); + /*VU0.VI[REG_VPU_STAT].UL&= ~0x100; + VU1.cycle++; + return;*/ + } + + assert( (VU1.VI[ REG_TPC ].UL&7) == 0 ); + + FreezeXMMRegs(1); + do { // while loop needed since not always will return finished + SuperVUExecuteProgram(VU1.VI[ REG_TPC ].UL & 0x3fff, 1); + } while( VU0.VI[ REG_VPU_STAT ].UL&0x100 ); + FreezeXMMRegs(0); + } +} + +using namespace VU1micro; + +const VUmicroCpu recVU1 = +{ + recReset +, recStep +, recExecuteBlock +, recClear +}; diff --git a/pcsx2/x86/iVUmicro.cpp b/pcsx2/x86/iVUmicro.cpp new file mode 100644 index 0000000000..749c5e9d32 --- /dev/null +++ b/pcsx2/x86/iVUmicro.cpp @@ -0,0 +1,1713 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "GS.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "iMMI.h" +#include "iFPU.h" +#include "iCOP0.h" +#include "VUmicro.h" +#include "VUflags.h" +#include "iVUmicro.h" +#include "iVUops.h" +#include "iVUzerorec.h" + +#ifdef _WIN32 +#pragma warning(disable:4244) +#pragma warning(disable:4761) +#endif +//------------------------------------------------------------------ + +// fixme - VUmicro should really use its own static vars for pc and branch. +// Sharing with the EE's copies of pc and branch is not cool! (air) + +//------------------------------------------------------------------ +// Helper Macros +//------------------------------------------------------------------ +#define _Ft_ (( VU->code >> 16) & 0x1F) // The rt part of the instruction register +#define _Fs_ (( VU->code >> 11) & 0x1F) // The rd part of the instruction register +#define _Fd_ (( VU->code >> 6) & 0x1F) // The sa part of the instruction register + +#define _X (( VU->code>>24) & 0x1) +#define _Y (( VU->code>>23) & 0x1) +#define _Z (( VU->code>>22) & 0x1) +#define _W (( VU->code>>21) & 0x1) + +#define _XYZW_SS (_X+_Y+_Z+_W==1) + +#define _Fsf_ (( VU->code >> 21) & 0x03) +#define _Ftf_ (( VU->code >> 23) & 0x03) + +#define _Imm11_ (s32)(VU->code & 0x400 ? 0xfffffc00 | (VU->code & 0x3ff) : VU->code & 0x3ff) +#define _UImm11_ (s32)(VU->code & 0x7ff) + +#define VU_VFx_ADDR(x) (uptr)&VU->VF[x].UL[0] +#define VU_VFy_ADDR(x) (uptr)&VU->VF[x].UL[1] +#define VU_VFz_ADDR(x) (uptr)&VU->VF[x].UL[2] +#define VU_VFw_ADDR(x) (uptr)&VU->VF[x].UL[3] + +#define VU_REGR_ADDR (uptr)&VU->VI[REG_R] +#define VU_REGQ_ADDR (uptr)&VU->VI[REG_Q] +#define VU_REGMAC_ADDR (uptr)&VU->VI[REG_MAC_FLAG] + +#define VU_VI_ADDR(x, read) GetVIAddr(VU, x, read, info) + +#define VU_ACCx_ADDR (uptr)&VU->ACC.UL[0] +#define VU_ACCy_ADDR (uptr)&VU->ACC.UL[1] +#define VU_ACCz_ADDR (uptr)&VU->ACC.UL[2] +#define VU_ACCw_ADDR (uptr)&VU->ACC.UL[3] + +#define _X_Y_Z_W ((( VU->code >> 21 ) & 0xF ) ) +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// Global Variables +//------------------------------------------------------------------ +int g_VuNanHandling = 0; +int vucycle; + +PCSX2_ALIGNED16(float s_fones[8]) = {1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f}; +PCSX2_ALIGNED16(u32 s_mask[4]) = {0x007fffff, 0x007fffff, 0x007fffff, 0x007fffff}; +PCSX2_ALIGNED16(u32 s_expmask[4]) = {0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000}; +PCSX2_ALIGNED16(u32 g_minvals[4]) = {0xff7fffff, 0xff7fffff, 0xff7fffff, 0xff7fffff}; +PCSX2_ALIGNED16(u32 g_maxvals[4]) = {0x7f7fffff, 0x7f7fffff, 0x7f7fffff, 0x7f7fffff}; +PCSX2_ALIGNED16(u32 const_clip[8]) = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, + 0x80000000, 0x80000000, 0x80000000, 0x80000000}; +PCSX2_ALIGNED(64, u32 g_ones[4]) = {0x00000001, 0x00000001, 0x00000001, 0x00000001}; +PCSX2_ALIGNED16(u32 g_minvals_XYZW[16][4]) = +{ + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }, //0000 + { 0xffffffff, 0xffffffff, 0xffffffff, 0xff7fffff }, //0001 + { 0xffffffff, 0xffffffff, 0xff7fffff, 0xffffffff }, //0010 + { 0xffffffff, 0xffffffff, 0xff7fffff, 0xff7fffff }, //0011 + { 0xffffffff, 0xff7fffff, 0xffffffff, 0xffffffff }, //0100 + { 0xffffffff, 0xff7fffff, 0xffffffff, 0xff7fffff }, //0101 + { 0xffffffff, 0xff7fffff, 0xff7fffff, 0xffffffff }, //0110 + { 0xffffffff, 0xff7fffff, 0xff7fffff, 0xff7fffff }, //0111 + { 0xff7fffff, 0xffffffff, 0xffffffff, 0xffffffff }, //1000 + { 0xff7fffff, 0xffffffff, 0xffffffff, 0xff7fffff }, //1001 + { 0xff7fffff, 0xffffffff, 0xff7fffff, 0xffffffff }, //1010 + { 0xff7fffff, 0xffffffff, 0xff7fffff, 0xff7fffff }, //1011 + { 0xff7fffff, 0xff7fffff, 0xffffffff, 0xffffffff }, //1100 + { 0xff7fffff, 0xff7fffff, 0xffffffff, 0xff7fffff }, //1101 + { 0xff7fffff, 0xff7fffff, 0xff7fffff, 0xffffffff }, //1110 + { 0xff7fffff, 0xff7fffff, 0xff7fffff, 0xff7fffff }, //1111 +}; +PCSX2_ALIGNED16(u32 g_maxvals_XYZW[16][4])= +{ + { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff }, //0000 + { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7f7fffff }, //0001 + { 0x7fffffff, 0x7fffffff, 0x7f7fffff, 0x7fffffff }, //0010 + { 0x7fffffff, 0x7fffffff, 0x7f7fffff, 0x7f7fffff }, //0011 + { 0x7fffffff, 0x7f7fffff, 0x7fffffff, 0x7fffffff }, //0100 + { 0x7fffffff, 0x7f7fffff, 0x7fffffff, 0x7f7fffff }, //0101 + { 0x7fffffff, 0x7f7fffff, 0x7f7fffff, 0x7fffffff }, //0110 + { 0x7fffffff, 0x7f7fffff, 0x7f7fffff, 0x7f7fffff }, //0111 + { 0x7f7fffff, 0x7fffffff, 0x7fffffff, 0x7fffffff }, //1000 + { 0x7f7fffff, 0x7fffffff, 0x7fffffff, 0x7f7fffff }, //1001 + { 0x7f7fffff, 0x7fffffff, 0x7f7fffff, 0x7fffffff }, //1010 + { 0x7f7fffff, 0x7fffffff, 0x7f7fffff, 0x7f7fffff }, //1011 + { 0x7f7fffff, 0x7f7fffff, 0x7fffffff, 0x7fffffff }, //1100 + { 0x7f7fffff, 0x7f7fffff, 0x7fffffff, 0x7f7fffff }, //1101 + { 0x7f7fffff, 0x7f7fffff, 0x7f7fffff, 0x7fffffff }, //1110 + { 0x7f7fffff, 0x7f7fffff, 0x7f7fffff, 0x7f7fffff }, //1111 +}; +//------------------------------------------------------------------ + +//------------------------------------------------------------------ +// VU Pipeline/Test Stalls/Analyzing Functions +//------------------------------------------------------------------ +void _recvuFMACflush(VURegs * VU) { + int i; + + for (i=0; i<8; i++) { + if (VU->fmac[i].enable == 0) continue; + + if ((vucycle - VU->fmac[i].sCycle) >= VU->fmac[i].Cycle) { +// VUM_LOG("flushing FMAC pipe[%d]\n", i); + VU->fmac[i].enable = 0; + } + } +} + +void _recvuFDIVflush(VURegs * VU) { + if (VU->fdiv.enable == 0) return; + + if ((vucycle - VU->fdiv.sCycle) >= VU->fdiv.Cycle) { +// SysPrintf("flushing FDIV pipe\n"); + VU->fdiv.enable = 0; + } +} + +void _recvuEFUflush(VURegs * VU) { + if (VU->efu.enable == 0) return; + + if ((vucycle - VU->efu.sCycle) >= VU->efu.Cycle) { +// SysPrintf("flushing FDIV pipe\n"); + VU->efu.enable = 0; + } +} + +void _recvuTestPipes(VURegs * VU) { + _recvuFMACflush(VU); + _recvuFDIVflush(VU); + _recvuEFUflush(VU); +} + +void _recvuFMACTestStall(VURegs * VU, int reg, int xyzw) { + int cycle; + int i; + u32 mask = 0; + + for (i=0; i<8; i++) { + if (VU->fmac[i].enable == 0) continue; + if (VU->fmac[i].reg == reg && (VU->fmac[i].xyzw & xyzw)) break; + } + + if (i == 8) return; + + // do a perchannel delay + // old code +// cycle = VU->fmac[i].Cycle - (vucycle - VU->fmac[i].sCycle); + + // new code + mask = 4; // w +// if( VU->fmac[i].xyzw & 1 ) mask = 4; // w +// else if( VU->fmac[i].xyzw & 2 ) mask = 3; // z +// else if( VU->fmac[i].xyzw & 4 ) mask = 2; // y +// else { +// assert(VU->fmac[i].xyzw & 8 ); +// mask = 1; // x +// } + +// mask = 0; +// if( VU->fmac[i].xyzw & 1 ) mask++; // w +// else if( VU->fmac[i].xyzw & 2 ) mask++; // z +// else if( VU->fmac[i].xyzw & 4 ) mask++; // y +// else if( VU->fmac[i].xyzw & 8 ) mask++; // x + + assert( (int)VU->fmac[i].sCycle < (int)vucycle ); + cycle = 0; + if( vucycle - VU->fmac[i].sCycle < mask ) + cycle = mask - (vucycle - VU->fmac[i].sCycle); + + VU->fmac[i].enable = 0; + vucycle+= cycle; + _recvuTestPipes(VU); +} + +void _recvuFMACAdd(VURegs * VU, int reg, int xyzw) { + int i; + + /* find a free fmac pipe */ + for (i=0; i<8; i++) { + if (VU->fmac[i].enable == 1) continue; + break; + } + + if (i==8) SysPrintf("*PCSX2*: error , out of fmacs\n"); +// VUM_LOG("adding FMAC pipe[%d]; reg %d\n", i, reg); + + VU->fmac[i].enable = 1; + VU->fmac[i].sCycle = vucycle; + VU->fmac[i].Cycle = 3; + VU->fmac[i].xyzw = xyzw; + VU->fmac[i].reg = reg; +} + +void _recvuFDIVAdd(VURegs * VU, int cycles) { +// SysPrintf("adding FDIV pipe\n"); + VU->fdiv.enable = 1; + VU->fdiv.sCycle = vucycle; + VU->fdiv.Cycle = cycles; +} + +void _recvuEFUAdd(VURegs * VU, int cycles) { +// SysPrintf("adding EFU pipe\n"); + VU->efu.enable = 1; + VU->efu.sCycle = vucycle; + VU->efu.Cycle = cycles; +} + +void _recvuTestFMACStalls(VURegs * VU, _VURegsNum *VUregsn) { + + if( VUregsn->VFread0 && (VUregsn->VFread0 == VUregsn->VFread1) ) { + _recvuFMACTestStall(VU, VUregsn->VFread0, VUregsn->VFr0xyzw|VUregsn->VFr1xyzw); + } + else { + if (VUregsn->VFread0) _recvuFMACTestStall(VU, VUregsn->VFread0, VUregsn->VFr0xyzw); + if (VUregsn->VFread1) _recvuFMACTestStall(VU, VUregsn->VFread1, VUregsn->VFr1xyzw); + } +} + +void _recvuAddFMACStalls(VURegs * VU, _VURegsNum *VUregsn) { + + if (VUregsn->VFwrite) _recvuFMACAdd(VU, VUregsn->VFwrite, VUregsn->VFwxyzw); + else if (VUregsn->VIwrite & (1 << REG_CLIP_FLAG)) _recvuFMACAdd(VU, -REG_CLIP_FLAG, 0); // REG_CLIP_FLAG pipe + else _recvuFMACAdd(VU, 0, 0); +} + +void _recvuFlushFDIV(VURegs * VU) { + int cycle; + + if (VU->fdiv.enable == 0) return; + + cycle = VU->fdiv.Cycle - (vucycle - VU->fdiv.sCycle); +// SysPrintf("waiting FDIV pipe %d\n", cycle); + VU->fdiv.enable = 0; + vucycle+= cycle; +} + +void _recvuFlushEFU(VURegs * VU) { + int cycle; + + if (VU->efu.enable == 0) return; + + cycle = VU->efu.Cycle - (vucycle - VU->efu.sCycle); +// SysPrintf("waiting FDIV pipe %d\n", cycle); + VU->efu.enable = 0; + vucycle+= cycle; +} + +void _recvuTestFDIVStalls(VURegs * VU, _VURegsNum *VUregsn) { +// _vuTestFMACStalls(VURegs * VU, _VURegsNum *VUregsn); + _recvuFlushFDIV(VU); +} + +void _recvuTestEFUStalls(VURegs * VU, _VURegsNum *VUregsn) { +// _vuTestFMACStalls(VURegs * VU, _VURegsNum *VUregsn); + _recvuFlushEFU(VU); +} + +void _recvuAddFDIVStalls(VURegs * VU, _VURegsNum *VUregsn) { +// _vuTestFMACStalls(VURegs * VU, _VURegsNum *VUregsn); + if (VUregsn->VIwrite & (1 << REG_Q)) { + _recvuFDIVAdd(VU, VUregsn->cycles); + } +} + +void _recvuAddEFUStalls(VURegs * VU, _VURegsNum *VUregsn) { +// _vuTestFMACStalls(VURegs * VU, _VURegsNum *VUregsn); + if (VUregsn->VIwrite & (1 << REG_P)) { + _recvuEFUAdd(VU, VUregsn->cycles); + } +} + +void _recvuTestUpperStalls(VURegs * VU, _VURegsNum *VUregsn) { + switch (VUregsn->pipe) { + case VUPIPE_FMAC: _recvuTestFMACStalls(VU, VUregsn); break; + } +} + +void _recvuTestLowerStalls(VURegs * VU, _VURegsNum *VUregsn) { + switch (VUregsn->pipe) { + case VUPIPE_FMAC: _recvuTestFMACStalls(VU, VUregsn); break; + case VUPIPE_FDIV: _recvuTestFDIVStalls(VU, VUregsn); break; + case VUPIPE_EFU: _recvuTestEFUStalls(VU, VUregsn); break; + } +} + +void _recvuAddUpperStalls(VURegs * VU, _VURegsNum *VUregsn) { + switch (VUregsn->pipe) { + case VUPIPE_FMAC: _recvuAddFMACStalls(VU, VUregsn); break; + } +} + +void _recvuAddLowerStalls(VURegs * VU, _VURegsNum *VUregsn) { + switch (VUregsn->pipe) { + case VUPIPE_FMAC: _recvuAddFMACStalls(VU, VUregsn); break; + case VUPIPE_FDIV: _recvuAddFDIVStalls(VU, VUregsn); break; + case VUPIPE_EFU: _recvuAddEFUStalls(VU, VUregsn); break; + } +} + +void SuperVUAnalyzeOp(VURegs *VU, _vuopinfo *info, _VURegsNum* pCodeRegs) +{ + _VURegsNum* lregs; + _VURegsNum* uregs; + int *ptr; + + lregs = pCodeRegs; + uregs = pCodeRegs+1; + + ptr = (int*)&VU->Micro[pc]; + pc += 8; + + if (ptr[1] & 0x40000000) { // EOP + branch |= 8; + } + + VU->code = ptr[1]; + if (VU == &VU1) VU1regs_UPPER_OPCODE[VU->code & 0x3f](uregs); + else VU0regs_UPPER_OPCODE[VU->code & 0x3f](uregs); + + _recvuTestUpperStalls(VU, uregs); + switch(VU->code & 0x3f) { + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x1d: case 0x1f: + case 0x2b: case 0x2f: + break; + + case 0x3c: + switch ((VU->code >> 6) & 0x1f) { + case 0x4: case 0x5: + break; + default: + info->statusflag = 4; + info->macflag = 4; + break; + } + break; + case 0x3d: + switch ((VU->code >> 6) & 0x1f) { + case 0x4: case 0x5: case 0x7: + break; + default: + info->statusflag = 4; + info->macflag = 4; + break; + } + break; + case 0x3e: + switch ((VU->code >> 6) & 0x1f) { + case 0x4: case 0x5: + break; + default: + info->statusflag = 4; + info->macflag = 4; + break; + } + break; + case 0x3f: + switch ((VU->code >> 6) & 0x1f) { + case 0x4: case 0x5: case 0x7: case 0xb: + break; + default: + info->statusflag = 4; + info->macflag = 4; + break; + } + break; + + default: + info->statusflag = 4; + info->macflag = 4; + break; + } + + if (uregs->VIread & (1 << REG_Q)) { info->q |= 2; } + if (uregs->VIread & (1 << REG_P)) { info->p |= 2; assert( VU == &VU1 ); } + + // check upper flags + if (ptr[1] & 0x80000000) { // I flag + info->cycle = vucycle; + memzero_obj(*lregs); + } + else { + + VU->code = ptr[0]; + if (VU == &VU1) VU1regs_LOWER_OPCODE[VU->code >> 25](lregs); + else VU0regs_LOWER_OPCODE[VU->code >> 25](lregs); + + _recvuTestLowerStalls(VU, lregs); + info->cycle = vucycle; + + if (lregs->pipe == VUPIPE_BRANCH) { + branch |= 1; + } + + if (lregs->VIwrite & (1 << REG_Q)) { + info->q |= 4; + info->cycles = lregs->cycles; + info->pqinst = (VU->code&2)>>1; // rsqrt is 2 + } + else if (lregs->pipe == VUPIPE_FDIV) { + info->q |= 8|1; + info->pqinst = 0; + } + + if (lregs->VIwrite & (1 << REG_P)) { + assert( VU == &VU1 ); + info->p |= 4; + info->cycles = lregs->cycles; + + switch( VU->code & 0xff ) { + case 0xfd: info->pqinst = 0; break; //eatan + case 0x7c: info->pqinst = 0; break; //eatanxy + case 0x7d: info->pqinst = 0; break; //eatanzy + case 0xfe: info->pqinst = 1; break; //eexp + case 0xfc: info->pqinst = 2; break; //esin + case 0x3f: info->pqinst = 3; break; //erleng + case 0x3e: info->pqinst = 4; break; //eleng + case 0x3d: info->pqinst = 4; break; //ersadd + case 0xbd: info->pqinst = 4; break; //ersqrt + case 0xbe: info->pqinst = 5; break; //ercpr + case 0xbc: info->pqinst = 5; break; //esqrt + case 0x7e: info->pqinst = 5; break; //esum + case 0x3c: info->pqinst = 6; break; //esadd + default: assert(0); + } + } + else if (lregs->pipe == VUPIPE_EFU) { + info->p |= 8|1; + } + + if (lregs->VIread & (1 << REG_STATUS_FLAG)) info->statusflag|= VUOP_READ; + if (lregs->VIread & (1 << REG_MAC_FLAG)) info->macflag|= VUOP_READ; + + if (lregs->VIwrite & (1 << REG_STATUS_FLAG)) info->statusflag|= VUOP_WRITE; + if (lregs->VIwrite & (1 << REG_MAC_FLAG)) info->macflag|= VUOP_WRITE; + + if (lregs->VIread & (1 << REG_Q)) { info->q |= 2; } + if (lregs->VIread & (1 << REG_P)) { info->p |= 2; assert( VU == &VU1 ); } + + _recvuAddLowerStalls(VU, lregs); + } + + _recvuAddUpperStalls(VU, uregs); + _recvuTestPipes(VU); + + vucycle++; +} + +int eeVURecompileCode(VURegs *VU, _VURegsNum* regs) +{ + int info = 0; + int vfread0=-1, vfread1 = -1, vfwrite = -1, vfacc = -1, vftemp=-1; + + assert( regs != NULL ); + + if( regs->VFread0 ) _addNeededVFtoXMMreg(regs->VFread0); + if( regs->VFread1 ) _addNeededVFtoXMMreg(regs->VFread1); + if( regs->VFwrite ) _addNeededVFtoXMMreg(regs->VFwrite); + if( regs->VIread & (1<VIread & (1<VFread0 ) vfread0 = _allocVFtoXMMreg(VU, -1, regs->VFread0, MODE_READ); + else if( regs->VIread & (1<VFread1 ) vfread1 = _allocVFtoXMMreg(VU, -1, regs->VFread1, MODE_READ); + else if( (regs->VIread & (1<VFr1xyzw != 0xff) vfread1 = _allocVFtoXMMreg(VU, -1, 0, MODE_READ); + + if( regs->VIread & (1<VIwrite&(1<VIwrite & (1<VFwxyzw != 0xf?MODE_READ:0)); + } + + if( regs->VFwrite ) { + assert( !(regs->VIwrite&(1<VFwrite, MODE_WRITE|(regs->VFwxyzw != 0xf?MODE_READ:0)); + } + + if( vfacc>= 0 ) info |= PROCESS_EE_SET_ACC(vfacc); + if( vfwrite >= 0 ) { + if( regs->VFwrite == _Ft_ && vfread1 < 0 ) { + info |= PROCESS_EE_SET_T(vfwrite); + } + else { + assert( regs->VFwrite == _Fd_ ); + info |= PROCESS_EE_SET_D(vfwrite); + } + } + + if( vfread0 >= 0 ) info |= PROCESS_EE_SET_S(vfread0); + if( vfread1 >= 0 ) info |= PROCESS_EE_SET_T(vfread1); + + vftemp = _allocTempXMMreg(XMMT_FPS, -1); + info |= PROCESS_VU_SET_TEMP(vftemp); + + if( regs->VIwrite & (1 << REG_CLIP_FLAG) ) { + // CLIP inst, need two extra temp registers, put it EEREC_D and EEREC_ACC + int t1reg = _allocTempXMMreg(XMMT_FPS, -1); + int t2reg = _allocTempXMMreg(XMMT_FPS, -1); + + info |= PROCESS_EE_SET_D(t1reg); + info |= PROCESS_EE_SET_ACC(t2reg); + + _freeXMMreg(t1reg); // don't need + _freeXMMreg(t2reg); // don't need + } + else if( regs->VIwrite & (1<VI[reg].UL; + + if( read != 1 ) { + if( reg == REG_MAC_FLAG ) return (uptr)&VU->macflag; + if( reg == REG_CLIP_FLAG ) return (uptr)&VU->clipflag; + if( reg == REG_STATUS_FLAG ) return (uptr)&VU->statusflag; + if( reg == REG_Q ) return (uptr)&VU->q; + if( reg == REG_P ) return (uptr)&VU->p; + } + + return (uptr)&VU->VI[reg].UL; +} + +// gets a temp reg that is not EEREC_TEMP +int _vuGetTempXMMreg(int info) +{ + int t1reg = -1; + + if( _hasFreeXMMreg() ) { + t1reg = _allocTempXMMreg(XMMT_FPS, -1); + + if( t1reg == EEREC_TEMP ) { + if( _hasFreeXMMreg() ) { + int t = _allocTempXMMreg(XMMT_FPS, -1); + _freeXMMreg(t1reg); + t1reg = t; + } + else { + _freeXMMreg(t1reg); + t1reg = -1; + } + } + } + + return t1reg; +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// Misc VU Reg Flipping/Merging Functions +//------------------------------------------------------------------ +void _unpackVF_xyzw(int dstreg, int srcreg, int xyzw) +{ + switch (xyzw) { + case 0: SSE2_PSHUFD_XMM_to_XMM(dstreg, srcreg, 0x00); break; + case 1: SSE2_PSHUFD_XMM_to_XMM(dstreg, srcreg, 0x55); break; + case 2: SSE2_PSHUFD_XMM_to_XMM(dstreg, srcreg, 0xaa); break; + case 3: SSE2_PSHUFD_XMM_to_XMM(dstreg, srcreg, 0xff); break; + } +} + +void _unpackVFSS_xyzw(int dstreg, int srcreg, int xyzw) +{ + switch (xyzw) { + case 0: SSE_MOVSS_XMM_to_XMM(dstreg, srcreg); break; + case 1: if ( cpucaps.hasStreamingSIMD4Extensions ) SSE4_INSERTPS_XMM_to_XMM(dstreg, srcreg, _MM_MK_INSERTPS_NDX(1, 0, 0)); + else SSE2_PSHUFLW_XMM_to_XMM(dstreg, srcreg, 0xee); + break; + case 2: SSE_MOVHLPS_XMM_to_XMM(dstreg, srcreg); break; + case 3: if ( cpucaps.hasStreamingSIMD4Extensions ) SSE4_INSERTPS_XMM_to_XMM(dstreg, srcreg, _MM_MK_INSERTPS_NDX(3, 0, 0)); + else { SSE_MOVHLPS_XMM_to_XMM(dstreg, srcreg); SSE2_PSHUFLW_XMM_to_XMM(dstreg, dstreg, 0xee); } + break; + } +} + +void _vuFlipRegSS(VURegs * VU, int reg) +{ + assert( _XYZW_SS ); + if( _Y ) SSE2_PSHUFLW_XMM_to_XMM(reg, reg, 0x4e); + else if( _Z ) SSE_SHUFPS_XMM_to_XMM(reg, reg, 0xc6); + else if( _W ) SSE_SHUFPS_XMM_to_XMM(reg, reg, 0x27); +} + +void _vuFlipRegSS_xyzw(int reg, int xyzw) +{ + switch ( xyzw ) { + case 1: SSE2_PSHUFLW_XMM_to_XMM(reg, reg, 0x4e); break; + case 2: SSE_SHUFPS_XMM_to_XMM(reg, reg, 0xc6); break; + case 3: SSE_SHUFPS_XMM_to_XMM(reg, reg, 0x27); break; + } +} + +void _vuMoveSS(VURegs * VU, int dstreg, int srcreg) +{ + assert( _XYZW_SS ); + if( _Y ) _unpackVFSS_xyzw(dstreg, srcreg, 1); + else if( _Z ) _unpackVFSS_xyzw(dstreg, srcreg, 2); + else if( _W ) _unpackVFSS_xyzw(dstreg, srcreg, 3); + else _unpackVFSS_xyzw(dstreg, srcreg, 0); +} + +// 1 - src, 0 - dest wzyx +void VU_MERGE0(int dest, int src) { // 0000s +} +void VU_MERGE1(int dest, int src) { // 1000 + SSE_MOVHLPS_XMM_to_XMM(src, dest); + SSE_SHUFPS_XMM_to_XMM(dest, src, 0xc4); +} +void VU_MERGE1b(int dest, int src) { // 1000s + SSE_SHUFPS_XMM_to_XMM(src, src, 0x27); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0x27); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0x27); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0x27); +} +void VU_MERGE2(int dest, int src) { // 0100 + SSE_MOVHLPS_XMM_to_XMM(src, dest); + SSE_SHUFPS_XMM_to_XMM(dest, src, 0x64); +} +void VU_MERGE2b(int dest, int src) { // 0100s + SSE_SHUFPS_XMM_to_XMM(src, src, 0xC6); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xC6); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xC6); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xC6); +} +void VU_MERGE3(int dest, int src) { // 1100s + SSE_SHUFPS_XMM_to_XMM(dest, src, 0xe4); +} +void VU_MERGE4(int dest, int src) { // 0010 + SSE_MOVSS_XMM_to_XMM(src, dest); + SSE2_MOVSD_XMM_to_XMM(dest, src); +} +void VU_MERGE4b(int dest, int src) { // 0010s + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); +} +void VU_MERGE5(int dest, int src) { // 1010 + SSE_SHUFPS_XMM_to_XMM(dest, src, 0xd8); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xd8); +} +void VU_MERGE5b(int dest, int src) { // 1010s + SSE_SHUFPS_XMM_to_XMM(src, src, 0x27); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0x27); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0x27); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0x27); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); +} +void VU_MERGE6(int dest, int src) { // 0110 + SSE_SHUFPS_XMM_to_XMM(dest, src, 0x9c); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0x78); +} +void VU_MERGE6b(int dest, int src) { // 0110s + SSE_SHUFPS_XMM_to_XMM(src, src, 0xC6); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xC6); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xC6); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xC6); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); +} +void VU_MERGE7(int dest, int src) { // 1110 + SSE_MOVSS_XMM_to_XMM(src, dest); + SSE_MOVAPS_XMM_to_XMM(dest, src); +} +void VU_MERGE7b(int dest, int src) { // 1110s + SSE_SHUFPS_XMM_to_XMM(dest, src, 0xe4); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); +} +void VU_MERGE8(int dest, int src) { // 0001s + SSE_MOVSS_XMM_to_XMM(dest, src); +} +void VU_MERGE9(int dest, int src) { // 1001 + SSE_SHUFPS_XMM_to_XMM(dest, src, 0xc9); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xd2); +} +void VU_MERGE9b(int dest, int src) { // 1001s + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0x27); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0x27); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0x27); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0x27); +} +void VU_MERGE10(int dest, int src) { // 0101 + SSE_SHUFPS_XMM_to_XMM(dest, src, 0x8d); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0x72); +} +void VU_MERGE10b(int dest, int src) { // 0101s + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xC6); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xC6); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xC6); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xC6); +} +void VU_MERGE11(int dest, int src) { // 1101s + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(dest, src, 0xe4); +} +void VU_MERGE12(int dest, int src) { // 0011 + SSE2_MOVSD_XMM_to_XMM(dest, src); +} +void VU_MERGE13(int dest, int src) { // 1011 + SSE_MOVHLPS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, dest, 0x64); + SSE_MOVAPS_XMM_to_XMM(dest, src); +} +void VU_MERGE13b(int dest, int src) { // 1011s + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0x27); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0x27); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0x27); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0x27); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); +} +void VU_MERGE14(int dest, int src) { // 0111 + SSE_MOVHLPS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, dest, 0xc4); + SSE_MOVAPS_XMM_to_XMM(dest, src); +} +void VU_MERGE14b(int dest, int src) { // 0111s + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xE1); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xE1); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xC6); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xC6); + SSE_MOVSS_XMM_to_XMM(dest, src); + SSE_SHUFPS_XMM_to_XMM(src, src, 0xC6); + SSE_SHUFPS_XMM_to_XMM(dest, dest, 0xC6); +} +void VU_MERGE15(int dest, int src) { // 1111s + SSE_MOVAPS_XMM_to_XMM(dest, src); +} + +typedef void (*VUMERGEFN)(int dest, int src); + +static VUMERGEFN s_VuMerge[16] = { + VU_MERGE0, VU_MERGE1, VU_MERGE2, VU_MERGE3, + VU_MERGE4, VU_MERGE5, VU_MERGE6, VU_MERGE7, + VU_MERGE8, VU_MERGE9, VU_MERGE10, VU_MERGE11, + VU_MERGE12, VU_MERGE13, VU_MERGE14, VU_MERGE15 }; + +static VUMERGEFN s_VuMerge2[16] = { + VU_MERGE0, VU_MERGE1b, VU_MERGE2b, VU_MERGE3, + VU_MERGE4b, VU_MERGE5b, VU_MERGE6b, VU_MERGE7b, + VU_MERGE8, VU_MERGE9b, VU_MERGE10b, VU_MERGE11, + VU_MERGE12, VU_MERGE13b, VU_MERGE14b, VU_MERGE15 }; + +// Modifies the Source Reg! +void VU_MERGE_REGS_CUSTOM(int dest, int src, int xyzw) { + xyzw &= 0xf; + if ( (dest != src) && (xyzw != 0) ) { + if ( cpucaps.hasStreamingSIMD4Extensions && (xyzw != 0x8) && (xyzw != 0xf) ) { + xyzw = ((xyzw & 1) << 3) | ((xyzw & 2) << 1) | ((xyzw & 4) >> 1) | ((xyzw & 8) >> 3); + SSE4_BLENDPS_XMM_to_XMM(dest, src, xyzw); + } + else s_VuMerge[xyzw](dest, src); + } +} +// Doesn't Modify the Source Reg! (ToDo: s_VuMerge2() has room for optimization) +void VU_MERGE_REGS_SAFE(int dest, int src, int xyzw) { + xyzw &= 0xf; + if ( (dest != src) && (xyzw != 0) ) { + if ( cpucaps.hasStreamingSIMD4Extensions && (xyzw != 0x8) && (xyzw != 0xf) ) { + xyzw = ((xyzw & 1) << 3) | ((xyzw & 2) << 1) | ((xyzw & 4) >> 1) | ((xyzw & 8) >> 3); + SSE4_BLENDPS_XMM_to_XMM(dest, src, xyzw); + } + else s_VuMerge2[xyzw](dest, src); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// Misc VU Reg Clamping/Overflow Functions +//------------------------------------------------------------------ +#define CLAMP_NORMAL_SSE4(n) \ + SSE_MOVAPS_XMM_to_XMM(regTemp, regd);\ + SSE4_PMINUD_M128_to_XMM(regd, (uptr)&g_minvals_XYZW[n][0]);\ + SSE2_PSUBD_XMM_to_XMM(regTemp, regd);\ + SSE2_PCMPGTD_M128_to_XMM(regTemp, (uptr)&g_ones[0]);\ + SSE4_PMINSD_M128_to_XMM(regd, (uptr)&g_maxvals_XYZW[n][0]);\ + SSE2_PSLLD_I8_to_XMM(regTemp, 31);\ + SSE_XORPS_XMM_to_XMM(regd, regTemp); + +#define CLAMP_SIGN_SSE4(n) \ + SSE4_PMINSD_M128_to_XMM(regd, (uptr)&g_maxvals_XYZW[n][0]);\ + SSE4_PMINUD_M128_to_XMM(regd, (uptr)&g_minvals_XYZW[n][0]); + +void vFloat0(int regd, int regTemp) { } //0000 +void vFloat1(int regd, int regTemp) { //1000 + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); +} +void vFloat1b(int regd, int regTemp) { //1000 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(1); + } + else { + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + } +} +void vFloat1c(int regd, int regTemp) { //1000 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(1); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat2(int regd, int regTemp) { //0100 + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); +} +void vFloat2b(int regd, int regTemp) { //0100 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(2); + } + else { + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + } +} +void vFloat2c(int regd, int regTemp) { //0100 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(2); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat3(int regd, int regTemp) { //1100 + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x36); +} +void vFloat3b(int regd, int regTemp) { //1100 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(3); + } + else { + SSE2_MOVSD_XMM_to_XMM(regTemp, regd); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + SSE2_MOVSD_XMM_to_XMM(regd, regTemp); + } +} +void vFloat3c(int regd, int regTemp) { //1100 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(3); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x36); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat4(int regd, int regTemp) { //0010 + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); +} +void vFloat4b(int regd, int regTemp) { //0010 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(4); + } + else { + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + } +} +void vFloat4c(int regd, int regTemp) { //0010 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(4); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat5(int regd, int regTemp) { //1010 + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x2d); +} +void vFloat5b(int regd, int regTemp) { //1010 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(5); + } + else { + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x2d); + } +} +void vFloat5c(int regd, int regTemp) { //1010 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(5); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x2d); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat6(int regd, int regTemp) { //0110 + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc9); +} +void vFloat6b(int regd, int regTemp) { //0110 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(6); + } + else { + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc9); + } +} +void vFloat6c(int regd, int regTemp) { //0110 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(6); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc9); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat7(int regd, int regTemp) { //1110 + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x39); +} +void vFloat7_useEAX(int regd, int regTemp) { //1110 //EAX is Modified + SSE2_MOVD_XMM_to_R(EAX, regd); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + if ( cpucaps.hasStreamingSIMD4Extensions ) + SSE4_PINSRD_R32_to_XMM(regd, EAX, 0x00); + else { + SSE_PINSRW_R32_to_XMM(regd, EAX, 0); + SHR32ItoR(EAX, 16); + SSE_PINSRW_R32_to_XMM(regd, EAX, 1); + } +} +void vFloat7b(int regd, int regTemp) { //1110 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(7); + } + else { + SSE_MOVSS_XMM_to_XMM(regTemp, regd); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + SSE_MOVSS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat7c(int regd, int regTemp) { //1110 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(7); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x39); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat7c_useEAX(int regd, int regTemp) { //1110 //EAX is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(7); + } + else { + SSE2_MOVD_XMM_to_R(EAX, regd); + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + SSE2_MOVD_R_to_XMM(regTemp, EAX); + SSE_MOVSS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat8(int regd, int regTemp) { //0001 + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); +} +void vFloat8b(int regd, int regTemp) { //0001 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(8); + } + else { + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + } +} +void vFloat8c(int regd, int regTemp) { //0001 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(8); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat9(int regd, int regTemp) { //1001 + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); +} +void vFloat9b(int regd, int regTemp) { //1001 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(9); + } + else { + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + } +} +void vFloat9c(int regd, int regTemp) { //1001 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(9); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat10(int regd, int regTemp) { //0101 + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); +} +void vFloat10b(int regd, int regTemp) { //0101 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(10); + } + else { + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + } +} +void vFloat10c(int regd, int regTemp) { //0101 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(10); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat11(int regd, int regTemp) { //1101 + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x36); +} +void vFloat11_useEAX(int regd, int regTemp) { //1101 //EAX is Modified + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE2_MOVD_XMM_to_R(EAX, regd); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + if ( cpucaps.hasStreamingSIMD4Extensions ) + SSE4_PINSRD_R32_to_XMM(regd, EAX, 0x00); + else { + SSE_PINSRW_R32_to_XMM(regd, EAX, 0); + SHR32ItoR(EAX, 16); + SSE_PINSRW_R32_to_XMM(regd, EAX, 1); + } + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); +} +void vFloat11b(int regd, int regTemp) { //1101 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(11); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + SSE_MOVSS_XMM_to_XMM(regTemp, regd); + SSE2_MOVSD_XMM_to_XMM(regd, regTemp); + } +} +void vFloat11c(int regd, int regTemp) { //1101 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(11); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x36); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat11c_useEAX(int regd, int regTemp) { //1101 // EAX is modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(11); + } + else { + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE2_MOVD_XMM_to_R(EAX, regTemp); + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + SSE_ORPS_XMM_to_XMM(regTemp, regd); + SSE2_MOVD_R_to_XMM(regd, EAX); + SSE_MOVLHPS_XMM_to_XMM(regd, regTemp); + SSE_SHUFPS_XMM_to_XMM(regd, regTemp, 0xe2); + } +} +void vFloat12(int regd, int regTemp) { //0011 + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); +} +void vFloat12b(int regd, int regTemp) { //0011 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(12); + } + else { + SSE_MOVHLPS_XMM_to_XMM(regTemp, regd); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + SSE2_PUNPCKLQDQ_XMM_to_XMM(regd, regTemp); + } +} +void vFloat12c(int regd, int regTemp) { //0011 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(12); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat13(int regd, int regTemp) { //1011 + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x2d); +} +void vFloat13_useEAX(int regd, int regTemp) { //1011 // EAX is modified + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE2_MOVD_XMM_to_R(EAX, regd); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + if ( cpucaps.hasStreamingSIMD4Extensions ) + SSE4_PINSRD_R32_to_XMM(regd, EAX, 0x00); + else { + SSE_PINSRW_R32_to_XMM(regd, EAX, 0); + SHR32ItoR(EAX, 16); + SSE_PINSRW_R32_to_XMM(regd, EAX, 1); + } + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); +} +void vFloat13b(int regd, int regTemp) { //1011 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(13); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + SSE_MOVHLPS_XMM_to_XMM(regTemp, regd); + SSE_SHUFPS_XMM_to_XMM(regd, regTemp, 0x64); + } +} +void vFloat13c(int regd, int regTemp) { //1011 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(13); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x2d); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat13c_useEAX(int regd, int regTemp) { //1011 // EAX is modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(13); + } + else { + SSE2_PSHUFD_XMM_to_XMM(regTemp, regd, 0xd2); + SSE2_MOVD_XMM_to_R(EAX, regTemp); + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + SSE2_MOVD_R_to_XMM(regTemp, EAX); + SSE_SHUFPS_XMM_to_XMM(regTemp, regd, 0xf0); + SSE_SHUFPS_XMM_to_XMM(regd, regTemp, 0x84); + } +} +void vFloat14(int regd, int regTemp) { //0111 + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc9); +} +void vFloat14_useEAX(int regd, int regTemp) { //0111 // EAX is modified + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE2_MOVD_XMM_to_R(EAX, regd); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + if ( cpucaps.hasStreamingSIMD4Extensions ) + SSE4_PINSRD_R32_to_XMM(regd, EAX, 0x00); + else { + SSE_PINSRW_R32_to_XMM(regd, EAX, 0); + SHR32ItoR(EAX, 16); + SSE_PINSRW_R32_to_XMM(regd, EAX, 1); + } + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); +} +void vFloat14b(int regd, int regTemp) { //0111 //regTemp is Modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(14); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + SSE_MOVHLPS_XMM_to_XMM(regTemp, regd); + SSE_SHUFPS_XMM_to_XMM(regd, regTemp, 0xc4); + } +} +void vFloat14c(int regd, int regTemp) { //0111 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(14); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE2_PSHUFLW_XMM_to_XMM(regd, regd, 0x4e); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc6); + SSE_MINSS_M32_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXSS_M32_to_XMM(regd, (uptr)g_minvals); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0xc9); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} +void vFloat14c_useEAX(int regd, int regTemp) { //0111 // EAX is modified + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(14); + } + else { + SSE2_PSHUFD_XMM_to_XMM(regTemp, regd, 0x93); + SSE2_MOVD_XMM_to_R(EAX, regTemp); + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + SSE2_MOVD_R_to_XMM(regTemp, EAX); + SSE_SHUFPS_XMM_to_XMM(regTemp, regd, 0xa0); + SSE_SHUFPS_XMM_to_XMM(regd, regTemp, 0x24); + } +} +void vFloat15(int regd, int regTemp) { //1111 + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); +} +void vFloat15b(int regd, int regTemp) { //1111 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_NORMAL_SSE4(15); + } + else { + SSE_MINPS_M128_to_XMM(regd, (uptr)g_maxvals); + SSE_MAXPS_M128_to_XMM(regd, (uptr)g_minvals); + } +} +void vFloat15c(int regd, int regTemp) { //1111 + if ( cpucaps.hasStreamingSIMD4Extensions ) { + CLAMP_SIGN_SSE4(15); + } + else { + SSE_MOVAPS_XMM_to_XMM(regTemp, regd); + SSE_ANDPS_M128_to_XMM(regTemp, (uptr)&const_clip[4]); + SSE_MINPS_M128_to_XMM(regd, (uptr)&g_maxvals[0]); + SSE_MAXPS_M128_to_XMM(regd, (uptr)&g_minvals[0]); + SSE_ORPS_XMM_to_XMM(regd, regTemp); + } +} + +vFloat vFloats1[16] = { //regTemp is not modified + vFloat0, vFloat1, vFloat2, vFloat3, + vFloat4, vFloat5, vFloat6, vFloat7, + vFloat8, vFloat9, vFloat10, vFloat11, + vFloat12, vFloat13, vFloat14, vFloat15 }; + +vFloat vFloats1_useEAX[16] = { //regTemp is not modified but EAX is used + vFloat0, vFloat1, vFloat2, vFloat3, + vFloat4, vFloat5, vFloat6, vFloat7_useEAX, + vFloat8, vFloat9, vFloat10, vFloat11_useEAX, + vFloat12, vFloat13_useEAX, vFloat14_useEAX, vFloat15 }; + +vFloat vFloats2[16] = { //regTemp is modified + vFloat0, vFloat1, vFloat2, vFloat3b, + vFloat4, vFloat5b, vFloat6b, vFloat7b, + vFloat8, vFloat9b, vFloat10b, vFloat11b, + vFloat12b, vFloat13b, vFloat14b, vFloat15 }; + +vFloat vFloats2_MUL_MADD[16] = { //regTemp is modified (Faster than vFloats2 if dealing with Denormals and using SSE4) + vFloat0, vFloat1b, vFloat2b, vFloat3b, + vFloat4b, vFloat5b, vFloat6b, vFloat7b, + vFloat8b, vFloat9b, vFloat10b, vFloat11b, + vFloat12b, vFloat13b, vFloat14b, vFloat15b }; + +vFloat vFloats4[16] = { //regTemp is modified + vFloat0, vFloat1c, vFloat2c, vFloat3c, + vFloat4c, vFloat5c, vFloat6c, vFloat7c, + vFloat8c, vFloat9c, vFloat10c, vFloat11c, + vFloat1c, vFloat13c, vFloat14c, vFloat15c }; + +vFloat vFloats4_useEAX[16] = { //regTemp is modified and EAX is used + vFloat0, vFloat1c, vFloat2c, vFloat3c, + vFloat4c, vFloat5c, vFloat6c, vFloat7c_useEAX, + vFloat8c, vFloat9c, vFloat10c, vFloat11c_useEAX, + vFloat1c, vFloat13c_useEAX, vFloat14c_useEAX, vFloat15c }; + +//------------------------------------------------------------------ +// Clamping Functions (wrapper for vFloat* functions) +// vuFloat : "normal" clamping +// vuFloat_useEAX : "normal" clamping (faster but EAX is modified) +// vuFloat2 : "normal" clamping (fastest but regTemp is modified) +// vuFloat3 : "preserve sign" clamping for pointer +// vuFloat4 : "preserve sign" clamping (regTemp is modified; *FASTEST* on SSE4 CPUs) +// vuFloat4_useEAX : "preserve sign" clamping (faster but regTemp and EAX are modified) +// vuFloat5 : wrapper function for vuFloat2 and vuFloat4 +// vuFloat5_useEAX : wrapper function for vuFloat2 and vuFloat4_useEAX +// vuFloatExtra : for debugging +// +// Notice 1: vuFloat*_useEAX may be slower on AMD CPUs, which have independent execution pipeline for +// vector and scalar instructions (need checks) +// Notice 2: recVUMI_MUL_xyzw_toD and recVUMI_MADD_xyzw_toD use vFloats directly! +//------------------------------------------------------------------ + +// Clamps +/-NaN to +fMax and +/-Inf to +/-fMax (doesn't use any temp regs) +void vuFloat( int info, int regd, int XYZW) { + if( CHECK_VU_OVERFLOW ) { + /*if ( (XYZW != 0) && (XYZW != 8) && (XYZW != 0xF) ) { + int t1reg = _vuGetTempXMMreg(info); + if (t1reg >= 0) { + vuFloat2( regd, t1reg, XYZW ); + _freeXMMreg( t1reg ); + return; + } + }*/ + //vuFloatExtra(regd, XYZW); + vFloats1[XYZW](regd, regd); + } +} + +// Clamps +/-NaN to +fMax and +/-Inf to +/-fMax (uses EAX as a temp register; faster but **destroys EAX**) +void vuFloat_useEAX( int info, int regd, int XYZW) { + if( CHECK_VU_OVERFLOW ) { + vFloats1_useEAX[XYZW](regd, regd); + } +} + +// Clamps +/-NaN to +fMax and +/-Inf to +/-fMax (uses a temp reg) +void vuFloat2(int regd, int regTemp, int XYZW) { + if( CHECK_VU_OVERFLOW ) { + //vuFloatExtra(regd, XYZW); + vFloats2[XYZW](regd, regTemp); + } +} + +// Clamps +/-NaN and +/-Inf to +/-fMax (uses a temp reg) +void vuFloat4(int regd, int regTemp, int XYZW) { + if( CHECK_VU_OVERFLOW ) { + vFloats4[XYZW](regd, regTemp); + } +} + +// Clamps +/-NaN and +/-Inf to +/-fMax (uses a temp reg, and uses EAX as a temp register; faster but **destroys EAX**) +void vuFloat4_useEAX(int regd, int regTemp, int XYZW) { + if( CHECK_VU_OVERFLOW ) { + vFloats4_useEAX[XYZW](regd, regTemp); + } +} + +// Uses vuFloat4 or vuFloat2 depending on the CHECK_VU_SIGN_OVERFLOW setting +void vuFloat5(int regd, int regTemp, int XYZW) { + if (CHECK_VU_SIGN_OVERFLOW) { + vuFloat4(regd, regTemp, XYZW); + } + else vuFloat2(regd, regTemp, XYZW); +} + +// Uses vuFloat4_useEAX or vuFloat2 depending on the CHECK_VU_SIGN_OVERFLOW setting (uses EAX as a temp register; faster but **destoroyes EAX**) +void vuFloat5_useEAX(int regd, int regTemp, int XYZW) { + if (CHECK_VU_SIGN_OVERFLOW) { + vuFloat4_useEAX(regd, regTemp, XYZW); + } + else vuFloat2(regd, regTemp, XYZW); +} + +// Clamps +/-infs to +/-fMax, and +/-NaNs to +/-fMax +void vuFloat3(uptr x86ptr) { + u8* pjmp; + + if( CHECK_VU_OVERFLOW ) { + CMP32ItoM(x86ptr, 0x7f800000 ); + pjmp = JL8(0); // Signed Comparison + MOV32ItoM(x86ptr, 0x7f7fffff ); + x86SetJ8(pjmp); + + CMP32ItoM(x86ptr, 0xff800000 ); + pjmp = JB8(0); // Unsigned Comparison + MOV32ItoM(x86ptr, 0xff7fffff ); + x86SetJ8(pjmp); + } +} + +PCSX2_ALIGNED16(u64 vuFloatData[2]); +PCSX2_ALIGNED16(u64 vuFloatData2[2]); +// Makes NaN == 0, Infinities stay the same; Very Slow - Use only for debugging +void vuFloatExtra( int regd, int XYZW) { + int t1reg = (regd == 0) ? (regd + 1) : (regd - 1); + int t2reg = (regd <= 1) ? (regd + 2) : (regd - 2); + SSE_MOVAPS_XMM_to_M128( (uptr)vuFloatData, t1reg ); + SSE_MOVAPS_XMM_to_M128( (uptr)vuFloatData2, t2reg ); + + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); + SSE_CMPORDPS_XMM_to_XMM(t1reg, regd); + SSE_MOVAPS_XMM_to_XMM(t2reg, regd); + SSE_ANDPS_XMM_to_XMM(t2reg, t1reg); + VU_MERGE_REGS_CUSTOM(regd, t2reg, XYZW); + + SSE_MOVAPS_M128_to_XMM( t1reg, (uptr)vuFloatData ); + SSE_MOVAPS_M128_to_XMM( t2reg, (uptr)vuFloatData2 ); +} + +static PCSX2_ALIGNED16(u32 tempRegX[]) = {0x00000000, 0x00000000, 0x00000000, 0x00000000}; + +// Called by testWhenOverflow() function +void testPrintOverflow() { + tempRegX[0] &= 0xff800000; + tempRegX[1] &= 0xff800000; + tempRegX[2] &= 0xff800000; + tempRegX[3] &= 0xff800000; + if ( (tempRegX[0] == 0x7f800000) || (tempRegX[1] == 0x7f800000) || (tempRegX[2] == 0x7f800000) || (tempRegX[3] == 0x7f800000) ) + SysPrintf( "VU OVERFLOW!: Changing to +Fmax!!!!!!!!!!!!\n" ); + if ( (tempRegX[0] == 0xff800000) || (tempRegX[1] == 0xff800000) || (tempRegX[2] == 0xff800000) || (tempRegX[3] == 0xff800000) ) + SysPrintf( "VU OVERFLOW!: Changing to -Fmax!!!!!!!!!!!!\n" ); +} + +// Outputs to the console when overflow has occured. +void testWhenOverflow(int info, int regd, int t0reg) { + SSE_MOVAPS_XMM_to_M128((uptr)tempRegX, regd); + CALLFunc((uptr)testPrintOverflow); +} + +// Sets NaN Mode, used by Patches (currently does nothing) +void SetVUNanMode(int mode) +{ + g_VuNanHandling = mode; + if ( mode ) SysPrintf("enabling vunan mode"); +} \ No newline at end of file diff --git a/pcsx2/x86/iVUmicro.h b/pcsx2/x86/iVUmicro.h new file mode 100644 index 0000000000..44cee593b7 --- /dev/null +++ b/pcsx2/x86/iVUmicro.h @@ -0,0 +1,289 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2003 Pcsx2 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 __IVUMICRO_H__ +#define __IVUMICRO_H__ + +#include "VUmicro.h" + +extern u32 vudump; + +#define VU0_MEMSIZE 0x1000 +#define VU1_MEMSIZE 0x4000 + +void recResetVU0(); +void recExecuteVU0Block(); +void recClearVU0( u32 Addr, u32 Size ); + +void recVU1Init(); +void recVU1Shutdown(); +void recResetVU1(); +void recExecuteVU1Block(); +void recClearVU1( u32 Addr, u32 Size ); + + +u32 GetVIAddr(VURegs * VU, int reg, int read, int info); // returns the correct VI addr +void recUpdateFlags(VURegs * VU, int reg, int info); + +void _recvuTestPipes(VURegs * VU); +void _recvuFlushFDIV(VURegs * VU); +void _recvuTestUpperStalls(VURegs * VU, _VURegsNum *VUregsn); +void _recvuTestLowerStalls(VURegs * VU, _VURegsNum *VUregsn); +void _recvuAddUpperStalls(VURegs * VU, _VURegsNum *VUregsn); +void _recvuAddLowerStalls(VURegs * VU, _VURegsNum *VUregsn); + +#define VUOP_READ 2 +#define VUOP_WRITE 4 + +// save on mem +struct _vuopinfo { + int cycle; + int cycles; + u8 statusflag; + u8 macflag; + u8 clipflag; + u8 dummy; + u8 q; + u8 p; + u16 pqinst; // bit of instruction specifying index (srec only) +}; + +void SuperVUAnalyzeOp(VURegs *VU, _vuopinfo *info, _VURegsNum* pCodeRegs); +int eeVURecompileCode(VURegs *VU, _VURegsNum* regs); // allocates all the necessary regs and returns the indices +void VU1XGKICK_MTGSTransfer(u32 *pMem, u32 addr); // used for MTGS in XGKICK + +extern int vucycle; +typedef void (*vFloat)(int regd, int regTemp); +extern vFloat vFloats1[16]; +extern vFloat vFloats1_useEAX[16]; +extern vFloat vFloats2[16]; +extern vFloat vFloats2_MUL_MADD[16]; +extern vFloat vFloats4[16]; +extern vFloat vFloats4_useEAX[16]; +extern PCSX2_ALIGNED16(float s_fones[8]); +extern PCSX2_ALIGNED16(u32 s_mask[4]); +extern PCSX2_ALIGNED16(u32 s_expmask[4]); +extern PCSX2_ALIGNED16(u32 g_minvals[4]); +extern PCSX2_ALIGNED16(u32 g_maxvals[4]); +extern PCSX2_ALIGNED16(u32 const_clip[8]); + +u32 GetVIAddr(VURegs * VU, int reg, int read, int info); +int _vuGetTempXMMreg(int info); +void vuFloat(int info, int regd, int XYZW); +void vuFloat_useEAX(int regd, int regTemp, int XYZW); +void vuFloat2(int regd, int regTemp, int XYZW); +void vuFloat3(uptr x86ptr); +void vuFloat4(int regd, int regTemp, int XYZW); +void vuFloat4_useEAX(int regd, int regTemp, int XYZW); +void vuFloat5(int regd, int regTemp, int XYZW); +void vuFloat5_useEAX(int regd, int regTemp, int XYZW); +void _vuFlipRegSS(VURegs * VU, int reg); +void _vuFlipRegSS_xyzw(int reg, int xyzw); +void _vuMoveSS(VURegs * VU, int dstreg, int srcreg); +void _unpackVF_xyzw(int dstreg, int srcreg, int xyzw); +void _unpackVFSS_xyzw(int dstreg, int srcreg, int xyzw); +void VU_MERGE_REGS_CUSTOM(int dest, int src, int xyzw); +void VU_MERGE_REGS_SAFE(int dest, int src, int xyzw); +#define VU_MERGE_REGS(dest, src) { \ + VU_MERGE_REGS_CUSTOM(dest, src, _X_Y_Z_W); \ +} + +// use for allocating vi regs +#define ALLOCTEMPX86(mode) _allocX86reg(-1, X86TYPE_TEMP, 0, ((info&PROCESS_VU_SUPER)?0:MODE_NOFRAME)|mode) +#define ALLOCVI(vi, mode) _allocX86reg(-1, X86TYPE_VI|((VU==&VU1)?X86TYPE_VU1:0), vi, ((info&PROCESS_VU_SUPER)?0:MODE_NOFRAME)|mode) +#define ADD_VI_NEEDED(vi) _addNeededX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), vi); + +#define SWAP(x, y) *(u32*)&y ^= *(u32*)&x ^= *(u32*)&y ^= *(u32*)&x; + +/***************************************** + VU Micromode Upper instructions +*****************************************/ + +void recVUMI_ABS(VURegs *vuRegs, int info); +void recVUMI_ADD(VURegs *vuRegs, int info); +void recVUMI_ADDi(VURegs *vuRegs, int info); +void recVUMI_ADDq(VURegs *vuRegs, int info); +void recVUMI_ADDx(VURegs *vuRegs, int info); +void recVUMI_ADDy(VURegs *vuRegs, int info); +void recVUMI_ADDz(VURegs *vuRegs, int info); +void recVUMI_ADDw(VURegs *vuRegs, int info); +void recVUMI_ADDA(VURegs *vuRegs, int info); +void recVUMI_ADDAi(VURegs *vuRegs, int info); +void recVUMI_ADDAq(VURegs *vuRegs, int info); +void recVUMI_ADDAx(VURegs *vuRegs, int info); +void recVUMI_ADDAy(VURegs *vuRegs, int info); +void recVUMI_ADDAz(VURegs *vuRegs, int info); +void recVUMI_ADDAw(VURegs *vuRegs, int info); +void recVUMI_SUB(VURegs *vuRegs, int info); +void recVUMI_SUBi(VURegs *vuRegs, int info); +void recVUMI_SUBq(VURegs *vuRegs, int info); +void recVUMI_SUBx(VURegs *vuRegs, int info); +void recVUMI_SUBy(VURegs *vuRegs, int info); +void recVUMI_SUBz(VURegs *vuRegs, int info); +void recVUMI_SUBw(VURegs *vuRegs, int info); +void recVUMI_SUBA(VURegs *vuRegs, int info); +void recVUMI_SUBAi(VURegs *vuRegs, int info); +void recVUMI_SUBAq(VURegs *vuRegs, int info); +void recVUMI_SUBAx(VURegs *vuRegs, int info); +void recVUMI_SUBAy(VURegs *vuRegs, int info); +void recVUMI_SUBAz(VURegs *vuRegs, int info); +void recVUMI_SUBAw(VURegs *vuRegs, int info); +void recVUMI_MUL(VURegs *vuRegs, int info); +void recVUMI_MULi(VURegs *vuRegs, int info); +void recVUMI_MULq(VURegs *vuRegs, int info); +void recVUMI_MULx(VURegs *vuRegs, int info); +void recVUMI_MULy(VURegs *vuRegs, int info); +void recVUMI_MULz(VURegs *vuRegs, int info); +void recVUMI_MULw(VURegs *vuRegs, int info); +void recVUMI_MULA(VURegs *vuRegs, int info); +void recVUMI_MULAi(VURegs *vuRegs, int info); +void recVUMI_MULAq(VURegs *vuRegs, int info); +void recVUMI_MULAx(VURegs *vuRegs, int info); +void recVUMI_MULAy(VURegs *vuRegs, int info); +void recVUMI_MULAz(VURegs *vuRegs, int info); +void recVUMI_MULAw(VURegs *vuRegs, int info); +void recVUMI_MADD(VURegs *vuRegs, int info); +void recVUMI_MADDi(VURegs *vuRegs, int info); +void recVUMI_MADDq(VURegs *vuRegs, int info); +void recVUMI_MADDx(VURegs *vuRegs, int info); +void recVUMI_MADDy(VURegs *vuRegs, int info); +void recVUMI_MADDz(VURegs *vuRegs, int info); +void recVUMI_MADDw(VURegs *vuRegs, int info); +void recVUMI_MADDA(VURegs *vuRegs, int info); +void recVUMI_MADDAi(VURegs *vuRegs, int info); +void recVUMI_MADDAq(VURegs *vuRegs, int info); +void recVUMI_MADDAx(VURegs *vuRegs, int info); +void recVUMI_MADDAy(VURegs *vuRegs, int info); +void recVUMI_MADDAz(VURegs *vuRegs, int info); +void recVUMI_MADDAw(VURegs *vuRegs, int info); +void recVUMI_MSUB(VURegs *vuRegs, int info); +void recVUMI_MSUBi(VURegs *vuRegs, int info); +void recVUMI_MSUBq(VURegs *vuRegs, int info); +void recVUMI_MSUBx(VURegs *vuRegs, int info); +void recVUMI_MSUBy(VURegs *vuRegs, int info); +void recVUMI_MSUBz(VURegs *vuRegs, int info); +void recVUMI_MSUBw(VURegs *vuRegs, int info); +void recVUMI_MSUBA(VURegs *vuRegs, int info); +void recVUMI_MSUBAi(VURegs *vuRegs, int info); +void recVUMI_MSUBAq(VURegs *vuRegs, int info); +void recVUMI_MSUBAx(VURegs *vuRegs, int info); +void recVUMI_MSUBAy(VURegs *vuRegs, int info); +void recVUMI_MSUBAz(VURegs *vuRegs, int info); +void recVUMI_MSUBAw(VURegs *vuRegs, int info); +void recVUMI_MAX(VURegs *vuRegs, int info); +void recVUMI_MAXi(VURegs *vuRegs, int info); +void recVUMI_MAXx(VURegs *vuRegs, int info); +void recVUMI_MAXy(VURegs *vuRegs, int info); +void recVUMI_MAXz(VURegs *vuRegs, int info); +void recVUMI_MAXw(VURegs *vuRegs, int info); +void recVUMI_MINI(VURegs *vuRegs, int info); +void recVUMI_MINIi(VURegs *vuRegs, int info); +void recVUMI_MINIx(VURegs *vuRegs, int info); +void recVUMI_MINIy(VURegs *vuRegs, int info); +void recVUMI_MINIz(VURegs *vuRegs, int info); +void recVUMI_MINIw(VURegs *vuRegs, int info); +void recVUMI_OPMULA(VURegs *vuRegs, int info); +void recVUMI_OPMSUB(VURegs *vuRegs, int info); +void recVUMI_NOP(VURegs *vuRegs, int info); +void recVUMI_FTOI0(VURegs *vuRegs, int info); +void recVUMI_FTOI4(VURegs *vuRegs, int info); +void recVUMI_FTOI12(VURegs *vuRegs, int info); +void recVUMI_FTOI15(VURegs *vuRegs, int info); +void recVUMI_ITOF0(VURegs *vuRegs, int info); +void recVUMI_ITOF4(VURegs *vuRegs, int info); +void recVUMI_ITOF12(VURegs *vuRegs, int info); +void recVUMI_ITOF15(VURegs *vuRegs, int info); +void recVUMI_CLIP(VURegs *vuRegs, int info); + +/***************************************** + VU Micromode Lower instructions +*****************************************/ + +void recVUMI_DIV(VURegs *vuRegs, int info); +void recVUMI_SQRT(VURegs *vuRegs, int info); +void recVUMI_RSQRT(VURegs *vuRegs, int info); +void recVUMI_IADD(VURegs *vuRegs, int info); +void recVUMI_IADDI(VURegs *vuRegs, int info); +void recVUMI_IADDIU(VURegs *vuRegs, int info); +void recVUMI_IAND(VURegs *vuRegs, int info); +void recVUMI_IOR(VURegs *vuRegs, int info); +void recVUMI_ISUB(VURegs *vuRegs, int info); +void recVUMI_ISUBIU(VURegs *vuRegs, int info); +void recVUMI_MOVE(VURegs *vuRegs, int info); +void recVUMI_MFIR(VURegs *vuRegs, int info); +void recVUMI_MTIR(VURegs *vuRegs, int info); +void recVUMI_MR32(VURegs *vuRegs, int info); +void recVUMI_LQ(VURegs *vuRegs, int info); +void recVUMI_LQD(VURegs *vuRegs, int info); +void recVUMI_LQI(VURegs *vuRegs, int info); +void recVUMI_SQ(VURegs *vuRegs, int info); +void recVUMI_SQD(VURegs *vuRegs, int info); +void recVUMI_SQI(VURegs *vuRegs, int info); +void recVUMI_ILW(VURegs *vuRegs, int info); +void recVUMI_ISW(VURegs *vuRegs, int info); +void recVUMI_ILWR(VURegs *vuRegs, int info); +void recVUMI_ISWR(VURegs *vuRegs, int info); +void recVUMI_LOI(VURegs *vuRegs, int info); +void recVUMI_RINIT(VURegs *vuRegs, int info); +void recVUMI_RGET(VURegs *vuRegs, int info); +void recVUMI_RNEXT(VURegs *vuRegs, int info); +void recVUMI_RXOR(VURegs *vuRegs, int info); +void recVUMI_WAITQ(VURegs *vuRegs, int info); +void recVUMI_FSAND(VURegs *vuRegs, int info); +void recVUMI_FSEQ(VURegs *vuRegs, int info); +void recVUMI_FSOR(VURegs *vuRegs, int info); +void recVUMI_FSSET(VURegs *vuRegs, int info); +void recVUMI_FMAND(VURegs *vuRegs, int info); +void recVUMI_FMEQ(VURegs *vuRegs, int info); +void recVUMI_FMOR(VURegs *vuRegs, int info); +void recVUMI_FCAND(VURegs *vuRegs, int info); +void recVUMI_FCEQ(VURegs *vuRegs, int info); +void recVUMI_FCOR(VURegs *vuRegs, int info); +void recVUMI_FCSET(VURegs *vuRegs, int info); +void recVUMI_FCGET(VURegs *vuRegs, int info); +void recVUMI_IBEQ(VURegs *vuRegs, int info); +void recVUMI_IBGEZ(VURegs *vuRegs, int info); +void recVUMI_IBGTZ(VURegs *vuRegs, int info); +void recVUMI_IBLTZ(VURegs *vuRegs, int info); +void recVUMI_IBLEZ(VURegs *vuRegs, int info); +void recVUMI_IBNE(VURegs *vuRegs, int info); +void recVUMI_B(VURegs *vuRegs, int info); +void recVUMI_BAL(VURegs *vuRegs, int info); +void recVUMI_JR(VURegs *vuRegs, int info); +void recVUMI_JALR(VURegs *vuRegs, int info); +void recVUMI_MFP(VURegs *vuRegs, int info); +void recVUMI_WAITP(VURegs *vuRegs, int info); +void recVUMI_ESADD(VURegs *vuRegs, int info); +void recVUMI_ERSADD(VURegs *vuRegs, int info); +void recVUMI_ELENG(VURegs *vuRegs, int info); +void recVUMI_ERLENG(VURegs *vuRegs, int info); +void recVUMI_EATANxy(VURegs *vuRegs, int info); +void recVUMI_EATANxz(VURegs *vuRegs, int info); +void recVUMI_ESUM(VURegs *vuRegs, int info); +void recVUMI_ERCPR(VURegs *vuRegs, int info); +void recVUMI_ESQRT(VURegs *vuRegs, int info); +void recVUMI_ERSQRT(VURegs *vuRegs, int info); +void recVUMI_ESIN(VURegs *vuRegs, int info); +void recVUMI_EATAN(VURegs *vuRegs, int info); +void recVUMI_EEXP(VURegs *vuRegs, int info); +void recVUMI_XGKICK(VURegs *vuRegs, int info); +void recVUMI_XTOP(VURegs *vuRegs, int info); +void recVUMI_XITOP(VURegs *vuRegs, int info); +void recVUMI_XTOP( VURegs *VU , int info); + +#endif /* __IVUMICRO_H__ */ diff --git a/pcsx2/x86/iVUmicroLower.cpp b/pcsx2/x86/iVUmicroLower.cpp new file mode 100644 index 0000000000..5def6d51e3 --- /dev/null +++ b/pcsx2/x86/iVUmicroLower.cpp @@ -0,0 +1,1983 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "GS.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "iMMI.h" +#include "iFPU.h" +#include "iCOP0.h" +#include "VUmicro.h" +#include "VUflags.h" +#include "iVUmicro.h" +#include "iVUops.h" +#include "iVUzerorec.h" +//------------------------------------------------------------------ + +//------------------------------------------------------------------ +// Helper Macros +//------------------------------------------------------------------ +#define _Ft_ (( VU->code >> 16) & 0x1F) // The rt part of the instruction register +#define _Fs_ (( VU->code >> 11) & 0x1F) // The rd part of the instruction register +#define _Fd_ (( VU->code >> 6) & 0x1F) // The sa part of the instruction register + +#define _X (( VU->code>>24) & 0x1) +#define _Y (( VU->code>>23) & 0x1) +#define _Z (( VU->code>>22) & 0x1) +#define _W (( VU->code>>21) & 0x1) + +#define _XYZW_SS (_X+_Y+_Z+_W==1) + +#define _Fsf_ (( VU->code >> 21) & 0x03) +#define _Ftf_ (( VU->code >> 23) & 0x03) + +#define _Imm11_ (s32)(VU->code & 0x400 ? 0xfffffc00 | (VU->code & 0x3ff) : VU->code & 0x3ff) +#define _UImm11_ (s32)(VU->code & 0x7ff) + +#define VU_VFx_ADDR(x) (uptr)&VU->VF[x].UL[0] +#define VU_VFy_ADDR(x) (uptr)&VU->VF[x].UL[1] +#define VU_VFz_ADDR(x) (uptr)&VU->VF[x].UL[2] +#define VU_VFw_ADDR(x) (uptr)&VU->VF[x].UL[3] + +#define VU_REGR_ADDR (uptr)&VU->VI[REG_R] +#define VU_REGQ_ADDR (uptr)&VU->VI[REG_Q] +#define VU_REGMAC_ADDR (uptr)&VU->VI[REG_MAC_FLAG] + +#define VU_VI_ADDR(x, read) GetVIAddr(VU, x, read, info) + +#define VU_ACCx_ADDR (uptr)&VU->ACC.UL[0] +#define VU_ACCy_ADDR (uptr)&VU->ACC.UL[1] +#define VU_ACCz_ADDR (uptr)&VU->ACC.UL[2] +#define VU_ACCw_ADDR (uptr)&VU->ACC.UL[3] + +#define _X_Y_Z_W ((( VU->code >> 21 ) & 0xF ) ) + + +static const PCSX2_ALIGNED16(u32 VU_ONE[4]) = {0x3f800000, 0xffffffff, 0xffffffff, 0xffffffff}; +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// *VU Lower Instructions!* +// +// Note: * = Checked for errors by cottonvibes +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// DIV* +//------------------------------------------------------------------ +PCSX2_ALIGNED16(u64 DIV_TEMP_XMM[2]); +void recVUMI_DIV(VURegs *VU, int info) +{ + u8 *pjmp, *pjmp1; + u32 *ajmp32, *bjmp32; + + //SysPrintf("recVUMI_DIV()\n"); + AND32ItoM(VU_VI_ADDR(REG_STATUS_FLAG, 2), 0xFCF); // Clear D/I flags + + // FT can be zero here! so we need to check if its zero and set the correct flag. + SSE_XORPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); // Clear EEREC_TEMP + SSE_CMPEQPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); // Set all F's if each vector is zero + + SSE_MOVMSKPS_XMM_to_R32( EAX, EEREC_TEMP); // Move the sign bits of the previous calculation + + AND32ItoR( EAX, (1<<_Ftf_) ); // Grab "Is Zero" bits from the previous calculation + ajmp32 = JZ32(0); // Skip if none are + + SSE_XORPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); // Clear EEREC_TEMP + SSE_CMPEQPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); // Set all F's if each vector is zero + SSE_MOVMSKPS_XMM_to_R32(EAX, EEREC_TEMP); // Move the sign bits of the previous calculation + + AND32ItoR( EAX, (1<<_Fsf_) ); // Grab "Is Zero" bits from the previous calculation + pjmp = JZ8(0); + OR32ItoM( VU_VI_ADDR(REG_STATUS_FLAG, 2), 0x410 ); // Set invalid flag (0/0) + pjmp1 = JMP8(0); + x86SetJ8(pjmp); + OR32ItoM( VU_VI_ADDR(REG_STATUS_FLAG, 2), 0x820 ); // Zero divide (only when not 0/0) + x86SetJ8(pjmp1); + + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_S, _Fsf_); + + _vuFlipRegSS_xyzw(EEREC_T, _Ftf_); + SSE_XORPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + _vuFlipRegSS_xyzw(EEREC_T, _Ftf_); + + SSE_ANDPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_clip[4]); + SSE_ORPS_M128_to_XMM(EEREC_TEMP, (uptr)&g_maxvals[0]); // If division by zero, then EEREC_TEMP = +/- fmax + + bjmp32 = JMP32(0); + + x86SetJ32(ajmp32); + + if (CHECK_VU_EXTRA_OVERFLOW) { + vuFloat5_useEAX(EEREC_S, EEREC_TEMP, (1 << (3-_Fsf_))); + vuFloat5_useEAX(EEREC_T, EEREC_TEMP, (1 << (3-_Ftf_))); + } + + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_S, _Fsf_); + + _vuFlipRegSS_xyzw(EEREC_T, _Ftf_); + SSE_DIVSS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + _vuFlipRegSS_xyzw(EEREC_T, _Ftf_); + + vuFloat_useEAX(info, EEREC_TEMP, 0x8); + + x86SetJ32(bjmp32); + + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_Q, 0), EEREC_TEMP); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// SQRT* +//------------------------------------------------------------------ +void recVUMI_SQRT( VURegs *VU, int info ) +{ + u8* pjmp; + //SysPrintf("recVUMI_SQRT()\n"); + + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, _Ftf_); + AND32ItoM(VU_VI_ADDR(REG_STATUS_FLAG, 2), 0xFCF); // Clear D/I flags + + /* Check for negative sqrt */ + SSE_MOVMSKPS_XMM_to_R32(EAX, EEREC_TEMP); + AND32ItoR(EAX, 1); //Check sign + pjmp = JZ8(0); //Skip if none are + OR32ItoM(VU_VI_ADDR(REG_STATUS_FLAG, 2), 0x410); // Invalid Flag - Negative number sqrt + x86SetJ8(pjmp); + + SSE_ANDPS_M128_to_XMM(EEREC_TEMP, (uptr)const_clip); // Do a cardinal sqrt + if (CHECK_VU_OVERFLOW) SSE_MINSS_M32_to_XMM(EEREC_TEMP, (uptr)g_maxvals); // Clamp infinities (only need to do positive clamp since EEREC_TEMP is positive) + SSE_SQRTSS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_Q, 0), EEREC_TEMP); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// RSQRT* +//------------------------------------------------------------------ +PCSX2_ALIGNED16(u64 RSQRT_TEMP_XMM[2]); +void recVUMI_RSQRT(VURegs *VU, int info) +{ + u8 *ajmp8, *bjmp8; + int t1reg, t1boolean; + //SysPrintf("recVUMI_RSQRT()\n"); + + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, _Ftf_); + AND32ItoM(VU_VI_ADDR(REG_STATUS_FLAG, 2), 0xFCF); // Clear D/I flags + + /* Check for negative divide */ + SSE_MOVMSKPS_XMM_to_R32(EAX, EEREC_TEMP); + AND32ItoR(EAX, 1); //Check sign + ajmp8 = JZ8(0); //Skip if none are + OR32ItoM(VU_VI_ADDR(REG_STATUS_FLAG, 2), 0x410); // Invalid Flag - Negative number sqrt + x86SetJ8(ajmp8); + + SSE_ANDPS_M128_to_XMM(EEREC_TEMP, (uptr)const_clip); // Do a cardinal sqrt + if (CHECK_VU_OVERFLOW) SSE_MINSS_M32_to_XMM(EEREC_TEMP, (uptr)g_maxvals); // Clamp Infinities to Fmax + SSE_SQRTSS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + + t1reg = _vuGetTempXMMreg(info); + if( t1reg < 0 ) { + for (t1reg = 0; ( (t1reg == EEREC_TEMP) || (t1reg == EEREC_S) ); t1reg++) + ; // Makes t1reg not be EEREC_TEMP or EEREC_S. + SSE_MOVAPS_XMM_to_M128( (uptr)&RSQRT_TEMP_XMM[0], t1reg ); // backup data in t1reg to a temp address + t1boolean = 1; + } + else t1boolean = 0; + + // Ft can still be zero here! so we need to check if its zero and set the correct flag. + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); // Clear t1reg + SSE_CMPEQSS_XMM_to_XMM(t1reg, EEREC_TEMP); // Set all F's if each vector is zero + + SSE_MOVMSKPS_XMM_to_R32(EAX, t1reg); // Move the sign bits of the previous calculation + + AND32ItoR( EAX, 0x01 ); // Grab "Is Zero" bits from the previous calculation + ajmp8 = JZ8(0); // Skip if none are + OR32ItoM(VU_VI_ADDR(REG_STATUS_FLAG, 2), 0x820); // Zero divide flag + + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_S, _Fsf_); + SSE_ANDPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_clip[4]); + SSE_ORPS_M128_to_XMM(EEREC_TEMP, (uptr)&g_maxvals[0]); // EEREC_TEMP = +/-Max + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_Q, 0), EEREC_TEMP); + bjmp8 = JMP8(0); + x86SetJ8(ajmp8); + + _unpackVFSS_xyzw(t1reg, EEREC_S, _Fsf_); + if (CHECK_VU_EXTRA_OVERFLOW) vuFloat_useEAX(info, t1reg, 0x8); // Clamp Infinities + SSE_DIVSS_XMM_to_XMM(t1reg, EEREC_TEMP); + vuFloat_useEAX(info, t1reg, 0x8); + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_Q, 0), t1reg); + + x86SetJ8(bjmp8); + + if (t1boolean) SSE_MOVAPS_M128_to_XMM( t1reg, (uptr)&RSQRT_TEMP_XMM[0] ); // restore t1reg data + else _freeXMMreg(t1reg); // free t1reg +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// _addISIMMtoIT() - Used in IADDI, IADDIU, and ISUBIU instructions +//------------------------------------------------------------------ +void _addISIMMtoIT(VURegs *VU, s16 imm, int info) +{ + int fsreg = -1, ftreg; + if (_Ft_ == 0) return; + + if( _Fs_ == 0 ) { + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + MOV32ItoR(ftreg, imm&0xffff); + return; + } + + ADD_VI_NEEDED(_Ft_); + fsreg = ALLOCVI(_Fs_, MODE_READ); + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + + if ( _Ft_ == _Fs_ ) { + if (imm != 0 ) ADD16ItoR(ftreg, imm); + } + else { + if( imm ) { + LEA32RtoR(ftreg, fsreg, imm); + MOVZX32R16toR(ftreg, ftreg); + } + else MOV32RtoR(ftreg, fsreg); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// IADDI +//------------------------------------------------------------------ +void recVUMI_IADDI(VURegs *VU, int info) +{ + s16 imm; + + if ( _Ft_ == 0 ) return; + //SysPrintf("recVUMI_IADDI \n"); + imm = ( VU->code >> 6 ) & 0x1f; + imm = ( imm & 0x10 ? 0xfff0 : 0) | ( imm & 0xf ); + _addISIMMtoIT(VU, imm, info); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// IADDIU +//------------------------------------------------------------------ +void recVUMI_IADDIU(VURegs *VU, int info) +{ + s16 imm; + + if ( _Ft_ == 0 ) return; + //SysPrintf("recVUMI_IADDIU \n"); + imm = ( ( VU->code >> 10 ) & 0x7800 ) | ( VU->code & 0x7ff ); + _addISIMMtoIT(VU, imm, info); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// IADD +//------------------------------------------------------------------ +void recVUMI_IADD( VURegs *VU, int info ) +{ + int fdreg, fsreg = -1, ftreg = -1; + if ( _Fd_ == 0 ) return; + //SysPrintf("recVUMI_IADD \n"); + if ( ( _Ft_ == 0 ) && ( _Fs_ == 0 ) ) { + fdreg = ALLOCVI(_Fd_, MODE_WRITE); + XOR32RtoR(fdreg, fdreg); + return; + } + + ADD_VI_NEEDED(_Fs_); + ADD_VI_NEEDED(_Ft_); + fdreg = ALLOCVI(_Fd_, MODE_WRITE); + + if ( _Fs_ == 0 ) + { + if( (ftreg = _checkX86reg(X86TYPE_VI|((VU==&VU1)?X86TYPE_VU1:0), _Ft_, MODE_READ)) >= 0 ) { + if( fdreg != ftreg ) MOV32RtoR(fdreg, ftreg); + } + else MOVZX32M16toR(fdreg, VU_VI_ADDR(_Ft_, 1)); + } + else if ( _Ft_ == 0 ) + { + if( (fsreg = _checkX86reg(X86TYPE_VI|((VU==&VU1)?X86TYPE_VU1:0), _Fs_, MODE_READ)) >= 0 ) { + if( fdreg != fsreg ) MOV32RtoR(fdreg, fsreg); + } + else MOVZX32M16toR(fdreg, VU_VI_ADDR(_Fs_, 1)); + } + else { + //ADD_VI_NEEDED(_Ft_); + fsreg = ALLOCVI(_Fs_, MODE_READ); + ftreg = ALLOCVI(_Ft_, MODE_READ); + + if( fdreg == fsreg ) ADD32RtoR(fdreg, ftreg); + else if( fdreg == ftreg ) ADD32RtoR(fdreg, fsreg); + else LEA16RRtoR(fdreg, fsreg, ftreg); + MOVZX32R16toR(fdreg, fdreg); // neeed since don't know if fdreg's upper bits are 0 + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// IAND +//------------------------------------------------------------------ +void recVUMI_IAND( VURegs *VU, int info ) +{ + int fdreg, fsreg = -1, ftreg = -1; + if ( _Fd_ == 0 ) return; + //SysPrintf("recVUMI_IAND \n"); + if ( ( _Fs_ == 0 ) || ( _Ft_ == 0 ) ) { + fdreg = ALLOCVI(_Fd_, MODE_WRITE); + XOR32RtoR(fdreg, fdreg); + return; + } + + ADD_VI_NEEDED(_Fs_); + ADD_VI_NEEDED(_Ft_); + fdreg = ALLOCVI(_Fd_, MODE_WRITE); + + fsreg = ALLOCVI(_Fs_, MODE_READ); + ftreg = ALLOCVI(_Ft_, MODE_READ); + + if( fdreg == fsreg ) AND16RtoR(fdreg, ftreg); + else if( fdreg == ftreg ) AND16RtoR(fdreg, fsreg); + else { + MOV32RtoR(fdreg, ftreg); + AND32RtoR(fdreg, fsreg); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// IOR +//------------------------------------------------------------------ +void recVUMI_IOR( VURegs *VU, int info ) +{ + int fdreg, fsreg = -1, ftreg = -1; + if ( _Fd_ == 0 ) return; + //SysPrintf("recVUMI_IOR \n"); + if ( ( _Ft_ == 0 ) && ( _Fs_ == 0 ) ) { + fdreg = ALLOCVI(_Fd_, MODE_WRITE); + XOR32RtoR(fdreg, fdreg); + return; + } + + ADD_VI_NEEDED(_Fs_); + ADD_VI_NEEDED(_Ft_); + fdreg = ALLOCVI(_Fd_, MODE_WRITE); + + if ( _Fs_ == 0 ) + { + if( (ftreg = _checkX86reg(X86TYPE_VI|((VU==&VU1)?X86TYPE_VU1:0), _Ft_, MODE_READ)) >= 0 ) { + if( fdreg != ftreg ) MOV32RtoR(fdreg, ftreg); + } + else MOVZX32M16toR(fdreg, VU_VI_ADDR(_Ft_, 1)); + } + else if ( _Ft_ == 0 ) + { + if( (fsreg = _checkX86reg(X86TYPE_VI|((VU==&VU1)?X86TYPE_VU1:0), _Fs_, MODE_READ)) >= 0 ) { + if( fdreg != fsreg ) MOV32RtoR(fdreg, fsreg); + } + else MOVZX32M16toR(fdreg, VU_VI_ADDR(_Fs_, 1)); + } + else + { + fsreg = ALLOCVI(_Fs_, MODE_READ); + ftreg = ALLOCVI(_Ft_, MODE_READ); + + if( fdreg == fsreg ) OR16RtoR(fdreg, ftreg); + else if( fdreg == ftreg ) OR16RtoR(fdreg, fsreg); + else { + MOV32RtoR(fdreg, fsreg); + OR32RtoR(fdreg, ftreg); + } + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ISUB +//------------------------------------------------------------------ +void recVUMI_ISUB( VURegs *VU, int info ) +{ + int fdreg, fsreg = -1, ftreg = -1; + if ( _Fd_ == 0 ) return; + //SysPrintf("recVUMI_ISUB \n"); + if ( ( _Ft_ == 0 ) && ( _Fs_ == 0 ) ) { + fdreg = ALLOCVI(_Fd_, MODE_WRITE); + XOR32RtoR(fdreg, fdreg); + return; + } + + ADD_VI_NEEDED(_Fs_); + ADD_VI_NEEDED(_Ft_); + fdreg = ALLOCVI(_Fd_, MODE_WRITE); + + if ( _Fs_ == 0 ) + { + if( (ftreg = _checkX86reg(X86TYPE_VI|((VU==&VU1)?X86TYPE_VU1:0), _Ft_, MODE_READ)) >= 0 ) { + if( fdreg != ftreg ) MOV32RtoR(fdreg, ftreg); + } + else MOVZX32M16toR(fdreg, VU_VI_ADDR(_Ft_, 1)); + NEG16R(fdreg); + } + else if ( _Ft_ == 0 ) + { + if( (fsreg = _checkX86reg(X86TYPE_VI|((VU==&VU1)?X86TYPE_VU1:0), _Fs_, MODE_READ)) >= 0 ) { + if( fdreg != fsreg ) MOV32RtoR(fdreg, fsreg); + } + else MOVZX32M16toR(fdreg, VU_VI_ADDR(_Fs_, 1)); + } + else + { + fsreg = ALLOCVI(_Fs_, MODE_READ); + ftreg = ALLOCVI(_Ft_, MODE_READ); + + if( fdreg == fsreg ) SUB16RtoR(fdreg, ftreg); + else if( fdreg == ftreg ) { + SUB16RtoR(fdreg, fsreg); + NEG16R(fdreg); + } + else { + MOV32RtoR(fdreg, fsreg); + SUB16RtoR(fdreg, ftreg); + } + } +} +//------------------------------------------------------------------ + +//------------------------------------------------------------------ +// ISUBIU +//------------------------------------------------------------------ +void recVUMI_ISUBIU( VURegs *VU, int info ) +{ + s16 imm; + + if ( _Ft_ == 0 ) return; + //SysPrintf("recVUMI_ISUBIU \n"); + imm = ( ( VU->code >> 10 ) & 0x7800 ) | ( VU->code & 0x7ff ); + imm = -imm; + _addISIMMtoIT(VU, imm, info); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MOVE* +//------------------------------------------------------------------ +void recVUMI_MOVE( VURegs *VU, int info ) +{ + if ( (_Ft_ == 0) || (_X_Y_Z_W == 0) ) return; + //SysPrintf("recVUMI_MOVE \n"); + if (_X_Y_Z_W == 0x8) SSE_MOVSS_XMM_to_XMM(EEREC_T, EEREC_S); + else if (_X_Y_Z_W == 0xf) SSE_MOVAPS_XMM_to_XMM(EEREC_T, EEREC_S); + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MFIR* +//------------------------------------------------------------------ +void recVUMI_MFIR( VURegs *VU, int info ) +{ + if ( (_Ft_ == 0) || (_X_Y_Z_W == 0) ) return; + //SysPrintf("recVUMI_MFIR \n"); + _deleteX86reg(X86TYPE_VI|((VU==&VU1)?X86TYPE_VU1:0), _Fs_, 1); + + if( _XYZW_SS ) { + SSE2_MOVD_M32_to_XMM(EEREC_TEMP, VU_VI_ADDR(_Fs_, 1)-2); + _vuFlipRegSS(VU, EEREC_T); + SSE2_PSRAD_I8_to_XMM(EEREC_TEMP, 16); + SSE_MOVSS_XMM_to_XMM(EEREC_T, EEREC_TEMP); + _vuFlipRegSS(VU, EEREC_T); + } + else if (_X_Y_Z_W != 0xf) { + SSE2_MOVD_M32_to_XMM(EEREC_TEMP, VU_VI_ADDR(_Fs_, 1)-2); + SSE2_PSRAD_I8_to_XMM(EEREC_TEMP, 16); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0); + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + } + else { + SSE2_MOVD_M32_to_XMM(EEREC_T, VU_VI_ADDR(_Fs_, 1)-2); + SSE2_PSRAD_I8_to_XMM(EEREC_T, 16); + SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MTIR* +//------------------------------------------------------------------ +void recVUMI_MTIR( VURegs *VU, int info ) +{ + if ( _Ft_ == 0 ) return; + //SysPrintf("recVUMI_MTIR \n"); + _deleteX86reg(X86TYPE_VI|((VU==&VU1)?X86TYPE_VU1:0), _Ft_, 2); + + if( _Fsf_ == 0 ) { + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(_Ft_, 0), EEREC_S); + } + else { + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_S, _Fsf_); + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(_Ft_, 0), EEREC_TEMP); + } + + AND32ItoM(VU_VI_ADDR(_Ft_, 0), 0xffff); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MR32* +//------------------------------------------------------------------ +void recVUMI_MR32( VURegs *VU, int info ) +{ + if ( (_Ft_ == 0) || (_X_Y_Z_W == 0) ) return; + //SysPrintf("recVUMI_MR32 \n"); + if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x39); + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_T, EEREC_S); + SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0x39); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// _loadEAX() +// +// NOTE: If x86reg < 0, reads directly from offset +//------------------------------------------------------------------ +void _loadEAX(VURegs *VU, int x86reg, uptr offset, int info) +{ + assert( offset < 0x80000000 ); + + if( x86reg >= 0 ) { + switch(_X_Y_Z_W) { + case 3: // ZW + SSE_MOVHPS_RmOffset_to_XMM(EEREC_T, x86reg, offset+8); + break; + case 6: // YZ + SSE_SHUFPS_RmOffset_to_XMM(EEREC_T, x86reg, offset, 0x9c); + SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0x78); + break; + + case 8: // X + SSE_MOVSS_RmOffset_to_XMM(EEREC_TEMP, x86reg, offset); + SSE_MOVSS_XMM_to_XMM(EEREC_T, EEREC_TEMP); + break; + case 9: // XW + SSE_SHUFPS_RmOffset_to_XMM(EEREC_T, x86reg, offset, 0xc9); + SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0xd2); + break; + case 12: // XY + SSE_MOVLPS_RmOffset_to_XMM(EEREC_T, x86reg, offset); + break; + case 15: + if( VU == &VU1 ) SSE_MOVAPSRmtoROffset(EEREC_T, x86reg, offset); + else SSE_MOVUPSRmtoROffset(EEREC_T, x86reg, offset); + break; + default: + if( VU == &VU1 ) SSE_MOVAPSRmtoROffset(EEREC_TEMP, x86reg, offset); + else SSE_MOVUPSRmtoROffset(EEREC_TEMP, x86reg, offset); + + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + break; + } + } + else { + switch(_X_Y_Z_W) { + case 3: // ZW + SSE_MOVHPS_M64_to_XMM(EEREC_T, offset+8); + break; + case 6: // YZ + SSE_SHUFPS_M128_to_XMM(EEREC_T, offset, 0x9c); + SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0x78); + break; + case 8: // X + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, offset); + SSE_MOVSS_XMM_to_XMM(EEREC_T, EEREC_TEMP); + break; + case 9: // XW + SSE_SHUFPS_M128_to_XMM(EEREC_T, offset, 0xc9); + SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0xd2); + break; + case 12: // XY + SSE_MOVLPS_M64_to_XMM(EEREC_T, offset); + break; + case 15: + if( VU == &VU1 ) SSE_MOVAPS_M128_to_XMM(EEREC_T, offset); + else SSE_MOVUPS_M128_to_XMM(EEREC_T, offset); + break; + default: + if( VU == &VU1 ) SSE_MOVAPS_M128_to_XMM(EEREC_TEMP, offset); + else SSE_MOVUPS_M128_to_XMM(EEREC_TEMP, offset); + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + break; + } + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// recVUTransformAddr() +//------------------------------------------------------------------ +int recVUTransformAddr(int x86reg, VURegs* VU, int vireg, int imm) +{ + u8* pjmp[2]; + if( x86reg == EAX ) { + if (imm) ADD32ItoR(x86reg, imm); + } + else { + if( imm ) LEA32RtoR(EAX, x86reg, imm); + else MOV32RtoR(EAX, x86reg); + } + + if( VU == &VU1 ) { + AND32ItoR(EAX, 0x3ff); // wrap around + SHL32ItoR(EAX, 4); + } + else { + CMP32ItoR(EAX, 0x400); + pjmp[0] = JL8(0); // if addr >= 0x4000, reads VU1's VF regs and VI regs + AND32ItoR(EAX, 0x43f); + pjmp[1] = JMP8(0); + x86SetJ8(pjmp[0]); + AND32ItoR(EAX, 0xff); // if addr < 0x4000, wrap around + x86SetJ8(pjmp[1]); + + SHL32ItoR(EAX, 4); // multiply by 16 (shift left by 4) + } + + return EAX; +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// LQ +//------------------------------------------------------------------ +void recVUMI_LQ(VURegs *VU, int info) +{ + s16 imm; + if ( _Ft_ == 0 ) return; + //SysPrintf("recVUMI_LQ \n"); + imm = (VU->code & 0x400) ? (VU->code & 0x3ff) | 0xfc00 : (VU->code & 0x3ff); + if (_Fs_ == 0) { + _loadEAX(VU, -1, (uptr)GET_VU_MEM(VU, (u32)imm*16), info); + } + else { + int fsreg = ALLOCVI(_Fs_, MODE_READ); + _loadEAX(VU, recVUTransformAddr(fsreg, VU, _Fs_, imm), (uptr)VU->Mem, info); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// LQD +//------------------------------------------------------------------ +void recVUMI_LQD( VURegs *VU, int info ) +{ + int fsreg; + //SysPrintf("recVUMI_LQD \n"); + if ( _Fs_ != 0 ) { + fsreg = ALLOCVI(_Fs_, MODE_READ|MODE_WRITE); + SUB16ItoR( fsreg, 1 ); + } + + if ( _Ft_ == 0 ) return; + + if ( _Fs_ == 0 ) _loadEAX(VU, -1, (uptr)VU->Mem, info); + else _loadEAX(VU, recVUTransformAddr(fsreg, VU, _Fs_, 0), (uptr)VU->Mem, info); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// LQI +//------------------------------------------------------------------ +void recVUMI_LQI(VURegs *VU, int info) +{ + int fsreg; + //SysPrintf("recVUMI_LQI \n"); + if ( _Ft_ == 0 ) { + if( _Fs_ != 0 ) { + if( (fsreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_WRITE|MODE_READ)) >= 0 ) { + ADD16ItoR(fsreg, 1); + } + else { + ADD16ItoM( VU_VI_ADDR( _Fs_, 0 ), 1 ); + } + } + return; + } + + if (_Fs_ == 0) { + _loadEAX(VU, -1, (uptr)VU->Mem, info); + } + else { + fsreg = ALLOCVI(_Fs_, MODE_READ|MODE_WRITE); + _loadEAX(VU, recVUTransformAddr(fsreg, VU, _Fs_, 0), (uptr)VU->Mem, info); + ADD16ItoR( fsreg, 1 ); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// _saveEAX() +//------------------------------------------------------------------ +void _saveEAX(VURegs *VU, int x86reg, uptr offset, int info) +{ + assert( offset < 0x80000000 ); + + if ( _Fs_ == 0 ) { + if ( _XYZW_SS ) { + u32 c = _W ? 0x3f800000 : 0; + if ( x86reg >= 0 ) MOV32ItoRmOffset(x86reg, c, offset+(_W?12:(_Z?8:(_Y?4:0)))); + else MOV32ItoM(offset+(_W?12:(_Z?8:(_Y?4:0))), c); + } + else { + if ( x86reg >= 0 ) { + if ( _X ) MOV32ItoRmOffset(x86reg, 0x00000000, offset); + if ( _Y ) MOV32ItoRmOffset(x86reg, 0x00000000, offset+4); + if ( _Z ) MOV32ItoRmOffset(x86reg, 0x00000000, offset+8); + if ( _W ) MOV32ItoRmOffset(x86reg, 0x3f800000, offset+12); + } + else { + if ( _X ) MOV32ItoM(offset, 0x00000000); + if ( _Y ) MOV32ItoM(offset+4, 0x00000000); + if ( _Z ) MOV32ItoM(offset+8, 0x00000000); + if ( _W ) MOV32ItoM(offset+12, 0x3f800000); + } + } + return; + } + + switch ( _X_Y_Z_W ) { + case 1: // W + SSE2_PSHUFD_XMM_to_XMM(EEREC_TEMP, EEREC_S, 0x27); + if ( x86reg >= 0 ) SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+12); + else SSE_MOVSS_XMM_to_M32(offset+12, EEREC_TEMP); + break; + case 2: // Z + SSE_MOVHLPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if ( x86reg >= 0 ) SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+8); + else SSE_MOVSS_XMM_to_M32(offset+8, EEREC_TEMP); + break; + case 3: // ZW + if ( x86reg >= 0 ) SSE_MOVHPS_XMM_to_RmOffset(x86reg, EEREC_S, offset+8); + else SSE_MOVHPS_XMM_to_M64(offset+8, EEREC_S); + break; + case 4: // Y + SSE2_PSHUFLW_XMM_to_XMM(EEREC_TEMP, EEREC_S, 0x4e); + if ( x86reg >= 0 ) SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+4); + else SSE_MOVSS_XMM_to_M32(offset+4, EEREC_TEMP); + break; + case 5: // YW + SSE_SHUFPS_XMM_to_XMM(EEREC_S, EEREC_S, 0xB1); + SSE_MOVHLPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if ( x86reg >= 0 ) { + SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_S, offset+4); + SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+12); + } + else { + SSE_MOVSS_XMM_to_M32(offset+4, EEREC_S); + SSE_MOVSS_XMM_to_M32(offset+12, EEREC_TEMP); + } + SSE_SHUFPS_XMM_to_XMM(EEREC_S, EEREC_S, 0xB1); + break; + case 6: // YZ + SSE2_PSHUFD_XMM_to_XMM(EEREC_TEMP, EEREC_S, 0xc9); + if ( x86reg >= 0 ) SSE_MOVLPS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+4); + else SSE_MOVLPS_XMM_to_M64(offset+4, EEREC_TEMP); + break; + case 7: // YZW + SSE2_PSHUFD_XMM_to_XMM(EEREC_TEMP, EEREC_S, 0x93); //ZYXW + if ( x86reg >= 0 ) { + SSE_MOVHPS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+4); + SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+12); + } + else { + SSE_MOVHPS_XMM_to_M64(offset+4, EEREC_TEMP); + SSE_MOVSS_XMM_to_M32(offset+12, EEREC_TEMP); + } + break; + case 8: // X + if ( x86reg >= 0 ) SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_S, offset); + else SSE_MOVSS_XMM_to_M32(offset, EEREC_S); + break; + case 9: // XW + SSE_MOVHLPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if ( x86reg >= 0 ) SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_S, offset); + else SSE_MOVSS_XMM_to_M32(offset, EEREC_S); + + if ( cpucaps.hasStreamingSIMD3Extensions ) SSE3_MOVSLDUP_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + else SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x55); + + if ( x86reg >= 0 ) SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+12); + else SSE_MOVSS_XMM_to_M32(offset+12, EEREC_TEMP); + + break; + case 10: //XZ + SSE_MOVHLPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if ( x86reg >= 0 ) { + SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_S, offset); + SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+8); + } + else { + SSE_MOVSS_XMM_to_M32(offset, EEREC_S); + SSE_MOVSS_XMM_to_M32(offset+8, EEREC_TEMP); + } + break; + case 11: //XZW + if ( x86reg >= 0 ) { + SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_S, offset); + SSE_MOVHPS_XMM_to_RmOffset(x86reg, EEREC_S, offset+8); + } + else { + SSE_MOVSS_XMM_to_M32(offset, EEREC_S); + SSE_MOVHPS_XMM_to_M64(offset+8, EEREC_S); + } + break; + case 12: // XY + if ( x86reg >= 0 ) SSE_MOVLPS_XMM_to_RmOffset(x86reg, EEREC_S, offset+0); + else SSE_MOVLPS_XMM_to_M64(offset, EEREC_S); + break; + case 13: // XYW + SSE2_PSHUFD_XMM_to_XMM(EEREC_TEMP, EEREC_S, 0x4b); //YXZW + if ( x86reg >= 0 ) { + SSE_MOVHPS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+0); + SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+12); + } + else { + SSE_MOVHPS_XMM_to_M64(offset, EEREC_TEMP); + SSE_MOVSS_XMM_to_M32(offset+12, EEREC_TEMP); + } + break; + case 14: // XYZ + SSE_MOVHLPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if ( x86reg >= 0 ) { + SSE_MOVLPS_XMM_to_RmOffset(x86reg, EEREC_S, offset+0); + SSE_MOVSS_XMM_to_RmOffset(x86reg, EEREC_TEMP, offset+8); + } + else { + SSE_MOVLPS_XMM_to_M64(offset, EEREC_S); + SSE_MOVSS_XMM_to_M32(offset+8, EEREC_TEMP); + } + break; + case 15: // XYZW + if ( VU == &VU1 ) { + if( x86reg >= 0 ) SSE_MOVAPSRtoRmOffset(x86reg, EEREC_S, offset+0); + else SSE_MOVAPS_XMM_to_M128(offset, EEREC_S); + } + else { + if( x86reg >= 0 ) SSE_MOVUPSRtoRmOffset(x86reg, EEREC_S, offset+0); + else { + if( offset & 15 ) SSE_MOVUPS_XMM_to_M128(offset, EEREC_S); + else SSE_MOVAPS_XMM_to_M128(offset, EEREC_S); + } + } + break; + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// SQ +//------------------------------------------------------------------ +void recVUMI_SQ(VURegs *VU, int info) +{ + s16 imm; + //SysPrintf("recVUMI_SQ \n"); + imm = ( VU->code & 0x400) ? ( VU->code & 0x3ff) | 0xfc00 : ( VU->code & 0x3ff); + if ( _Ft_ == 0 ) _saveEAX(VU, -1, (uptr)GET_VU_MEM(VU, (int)imm * 16), info); + else { + int ftreg = ALLOCVI(_Ft_, MODE_READ); + _saveEAX(VU, recVUTransformAddr(ftreg, VU, _Ft_, imm), (uptr)VU->Mem, info); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// SQD +//------------------------------------------------------------------ +void recVUMI_SQD(VURegs *VU, int info) +{ + //SysPrintf("recVUMI_SQD \n"); + if (_Ft_ == 0) _saveEAX(VU, -1, (uptr)VU->Mem, info); + else { + int ftreg = ALLOCVI(_Ft_, MODE_READ|MODE_WRITE); + SUB16ItoR( ftreg, 1 ); + _saveEAX(VU, recVUTransformAddr(ftreg, VU, _Ft_, 0), (uptr)VU->Mem, info); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// SQI +//------------------------------------------------------------------ +void recVUMI_SQI(VURegs *VU, int info) +{ + //SysPrintf("recVUMI_SQI \n"); + if (_Ft_ == 0) _saveEAX(VU, -1, (uptr)VU->Mem, info); + else { + int ftreg = ALLOCVI(_Ft_, MODE_READ|MODE_WRITE); + _saveEAX(VU, recVUTransformAddr(ftreg, VU, _Ft_, 0), (uptr)VU->Mem, info); + ADD16ItoR( ftreg, 1 ); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ILW +//------------------------------------------------------------------ +void recVUMI_ILW(VURegs *VU, int info) +{ + int ftreg; + s16 imm, off; + + if ( ( _Ft_ == 0 ) || ( _X_Y_Z_W == 0 ) ) return; + //SysPrintf("recVUMI_ILW \n"); + imm = ( VU->code & 0x400) ? ( VU->code & 0x3ff) | 0xfc00 : ( VU->code & 0x3ff); + if (_X) off = 0; + else if (_Y) off = 4; + else if (_Z) off = 8; + else if (_W) off = 12; + + ADD_VI_NEEDED(_Fs_); + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + + if ( _Fs_ == 0 ) { + MOVZX32M16toR( ftreg, (uptr)GET_VU_MEM(VU, (int)imm * 16 + off) ); + } + else { + int fsreg = ALLOCVI(_Fs_, MODE_READ); + MOV32RmtoROffset(ftreg, recVUTransformAddr(fsreg, VU, _Fs_, imm), (uptr)VU->Mem + off); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ISW +//------------------------------------------------------------------ +void recVUMI_ISW( VURegs *VU, int info ) +{ + s16 imm; + //SysPrintf("recVUMI_ISW \n"); + imm = ( VU->code & 0x400) ? ( VU->code & 0x3ff) | 0xfc00 : ( VU->code & 0x3ff); + + if (_Fs_ == 0) { + uptr off = (uptr)GET_VU_MEM(VU, (int)imm * 16); + int ftreg = ALLOCVI(_Ft_, MODE_READ); + + if (_X) MOV32RtoM(off, ftreg); + if (_Y) MOV32RtoM(off+4, ftreg); + if (_Z) MOV32RtoM(off+8, ftreg); + if (_W) MOV32RtoM(off+12, ftreg); + } + else { + int x86reg, fsreg, ftreg; + + ADD_VI_NEEDED(_Ft_); + fsreg = ALLOCVI(_Fs_, MODE_READ); + ftreg = ALLOCVI(_Ft_, MODE_READ); + + x86reg = recVUTransformAddr(fsreg, VU, _Fs_, imm); + + if (_X) MOV32RtoRmOffset(x86reg, ftreg, (uptr)VU->Mem); + if (_Y) MOV32RtoRmOffset(x86reg, ftreg, (uptr)VU->Mem+4); + if (_Z) MOV32RtoRmOffset(x86reg, ftreg, (uptr)VU->Mem+8); + if (_W) MOV32RtoRmOffset(x86reg, ftreg, (uptr)VU->Mem+12); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ILWR +//------------------------------------------------------------------ +void recVUMI_ILWR( VURegs *VU, int info ) +{ + int off, ftreg; + + if ( ( _Ft_ == 0 ) || ( _X_Y_Z_W == 0 ) ) return; + //SysPrintf("recVUMI_ILWR \n"); + if (_X) off = 0; + else if (_Y) off = 4; + else if (_Z) off = 8; + else if (_W) off = 12; + + ADD_VI_NEEDED(_Fs_); + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + + if ( _Fs_ == 0 ) { + MOVZX32M16toR( ftreg, (uptr)VU->Mem + off ); + } + else { + int fsreg = ALLOCVI(_Fs_, MODE_READ); + MOVZX32Rm16toROffset(ftreg, recVUTransformAddr(fsreg, VU, _Fs_, 0), (uptr)VU->Mem + off); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ISWR +//------------------------------------------------------------------ +void recVUMI_ISWR( VURegs *VU, int info ) +{ + int ftreg; + //SysPrintf("recVUMI_ISWR \n"); + ADD_VI_NEEDED(_Fs_); + ftreg = ALLOCVI(_Ft_, MODE_READ); + + if (_Fs_ == 0) { + if (_X) MOV32RtoM((uptr)VU->Mem, ftreg); + if (_Y) MOV32RtoM((uptr)VU->Mem+4, ftreg); + if (_Z) MOV32RtoM((uptr)VU->Mem+8, ftreg); + if (_W) MOV32RtoM((uptr)VU->Mem+12, ftreg); + } + else { + int x86reg; + int fsreg = ALLOCVI(_Fs_, MODE_READ); + x86reg = recVUTransformAddr(fsreg, VU, _Fs_, 0); + + if (_X) MOV32RtoRmOffset(x86reg, ftreg, (uptr)VU->Mem); + if (_Y) MOV32RtoRmOffset(x86reg, ftreg, (uptr)VU->Mem+4); + if (_Z) MOV32RtoRmOffset(x86reg, ftreg, (uptr)VU->Mem+8); + if (_W) MOV32RtoRmOffset(x86reg, ftreg, (uptr)VU->Mem+12); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// RINIT* +//------------------------------------------------------------------ +void recVUMI_RINIT(VURegs *VU, int info) +{ + //SysPrintf("recVUMI_RINIT()\n"); + if( (xmmregs[EEREC_S].mode & MODE_WRITE) && (xmmregs[EEREC_S].mode & MODE_NOFLUSH) ) { + _deleteX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), REG_R, 2); + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_S, _Fsf_); + + SSE_ANDPS_M128_to_XMM(EEREC_TEMP, (uptr)s_mask); + SSE_ORPS_M128_to_XMM(EEREC_TEMP, (uptr)VU_ONE); + SSE_MOVSS_XMM_to_M32(VU_REGR_ADDR, EEREC_TEMP); + } + else { + int rreg = ALLOCVI(REG_R, MODE_WRITE); + + if( xmmregs[EEREC_S].mode & MODE_WRITE ) { + SSE_MOVAPS_XMM_to_M128((uptr)&VU->VF[_Fs_], EEREC_S); + xmmregs[EEREC_S].mode &= ~MODE_WRITE; + } + + MOV32MtoR( rreg, VU_VFx_ADDR( _Fs_ ) + 4 * _Fsf_ ); + AND32ItoR( rreg, 0x7fffff ); + OR32ItoR( rreg, 0x7f << 23 ); + + _deleteX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), REG_R, 1); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// RGET* +//------------------------------------------------------------------ +void recVUMI_RGET(VURegs *VU, int info) +{ + //SysPrintf("recVUMI_RGET()\n"); + if ( (_Ft_ == 0) || (_X_Y_Z_W == 0) ) return; + + _deleteX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), REG_R, 1); + + if (_X_Y_Z_W != 0xf) { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, VU_REGR_ADDR); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0); + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_T, VU_REGR_ADDR); + SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// RNEXT* +//------------------------------------------------------------------ +void recVUMI_RNEXT( VURegs *VU, int info ) +{ + int rreg, x86temp0, x86temp1; + //SysPrintf("recVUMI_RNEXT()\n"); + + rreg = ALLOCVI(REG_R, MODE_WRITE|MODE_READ); + + x86temp0 = ALLOCTEMPX86(0); + x86temp1 = ALLOCTEMPX86(0); + + // code from www.project-fao.org + //MOV32MtoR(rreg, VU_REGR_ADDR); + MOV32RtoR(x86temp0, rreg); + SHR32ItoR(x86temp0, 4); + AND32ItoR(x86temp0, 1); + + MOV32RtoR(x86temp1, rreg); + SHR32ItoR(x86temp1, 22); + AND32ItoR(x86temp1, 1); + + SHL32ItoR(rreg, 1); + XOR32RtoR(x86temp0, x86temp1); + XOR32RtoR(rreg, x86temp0); + AND32ItoR(rreg, 0x7fffff); + OR32ItoR(rreg, 0x3f800000); + + _freeX86reg(x86temp0); + _freeX86reg(x86temp1); + + if ( (_Ft_ == 0) || (_X_Y_Z_W == 0) ) { + _deleteX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), REG_R, 1); + return; + } + + recVUMI_RGET(VU, info); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// RXOR* +//------------------------------------------------------------------ +void recVUMI_RXOR( VURegs *VU, int info ) +{ + //SysPrintf("recVUMI_RXOR()\n"); + if( (xmmregs[EEREC_S].mode & MODE_WRITE) && (xmmregs[EEREC_S].mode & MODE_NOFLUSH) ) { + _deleteX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), REG_R, 1); + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_S, _Fsf_); + + SSE_XORPS_M128_to_XMM(EEREC_TEMP, VU_REGR_ADDR); + SSE_ANDPS_M128_to_XMM(EEREC_TEMP, (uptr)s_mask); + SSE_ORPS_M128_to_XMM(EEREC_TEMP, (uptr)s_fones); + SSE_MOVSS_XMM_to_M32(VU_REGR_ADDR, EEREC_TEMP); + } + else { + int rreg = ALLOCVI(REG_R, MODE_WRITE|MODE_READ); + + if( xmmregs[EEREC_S].mode & MODE_WRITE ) { + SSE_MOVAPS_XMM_to_M128((uptr)&VU->VF[_Fs_], EEREC_S); + xmmregs[EEREC_S].mode &= ~MODE_WRITE; + } + + XOR32MtoR( rreg, VU_VFx_ADDR( _Fs_ ) + 4 * _Fsf_ ); + AND32ItoR( rreg, 0x7fffff ); + OR32ItoR ( rreg, 0x3f800000 ); + + _deleteX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), REG_R, 1); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// WAITQ +//------------------------------------------------------------------ +void recVUMI_WAITQ( VURegs *VU, int info ) +{ + //SysPrintf("recVUMI_WAITQ \n"); +// if( info & PROCESS_VU_SUPER ) { +// //CALLFunc(waitqfn); +// SuperVUFlush(0, 1); +// } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FSAND +//------------------------------------------------------------------ +void recVUMI_FSAND( VURegs *VU, int info ) +{ + int ftreg; + u16 imm; + //SysPrintf("recVUMI_FSAND \n"); + imm = (((VU->code >> 21 ) & 0x1) << 11) | (VU->code & 0x7ff); + if(_Ft_ == 0) return; + + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + MOV32MtoR( ftreg, VU_VI_ADDR(REG_STATUS_FLAG, 1) ); + AND32ItoR( ftreg, imm ); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FSEQ +//------------------------------------------------------------------ +void recVUMI_FSEQ( VURegs *VU, int info ) +{ + int ftreg; + u32 imm; + if ( _Ft_ == 0 ) return; + //SysPrintf("recVUMI_FSEQ \n"); + imm = (((VU->code >> 21 ) & 0x1) << 11) | (VU->code & 0x7ff); + + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + + MOVZX32M16toR( EAX, VU_VI_ADDR(REG_STATUS_FLAG, 1) ); + XOR32RtoR(ftreg, ftreg); + + CMP16ItoR(EAX, imm); + SETE8R(ftreg); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FSOR +//------------------------------------------------------------------ +void recVUMI_FSOR( VURegs *VU, int info ) +{ + int ftreg; + u32 imm; + if(_Ft_ == 0) return; + //SysPrintf("recVUMI_FSOR \n"); + imm = (((VU->code >> 21 ) & 0x1) << 11) | (VU->code & 0x7ff); + + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + + MOVZX32M16toR( ftreg, VU_VI_ADDR(REG_STATUS_FLAG, 1) ); + OR32ItoR( ftreg, imm ); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FSSET +//------------------------------------------------------------------ +void recVUMI_FSSET(VURegs *VU, int info) +{ + u32 writeaddr = VU_VI_ADDR(REG_STATUS_FLAG, 0); + u32 prevaddr = VU_VI_ADDR(REG_STATUS_FLAG, 2); + + u16 imm = 0; + //SysPrintf("recVUMI_FSSET \n"); + imm = (((VU->code >> 21 ) & 0x1) << 11) | (VU->code & 0x7FF); + + // keep the low 6 bits ONLY if the upper instruction is an fmac instruction (otherwise rewrite) - metal gear solid 3 + //if( (info & PROCESS_VU_SUPER) && VUREC_FMAC ) { + MOV32MtoR(EAX, prevaddr); + AND32ItoR(EAX, 0x3f); + if ((imm&0xfc0) != 0) OR32ItoR(EAX, imm & 0xFC0); + MOV32RtoM(writeaddr ? writeaddr : prevaddr, EAX); + //} + //else { + // MOV32ItoM(writeaddr ? writeaddr : prevaddr, imm&0xfc0); + //} +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FMAND +//------------------------------------------------------------------ +void recVUMI_FMAND( VURegs *VU, int info ) +{ + int fsreg, ftreg; + if ( _Ft_ == 0 ) return; + //SysPrintf("recVUMI_FMAND \n"); + fsreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_READ); + ftreg = ALLOCVI(_Ft_, MODE_WRITE);//|MODE_8BITREG); + + if( fsreg >= 0 ) { + if( ftreg != fsreg ) MOV32RtoR(ftreg, fsreg); + } + else MOV16MtoR(ftreg, VU_VI_ADDR(_Fs_, 1)); + + AND16MtoR( ftreg, VU_VI_ADDR(REG_MAC_FLAG, 1)); + //MOVZX32R16toR(ftreg, ftreg); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FMEQ +//------------------------------------------------------------------ +void recVUMI_FMEQ( VURegs *VU, int info ) +{ + int ftreg, fsreg; + if ( _Ft_ == 0 ) return; + //SysPrintf("recVUMI_FMEQ \n"); + if( _Ft_ == _Fs_ ) { + ftreg = ALLOCVI(_Ft_, MODE_WRITE|MODE_READ);//|MODE_8BITREG); + + CMP16MtoR(ftreg, VU_VI_ADDR(REG_MAC_FLAG, 1)); + SETE8R(EAX); + MOVZX32R8toR(ftreg, EAX); + } + else { + ADD_VI_NEEDED(_Fs_); + fsreg = ALLOCVI(_Fs_, MODE_READ); + ftreg = ALLOCVI(_Ft_, MODE_WRITE|MODE_8BITREG); + + XOR32RtoR(ftreg, ftreg); + + CMP16MtoR(fsreg, VU_VI_ADDR(REG_MAC_FLAG, 1)); + SETE8R(ftreg); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FMOR +//------------------------------------------------------------------ +void recVUMI_FMOR( VURegs *VU, int info ) +{ + int fsreg, ftreg; + if ( _Ft_ == 0 ) return; + //SysPrintf("recVUMI_FMOR \n"); + if( _Fs_ == 0 ) { + ftreg = ALLOCVI(_Ft_, MODE_WRITE);//|MODE_8BITREG); + MOVZX32M16toR( ftreg, VU_VI_ADDR(REG_MAC_FLAG, 1) ); + } + else if( _Ft_ == _Fs_ ) { + ftreg = ALLOCVI(_Ft_, MODE_WRITE);//|MODE_READ|MODE_8BITREG); + OR16MtoR( ftreg, VU_VI_ADDR(REG_MAC_FLAG, 1) ); + } + else { + fsreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_READ); + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + + MOVZX32M16toR( ftreg, VU_VI_ADDR(REG_MAC_FLAG, 1) ); + + if( fsreg >= 0 ) + OR16RtoR( ftreg, fsreg ); + else + OR16MtoR( ftreg, VU_VI_ADDR(_Fs_, 1) ); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FCAND +//------------------------------------------------------------------ +void recVUMI_FCAND( VURegs *VU, int info ) +{ + int ftreg = ALLOCVI(1, MODE_WRITE|MODE_8BITREG); + //SysPrintf("recVUMI_FCAND \n"); + MOV32MtoR( EAX, VU_VI_ADDR(REG_CLIP_FLAG, 1) ); + XOR32RtoR( ftreg, ftreg ); + AND32ItoR( EAX, VU->code & 0xFFFFFF ); + + SETNZ8R(ftreg); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FCEQ +//------------------------------------------------------------------ +void recVUMI_FCEQ( VURegs *VU, int info ) +{ + int ftreg = ALLOCVI(1, MODE_WRITE|MODE_8BITREG); + //SysPrintf("recVUMI_FCEQ \n"); + MOV32MtoR( EAX, VU_VI_ADDR(REG_CLIP_FLAG, 1) ); + AND32ItoR( EAX, 0xffffff ); + XOR32RtoR( ftreg, ftreg ); + CMP32ItoR( EAX, VU->code&0xffffff ); + + SETE8R(ftreg); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FCOR +//------------------------------------------------------------------ +void recVUMI_FCOR( VURegs *VU, int info ) +{ + int ftreg = ALLOCVI(1, MODE_WRITE|MODE_8BITREG); + //SysPrintf("recVUMI_FCOR \n"); + MOV32MtoR( EAX, VU_VI_ADDR(REG_CLIP_FLAG, 1) ); + XOR32RtoR( ftreg, ftreg ); + OR32ItoR( EAX, VU->code ); + AND32ItoR( EAX, 0xffffff ); + CMP32ItoR( EAX, 0xffffff ); + + SETZ8R(ftreg); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FCSET +//------------------------------------------------------------------ +void recVUMI_FCSET( VURegs *VU, int info ) +{ + u32 addr = VU_VI_ADDR(REG_CLIP_FLAG, 0); + //SysPrintf("recVUMI_FCSET \n"); + MOV32ItoM(addr ? addr : VU_VI_ADDR(REG_CLIP_FLAG, 2), VU->code&0xffffff ); + + if( !(info & (PROCESS_VU_SUPER|PROCESS_VU_COP2)) ) + MOV32ItoM( VU_VI_ADDR(REG_CLIP_FLAG, 1), VU->code&0xffffff ); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FCGET +//------------------------------------------------------------------ +void recVUMI_FCGET( VURegs *VU, int info ) +{ + int ftreg; + if(_Ft_ == 0) return; + //SysPrintf("recVUMI_FCGET \n"); + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + + MOV32MtoR(ftreg, VU_VI_ADDR(REG_CLIP_FLAG, 1)); + AND32ItoR(ftreg, 0x0fff); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// _recbranchAddr() +// +// NOTE: Due to static var dependencies, several SuperVU branch instructions +// are still located in iVUzerorec.cpp. +//------------------------------------------------------------------ + +//------------------------------------------------------------------ +// MFP* +//------------------------------------------------------------------ +void recVUMI_MFP(VURegs *VU, int info) +{ + if ( (_Ft_ == 0) || (_X_Y_Z_W == 0) ) return; + //SysPrintf("recVUMI_MFP \n"); + if( _XYZW_SS ) { + _vuFlipRegSS(VU, EEREC_T); + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, VU_VI_ADDR(REG_P, 1)); + SSE_MOVSS_XMM_to_XMM(EEREC_T, EEREC_TEMP); + _vuFlipRegSS(VU, EEREC_T); + } + else if (_X_Y_Z_W != 0xf) { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, VU_VI_ADDR(REG_P, 1)); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0); + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_T, VU_VI_ADDR(REG_P, 1)); + SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// WAITP +//------------------------------------------------------------------ +static PCSX2_ALIGNED16(float s_tempmem[4]); +void recVUMI_WAITP(VURegs *VU, int info) +{ + //SysPrintf("recVUMI_WAITP \n"); +// if( info & PROCESS_VU_SUPER ) +// SuperVUFlush(1, 1); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// vuSqSumXYZ()* +// +// NOTE: In all EFU insts, EEREC_D is a temp reg +//------------------------------------------------------------------ +void vuSqSumXYZ(int regd, int regs, int regtemp) // regd.x = x ^ 2 + y ^ 2 + z ^ 2 +{ + //SysPrintf("VU: SUMXYZ\n"); + if( cpucaps.hasStreamingSIMD4Extensions ) + { + SSE_MOVAPS_XMM_to_XMM(regd, regs); + if (CHECK_VU_EXTRA_OVERFLOW) vuFloat2(regd, regtemp, 0xf); + SSE4_DPPS_XMM_to_XMM(regd, regd, 0x71); + } + else + { + SSE_MOVAPS_XMM_to_XMM(regtemp, regs); + if (CHECK_VU_EXTRA_OVERFLOW) vuFloat2(regtemp, regd, 0xf); + SSE_MULPS_XMM_to_XMM(regtemp, regtemp); // xyzw ^ 2 + + if( cpucaps.hasStreamingSIMD3Extensions ) { + SSE3_HADDPS_XMM_to_XMM(regd, regtemp); + SSE_ADDPS_XMM_to_XMM(regd, regtemp); // regd.z = x ^ 2 + y ^ 2 + z ^ 2 + SSE_MOVHLPS_XMM_to_XMM(regd, regd); // regd.x = regd.z + } + else { + SSE_MOVSS_XMM_to_XMM(regd, regtemp); + SSE2_PSHUFLW_XMM_to_XMM(regtemp, regtemp, 0x4e); // wzyx -> wzxy + SSE_ADDSS_XMM_to_XMM(regd, regtemp); // x ^ 2 + y ^ 2 + SSE_SHUFPS_XMM_to_XMM(regtemp, regtemp, 0xD2); // wzxy -> wxyz + SSE_ADDSS_XMM_to_XMM(regd, regtemp); // x ^ 2 + y ^ 2 + z ^ 2 + } + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ESADD* +//------------------------------------------------------------------ +void recVUMI_ESADD( VURegs *VU, int info) +{ + //SysPrintf("VU: ESADD\n"); + assert( VU == &VU1 ); + if( EEREC_TEMP == EEREC_D ) { // special code to reset P ( FixMe: don't know if this is still needed! (cottonvibes) ) + Console::Notice("ESADD: Resetting P reg!!!\n"); + MOV32ItoM(VU_VI_ADDR(REG_P, 0), 0); + return; + } + vuSqSumXYZ(EEREC_D, EEREC_S, EEREC_TEMP); + if (CHECK_VU_OVERFLOW) SSE_MINSS_M32_to_XMM(EEREC_D, (uptr)g_maxvals); // Only need to do positive clamp since (x ^ 2 + y ^ 2 + z ^ 2) is positive + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_P, 0), EEREC_D); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ERSADD* +//------------------------------------------------------------------ +void recVUMI_ERSADD( VURegs *VU, int info ) +{ + //SysPrintf("VU: ERSADD\n"); + assert( VU == &VU1 ); + vuSqSumXYZ(EEREC_D, EEREC_S, EEREC_TEMP); + // don't use RCPSS (very bad precision) + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, (uptr)VU_ONE); + SSE_DIVSS_XMM_to_XMM(EEREC_TEMP, EEREC_D); + if (CHECK_VU_OVERFLOW) SSE_MINSS_M32_to_XMM(EEREC_TEMP, (uptr)g_maxvals); // Only need to do positive clamp since (x ^ 2 + y ^ 2 + z ^ 2) is positive + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_P, 0), EEREC_TEMP); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ELENG* +//------------------------------------------------------------------ +void recVUMI_ELENG( VURegs *VU, int info ) +{ + //SysPrintf("VU: ELENG\n"); + assert( VU == &VU1 ); + vuSqSumXYZ(EEREC_D, EEREC_S, EEREC_TEMP); + if (CHECK_VU_OVERFLOW) SSE_MINSS_M32_to_XMM(EEREC_D, (uptr)g_maxvals); // Only need to do positive clamp since (x ^ 2 + y ^ 2 + z ^ 2) is positive + SSE_SQRTSS_XMM_to_XMM(EEREC_D, EEREC_D); + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_P, 0), EEREC_D); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ERLENG* +//------------------------------------------------------------------ +void recVUMI_ERLENG( VURegs *VU, int info ) +{ + //SysPrintf("VU: ERLENG\n"); + assert( VU == &VU1 ); + vuSqSumXYZ(EEREC_D, EEREC_S, EEREC_TEMP); + if (CHECK_VU_OVERFLOW) SSE_MINSS_M32_to_XMM(EEREC_D, (uptr)g_maxvals); // Only need to do positive clamp since (x ^ 2 + y ^ 2 + z ^ 2) is positive + SSE_SQRTSS_XMM_to_XMM(EEREC_D, EEREC_D); // regd <- sqrt(x^2 + y^2 + z^2) + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, (uptr)VU_ONE); // temp <- 1 + SSE_DIVSS_XMM_to_XMM(EEREC_TEMP, EEREC_D); // temp = 1 / sqrt(x^2 + y^2 + z^2) + if (CHECK_VU_OVERFLOW) SSE_MINSS_M32_to_XMM(EEREC_TEMP, (uptr)g_maxvals); // Only need to do positive clamp + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_P, 0), EEREC_TEMP); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// EATANxy +//------------------------------------------------------------------ +void recVUMI_EATANxy( VURegs *VU, int info ) +{ + assert( VU == &VU1 ); + //SysPrintf("recVUMI_EATANxy \n"); + if( (xmmregs[EEREC_S].mode & MODE_WRITE) && (xmmregs[EEREC_S].mode&MODE_NOFLUSH) ) { + SSE_MOVLPS_XMM_to_M64((uptr)s_tempmem, EEREC_S); + FLD32((uptr)&s_tempmem[0]); + FLD32((uptr)&s_tempmem[1]); + } + else { + if( xmmregs[EEREC_S].mode & MODE_WRITE ) { + SSE_MOVAPS_XMM_to_M128((uptr)&VU->VF[_Fs_], EEREC_S); + xmmregs[EEREC_S].mode &= ~MODE_WRITE; + } + + FLD32((uptr)&VU->VF[_Fs_].UL[0]); + FLD32((uptr)&VU->VF[_Fs_].UL[1]); + } + + FPATAN(); + FSTP32(VU_VI_ADDR(REG_P, 0)); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// EATANxz +//------------------------------------------------------------------ +void recVUMI_EATANxz( VURegs *VU, int info ) +{ + assert( VU == &VU1 ); + //SysPrintf("recVUMI_EATANxz \n"); + if( (xmmregs[EEREC_S].mode & MODE_WRITE) && (xmmregs[EEREC_S].mode&MODE_NOFLUSH) ) { + SSE_MOVLPS_XMM_to_M64((uptr)s_tempmem, EEREC_S); + FLD32((uptr)&s_tempmem[0]); + FLD32((uptr)&s_tempmem[2]); + } + else { + if( xmmregs[EEREC_S].mode & MODE_WRITE ) { + SSE_MOVAPS_XMM_to_M128((uptr)&VU->VF[_Fs_], EEREC_S); + xmmregs[EEREC_S].mode &= ~MODE_WRITE; + } + + FLD32((uptr)&VU->VF[_Fs_].UL[0]); + FLD32((uptr)&VU->VF[_Fs_].UL[2]); + } + FPATAN(); + FSTP32(VU_VI_ADDR(REG_P, 0)); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ESUM* +//------------------------------------------------------------------ +void recVUMI_ESUM( VURegs *VU, int info ) +{ + //SysPrintf("VU: ESUM\n"); + assert( VU == &VU1 ); + + if( cpucaps.hasStreamingSIMD3Extensions ) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) vuFloat_useEAX(info, EEREC_TEMP, 0xf); + SSE3_HADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + SSE3_HADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + } + else { + SSE_MOVHLPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); // z, w, z, w + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); // z+x, w+y, z+z, w+w + SSE_UNPCKLPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); // z+x, z+x, w+y, w+y + SSE_MOVHLPS_XMM_to_XMM(EEREC_D, EEREC_TEMP); // w+y, w+y, w+y, w+y + SSE_ADDSS_XMM_to_XMM(EEREC_TEMP, EEREC_D); // x+y+z+w, w+y, w+y, w+y + } + + vuFloat_useEAX(info, EEREC_TEMP, 8); + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_P, 0), EEREC_TEMP); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ERCPR* +//------------------------------------------------------------------ +void recVUMI_ERCPR( VURegs *VU, int info ) +{ + assert( VU == &VU1 ); + //SysPrintf("VU1: ERCPR\n"); + + // don't use RCPSS (very bad precision) + switch ( _Fsf_ ) { + case 0: //0001 + if (CHECK_VU_EXTRA_OVERFLOW) vuFloat5_useEAX(EEREC_S, EEREC_TEMP, 8); + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, (uptr)VU_ONE); // temp <- 1 + SSE_DIVSS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + break; + case 1: //0010 + SSE2_PSHUFLW_XMM_to_XMM(EEREC_S, EEREC_S, 0x4e); + if (CHECK_VU_EXTRA_OVERFLOW) vuFloat5_useEAX(EEREC_S, EEREC_TEMP, 8); + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, (uptr)VU_ONE); // temp <- 1 + SSE_DIVSS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE2_PSHUFLW_XMM_to_XMM(EEREC_S, EEREC_S, 0x4e); + break; + case 2: //0100 + SSE_SHUFPS_XMM_to_XMM(EEREC_S, EEREC_S, 0xc6); + if (CHECK_VU_EXTRA_OVERFLOW) vuFloat5_useEAX(EEREC_S, EEREC_TEMP, 8); + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, (uptr)VU_ONE); // temp <- 1 + SSE_DIVSS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_SHUFPS_XMM_to_XMM(EEREC_S, EEREC_S, 0xc6); + break; + case 3: //1000 + SSE_SHUFPS_XMM_to_XMM(EEREC_S, EEREC_S, 0x27); + if (CHECK_VU_EXTRA_OVERFLOW) vuFloat5_useEAX(EEREC_S, EEREC_TEMP, 8); + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, (uptr)VU_ONE); // temp <- 1 + SSE_DIVSS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_SHUFPS_XMM_to_XMM(EEREC_S, EEREC_S, 0x27); + break; + } + + vuFloat_useEAX(info, EEREC_TEMP, 8); + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_P, 0), EEREC_TEMP); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ESQRT* +//------------------------------------------------------------------ +void recVUMI_ESQRT( VURegs *VU, int info ) +{ + assert( VU == &VU1 ); + + //SysPrintf("VU1: ESQRT\n"); + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_S, _Fsf_); + SSE_ANDPS_M128_to_XMM(EEREC_TEMP, (uptr)const_clip); // abs(x) + if (CHECK_VU_OVERFLOW) SSE_MINSS_M32_to_XMM(EEREC_TEMP, (uptr)g_maxvals); // Only need to do positive clamp + SSE_SQRTSS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_P, 0), EEREC_TEMP); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ERSQRT* +//------------------------------------------------------------------ +void recVUMI_ERSQRT( VURegs *VU, int info ) +{ + int t1reg = _vuGetTempXMMreg(info); + + assert( VU == &VU1 ); + //SysPrintf("VU1: ERSQRT\n"); + + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_S, _Fsf_); + SSE_ANDPS_M128_to_XMM(EEREC_TEMP, (uptr)const_clip); // abs(x) + SSE_MINSS_M32_to_XMM(EEREC_TEMP, (uptr)g_maxvals); // Clamp Infinities to Fmax + SSE_SQRTSS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); // SQRT(abs(x)) + + if( t1reg >= 0 ) + { + SSE_MOVSS_M32_to_XMM(t1reg, (uptr)VU_ONE); + SSE_DIVSS_XMM_to_XMM(t1reg, EEREC_TEMP); + vuFloat_useEAX(info, t1reg, 8); + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_P, 0), t1reg); + _freeXMMreg(t1reg); + } + else + { + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_P, 0), EEREC_TEMP); + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, (uptr)VU_ONE); + SSE_DIVSS_M32_to_XMM(EEREC_TEMP, VU_VI_ADDR(REG_P, 0)); + vuFloat_useEAX(info, EEREC_TEMP, 8); + SSE_MOVSS_XMM_to_M32(VU_VI_ADDR(REG_P, 0), EEREC_TEMP); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ESIN +//------------------------------------------------------------------ +void recVUMI_ESIN( VURegs *VU, int info ) +{ + assert( VU == &VU1 ); + + //SysPrintf("recVUMI_ESIN \n"); + if( (xmmregs[EEREC_S].mode & MODE_WRITE) && (xmmregs[EEREC_S].mode&MODE_NOFLUSH) ) { + switch(_Fsf_) { + case 0: SSE_MOVSS_XMM_to_M32((uptr)s_tempmem, EEREC_S); + case 1: SSE_MOVLPS_XMM_to_M64((uptr)s_tempmem, EEREC_S); + default: SSE_MOVHPS_XMM_to_M64((uptr)&s_tempmem[2], EEREC_S); + } + FLD32((uptr)&s_tempmem[_Fsf_]); + } + else { + if( xmmregs[EEREC_S].mode & MODE_WRITE ) { + SSE_MOVAPS_XMM_to_M128((uptr)&VU->VF[_Fs_], EEREC_S); + xmmregs[EEREC_S].mode &= ~MODE_WRITE; + } + + FLD32((uptr)&VU->VF[_Fs_].UL[_Fsf_]); + } + + FSIN(); + FSTP32(VU_VI_ADDR(REG_P, 0)); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// EATAN +//------------------------------------------------------------------ +void recVUMI_EATAN( VURegs *VU, int info ) +{ + assert( VU == &VU1 ); + + //SysPrintf("recVUMI_EATAN \n"); + if( (xmmregs[EEREC_S].mode & MODE_WRITE) && (xmmregs[EEREC_S].mode&MODE_NOFLUSH) ) { + switch(_Fsf_) { + case 0: SSE_MOVSS_XMM_to_M32((uptr)s_tempmem, EEREC_S); + case 1: SSE_MOVLPS_XMM_to_M64((uptr)s_tempmem, EEREC_S); + default: SSE_MOVHPS_XMM_to_M64((uptr)&s_tempmem[2], EEREC_S); + } + FLD32((uptr)&s_tempmem[_Fsf_]); + } + else { + if( xmmregs[EEREC_S].mode & MODE_WRITE ) { + SSE_MOVAPS_XMM_to_M128((uptr)&VU->VF[_Fs_], EEREC_S); + xmmregs[EEREC_S].mode &= ~MODE_WRITE; + } + } + + FLD1(); + FLD32((uptr)&VU->VF[_Fs_].UL[_Fsf_]); + FPATAN(); + FSTP32(VU_VI_ADDR(REG_P, 0)); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// EEXP +//------------------------------------------------------------------ +void recVUMI_EEXP( VURegs *VU, int info ) +{ + assert( VU == &VU1 ); + //SysPrintf("recVUMI_EEXP \n"); + FLDL2E(); + + if( (xmmregs[EEREC_S].mode & MODE_WRITE) && (xmmregs[EEREC_S].mode&MODE_NOFLUSH) ) { + switch(_Fsf_) { + case 0: SSE_MOVSS_XMM_to_M32((uptr)s_tempmem, EEREC_S); + case 1: SSE_MOVLPS_XMM_to_M64((uptr)s_tempmem, EEREC_S); + default: SSE_MOVHPS_XMM_to_M64((uptr)&s_tempmem[2], EEREC_S); + } + FMUL32((uptr)&s_tempmem[_Fsf_]); + } + else { + if( xmmregs[EEREC_S].mode & MODE_WRITE ) { + SSE_MOVAPS_XMM_to_M128((uptr)&VU->VF[_Fs_], EEREC_S); + xmmregs[EEREC_S].mode &= ~MODE_WRITE; + } + + FMUL32((uptr)&VU->VF[_Fs_].UL[_Fsf_]); + } + + // basically do 2^(log_2(e) * val) + FLD(0); + FRNDINT(); + FXCH(1); + FSUB32Rto0(1); + F2XM1(); + FLD1(); + FADD320toR(1); + FSCALE(); + FSTP(1); + + FSTP32(VU_VI_ADDR(REG_P, 0)); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// XITOP +//------------------------------------------------------------------ +void recVUMI_XITOP( VURegs *VU, int info ) +{ + int ftreg; + if (_Ft_ == 0) return; + //SysPrintf("recVUMI_XITOP \n"); + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + MOVZX32M16toR( ftreg, (uptr)&VU->vifRegs->itop ); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// XTOP +//------------------------------------------------------------------ +void recVUMI_XTOP( VURegs *VU, int info ) +{ + int ftreg; + if ( _Ft_ == 0 ) return; + //SysPrintf("recVUMI_XTOP \n"); + ftreg = ALLOCVI(_Ft_, MODE_WRITE); + MOVZX32M16toR( ftreg, (uptr)&VU->vifRegs->top ); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// VU1XGKICK_MTGSTransfer() - Called by ivuZerorec.cpp +//------------------------------------------------------------------ +void VU1XGKICK_MTGSTransfer(u32 *pMem, u32 addr) +{ + u32 size; + u32* data = (u32*)((u8*)pMem + (addr&0x3fff)); + + // fixme: The gifTagDummy function in the MTGS (called by PrepDataPacket) has a + // hack that aborts the packet if it goes past the end of VU1 memory. + // Chances are this should be a "loops around memory" situation, and the packet + // should be continued starting at addr zero (0). + + size = mtgsThread->PrepDataPacket( GIF_PATH_1, data, (0x4000-(addr&0x3fff)) >> 4); + jASSUME( size > 0 ); + + //if( size > 0 ) + { + u8* pmem = mtgsThread->GetDataPacketPtr(); + memcpy_aligned(pmem, (u8*)pMem+addr, size<<4); + mtgsThread->SendDataPacket(); + } +} +//------------------------------------------------------------------ diff --git a/pcsx2/x86/iVUmicroUpper.cpp b/pcsx2/x86/iVUmicroUpper.cpp new file mode 100644 index 0000000000..99bc255e93 --- /dev/null +++ b/pcsx2/x86/iVUmicroUpper.cpp @@ -0,0 +1,3078 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "GS.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "iMMI.h" +#include "iFPU.h" +#include "iCOP0.h" +#include "VUmicro.h" +#include "VUflags.h" +#include "iVUmicro.h" +#include "iVUops.h" +#include "iVUzerorec.h" +//------------------------------------------------------------------ + +//------------------------------------------------------------------ +// Helper Macros +//------------------------------------------------------------------ +#define _Ft_ (( VU->code >> 16) & 0x1F) // The rt part of the instruction register +#define _Fs_ (( VU->code >> 11) & 0x1F) // The rd part of the instruction register +#define _Fd_ (( VU->code >> 6) & 0x1F) // The sa part of the instruction register + +#define _X (( VU->code>>24) & 0x1) +#define _Y (( VU->code>>23) & 0x1) +#define _Z (( VU->code>>22) & 0x1) +#define _W (( VU->code>>21) & 0x1) + +#define _XYZW_SS (_X+_Y+_Z+_W==1) + +#define _Fsf_ (( VU->code >> 21) & 0x03) +#define _Ftf_ (( VU->code >> 23) & 0x03) + +#define _Imm11_ (s32)(VU->code & 0x400 ? 0xfffffc00 | (VU->code & 0x3ff) : VU->code & 0x3ff) +#define _UImm11_ (s32)(VU->code & 0x7ff) + +#define VU_VFx_ADDR(x) (uptr)&VU->VF[x].UL[0] +#define VU_VFy_ADDR(x) (uptr)&VU->VF[x].UL[1] +#define VU_VFz_ADDR(x) (uptr)&VU->VF[x].UL[2] +#define VU_VFw_ADDR(x) (uptr)&VU->VF[x].UL[3] + +#define VU_REGR_ADDR (uptr)&VU->VI[REG_R] +#define VU_REGQ_ADDR (uptr)&VU->VI[REG_Q] +#define VU_REGMAC_ADDR (uptr)&VU->VI[REG_MAC_FLAG] + +#define VU_VI_ADDR(x, read) GetVIAddr(VU, x, read, info) + +#define VU_ACCx_ADDR (uptr)&VU->ACC.UL[0] +#define VU_ACCy_ADDR (uptr)&VU->ACC.UL[1] +#define VU_ACCz_ADDR (uptr)&VU->ACC.UL[2] +#define VU_ACCw_ADDR (uptr)&VU->ACC.UL[3] + +#define _X_Y_Z_W ((( VU->code >> 21 ) & 0xF ) ) +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// Global Variables +//------------------------------------------------------------------ +static const PCSX2_ALIGNED16(int SSEmovMask[ 16 ][ 4 ]) = +{ + { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF }, + { 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000 }, + { 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF }, + { 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000 }, + { 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF }, + { 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }, + { 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF }, + { 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000 }, + { 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF } +}; + +static const PCSX2_ALIGNED16(u32 const_abs_table[16][4]) = +{ + { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }, //0000 + { 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff }, //0001 + { 0xffffffff, 0xffffffff, 0x7fffffff, 0xffffffff }, //0010 + { 0xffffffff, 0xffffffff, 0x7fffffff, 0x7fffffff }, //0011 + { 0xffffffff, 0x7fffffff, 0xffffffff, 0xffffffff }, //0100 + { 0xffffffff, 0x7fffffff, 0xffffffff, 0x7fffffff }, //0101 + { 0xffffffff, 0x7fffffff, 0x7fffffff, 0xffffffff }, //0110 + { 0xffffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff }, //0111 + { 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff }, //1000 + { 0x7fffffff, 0xffffffff, 0xffffffff, 0x7fffffff }, //1001 + { 0x7fffffff, 0xffffffff, 0x7fffffff, 0xffffffff }, //1010 + { 0x7fffffff, 0xffffffff, 0x7fffffff, 0x7fffffff }, //1011 + { 0x7fffffff, 0x7fffffff, 0xffffffff, 0xffffffff }, //1100 + { 0x7fffffff, 0x7fffffff, 0xffffffff, 0x7fffffff }, //1101 + { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0xffffffff }, //1110 + { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff }, //1111 +}; + +static const PCSX2_ALIGNED16(float recMult_float_to_int4[4]) = { 16.0, 16.0, 16.0, 16.0 }; +static const PCSX2_ALIGNED16(float recMult_float_to_int12[4]) = { 4096.0, 4096.0, 4096.0, 4096.0 }; +static const PCSX2_ALIGNED16(float recMult_float_to_int15[4]) = { 32768.0, 32768.0, 32768.0, 32768.0 }; + +static const PCSX2_ALIGNED16(float recMult_int_to_float4[4]) = { 0.0625f, 0.0625f, 0.0625f, 0.0625f }; +static const PCSX2_ALIGNED16(float recMult_int_to_float12[4]) = { 0.000244140625, 0.000244140625, 0.000244140625, 0.000244140625 }; +static const PCSX2_ALIGNED16(float recMult_int_to_float15[4]) = { 0.000030517578125, 0.000030517578125, 0.000030517578125, 0.000030517578125 }; + +static const PCSX2_ALIGNED16(u32 VU_Underflow_Mask1[4]) = {0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000}; +static const PCSX2_ALIGNED16(u32 VU_Underflow_Mask2[4]) = {0x007fffff, 0x007fffff, 0x007fffff, 0x007fffff}; +static const PCSX2_ALIGNED16(u32 VU_Zero_Mask[4]) = {0x00000000, 0x00000000, 0x00000000, 0x00000000}; +static const PCSX2_ALIGNED16(u32 VU_Zero_Helper_Mask[4]) = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}; +static const PCSX2_ALIGNED16(u32 VU_Signed_Zero_Mask[4]) = {0x80000000, 0x80000000, 0x80000000, 0x80000000}; +static const PCSX2_ALIGNED16(u32 VU_Pos_Infinity[4]) = {0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000}; +static const PCSX2_ALIGNED16(u32 VU_Neg_Infinity[4]) = {0xff800000, 0xff800000, 0xff800000, 0xff800000}; +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// recUpdateFlags() - Computes the flags for the Upper Opcodes +// +// Note: Computes under/overflow flags if CHECK_VU_EXTRA_FLAGS is 1 +//------------------------------------------------------------------ +PCSX2_ALIGNED16(u64 TEMPXMMData[2]); +void recUpdateFlags(VURegs * VU, int reg, int info) +{ + static u8 *pjmp, *pjmp2; + static u32 *pjmp32; + static u32 macaddr, stataddr, prevstataddr; + static int x86macflag, x86statflag, x86temp; + static int t1reg, t1regBoolean; + static const int flipMask[16] = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}; + + if( !(info & PROCESS_VU_UPDATEFLAGS) ) return; + + //SysPrintf ("recUpdateFlags\n"); + + macaddr = VU_VI_ADDR(REG_MAC_FLAG, 0); + stataddr = VU_VI_ADDR(REG_STATUS_FLAG, 0); // write address + prevstataddr = VU_VI_ADDR(REG_STATUS_FLAG, 2); // previous address + + if( stataddr == 0 ) stataddr = prevstataddr; + if( macaddr == 0 ) { + SysPrintf( "VU ALLOCATION WARNING: Using Mac Flag Previous Address!\n" ); + macaddr = VU_VI_ADDR(REG_MAC_FLAG, 2); + } + + x86macflag = ALLOCTEMPX86(0); + x86statflag = ALLOCTEMPX86(0); + + if (reg == EEREC_TEMP) { + t1reg = _vuGetTempXMMreg(info); + if (t1reg < 0) { + //SysPrintf( "VU ALLOCATION ERROR: Temp reg can't be allocated!!!!\n" ); + t1reg = (reg == 0) ? 1 : 0; // Make t1reg != reg + SSE_MOVAPS_XMM_to_M128( (uptr)TEMPXMMData, t1reg ); // Backup data to temp address + t1regBoolean = 1; + } + else t1regBoolean = 0; + } + else { + t1reg = EEREC_TEMP; + t1regBoolean = 2; + } + + SSE_SHUFPS_XMM_to_XMM(reg, reg, 0x1B); // Flip wzyx to xyzw + MOV32MtoR(x86statflag, prevstataddr); // Load the previous status in to x86statflag + AND16ItoR(x86statflag, 0xff0); // Keep Sticky and D/I flags + + + if (CHECK_VU_EXTRA_FLAGS) { // Checks all flags + + x86temp = ALLOCTEMPX86(0); + + //-------------------------Check for Overflow flags------------------------------ + + //SSE_XORPS_XMM_to_XMM(t1reg, t1reg); // Clear t1reg + //SSE_CMPUNORDPS_XMM_to_XMM(t1reg, reg); // If reg == NaN then set Vector to 0xFFFFFFFF + + //SSE_MOVAPS_XMM_to_XMM(t1reg, reg); + //SSE_MINPS_M128_to_XMM(t1reg, (uptr)g_maxvals); + //SSE_MAXPS_M128_to_XMM(t1reg, (uptr)g_minvals); + //SSE_CMPNEPS_XMM_to_XMM(t1reg, reg); // If they're not equal, then overflow has occured + + SSE_MOVAPS_XMM_to_XMM(t1reg, reg); + SSE_ANDPS_M128_to_XMM(t1reg, (uptr)VU_Zero_Helper_Mask); + SSE_CMPEQPS_M128_to_XMM(t1reg, (uptr)VU_Pos_Infinity); // If infinity, then overflow has occured (NaN's don't report as overflow) + + SSE_MOVMSKPS_XMM_to_R32(x86macflag, t1reg); // Move the sign bits of the previous calculation + + AND16ItoR(x86macflag, _X_Y_Z_W ); // Grab "Has Overflowed" bits from the previous calculation (also make sure we're only grabbing from the XYZW being modified) + pjmp = JZ8(0); // Skip if none are + OR16ItoR(x86statflag, 0x208); // OS, O flags + SHL16ItoR(x86macflag, 12); + if (_XYZW_SS) pjmp32 = JMP32(0); // Skip Underflow Check + x86SetJ8(pjmp); + + //-------------------------Check for Underflow flags------------------------------ + + SSE_MOVAPS_XMM_to_XMM(t1reg, reg); // t1reg <- reg + + SSE_ANDPS_M128_to_XMM(t1reg, (uptr)&VU_Underflow_Mask1[ 0 ]); + SSE_CMPEQPS_M128_to_XMM(t1reg, (uptr)&VU_Zero_Mask[ 0 ]); // If (t1reg == zero exponent) then set Vector to 0xFFFFFFFF + + SSE_ANDPS_XMM_to_XMM(t1reg, reg); + SSE_ANDPS_M128_to_XMM(t1reg, (uptr)&VU_Underflow_Mask2[ 0 ]); + SSE_CMPNEPS_M128_to_XMM(t1reg, (uptr)&VU_Zero_Mask[ 0 ]); // If (t1reg != zero mantisa) then set Vector to 0xFFFFFFFF + + SSE_MOVMSKPS_XMM_to_R32(EAX, t1reg); // Move the sign bits of the previous calculation + + AND16ItoR(EAX, _X_Y_Z_W ); // Grab "Has Underflowed" bits from the previous calculation + pjmp = JZ8(0); // Skip if none are + OR16ItoR(x86statflag, 0x104); // US, U flags + SHL16ItoR(EAX, 8); + OR32RtoR(x86macflag, EAX); + x86SetJ8(pjmp); + + //-------------------------Optional Code: Denormals Are Zero------------------------------ + if (CHECK_VU_UNDERFLOW) { // Sets underflow/denormals to zero + SSE_ANDNPS_XMM_to_XMM(t1reg, reg); // t1reg = !t1reg & reg (t1reg = denormals are positive zero) + VU_MERGE_REGS_SAFE(t1reg, reg, (15 - flipMask[_X_Y_Z_W])); // Send t1reg the vectors that shouldn't be modified (since reg was flipped, we need a mask to get the unmodified vectors) + // Now we have Denormals are Positive Zero in t1reg; the next two lines take Signed Zero into account + SSE_ANDPS_M128_to_XMM(reg, (uptr)&VU_Signed_Zero_Mask[ 0 ]); // Only keep the sign bit for each vector + SSE_ORPS_XMM_to_XMM(reg, t1reg); // Denormals are Signed Zero, and unmodified vectors stay the same! + } + + if (_XYZW_SS) x86SetJ32(pjmp32); // If we skipped the Underflow Flag Checking (when we had an Overflow), return here + + vuFloat2(reg, t1reg, flipMask[_X_Y_Z_W]); // Clamp overflowed vectors that were modified (remember reg's vectors have been flipped, so have to use a flipmask) + + //-------------------------Check for Signed flags------------------------------ + + // The following code makes sure the Signed Bit isn't set with Negative Zero + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); // Clear t1reg + SSE_CMPEQPS_XMM_to_XMM(t1reg, reg); // Set all F's if each vector is zero + SSE_MOVMSKPS_XMM_to_R32(x86temp, t1reg); // Used for Zero Flag Calculation + SSE_ANDNPS_XMM_to_XMM(t1reg, reg); + + SSE_MOVMSKPS_XMM_to_R32(EAX, t1reg); // Move the sign bits of the t1reg + + AND16ItoR(EAX, _X_Y_Z_W ); // Grab "Is Signed" bits from the previous calculation + pjmp = JZ8(0); // Skip if none are + OR16ItoR(x86statflag, 0x82); // SS, S flags + SHL16ItoR(EAX, 4); + OR32RtoR(x86macflag, EAX); + if (_XYZW_SS) pjmp2 = JMP8(0); // If negative and not Zero, we can skip the Zero Flag checking + x86SetJ8(pjmp); + + //-------------------------Check for Zero flags------------------------------ + + AND16ItoR(x86temp, _X_Y_Z_W ); // Grab "Is Zero" bits from the previous calculation + pjmp = JZ8(0); // Skip if none are + OR16ItoR(x86statflag, 0x41); // ZS, Z flags + OR32RtoR(x86macflag, x86temp); + x86SetJ8(pjmp); + + _freeX86reg(x86temp); + } + else { // Only Checks for Sign and Zero Flags + + vuFloat2(reg, t1reg, flipMask[_X_Y_Z_W]); // Clamp overflowed vectors that were modified (remember reg's vectors have been flipped, so have to use a flipmask) + + //-------------------------Check for Signed flags------------------------------ + + // The following code makes sure the Signed Bit isn't set with Negative Zero + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); // Clear t1reg + SSE_CMPEQPS_XMM_to_XMM(t1reg, reg); // Set all F's if each vector is zero + SSE_MOVMSKPS_XMM_to_R32(EAX, t1reg); // Used for Zero Flag Calculation + SSE_ANDNPS_XMM_to_XMM(t1reg, reg); + + SSE_MOVMSKPS_XMM_to_R32(x86macflag, t1reg); // Move the sign bits of the t1reg + + AND16ItoR(x86macflag, _X_Y_Z_W ); // Grab "Is Signed" bits from the previous calculation + pjmp = JZ8(0); // Skip if none are + OR16ItoR(x86statflag, 0x82); // SS, S flags + SHL16ItoR(x86macflag, 4); + if (_XYZW_SS) pjmp2 = JMP8(0); // If negative and not Zero, we can skip the Zero Flag checking + x86SetJ8(pjmp); + + //-------------------------Check for Zero flags------------------------------ + + AND16ItoR(EAX, _X_Y_Z_W ); // Grab "Is Zero" bits from the previous calculation + pjmp = JZ8(0); // Skip if none are + OR16ItoR(x86statflag, 0x41); // ZS, Z flags + OR32RtoR(x86macflag, EAX); + x86SetJ8(pjmp); + } + //-------------------------Finally: Send the Flags to the Mac Flag Address------------------------------ + + if (_XYZW_SS) x86SetJ8(pjmp2); // If we skipped the Zero Flag Checking, return here + + if (t1regBoolean == 2) SSE_SHUFPS_XMM_to_XMM(reg, reg, 0x1B); // Flip back reg to wzyx (have to do this because reg != EEREC_TEMP) + else if (t1regBoolean == 1) SSE_MOVAPS_M128_to_XMM( t1reg, (uptr)TEMPXMMData ); // Restore data from temo address + else _freeXMMreg(t1reg); // Free temp reg + + MOV16RtoM(macaddr, x86macflag); + MOV16RtoM(stataddr, x86statflag); + + _freeX86reg(x86macflag); + _freeX86reg(x86statflag); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// Custom VU ADD/SUB routines by Nneeve +// +// Note: See FPU_ADD_SUB() for more info on what this is doing. +//------------------------------------------------------------------ +static PCSX2_ALIGNED16(u32 VU_addsuband[2][4]); +static PCSX2_ALIGNED16(u32 VU_addsub_reg[2][4]); +static u32 ecx_temp_loc; + +void VU_ADD_SUB(u32 regd, u32 regt, int is_sub, int info) +{ + u8 *localptr[4][8]; + int temp1 = _allocX86reg(ECX, X86TYPE_TEMP, 0, 0); //receives regd + int temp2 = ALLOCTEMPX86(0); + + SSE_MOVAPS_XMM_to_M128((uptr)&VU_addsub_reg[0][0], regd); + SSE_MOVAPS_XMM_to_M128((uptr)&VU_addsub_reg[1][0], regt); + + SSE2_PCMPEQB_XMM_to_XMM(regd, regd); + SSE_MOVAPS_XMM_to_M128((uptr)&VU_addsuband[0][0], regd); + SSE_MOVAPS_XMM_to_M128((uptr)&VU_addsuband[1][0], regd); + SSE_MOVAPS_M128_to_XMM(regd, (uptr)&VU_addsub_reg[0][0]); + + SSE2_PSLLD_I8_to_XMM(regd, 1); + SSE2_PSLLD_I8_to_XMM(regt, 1); + + SSE2_PSRLD_I8_to_XMM(regd, 24); + SSE2_PSRLD_I8_to_XMM(regt, 24); + + SSE2_PSUBD_XMM_to_XMM(regd, regt); + +#define PERFORM(i) \ + \ + SSE_PEXTRW_XMM_to_R32(temp1, regd, i*2); \ + MOVSX32R16toR(temp1, temp1); \ + CMP32ItoR(temp1, 25);\ + localptr[i][0] = JGE8(0);\ + CMP32ItoR(temp1, 0);\ + localptr[i][1] = JG8(0);\ + localptr[i][2] = JE8(0);\ + CMP32ItoR(temp1, -25);\ + localptr[i][3] = JLE8(0);\ + \ + NEG32R(temp1); \ + DEC32R(temp1);\ + MOV32ItoR(temp2, 0xffffffff); \ + SHL32CLtoR(temp2); \ + MOV32RtoM((uptr)&VU_addsuband[0][i], temp2);\ + localptr[i][4] = JMP8(0);\ + \ + x86SetJ8(localptr[i][0]);\ + MOV32ItoM((uptr)&VU_addsuband[1][i], 0x80000000);\ + localptr[i][5] = JMP8(0);\ + \ + x86SetJ8(localptr[i][1]);\ + DEC32R(temp1);\ + MOV32ItoR(temp2, 0xffffffff);\ + SHL32CLtoR(temp2); \ + MOV32RtoM((uptr)&VU_addsuband[1][i], temp2);\ + localptr[i][6] = JMP8(0);\ + \ + x86SetJ8(localptr[i][3]);\ + MOV32ItoM((uptr)&VU_addsuband[0][i], 0x80000000);\ + localptr[i][7] = JMP8(0);\ + \ + x86SetJ8(localptr[i][2]);\ + \ + x86SetJ8(localptr[i][4]);\ + x86SetJ8(localptr[i][5]);\ + x86SetJ8(localptr[i][6]);\ + x86SetJ8(localptr[i][7]); + + PERFORM(0); + PERFORM(1); + PERFORM(2); + PERFORM(3); +#undef PERFORM + + SSE_MOVAPS_M128_to_XMM(regd, (uptr)&VU_addsub_reg[0][0]); + SSE_MOVAPS_M128_to_XMM(regt, (uptr)&VU_addsub_reg[1][0]); + + SSE_ANDPS_M128_to_XMM(regd, (uptr)&VU_addsuband[0][0]); + SSE_ANDPS_M128_to_XMM(regt, (uptr)&VU_addsuband[1][0]); + + if (is_sub) SSE_SUBPS_XMM_to_XMM(regd, regt); + else SSE_ADDPS_XMM_to_XMM(regd, regt); + + SSE_MOVAPS_M128_to_XMM(regt, (uptr)&VU_addsub_reg[1][0]); + + _freeX86reg(temp1); + _freeX86reg(temp2); +} + +void VU_ADD_SUB_SSE4(u32 regd, u32 regt, int is_sub, int info) +{ + u8 *localptr[4][8]; + int temp1 = _allocX86reg(ECX, X86TYPE_TEMP, 0, 0); //receives regd + int temp2 = ALLOCTEMPX86(0); + + SSE_MOVAPS_XMM_to_M128((uptr)&VU_addsub_reg[0][0], regd); + SSE_MOVAPS_XMM_to_M128((uptr)&VU_addsub_reg[1][0], regt); + + SSE2_PSLLD_I8_to_XMM(regd, 1); + SSE2_PSLLD_I8_to_XMM(regt, 1); + + SSE2_PSRLD_I8_to_XMM(regd, 24); + SSE2_PSRLD_I8_to_XMM(regt, 24); + + SSE2_PSUBD_XMM_to_XMM(regd, regt); + +#define PERFORM_SSE4(i) \ + \ + SSE_PEXTRW_XMM_to_R32(temp1, regd, i*2); \ + MOVSX32R16toR(temp1, temp1); \ + CMP32ItoR(temp1, 25);\ + localptr[i][0] = JGE8(0);\ + CMP32ItoR(temp1, 0);\ + localptr[i][1] = JG8(0);\ + localptr[i][2] = JE8(0);\ + CMP32ItoR(temp1, -25);\ + localptr[i][3] = JLE8(0);\ + \ + NEG32R(temp1); \ + DEC32R(temp1);\ + MOV32ItoR(temp2, 0xffffffff); \ + SHL32CLtoR(temp2); \ + SSE4_PINSRD_R32_to_XMM(regd, temp2, i); \ + localptr[i][4] = JMP8(0);\ + \ + x86SetJ8(localptr[i][0]);\ + MOV32ItoR(temp2, 0xffffffff); \ + SSE4_PINSRD_R32_to_XMM(regd, temp2, i); \ + SHL32ItoR(temp2, 31); \ + SSE4_PINSRD_R32_to_XMM(regt, temp2, i); \ + localptr[i][5] = JMP8(0);\ + \ + x86SetJ8(localptr[i][1]);\ + DEC32R(temp1);\ + MOV32ItoR(temp2, 0xffffffff);\ + SSE4_PINSRD_R32_to_XMM(regd, temp2, i); \ + SHL32CLtoR(temp2); \ + SSE4_PINSRD_R32_to_XMM(regt, temp2, i); \ + localptr[i][6] = JMP8(0);\ + \ + x86SetJ8(localptr[i][3]);\ + MOV32ItoR(temp2, 0x80000000); \ + SSE4_PINSRD_R32_to_XMM(regd, temp2, i); \ + localptr[i][7] = JMP8(0);\ + \ + x86SetJ8(localptr[i][2]);\ + \ + x86SetJ8(localptr[i][4]);\ + x86SetJ8(localptr[i][5]);\ + x86SetJ8(localptr[i][6]);\ + x86SetJ8(localptr[i][7]); + + SSE2_PCMPEQB_XMM_to_XMM(regt, regt); + PERFORM_SSE4(0); + PERFORM_SSE4(1); + PERFORM_SSE4(2); + PERFORM_SSE4(3); +#undef PERFORM_SSE4 + + SSE_ANDPS_M128_to_XMM(regd, (uptr)&VU_addsub_reg[0][0]); //regd contains mask + SSE_ANDPS_M128_to_XMM(regt, (uptr)&VU_addsub_reg[1][0]); //regt contains mask + + if (is_sub) SSE_SUBPS_XMM_to_XMM(regd, regt); + else SSE_ADDPS_XMM_to_XMM(regd, regt); + + SSE_MOVAPS_M128_to_XMM(regt, (uptr)&VU_addsub_reg[1][0]); + + _freeX86reg(temp1); + _freeX86reg(temp2); +} + +void VU_ADD_SUB_SS(u32 regd, u32 regt, int is_sub, int is_mem, int info) +{ + u8 *localptr[8]; + u32 addrt = regt; //for case is_mem + int temp1 = _allocX86reg(ECX, X86TYPE_TEMP, 0, 0); //receives regd //_allocX86reg(ECX, X86TYPE_TEMP, 0, ((info&PROCESS_VU_SUPER)?0:MODE_NOFRAME)|mode); + int temp2 = ALLOCTEMPX86(0); + + SSE_MOVAPS_XMM_to_M128((uptr)&VU_addsub_reg[0][0], regd); + if (!is_mem) SSE_MOVAPS_XMM_to_M128((uptr)&VU_addsub_reg[1][0], regt); + + SSE2_MOVD_XMM_to_R(temp1, regd); + SHR32ItoR(temp1, 23); + + if (is_mem) { + MOV32MtoR(temp2, addrt); + MOV32RtoM((uptr)&VU_addsub_reg[1][0], temp2); + SHR32ItoR(temp2, 23); + } + else { + SSE2_MOVD_XMM_to_R(temp2, regt); + SHR32ItoR(temp2, 23); + } + + AND32ItoR(temp1, 0xff); + AND32ItoR(temp2, 0xff); + + SUB32RtoR(temp1, temp2); //temp1 = exponent difference + + CMP32ItoR(temp1, 25); + localptr[0] = JGE8(0); + CMP32ItoR(temp1, 0); + localptr[1] = JG8(0); + localptr[2] = JE8(0); + CMP32ItoR(temp1, -25); + localptr[3] = JLE8(0); + + NEG32R(temp1); + DEC32R(temp1); + MOV32ItoR(temp2, 0xffffffff); + SHL32CLtoR(temp2); + SSE2_PCMPEQB_XMM_to_XMM(regd, regd); + if (is_mem) { + SSE_PINSRW_R32_to_XMM(regd, temp2, 0); + SHR32ItoR(temp2, 16); + SSE_PINSRW_R32_to_XMM(regd, temp2, 1); + } + else { + SSE2_MOVD_R_to_XMM(regt, temp2); + SSE_MOVSS_XMM_to_XMM(regd, regt); + SSE2_PCMPEQB_XMM_to_XMM(regt, regt); + } + localptr[4] = JMP8(0); + + x86SetJ8(localptr[0]); + MOV32ItoR(temp2, 0x80000000); + if (is_mem) + AND32RtoM((uptr)&VU_addsub_reg[1][0], temp2); + else { + SSE2_PCMPEQB_XMM_to_XMM(regt, regt); + SSE2_MOVD_R_to_XMM(regd, temp2); + SSE_MOVSS_XMM_to_XMM(regt, regd); + } + SSE2_PCMPEQB_XMM_to_XMM(regd, regd); + localptr[5] = JMP8(0); + + x86SetJ8(localptr[1]); + DEC32R(temp1); + MOV32ItoR(temp2, 0xffffffff); + SHL32CLtoR(temp2); + if (is_mem) + AND32RtoM((uptr)&VU_addsub_reg[1][0], temp2); + else { + SSE2_PCMPEQB_XMM_to_XMM(regt, regt); + SSE2_MOVD_R_to_XMM(regd, temp2); + SSE_MOVSS_XMM_to_XMM(regt, regd); + } + SSE2_PCMPEQB_XMM_to_XMM(regd, regd); + localptr[6] = JMP8(0); + + x86SetJ8(localptr[3]); + MOV32ItoR(temp2, 0x80000000); + SSE2_PCMPEQB_XMM_to_XMM(regd, regd); + if (is_mem) { + SSE_PINSRW_R32_to_XMM(regd, temp2, 0); + SHR32ItoR(temp2, 16); + SSE_PINSRW_R32_to_XMM(regd, temp2, 1); + } + else { + SSE2_MOVD_R_to_XMM(regt, temp2); + SSE_MOVSS_XMM_to_XMM(regd, regt); + SSE2_PCMPEQB_XMM_to_XMM(regt, regt); + } + localptr[7] = JMP8(0); + + x86SetJ8(localptr[2]); + x86SetJ8(localptr[4]); + x86SetJ8(localptr[5]); + x86SetJ8(localptr[6]); + x86SetJ8(localptr[7]); + + if (is_mem) + { + SSE_ANDPS_M128_to_XMM(regd, (uptr)&VU_addsub_reg[0][0]); //regd contains mask + + if (is_sub) SSE_SUBSS_M32_to_XMM(regd, (uptr)&VU_addsub_reg[1][0]); + else SSE_ADDSS_M32_to_XMM(regd, (uptr)&VU_addsub_reg[1][0]); + } + else + { + SSE_ANDPS_M128_to_XMM(regd, (uptr)&VU_addsub_reg[0][0]); //regd contains mask + SSE_ANDPS_M128_to_XMM(regt, (uptr)&VU_addsub_reg[1][0]); //regt contains mask + + if (is_sub) SSE_SUBSS_XMM_to_XMM(regd, regt); + else SSE_ADDSS_XMM_to_XMM(regd, regt); + + SSE_MOVAPS_M128_to_XMM(regt, (uptr)&VU_addsub_reg[1][0]); + } + + _freeX86reg(temp1); + _freeX86reg(temp2); +} + +void VU_ADD_SUB_SS_SSE4(u32 regd, u32 regt, int is_sub, int is_mem, int info) +{ + u8 *localptr[8]; + u32 addrt = regt; //for case is_mem + int temp1 = _allocX86reg(ECX, X86TYPE_TEMP, 0, 0); //receives regd //_allocX86reg(ECX, X86TYPE_TEMP, 0, ((info&PROCESS_VU_SUPER)?0:MODE_NOFRAME)|mode); + int temp2 = ALLOCTEMPX86(0); + + SSE_MOVAPS_XMM_to_M128((uptr)&VU_addsub_reg[0][0], regd); + if (!is_mem) SSE_MOVAPS_XMM_to_M128((uptr)&VU_addsub_reg[1][0], regt); + + SSE2_MOVD_XMM_to_R(temp1, regd); + SHR32ItoR(temp1, 23); + + if (is_mem) { + MOV32MtoR(temp2, addrt); + MOV32RtoM((uptr)&VU_addsub_reg[1][0], temp2); + SHR32ItoR(temp2, 23); + } + else { + SSE2_MOVD_XMM_to_R(temp2, regt); + SHR32ItoR(temp2, 23); + } + + AND32ItoR(temp1, 0xff); + AND32ItoR(temp2, 0xff); + + SUB32RtoR(temp1, temp2); //temp1 = exponent difference + + CMP32ItoR(temp1, 25); + localptr[0] = JGE8(0); + CMP32ItoR(temp1, 0); + localptr[1] = JG8(0); + localptr[2] = JE8(0); + CMP32ItoR(temp1, -25); + localptr[3] = JLE8(0); + + NEG32R(temp1); + DEC32R(temp1); + MOV32ItoR(temp2, 0xffffffff); + SHL32CLtoR(temp2); + SSE2_PCMPEQB_XMM_to_XMM(regd, regd); + SSE4_PINSRD_R32_to_XMM(regd, temp2, 0); + if (!is_mem) + SSE2_PCMPEQB_XMM_to_XMM(regt, regt); + localptr[4] = JMP8(0); + + x86SetJ8(localptr[0]); + MOV32ItoR(temp2, 0x80000000); + if (is_mem) + AND32RtoM((uptr)&VU_addsub_reg[1][0], temp2); + else { + SSE2_PCMPEQB_XMM_to_XMM(regt, regt); + SSE4_PINSRD_R32_to_XMM(regt, temp2, 0); + } + SSE2_PCMPEQB_XMM_to_XMM(regd, regd); + localptr[5] = JMP8(0); + + x86SetJ8(localptr[1]); + DEC32R(temp1); + MOV32ItoR(temp2, 0xffffffff); + SHL32CLtoR(temp2); + if (is_mem) + AND32RtoM((uptr)&VU_addsub_reg[1][0], temp2); + else { + SSE2_PCMPEQB_XMM_to_XMM(regt, regt); + SSE4_PINSRD_R32_to_XMM(regt, temp2, 0); + } + SSE2_PCMPEQB_XMM_to_XMM(regd, regd); + localptr[6] = JMP8(0); + + x86SetJ8(localptr[3]); + MOV32ItoR(temp2, 0x80000000); + SSE2_PCMPEQB_XMM_to_XMM(regd, regd); + SSE4_PINSRD_R32_to_XMM(regd, temp2, 0); + if (!is_mem) + SSE2_PCMPEQB_XMM_to_XMM(regt, regt); + localptr[7] = JMP8(0); + + x86SetJ8(localptr[2]); + x86SetJ8(localptr[4]); + x86SetJ8(localptr[5]); + x86SetJ8(localptr[6]); + x86SetJ8(localptr[7]); + + if (is_mem) + { + SSE_ANDPS_M128_to_XMM(regd, (uptr)&VU_addsub_reg[0][0]); //regd contains mask + + if (is_sub) SSE_SUBSS_M32_to_XMM(regd, (uptr)&VU_addsub_reg[1][0]); + else SSE_ADDSS_M32_to_XMM(regd, (uptr)&VU_addsub_reg[1][0]); + } + else + { + SSE_ANDPS_M128_to_XMM(regd, (uptr)&VU_addsub_reg[0][0]); //regd contains mask + SSE_ANDPS_M128_to_XMM(regt, (uptr)&VU_addsub_reg[1][0]); //regt contains mask + + if (is_sub) SSE_SUBSS_XMM_to_XMM(regd, regt); + else SSE_ADDSS_XMM_to_XMM(regd, regt); + + SSE_MOVAPS_M128_to_XMM(regt, (uptr)&VU_addsub_reg[1][0]); + } + + _freeX86reg(temp1); + _freeX86reg(temp2); +} + +void SSE_ADDPS_XMM_to_XMM_custom(int info, int regd, int regt) { + if (CHECK_VUADDSUBHACK) { + if ( cpucaps.hasStreamingSIMD4Extensions ) + VU_ADD_SUB_SSE4(regd, regt, 0, info); + else + VU_ADD_SUB(regd, regt, 0, info); + } + else SSE_ADDPS_XMM_to_XMM(regd, regt); +} +void SSE_SUBPS_XMM_to_XMM_custom(int info, int regd, int regt) { + if (CHECK_VUADDSUBHACK) { + if ( cpucaps.hasStreamingSIMD4Extensions ) + VU_ADD_SUB_SSE4(regd, regt, 1, info); + else + VU_ADD_SUB(regd, regt, 1, info); + } + else SSE_SUBPS_XMM_to_XMM(regd, regt); +} +void SSE_ADDSS_XMM_to_XMM_custom(int info, int regd, int regt) { + if (CHECK_VUADDSUBHACK) { + if ( cpucaps.hasStreamingSIMD4Extensions ) + VU_ADD_SUB_SS_SSE4(regd, regt, 0, 0, info); + else + VU_ADD_SUB_SS(regd, regt, 0, 0, info); + } + else SSE_ADDSS_XMM_to_XMM(regd, regt); +} +void SSE_SUBSS_XMM_to_XMM_custom(int info, int regd, int regt) { + if (CHECK_VUADDSUBHACK) { + if ( cpucaps.hasStreamingSIMD4Extensions ) + VU_ADD_SUB_SS_SSE4(regd, regt, 1, 0, info); + else + VU_ADD_SUB_SS(regd, regt, 1, 0, info); + } + else SSE_SUBSS_XMM_to_XMM(regd, regt); +} +void SSE_ADDSS_M32_to_XMM_custom(int info, int regd, int regt) { + if (CHECK_VUADDSUBHACK) { + if ( cpucaps.hasStreamingSIMD4Extensions ) + VU_ADD_SUB_SS_SSE4(regd, regt, 0, 1, info); + else + VU_ADD_SUB_SS(regd, regt, 0, 1, info); + } + else SSE_ADDSS_M32_to_XMM(regd, regt); +} +void SSE_SUBSS_M32_to_XMM_custom(int info, int regd, int regt) { + if (CHECK_VUADDSUBHACK) { + if ( cpucaps.hasStreamingSIMD4Extensions ) + VU_ADD_SUB_SS_SSE4(regd, regt, 1, 1, info); + else + VU_ADD_SUB_SS(regd, regt, 1, 1, info); + } + else SSE_SUBSS_M32_to_XMM(regd, regt); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// *VU Upper Instructions!* +// +// Note: * = Checked for errors by cottonvibes +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ABS* +//------------------------------------------------------------------ +void recVUMI_ABS(VURegs *VU, int info) +{ + //SysPrintf("recVUMI_ABS()\n"); + if ( (_Ft_ == 0) || (_X_Y_Z_W == 0) ) return; + + if ((_X_Y_Z_W == 0x8) || (_X_Y_Z_W == 0xf)) { + VU_MERGE_REGS(EEREC_T, EEREC_S); + SSE_ANDPS_M128_to_XMM(EEREC_T, (uptr)&const_abs_table[ _X_Y_Z_W ][ 0 ] ); + } + else { // Use a temp reg because VU_MERGE_REGS() modifies source reg! + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_ANDPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_abs_table[ _X_Y_Z_W ][ 0 ] ); + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + } +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ADD*, ADD_iq*, ADD_xyzw* +//------------------------------------------------------------------ +PCSX2_ALIGNED16(float s_two[4]) = {0,0,0,2}; +void recVUMI_ADD(VURegs *VU, int info) +{ + //SysPrintf("recVUMI_ADD()\n"); + if ( _X_Y_Z_W == 0 ) goto flagUpdate; // Don't do anything and just clear flags + if ( !_Fd_ ) info = (info & ~PROCESS_EE_SET_D(0xf)) | PROCESS_EE_SET_D(EEREC_TEMP); + + if ( _Fs_ == 0 && _Ft_ == 0 ) { // if adding VF00 with VF00, then the result is always 0,0,0,2 + if ( _X_Y_Z_W != 0xf ) { + SSE_MOVAPS_M128_to_XMM(EEREC_TEMP, (uptr)s_two); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else SSE_MOVAPS_M128_to_XMM(EEREC_D, (uptr)s_two); + } + else { + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, _X_Y_Z_W ); + } + if( _X_Y_Z_W == 8 ) { // If only adding x, then we can do a Scalar Add + if (EEREC_D == EEREC_S) SSE_ADDSS_XMM_to_XMM(EEREC_D, EEREC_T); + else if (EEREC_D == EEREC_T) SSE_ADDSS_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_ADDSS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + else if (_X_Y_Z_W != 0xf) { // If xyzw != 1111, then we have to use a temp reg + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { // All xyzw being modified (xyzw == 1111) + if (EEREC_D == EEREC_S) SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_T); + else if (EEREC_D == EEREC_T) SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + } +flagUpdate: + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_ADD_iq(VURegs *VU, uptr addr, int info) +{ + //SysPrintf("recVUMI_ADD_iq()\n"); + if ( _X_Y_Z_W == 0 ) goto flagUpdate; + if ( !_Fd_ ) info = (info & ~PROCESS_EE_SET_D(0xf)) | PROCESS_EE_SET_D(EEREC_TEMP); + if (CHECK_VU_EXTRA_OVERFLOW) { + vuFloat3(addr); + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + } + + if ( _XYZW_SS ) { + if ( EEREC_D == EEREC_TEMP ) { + _vuFlipRegSS(VU, EEREC_S); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_ADDSS_M32_to_XMM(EEREC_D, addr); + _vuFlipRegSS(VU, EEREC_S); + _vuFlipRegSS(VU, EEREC_D); // have to flip over EEREC_D for computing flags! + } + else if ( EEREC_D == EEREC_S ) { + _vuFlipRegSS(VU, EEREC_D); + SSE_ADDSS_M32_to_XMM(EEREC_D, addr); + _vuFlipRegSS(VU, EEREC_D); + } + else { + if ( _X ) { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_ADDSS_M32_to_XMM_custom(info, EEREC_D, addr); + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + SSE_ADDPS_XMM_to_XMM_custom(info, EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + } + } + else { + if ( (_X_Y_Z_W != 0xf) || (EEREC_D == EEREC_S) || (EEREC_D == EEREC_TEMP) ) { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + } + + if (_X_Y_Z_W != 0xf) { + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { + if ( EEREC_D == EEREC_TEMP ) SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_S); + else if ( EEREC_D == EEREC_S ) SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + else { + SSE_MOVSS_M32_to_XMM(EEREC_D, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_D, EEREC_D, 0x00); + SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_S); + } + } + } +flagUpdate: + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_ADD_xyzw(VURegs *VU, int xyzw, int info) +{ + //SysPrintf("recVUMI_ADD_xyzw()\n"); + if ( _X_Y_Z_W == 0 ) goto flagUpdate; + if ( !_Fd_ ) info = (info & ~PROCESS_EE_SET_D(0xf)) | PROCESS_EE_SET_D(EEREC_TEMP); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, ( 1 << (3 - xyzw) ) ); + } + + if ( _Ft_ == 0 && xyzw < 3 ) { // just move since adding zero + if ( _X_Y_Z_W == 0x8 ) { VU_MERGE_REGS(EEREC_D, EEREC_S); } + else if ( _X_Y_Z_W != 0xf ) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else SSE_MOVAPS_XMM_to_XMM(EEREC_D, EEREC_S); + } + else if ( _X_Y_Z_W == 8 && (EEREC_D != EEREC_TEMP) ) { + if ( xyzw == 0 ) { + if ( EEREC_D == EEREC_T ) SSE_ADDSS_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_ADDSS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + else { + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_ADDSS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + } + else if( _Fs_ == 0 && !_W ) { // just move + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { + if ( _X_Y_Z_W != 0xf ) { + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { + if( EEREC_D == EEREC_TEMP ) { _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_S); } + else if( EEREC_D == EEREC_S ) { _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_TEMP); } + else { _unpackVF_xyzw(EEREC_D, EEREC_T, xyzw); SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_S); } + } + } +flagUpdate: + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_ADDi(VURegs *VU, int info) { recVUMI_ADD_iq(VU, VU_VI_ADDR(REG_I, 1), info); } +void recVUMI_ADDq(VURegs *VU, int info) { recVUMI_ADD_iq(VU, VU_REGQ_ADDR, info); } +void recVUMI_ADDx(VURegs *VU, int info) { recVUMI_ADD_xyzw(VU, 0, info); } +void recVUMI_ADDy(VURegs *VU, int info) { recVUMI_ADD_xyzw(VU, 1, info); } +void recVUMI_ADDz(VURegs *VU, int info) { recVUMI_ADD_xyzw(VU, 2, info); } +void recVUMI_ADDw(VURegs *VU, int info) { recVUMI_ADD_xyzw(VU, 3, info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ADDA*, ADDA_iq*, ADDA_xyzw* +//------------------------------------------------------------------ +void recVUMI_ADDA(VURegs *VU, int info) +{ + //SysPrintf("recVUMI_ADDA()\n"); + if ( _X_Y_Z_W == 0 ) goto flagUpdate; + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, _X_Y_Z_W ); + } + + if( _X_Y_Z_W == 8 ) { + if (EEREC_ACC == EEREC_S) SSE_ADDSS_XMM_to_XMM(EEREC_ACC, EEREC_T); // Can this case happen? (cottonvibes) + else if (EEREC_ACC == EEREC_T) SSE_ADDSS_XMM_to_XMM(EEREC_ACC, EEREC_S); // Can this case happen? + else { + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_ADDSS_XMM_to_XMM(EEREC_ACC, EEREC_T); + } + } + else if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + + VU_MERGE_REGS(EEREC_ACC, EEREC_TEMP); + } + else { + if( EEREC_ACC == EEREC_S ) SSE_ADDPS_XMM_to_XMM(EEREC_ACC, EEREC_T); // Can this case happen? + else if( EEREC_ACC == EEREC_T ) SSE_ADDPS_XMM_to_XMM(EEREC_ACC, EEREC_S); // Can this case happen? + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_ADDPS_XMM_to_XMM(EEREC_ACC, EEREC_T); + } + } +flagUpdate: + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_ADDA_iq(VURegs *VU, uptr addr, int info) +{ + //SysPrintf("recVUMI_ADDA_iq()\n"); + if ( _X_Y_Z_W == 0 ) goto flagUpdate; + if (CHECK_VU_EXTRA_OVERFLOW) { + vuFloat3(addr); + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + } + + if( _XYZW_SS ) { + assert( EEREC_ACC != EEREC_TEMP ); + if( EEREC_ACC == EEREC_S ) { + _vuFlipRegSS(VU, EEREC_ACC); + SSE_ADDSS_M32_to_XMM(EEREC_ACC, addr); + _vuFlipRegSS(VU, EEREC_ACC); + } + else { + if( _X ) { + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_ADDSS_M32_to_XMM(EEREC_ACC, addr); + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_ACC, EEREC_TEMP); + } + } + } + else { + if( _X_Y_Z_W != 0xf || EEREC_ACC == EEREC_S ) { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + } + + if (_X_Y_Z_W != 0xf) { + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_ACC, EEREC_TEMP); + } + else { + if( EEREC_ACC == EEREC_S ) SSE_ADDPS_XMM_to_XMM(EEREC_ACC, EEREC_TEMP); + else { + SSE_MOVSS_M32_to_XMM(EEREC_ACC, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_ACC, EEREC_ACC, 0x00); + SSE_ADDPS_XMM_to_XMM(EEREC_ACC, EEREC_S); + } + } + } +flagUpdate: + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_ADDA_xyzw(VURegs *VU, int xyzw, int info) +{ + //SysPrintf("recVUMI_ADDA_xyzw()\n"); + if ( _X_Y_Z_W == 0 ) goto flagUpdate; + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, ( 1 << (3 - xyzw) ) ); + } + + if( _X_Y_Z_W == 8 ) { + assert( EEREC_ACC != EEREC_T ); + if( xyzw == 0 ) { + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_ADDSS_XMM_to_XMM(EEREC_ACC, EEREC_T); + } + else { + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, xyzw); + if( _Fs_ == 0 ) { + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_TEMP); + } + else { + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_ADDSS_XMM_to_XMM(EEREC_ACC, EEREC_TEMP); + } + } + } + else { + if( _X_Y_Z_W != 0xf || EEREC_ACC == EEREC_S ) + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + + if (_X_Y_Z_W != 0xf) { + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_ACC, EEREC_TEMP); + } + else { + if( EEREC_ACC == EEREC_S ) SSE_ADDPS_XMM_to_XMM(EEREC_ACC, EEREC_TEMP); + else { + _unpackVF_xyzw(EEREC_ACC, EEREC_T, xyzw); + SSE_ADDPS_XMM_to_XMM(EEREC_ACC, EEREC_S); + } + } + } +flagUpdate: + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_ADDAi(VURegs *VU, int info) { recVUMI_ADDA_iq(VU, VU_VI_ADDR(REG_I, 1), info); } +void recVUMI_ADDAq(VURegs *VU, int info) { recVUMI_ADDA_iq(VU, VU_REGQ_ADDR, info); } +void recVUMI_ADDAx(VURegs *VU, int info) { recVUMI_ADDA_xyzw(VU, 0, info); } +void recVUMI_ADDAy(VURegs *VU, int info) { recVUMI_ADDA_xyzw(VU, 1, info); } +void recVUMI_ADDAz(VURegs *VU, int info) { recVUMI_ADDA_xyzw(VU, 2, info); } +void recVUMI_ADDAw(VURegs *VU, int info) { recVUMI_ADDA_xyzw(VU, 3, info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// SUB*, SUB_iq*, SUB_xyzw* +//------------------------------------------------------------------ +void recVUMI_SUB(VURegs *VU, int info) +{ + //SysPrintf("recVUMI_SUB()\n"); + if ( _X_Y_Z_W == 0 ) goto flagUpdate; + if ( !_Fd_ ) info = (info & ~PROCESS_EE_SET_D(0xf)) | PROCESS_EE_SET_D(EEREC_TEMP); + + if( EEREC_S == EEREC_T ) { + if (_X_Y_Z_W != 0xf) SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)&SSEmovMask[15-_X_Y_Z_W][0]); + else SSE_XORPS_XMM_to_XMM(EEREC_D, EEREC_D); + } + else if( _X_Y_Z_W == 8 ) { + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, _X_Y_Z_W ); + } + if (EEREC_D == EEREC_S) { + if (_Ft_) SSE_SUBSS_XMM_to_XMM(EEREC_D, EEREC_T); + } + else if (EEREC_D == EEREC_T) { + if (_Ft_) { + SSE_MOVSS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_SUBSS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + else SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + } + else { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + if (_Ft_) SSE_SUBSS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + else { + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, _X_Y_Z_W ); + } + if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if( ( _Ft_ > 0 ) || _W ) SSE_SUBPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { + if (EEREC_D == EEREC_S) SSE_SUBPS_XMM_to_XMM(EEREC_D, EEREC_T); + else if (EEREC_D == EEREC_T) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_SUBPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + SSE_MOVAPS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_SUBPS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + } +flagUpdate: + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_SUB_iq(VURegs *VU, uptr addr, int info) +{ + //SysPrintf("recVUMI_SUB_iq()\n"); + if ( _X_Y_Z_W == 0 ) goto flagUpdate; + if (CHECK_VU_EXTRA_OVERFLOW) { + vuFloat3(addr); + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + } + if ( !_Fd_ ) info = (info & ~PROCESS_EE_SET_D(0xf)) | PROCESS_EE_SET_D(EEREC_TEMP); + + if( _XYZW_SS ) { + if( EEREC_D == EEREC_TEMP ) { + _vuFlipRegSS(VU, EEREC_S); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_SUBSS_M32_to_XMM(EEREC_D, addr); + _vuFlipRegSS(VU, EEREC_S); + _vuFlipRegSS(VU, EEREC_D); + } + else if( EEREC_D == EEREC_S ) { + _vuFlipRegSS(VU, EEREC_D); + SSE_SUBSS_M32_to_XMM(EEREC_D, addr); + _vuFlipRegSS(VU, EEREC_D); + } + else { + if( _X ) { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_SUBSS_M32_to_XMM(EEREC_D, addr); + } + else { + _vuMoveSS(VU, EEREC_TEMP, EEREC_S); + _vuFlipRegSS(VU, EEREC_D); + SSE_SUBSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + _vuFlipRegSS(VU, EEREC_D); + } + } + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + + if (_X_Y_Z_W != 0xf) { + int t1reg = _vuGetTempXMMreg(info); + + if( t1reg >= 0 ) { + SSE_MOVAPS_XMM_to_XMM(t1reg, EEREC_S); + SSE_SUBPS_XMM_to_XMM(t1reg, EEREC_TEMP); + + VU_MERGE_REGS(EEREC_D, t1reg); + _freeXMMreg(t1reg); + } + else { + // negate + SSE_XORPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_clip[4]); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + } + else { + if( EEREC_D == EEREC_TEMP ) { + SSE_XORPS_M128_to_XMM(EEREC_D, (uptr)&const_clip[4]); + SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_S); + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_SUBPS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + } + } +flagUpdate: + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_SUB_xyzw(VURegs *VU, int xyzw, int info) +{ + //SysPrintf("recVUMI_SUB_xyzw()\n"); + if ( _X_Y_Z_W == 0 ) goto flagUpdate; + if ( !_Fd_ ) info = (info & ~PROCESS_EE_SET_D(0xf)) | PROCESS_EE_SET_D(EEREC_TEMP); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, ( 1 << (3 - xyzw) ) ); + } + + if ( _X_Y_Z_W == 8 ) { + if ( (xyzw == 0) && (_Ft_ == _Fs_) ) { + SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)&SSEmovMask[7][0]); + } + else if ( EEREC_D == EEREC_TEMP ) { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + if ( (_Ft_ > 0) || (xyzw == 3) ) { + _vuFlipRegSS_xyzw(EEREC_T, xyzw); + SSE_SUBSS_XMM_to_XMM(EEREC_D, EEREC_T); + _vuFlipRegSS_xyzw(EEREC_T, xyzw); + } + } + else { + if ( (_Ft_ > 0) || (xyzw == 3) ) { + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_SUBSS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + else SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + } + } + else { + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + + if (_X_Y_Z_W != 0xf) { + int t1reg = _vuGetTempXMMreg(info); + + if( t1reg >= 0 ) { + SSE_MOVAPS_XMM_to_XMM(t1reg, EEREC_S); + SSE_SUBPS_XMM_to_XMM(t1reg, EEREC_TEMP); + + VU_MERGE_REGS(EEREC_D, t1reg); + _freeXMMreg(t1reg); + } + else { + // negate + SSE_XORPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_clip[4]); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + } + else { + if( EEREC_D == EEREC_TEMP ) { + SSE_XORPS_M128_to_XMM(EEREC_D, (uptr)&const_clip[4]); + SSE_ADDPS_XMM_to_XMM(EEREC_D, EEREC_S); + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_SUBPS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + } + } +flagUpdate: + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_SUBi(VURegs *VU, int info) { recVUMI_SUB_iq(VU, VU_VI_ADDR(REG_I, 1), info); } +void recVUMI_SUBq(VURegs *VU, int info) { recVUMI_SUB_iq(VU, VU_REGQ_ADDR, info); } +void recVUMI_SUBx(VURegs *VU, int info) { recVUMI_SUB_xyzw(VU, 0, info); } +void recVUMI_SUBy(VURegs *VU, int info) { recVUMI_SUB_xyzw(VU, 1, info); } +void recVUMI_SUBz(VURegs *VU, int info) { recVUMI_SUB_xyzw(VU, 2, info); } +void recVUMI_SUBw(VURegs *VU, int info) { recVUMI_SUB_xyzw(VU, 3, info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// SUBA*, SUBA_iq, SUBA_xyzw +//------------------------------------------------------------------ +void recVUMI_SUBA(VURegs *VU, int info) +{ + //SysPrintf("recVUMI_SUBA()\n"); + if ( _X_Y_Z_W == 0 ) goto flagUpdate; + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, _X_Y_Z_W ); + } + + if( EEREC_S == EEREC_T ) { + if (_X_Y_Z_W != 0xf) SSE_ANDPS_M128_to_XMM(EEREC_ACC, (uptr)&SSEmovMask[15-_X_Y_Z_W][0]); + else SSE_XORPS_XMM_to_XMM(EEREC_ACC, EEREC_ACC); + } + else if( _X_Y_Z_W == 8 ) { + if (EEREC_ACC == EEREC_S) SSE_SUBSS_XMM_to_XMM(EEREC_ACC, EEREC_T); + else if (EEREC_ACC == EEREC_T) { + SSE_MOVSS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_SUBSS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_TEMP); + } + else { + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_SUBSS_XMM_to_XMM(EEREC_ACC, EEREC_T); + } + } + else if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_SUBPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + + VU_MERGE_REGS(EEREC_ACC, EEREC_TEMP); + } + else { + if( EEREC_ACC == EEREC_S ) SSE_SUBPS_XMM_to_XMM(EEREC_ACC, EEREC_T); + else if( EEREC_ACC == EEREC_T ) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_SUBPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + SSE_MOVAPS_XMM_to_XMM(EEREC_ACC, EEREC_TEMP); + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_SUBPS_XMM_to_XMM(EEREC_ACC, EEREC_T); + } + } +flagUpdate: + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_SUBA_iq(VURegs *VU, uptr addr, int info) +{ + //SysPrintf ("recVUMI_SUBA_iq \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + vuFloat3(addr); + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + } + + if( _XYZW_SS ) { + if( EEREC_ACC == EEREC_S ) { + _vuFlipRegSS(VU, EEREC_ACC); + SSE_SUBSS_M32_to_XMM(EEREC_ACC, addr); + _vuFlipRegSS(VU, EEREC_ACC); + } + else { + if( _X ) { + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_SUBSS_M32_to_XMM(EEREC_ACC, addr); + } + else { + _vuMoveSS(VU, EEREC_TEMP, EEREC_S); + _vuFlipRegSS(VU, EEREC_ACC); + SSE_SUBSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_TEMP); + _vuFlipRegSS(VU, EEREC_ACC); + } + } + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + + if (_X_Y_Z_W != 0xf) { + int t1reg = _vuGetTempXMMreg(info); + + if( t1reg >= 0 ) { + SSE_MOVAPS_XMM_to_XMM(t1reg, EEREC_S); + SSE_SUBPS_XMM_to_XMM(t1reg, EEREC_TEMP); + + VU_MERGE_REGS(EEREC_ACC, t1reg); + _freeXMMreg(t1reg); + } + else { + // negate + SSE_XORPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_clip[4]); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_ACC, EEREC_TEMP); + } + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_SUBPS_XMM_to_XMM(EEREC_ACC, EEREC_TEMP); + } + } + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_SUBA_xyzw(VURegs *VU, int xyzw, int info) +{ + //SysPrintf ("recVUMI_SUBA_xyzw \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, ( 1 << (3 - xyzw) ) ); + } + + if( _X_Y_Z_W == 8 ) { + if( xyzw == 0 ) { + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_SUBSS_XMM_to_XMM(EEREC_ACC, EEREC_T); + } + else { + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MOVSS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_SUBSS_XMM_to_XMM(EEREC_ACC, EEREC_TEMP); + } + } + else { + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + + if (_X_Y_Z_W != 0xf) { + int t1reg = _vuGetTempXMMreg(info); + + if( t1reg >= 0 ) { + SSE_MOVAPS_XMM_to_XMM(t1reg, EEREC_S); + SSE_SUBPS_XMM_to_XMM(t1reg, EEREC_TEMP); + + VU_MERGE_REGS(EEREC_ACC, t1reg); + _freeXMMreg(t1reg); + } + else { + // negate + SSE_XORPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_clip[4]); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_ACC, EEREC_TEMP); + } + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_ACC, EEREC_S); + SSE_SUBPS_XMM_to_XMM(EEREC_ACC, EEREC_TEMP); + } + } + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_SUBAi(VURegs *VU, int info) { recVUMI_SUBA_iq(VU, VU_VI_ADDR(REG_I, 1), info); } +void recVUMI_SUBAq(VURegs *VU, int info) { recVUMI_SUBA_iq(VU, VU_REGQ_ADDR, info); } +void recVUMI_SUBAx(VURegs *VU, int info) { recVUMI_SUBA_xyzw(VU, 0, info); } +void recVUMI_SUBAy(VURegs *VU, int info) { recVUMI_SUBA_xyzw(VU, 1, info); } +void recVUMI_SUBAz(VURegs *VU, int info) { recVUMI_SUBA_xyzw(VU, 2, info); } +void recVUMI_SUBAw(VURegs *VU, int info) { recVUMI_SUBA_xyzw(VU, 3, info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MUL +//------------------------------------------------------------------ +void recVUMI_MUL_toD(VURegs *VU, int regd, int info) +{ + //SysPrintf ("recVUMI_MUL_toD \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, _X_Y_Z_W ); + } + + if (_X_Y_Z_W == 1 && (_Ft_ == 0 || _Fs_==0) ) { // W + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, _Ft_ ? EEREC_T : EEREC_S); + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else if( _Fd_ == _Fs_ && _Fs_ == _Ft_ && _XYZW_SS ) { + _vuFlipRegSS(VU, EEREC_D); + SSE_MULSS_XMM_to_XMM(EEREC_D, EEREC_D); + _vuFlipRegSS(VU, EEREC_D); + } + else if( _X_Y_Z_W == 8 ) { + if (regd == EEREC_S) SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + else if (regd == EEREC_T) SSE_MULSS_XMM_to_XMM(regd, EEREC_S); + else { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + } + } + else if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else { + if (regd == EEREC_S) SSE_MULPS_XMM_to_XMM(regd, EEREC_T); + else if (regd == EEREC_T) SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + else { + SSE_MOVAPS_XMM_to_XMM(regd, EEREC_S); + SSE_MULPS_XMM_to_XMM(regd, EEREC_T); + } + } +} + +void recVUMI_MUL_iq_toD(VURegs *VU, uptr addr, int regd, int info) +{ + //SysPrintf ("recVUMI_MUL_iq_toD \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + vuFloat3(addr); + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + } + + if( _XYZW_SS ) { + if( regd == EEREC_TEMP ) { + _vuFlipRegSS(VU, EEREC_S); + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + SSE_MULSS_M32_to_XMM(regd, addr); + _vuFlipRegSS(VU, EEREC_S); + } + else if( regd == EEREC_S ) { + _vuFlipRegSS(VU, regd); + SSE_MULSS_M32_to_XMM(regd, addr); + _vuFlipRegSS(VU, regd); + } + else { + if( _X ) { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + SSE_MULSS_M32_to_XMM(regd, addr); + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(regd, EEREC_TEMP); + } + } + } + else { + if( _X_Y_Z_W != 0xf || regd == EEREC_TEMP || regd == EEREC_S ) { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + } + + if (_X_Y_Z_W != 0xf) { + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else { + if( regd == EEREC_TEMP ) SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + else if (regd == EEREC_S) SSE_MULPS_XMM_to_XMM(regd, EEREC_TEMP); + else { + SSE_MOVSS_M32_to_XMM(regd, addr); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x00); + SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + } + } + } +} + +void recVUMI_MUL_xyzw_toD(VURegs *VU, int xyzw, int regd, int info) +{ + //SysPrintf ("recVUMI_MUL_xyzw_toD \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, ( 1 << (3 - xyzw) ) ); + } + if (_Fs_) { // This is needed for alot of games; so always clamp this operand + if (CHECK_VU_SIGN_OVERFLOW) vFloats4_useEAX[_X_Y_Z_W]( EEREC_S, EEREC_TEMP ); // Always clamp EEREC_S, regardless if CHECK_VU_OVERFLOW is set + else vFloats2_MUL_MADD[_X_Y_Z_W]( EEREC_S, EEREC_TEMP ); // Always clamp EEREC_S, regardless if CHECK_VU_OVERFLOW is set + } + if( _Ft_ == 0 ) { + if( xyzw < 3 ) { + if (_X_Y_Z_W != 0xf) { + SSE_XORPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else SSE_XORPS_XMM_to_XMM(regd, regd); + } + else { + assert(xyzw==3); + if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else SSE_MOVAPS_XMM_to_XMM(regd, EEREC_S); + } + } + else if( _X_Y_Z_W == 8 ) { + if( regd == EEREC_TEMP ) { + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MULSS_XMM_to_XMM(regd, EEREC_S); + } + else { + if( xyzw == 0 ) { + if( regd == EEREC_T ) { + SSE_MULSS_XMM_to_XMM(regd, EEREC_S); + } + else { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + } + } + else { + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + SSE_MULSS_XMM_to_XMM(regd, EEREC_TEMP); + } + } + } + else { + if( _X_Y_Z_W != 0xf || regd == EEREC_TEMP || regd == EEREC_S ) + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + + if (_X_Y_Z_W != 0xf) { + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else { + if( regd == EEREC_TEMP ) SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + else if (regd == EEREC_S) SSE_MULPS_XMM_to_XMM(regd, EEREC_TEMP); + else { + _unpackVF_xyzw(regd, EEREC_T, xyzw); + SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + } + } + } +} + +void recVUMI_MUL(VURegs *VU, int info) +{ + //SysPrintf ("recVUMI_MUL \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MUL_toD(VU, EEREC_D, info); + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_MUL_iq(VURegs *VU, int addr, int info) +{ + //SysPrintf ("recVUMI_MUL_iq \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MUL_iq_toD(VU, addr, EEREC_D, info); + recUpdateFlags(VU, EEREC_D, info); + // spacefisherman needs overflow checking on MULi.z +} + +void recVUMI_MUL_xyzw(VURegs *VU, int xyzw, int info) +{ + //SysPrintf ("recVUMI_MUL_xyzw \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MUL_xyzw_toD(VU, xyzw, EEREC_D, info); + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_MULi(VURegs *VU, int info) { recVUMI_MUL_iq(VU, VU_VI_ADDR(REG_I, 1), info); } +void recVUMI_MULq(VURegs *VU, int info) { recVUMI_MUL_iq(VU, VU_REGQ_ADDR, info); } +void recVUMI_MULx(VURegs *VU, int info) { recVUMI_MUL_xyzw(VU, 0, info); } +void recVUMI_MULy(VURegs *VU, int info) { recVUMI_MUL_xyzw(VU, 1, info); } +void recVUMI_MULz(VURegs *VU, int info) { recVUMI_MUL_xyzw(VU, 2, info); } +void recVUMI_MULw(VURegs *VU, int info) { recVUMI_MUL_xyzw(VU, 3, info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MULA +//------------------------------------------------------------------ +void recVUMI_MULA( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_MULA \n"); + recVUMI_MUL_toD(VU, EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MULA_iq(VURegs *VU, int addr, int info) +{ + //SysPrintf ("recVUMI_MULA_iq \n"); + recVUMI_MUL_iq_toD(VU, addr, EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MULA_xyzw(VURegs *VU, int xyzw, int info) +{ + //SysPrintf ("recVUMI_MULA_xyzw \n"); + recVUMI_MUL_xyzw_toD(VU, xyzw, EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MULAi(VURegs *VU, int info) { recVUMI_MULA_iq(VU, VU_VI_ADDR(REG_I, 1), info); } +void recVUMI_MULAq(VURegs *VU, int info) { recVUMI_MULA_iq(VU, VU_REGQ_ADDR, info); } +void recVUMI_MULAx(VURegs *VU, int info) { recVUMI_MULA_xyzw(VU, 0, info); } +void recVUMI_MULAy(VURegs *VU, int info) { recVUMI_MULA_xyzw(VU, 1, info); } +void recVUMI_MULAz(VURegs *VU, int info) { recVUMI_MULA_xyzw(VU, 2, info); } +void recVUMI_MULAw(VURegs *VU, int info) { recVUMI_MULA_xyzw(VU, 3, info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MADD +//------------------------------------------------------------------ +void recVUMI_MADD_toD(VURegs *VU, int regd, int info) +{ + //SysPrintf ("recVUMI_MADD_toD \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, _X_Y_Z_W ); + vuFloat5_useEAX( EEREC_ACC, EEREC_TEMP, _X_Y_Z_W ); + } + + if( _X_Y_Z_W == 8 ) { + if( regd == EEREC_ACC ) { + SSE_MOVSS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MULSS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, 8); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_TEMP); + } + else if (regd == EEREC_T) { + SSE_MULSS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, 8); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_ACC); + } + else if (regd == EEREC_S) { + SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, 8); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_ACC); + } + else { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + SSE_MULSS_XMM_to_XMM(regd, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, 8); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_ACC); + } + } + else if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_ACC); + + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else { + if( regd == EEREC_ACC ) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_TEMP); + } + else if (regd == EEREC_T) { + SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_ACC); + } + else if (regd == EEREC_S) { + SSE_MULPS_XMM_to_XMM(regd, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_ACC); + } + else { + SSE_MOVAPS_XMM_to_XMM(regd, EEREC_S); + SSE_MULPS_XMM_to_XMM(regd, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_ACC); + } + } +} + +void recVUMI_MADD_iq_toD(VURegs *VU, uptr addr, int regd, int info) +{ + //SysPrintf ("recVUMI_MADD_iq_toD \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + vuFloat3(addr); + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + vuFloat5_useEAX( EEREC_ACC, EEREC_TEMP, _X_Y_Z_W ); + } + + if( _X_Y_Z_W == 8 ) { + if( regd == EEREC_ACC ) { + if( _Fs_ == 0 ) { + // add addr to w + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_ADDSS_M32_to_XMM(regd, addr); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + } + else { + assert( EEREC_TEMP < XMMREGS ); + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_MULSS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, 8); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_TEMP); + } + } + else if( regd == EEREC_S ) { + SSE_MULSS_M32_to_XMM(regd, addr); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_ACC); + } + else { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_S); + SSE_MULSS_M32_to_XMM(regd, addr); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_ACC); + } + } + else { + if( _Fs_ == 0 ) { + // add addr to w + if( _W ) { + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + SSE_ADDSS_M32_to_XMM(regd, addr); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x27); + } + + return; + } + + if( _X_Y_Z_W != 0xf || regd == EEREC_ACC || regd == EEREC_TEMP || regd == EEREC_S ) { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + } + + if (_X_Y_Z_W != 0xf) { + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_ACC); + + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else { + if( regd == EEREC_ACC ) { + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_TEMP); + } + else if( regd == EEREC_S ) { + SSE_MULPS_XMM_to_XMM(regd, EEREC_TEMP); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_ACC); + } + else if( regd == EEREC_TEMP ) { + SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_ACC); + } + else { + SSE_MOVSS_M32_to_XMM(regd, addr); + SSE_SHUFPS_XMM_to_XMM(regd, regd, 0x00); + SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_ACC); + } + } + } +} + +void recVUMI_MADD_xyzw_toD(VURegs *VU, int xyzw, int regd, int info) +{ + //SysPrintf ("recVUMI_MADD_xyzw_toD \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, ( 1 << (3 - xyzw) ) ); + vuFloat5_useEAX( EEREC_ACC, EEREC_TEMP, _X_Y_Z_W ); + } + if (_Fs_) { // This is needed for alot of games; so always clamp this operand + if (CHECK_VU_SIGN_OVERFLOW) vFloats4_useEAX[_X_Y_Z_W]( EEREC_S, EEREC_TEMP ); // Always clamp EEREC_S, regardless if CHECK_VU_OVERFLOW is set + else vFloats2_MUL_MADD[_X_Y_Z_W]( EEREC_S, EEREC_TEMP ); // Always clamp EEREC_S, regardless if CHECK_VU_OVERFLOW is set + } + if( _Ft_ == 0 ) { + + if( xyzw == 3 ) { + // just add + if( _X_Y_Z_W == 8 ) { + if( regd == EEREC_S ) SSE_ADDSS_XMM_to_XMM(regd, EEREC_ACC); + else { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_ACC); + SSE_ADDSS_XMM_to_XMM(regd, EEREC_S); + } + } + else { + if( _X_Y_Z_W != 0xf ) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_ACC); + + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else { + if( regd == EEREC_S ) SSE_ADDPS_XMM_to_XMM(regd, EEREC_ACC); + else { + SSE_MOVAPS_XMM_to_XMM(regd, EEREC_ACC); + SSE_ADDPS_XMM_to_XMM(regd, EEREC_S); + } + } + } + } + else { + // just move acc to regd + if( _X_Y_Z_W != 0xf ) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_ACC); + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else SSE_MOVAPS_XMM_to_XMM(regd, EEREC_ACC); + } + + return; + } + + if( _X_Y_Z_W == 8 ) { + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, xyzw); + + if( regd == EEREC_ACC ) { + SSE_MULSS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, 8); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_TEMP); + } + else if( regd == EEREC_S ) { + SSE_MULSS_XMM_to_XMM(regd, EEREC_TEMP); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, 8); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_ACC); + } + else if( regd == EEREC_TEMP ) { + SSE_MULSS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, 8); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_ACC); + } + else { + SSE_MOVSS_XMM_to_XMM(regd, EEREC_ACC); + SSE_MULSS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, 8); } + SSE_ADDSS_XMM_to_XMM(regd, EEREC_TEMP); + } + } + else { + if( _X_Y_Z_W != 0xf || regd == EEREC_ACC || regd == EEREC_TEMP || regd == EEREC_S ) { + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + } + + if (_X_Y_Z_W != 0xf) { + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_ACC); + + VU_MERGE_REGS(regd, EEREC_TEMP); + } + else { + if( regd == EEREC_ACC ) { + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_TEMP); + } + else if( regd == EEREC_S ) { + SSE_MULPS_XMM_to_XMM(regd, EEREC_TEMP); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_ACC); + } + else if( regd == EEREC_TEMP ) { + SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_ACC); + } + else { + _unpackVF_xyzw(regd, EEREC_T, xyzw); + SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_ADDPS_XMM_to_XMM(regd, EEREC_ACC); + } + } + } +} + +void recVUMI_MADD(VURegs *VU, int info) +{ + //SysPrintf ("recVUMI_MADD \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MADD_toD(VU, EEREC_D, info); + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_MADD_iq(VURegs *VU, int addr, int info) +{ + //SysPrintf ("recVUMI_MADD_iq \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MADD_iq_toD(VU, addr, EEREC_D, info); + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_MADD_xyzw(VURegs *VU, int xyzw, int info) +{ + //SysPrintf ("recVUMI_MADD_xyzw \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MADD_xyzw_toD(VU, xyzw, EEREC_D, info); + recUpdateFlags(VU, EEREC_D, info); + // super bust-a-move arrows needs overflow clamping +} + +void recVUMI_MADDi(VURegs *VU, int info) { recVUMI_MADD_iq(VU, VU_VI_ADDR(REG_I, 1), info); } +void recVUMI_MADDq(VURegs *VU, int info) { recVUMI_MADD_iq(VU, VU_REGQ_ADDR, info); } +void recVUMI_MADDx(VURegs *VU, int info) { recVUMI_MADD_xyzw(VU, 0, info); } +void recVUMI_MADDy(VURegs *VU, int info) { recVUMI_MADD_xyzw(VU, 1, info); } +void recVUMI_MADDz(VURegs *VU, int info) { recVUMI_MADD_xyzw(VU, 2, info); } +void recVUMI_MADDw(VURegs *VU, int info) { recVUMI_MADD_xyzw(VU, 3, info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MADDA +//------------------------------------------------------------------ +void recVUMI_MADDA( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_MADDA \n"); + recVUMI_MADD_toD(VU, EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MADDAi( VURegs *VU , int info) +{ + //SysPrintf ("recVUMI_MADDAi \n"); + recVUMI_MADD_iq_toD( VU, VU_VI_ADDR(REG_I, 1), EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MADDAq( VURegs *VU , int info) +{ + //SysPrintf ("recVUMI_MADDAq \n"); + recVUMI_MADD_iq_toD( VU, VU_REGQ_ADDR, EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MADDAx( VURegs *VU , int info) +{ + //SysPrintf ("recVUMI_MADDAx \n"); + recVUMI_MADD_xyzw_toD(VU, 0, EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MADDAy( VURegs *VU , int info) +{ + //SysPrintf ("recVUMI_MADDAy \n"); + recVUMI_MADD_xyzw_toD(VU, 1, EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MADDAz( VURegs *VU , int info) +{ + //SysPrintf ("recVUMI_MADDAz \n"); + recVUMI_MADD_xyzw_toD(VU, 2, EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MADDAw( VURegs *VU , int info) +{ + //SysPrintf ("recVUMI_MADDAw \n"); + recVUMI_MADD_xyzw_toD(VU, 3, EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MSUB +//------------------------------------------------------------------ +void recVUMI_MSUB_toD(VURegs *VU, int regd, int info) +{ + //SysPrintf ("recVUMI_MSUB_toD \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + if (_Ft_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + vuFloat5_useEAX( EEREC_ACC, EEREC_TEMP, _X_Y_Z_W ); + } + + if (_X_Y_Z_W != 0xf) { + int t1reg = _vuGetTempXMMreg(info); + + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + + if( t1reg >= 0 ) { + SSE_MOVAPS_XMM_to_XMM(t1reg, EEREC_ACC); + SSE_SUBPS_XMM_to_XMM(t1reg, EEREC_TEMP); + + VU_MERGE_REGS(regd, t1reg); + _freeXMMreg(t1reg); + } + else { + SSE_XORPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_clip[4]); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_ACC); + VU_MERGE_REGS(regd, EEREC_TEMP); + } + } + else { + if( regd == EEREC_S ) { + assert( regd != EEREC_ACC ); + SSE_MULPS_XMM_to_XMM(regd, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_SUBPS_XMM_to_XMM(regd, EEREC_ACC); + SSE_XORPS_M128_to_XMM(regd, (uptr)&const_clip[4]); + } + else if( regd == EEREC_T ) { + assert( regd != EEREC_ACC ); + SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_SUBPS_XMM_to_XMM(regd, EEREC_ACC); + SSE_XORPS_M128_to_XMM(regd, (uptr)&const_clip[4]); + } + else if( regd == EEREC_TEMP ) { + SSE_MOVAPS_XMM_to_XMM(regd, EEREC_S); + SSE_MULPS_XMM_to_XMM(regd, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_SUBPS_XMM_to_XMM(regd, EEREC_ACC); + SSE_XORPS_M128_to_XMM(regd, (uptr)&const_clip[4]); + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MOVAPS_XMM_to_XMM(regd, EEREC_ACC); + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + SSE_SUBPS_XMM_to_XMM(regd, EEREC_TEMP); + } + } +} + +void recVUMI_MSUB_temp_toD(VURegs *VU, int regd, int info) +{ + //SysPrintf ("recVUMI_MSUB_temp_toD \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); + vuFloat5_useEAX( EEREC_ACC, EEREC_TEMP, _X_Y_Z_W ); + } + + if (_X_Y_Z_W != 0xf) { + int t1reg = _vuGetTempXMMreg(info); + + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + + if( t1reg >= 0 ) { + SSE_MOVAPS_XMM_to_XMM(t1reg, EEREC_ACC); + SSE_SUBPS_XMM_to_XMM(t1reg, EEREC_TEMP); + + if ( regd != EEREC_TEMP ) { VU_MERGE_REGS(regd, t1reg); } + else SSE_MOVAPS_XMM_to_XMM(regd, t1reg); + + _freeXMMreg(t1reg); + } + else { + SSE_XORPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_clip[4]); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_ACC); + VU_MERGE_REGS(regd, EEREC_TEMP); + } + } + else { + if( regd == EEREC_ACC ) { + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + SSE_SUBPS_XMM_to_XMM(regd, EEREC_TEMP); + } + else if( regd == EEREC_S ) { + SSE_MULPS_XMM_to_XMM(regd, EEREC_TEMP); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_SUBPS_XMM_to_XMM(regd, EEREC_ACC); + SSE_XORPS_M128_to_XMM(regd, (uptr)&const_clip[4]); + } + else if( regd == EEREC_TEMP ) { + SSE_MULPS_XMM_to_XMM(regd, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, regd, _X_Y_Z_W ); } + SSE_SUBPS_XMM_to_XMM(regd, EEREC_ACC); + SSE_XORPS_M128_to_XMM(regd, (uptr)&const_clip[4]); + } + else { + SSE_MOVAPS_XMM_to_XMM(regd, EEREC_ACC); + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + if (CHECK_VU_EXTRA_OVERFLOW) { vuFloat_useEAX( info, EEREC_TEMP, _X_Y_Z_W ); } + SSE_SUBPS_XMM_to_XMM(regd, EEREC_TEMP); + } + } +} + +void recVUMI_MSUB_iq_toD(VURegs *VU, int regd, int addr, int info) +{ + //SysPrintf ("recVUMI_MSUB_iq_toD \n"); + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + recVUMI_MSUB_temp_toD(VU, regd, info); +} + +void recVUMI_MSUB_xyzw_toD(VURegs *VU, int regd, int xyzw, int info) +{ + //SysPrintf ("recVUMI_MSUB_xyzw_toD \n"); + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + recVUMI_MSUB_temp_toD(VU, regd, info); +} + +void recVUMI_MSUB(VURegs *VU, int info) +{ + //SysPrintf ("recVUMI_MSUB \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MSUB_toD(VU, EEREC_D, info); + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_MSUB_iq(VURegs *VU, int addr, int info) +{ + //SysPrintf ("recVUMI_MSUB_iq \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MSUB_iq_toD(VU, EEREC_D, addr, info); + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_MSUBi(VURegs *VU, int info) { recVUMI_MSUB_iq(VU, VU_VI_ADDR(REG_I, 1), info); } +void recVUMI_MSUBq(VURegs *VU, int info) { recVUMI_MSUB_iq(VU, VU_REGQ_ADDR, info); } +void recVUMI_MSUBx(VURegs *VU, int info) +{ + //SysPrintf ("recVUMI_MSUBx \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MSUB_xyzw_toD(VU, EEREC_D, 0, info); + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_MSUBy(VURegs *VU, int info) +{ + //SysPrintf ("recVUMI_MSUBy \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MSUB_xyzw_toD(VU, EEREC_D, 1, info); + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_MSUBz(VURegs *VU, int info) +{ + //SysPrintf ("recVUMI_MSUBz \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MSUB_xyzw_toD(VU, EEREC_D, 2, info); + recUpdateFlags(VU, EEREC_D, info); +} + +void recVUMI_MSUBw(VURegs *VU, int info) +{ + //SysPrintf ("recVUMI_MSUBw \n"); + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + recVUMI_MSUB_xyzw_toD(VU, EEREC_D, 3, info); + recUpdateFlags(VU, EEREC_D, info); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MSUBA +//------------------------------------------------------------------ +void recVUMI_MSUBA( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_MSUBA \n"); + recVUMI_MSUB_toD(VU, EEREC_ACC, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MSUBAi( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_MSUBAi \n"); + recVUMI_MSUB_iq_toD( VU, EEREC_ACC, VU_VI_ADDR(REG_I, 1), info ); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MSUBAq( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_MSUBAq \n"); + recVUMI_MSUB_iq_toD( VU, EEREC_ACC, VU_REGQ_ADDR, info ); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MSUBAx( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_MSUBAx \n"); + recVUMI_MSUB_xyzw_toD(VU, EEREC_ACC, 0, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MSUBAy( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_MSUBAy \n"); + recVUMI_MSUB_xyzw_toD(VU, EEREC_ACC, 1, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MSUBAz( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_MSUBAz \n"); + recVUMI_MSUB_xyzw_toD(VU, EEREC_ACC, 2, info); + recUpdateFlags(VU, EEREC_ACC, info); +} + +void recVUMI_MSUBAw( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_MSUBAw \n"); + recVUMI_MSUB_xyzw_toD(VU, EEREC_ACC, 3, info); + recUpdateFlags(VU, EEREC_ACC, info); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MAX +//------------------------------------------------------------------ +void recVUMI_MAX(VURegs *VU, int info) +{ + if ( _Fd_ == 0 ) return; + + //SysPrintf ("recVUMI_MAX \n"); + if (_Fs_) vuFloat4_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); // Always do Preserved Sign Clamping + if (_Ft_) vuFloat4_useEAX( EEREC_T, EEREC_TEMP, _X_Y_Z_W ); + + if( _X_Y_Z_W == 8 ) { + if (EEREC_D == EEREC_S) SSE_MAXSS_XMM_to_XMM(EEREC_D, EEREC_T); + else if (EEREC_D == EEREC_T) SSE_MAXSS_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MAXSS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + else if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MAXPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { + if( EEREC_D == EEREC_S ) SSE_MAXPS_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE_MAXPS_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MAXPS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } +} + +void recVUMI_MAX_iq(VURegs *VU, uptr addr, int info) +{ + if ( _Fd_ == 0 ) return; + + //SysPrintf ("recVUMI_MAX_iq \n"); + if (_Fs_) vuFloat4_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); // Always do Preserved Sign Clamping + vuFloat3(addr); + + if( _XYZW_SS ) { + if( EEREC_D == EEREC_TEMP ) { + _vuFlipRegSS(VU, EEREC_S); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MAXSS_M32_to_XMM(EEREC_D, addr); + _vuFlipRegSS(VU, EEREC_S); + + // have to flip over EEREC_D if computing flags! + //if( (info & PROCESS_VU_UPDATEFLAGS) ) + _vuFlipRegSS(VU, EEREC_D); + } + else if( EEREC_D == EEREC_S ) { + _vuFlipRegSS(VU, EEREC_D); + SSE_MAXSS_M32_to_XMM(EEREC_D, addr); + _vuFlipRegSS(VU, EEREC_D); + } + else { + if( _X ) { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MAXSS_M32_to_XMM(EEREC_D, addr); + } + else { + _vuMoveSS(VU, EEREC_TEMP, EEREC_S); + _vuFlipRegSS(VU, EEREC_D); + SSE_MAXSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + _vuFlipRegSS(VU, EEREC_D); + } + } + } + else if (_X_Y_Z_W != 0xf) { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + SSE_MAXPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { + if(EEREC_D == EEREC_S) { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + SSE_MAXPS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_D, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_D, EEREC_D, 0x00); + SSE_MAXPS_XMM_to_XMM(EEREC_D, EEREC_S); + } + } +} + +void recVUMI_MAX_xyzw(VURegs *VU, int xyzw, int info) +{ + if ( _Fd_ == 0 ) return; + + //SysPrintf ("recVUMI_MAX_xyzw \n"); + if (_Fs_) vuFloat4_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); // Always do Preserved Sign Clamping + if (_Ft_) vuFloat4_useEAX( EEREC_T, EEREC_TEMP, ( 1 << (3 - xyzw) ) ); + + if( _X_Y_Z_W == 8 && (EEREC_D != EEREC_TEMP)) { + if( _Fs_ == 0 && _Ft_ == 0 ) { + if( xyzw < 3 ) { + SSE_XORPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, (uptr)s_fones); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + } + else { + if( xyzw == 0 ) { + if( EEREC_D == EEREC_S ) SSE_MAXSS_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE_MAXSS_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MAXSS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + else { + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MAXSS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + } + } + else if (_X_Y_Z_W != 0xf) { + if( _Fs_ == 0 && _Ft_ == 0 ) { + if( xyzw < 3 ) { + if( _X_Y_Z_W & 1 ) SSE_MOVAPS_M128_to_XMM(EEREC_TEMP, (uptr)&VU->VF[0].UL[0]); // w included, so insert the whole reg + else SSE_XORPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); // w not included, can zero out + } + else SSE_MOVAPS_M128_to_XMM(EEREC_TEMP, (uptr)s_fones); + } + else { + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MAXPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + } + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { + if( _Fs_ == 0 && _Ft_ == 0 ) { + if( xyzw < 3 ) SSE_XORPS_XMM_to_XMM(EEREC_D, EEREC_D); + else SSE_MOVAPS_M128_to_XMM(EEREC_D, (uptr)s_fones); + } + else { + if (EEREC_D == EEREC_S) { + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MAXPS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + else { + _unpackVF_xyzw(EEREC_D, EEREC_T, xyzw); + SSE_MAXPS_XMM_to_XMM(EEREC_D, EEREC_S); + } + } + } +} + +void recVUMI_MAXi(VURegs *VU, int info) { recVUMI_MAX_iq(VU, VU_VI_ADDR(REG_I, 1), info); } +void recVUMI_MAXx(VURegs *VU, int info) { recVUMI_MAX_xyzw(VU, 0, info); } +void recVUMI_MAXy(VURegs *VU, int info) { recVUMI_MAX_xyzw(VU, 1, info); } +void recVUMI_MAXz(VURegs *VU, int info) { recVUMI_MAX_xyzw(VU, 2, info); } +void recVUMI_MAXw(VURegs *VU, int info) { recVUMI_MAX_xyzw(VU, 3, info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MINI +//------------------------------------------------------------------ +void recVUMI_MINI(VURegs *VU, int info) +{ + if ( _Fd_ == 0 ) return; + + //SysPrintf ("recVUMI_MINI\n"); + if (_Fs_) vuFloat4_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); // Always do Preserved Sign Clamping + if (_Ft_) vuFloat4_useEAX( EEREC_T, EEREC_TEMP, _X_Y_Z_W ); + + if( _X_Y_Z_W == 8 ) { + if (EEREC_D == EEREC_S) SSE_MINSS_XMM_to_XMM(EEREC_D, EEREC_T); + else if (EEREC_D == EEREC_T) SSE_MINSS_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MINSS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + else if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MINPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { + if( EEREC_D == EEREC_S ) { + //ClampUnordered(EEREC_T, EEREC_TEMP, 0); // need for GT4 vu0rec + SSE_MINPS_XMM_to_XMM(EEREC_D, EEREC_T); + } + else if( EEREC_D == EEREC_T ) { + //ClampUnordered(EEREC_S, EEREC_TEMP, 0); // need for GT4 vu0rec + SSE_MINPS_XMM_to_XMM(EEREC_D, EEREC_S); + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MINPS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } +} + +void recVUMI_MINI_iq(VURegs *VU, uptr addr, int info) +{ + if ( _Fd_ == 0 ) return; + + //SysPrintf ("recVUMI_MINI_iq \n"); + if (_Fs_) vuFloat4_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); // Always do Preserved Sign Clamping + vuFloat3(addr); + + if( _XYZW_SS ) { + if( EEREC_D == EEREC_TEMP ) { + _vuFlipRegSS(VU, EEREC_S); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MINSS_M32_to_XMM(EEREC_D, addr); + _vuFlipRegSS(VU, EEREC_S); + + // have to flip over EEREC_D if computing flags! + //if( (info & PROCESS_VU_UPDATEFLAGS) ) + _vuFlipRegSS(VU, EEREC_D); + } + else if( EEREC_D == EEREC_S ) { + _vuFlipRegSS(VU, EEREC_D); + SSE_MINSS_M32_to_XMM(EEREC_D, addr); + _vuFlipRegSS(VU, EEREC_D); + } + else { + if( _X ) { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MINSS_M32_to_XMM(EEREC_D, addr); + } + else { + _vuMoveSS(VU, EEREC_TEMP, EEREC_S); + _vuFlipRegSS(VU, EEREC_D); + SSE_MINSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + _vuFlipRegSS(VU, EEREC_D); + } + } + } + else if (_X_Y_Z_W != 0xf) { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + SSE_MINPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { + if(EEREC_D == EEREC_S) { + SSE_MOVSS_M32_to_XMM(EEREC_TEMP, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0x00); + SSE_MINPS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + else { + SSE_MOVSS_M32_to_XMM(EEREC_D, addr); + SSE_SHUFPS_XMM_to_XMM(EEREC_D, EEREC_D, 0x00); + SSE_MINPS_XMM_to_XMM(EEREC_D, EEREC_S); + } + } +} + +void recVUMI_MINI_xyzw(VURegs *VU, int xyzw, int info) +{ + if ( _Fd_ == 0 ) return; + + //SysPrintf ("recVUMI_MINI_xyzw \n"); + if (_Fs_) vuFloat4_useEAX( EEREC_S, EEREC_TEMP, _X_Y_Z_W ); // Always do Preserved Sign Clamping + if (_Ft_) vuFloat4_useEAX( EEREC_T, EEREC_TEMP, ( 1 << (3 - xyzw) ) ); + + if( _X_Y_Z_W == 8 && (EEREC_D != EEREC_TEMP)) { + if( xyzw == 0 ) { + if( EEREC_D == EEREC_S ) SSE_MINSS_XMM_to_XMM(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) SSE_MINSS_XMM_to_XMM(EEREC_D, EEREC_S); + else { + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MINSS_XMM_to_XMM(EEREC_D, EEREC_T); + } + } + else { + _unpackVFSS_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MOVSS_XMM_to_XMM(EEREC_D, EEREC_S); + SSE_MINSS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + } + else if (_X_Y_Z_W != 0xf) { + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MINPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + VU_MERGE_REGS(EEREC_D, EEREC_TEMP); + } + else { + if (EEREC_D == EEREC_S) { + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, xyzw); + SSE_MINPS_XMM_to_XMM(EEREC_D, EEREC_TEMP); + } + else { + _unpackVF_xyzw(EEREC_D, EEREC_T, xyzw); + SSE_MINPS_XMM_to_XMM(EEREC_D, EEREC_S); + } + } +} + +void recVUMI_MINIi(VURegs *VU, int info) { recVUMI_MINI_iq(VU, VU_VI_ADDR(REG_I, 1), info); } +void recVUMI_MINIx(VURegs *VU, int info) { recVUMI_MINI_xyzw(VU, 0, info); } +void recVUMI_MINIy(VURegs *VU, int info) { recVUMI_MINI_xyzw(VU, 1, info); } +void recVUMI_MINIz(VURegs *VU, int info) { recVUMI_MINI_xyzw(VU, 2, info); } +void recVUMI_MINIw(VURegs *VU, int info) { recVUMI_MINI_xyzw(VU, 3, info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// OPMULA +//------------------------------------------------------------------ +void recVUMI_OPMULA( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_OPMULA \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, 0xE); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, 0xE); + } + + SSE_MOVAPS_XMM_to_XMM( EEREC_TEMP, EEREC_S ); + SSE_SHUFPS_XMM_to_XMM( EEREC_T, EEREC_T, 0xD2 ); // EEREC_T = WYXZ + SSE_SHUFPS_XMM_to_XMM( EEREC_TEMP, EEREC_TEMP, 0xC9 ); // EEREC_TEMP = WXZY + SSE_MULPS_XMM_to_XMM( EEREC_TEMP, EEREC_T ); + + VU_MERGE_REGS_CUSTOM(EEREC_ACC, EEREC_TEMP, 14); + + // revert EEREC_T + if( EEREC_T != EEREC_ACC ) + SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0xC9); + + recUpdateFlags(VU, EEREC_ACC, info); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// OPMSUB +//------------------------------------------------------------------ +void recVUMI_OPMSUB( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_OPMSUB \n"); + if (CHECK_VU_EXTRA_OVERFLOW) { + if (_Fs_) vuFloat5_useEAX( EEREC_S, EEREC_TEMP, 0xE); + if (_Ft_) vuFloat5_useEAX( EEREC_T, EEREC_TEMP, 0xE); + } + + if( !_Fd_ ) info |= PROCESS_EE_SET_D(EEREC_TEMP); + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0xD2); // EEREC_T = WYXZ + SSE_SHUFPS_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP, 0xC9); // EEREC_TEMP = WXZY + SSE_MULPS_XMM_to_XMM(EEREC_TEMP, EEREC_T); + + // negate and add + SSE_XORPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_clip[4]); + SSE_ADDPS_XMM_to_XMM(EEREC_TEMP, EEREC_ACC); + VU_MERGE_REGS_CUSTOM(EEREC_D, EEREC_TEMP, 14); + + // revert EEREC_T + if( EEREC_T != EEREC_D ) SSE_SHUFPS_XMM_to_XMM(EEREC_T, EEREC_T, 0xC9); + + recUpdateFlags(VU, EEREC_D, info); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// NOP +//------------------------------------------------------------------ +void recVUMI_NOP( VURegs *VU, int info ) +{ + //SysPrintf ("recVUMI_NOP \n"); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// recVUMI_FTOI_Saturate() - Saturates result from FTOI Instructions +//------------------------------------------------------------------ +static const PCSX2_ALIGNED16(int rec_const_0x8000000[4]) = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }; + +void recVUMI_FTOI_Saturate(int rec_s, int rec_t, int rec_tmp1, int rec_tmp2) +{ + //SysPrintf ("recVUMI_FTOI_Saturate \n"); + //Duplicate the xor'd sign bit to the whole value + //FFFF FFFF for positive, 0 for negative + SSE_MOVAPS_XMM_to_XMM(rec_tmp1, rec_s); + SSE2_PXOR_M128_to_XMM(rec_tmp1, (uptr)&const_clip[4]); + SSE2_PSRAD_I8_to_XMM(rec_tmp1, 31); + + //Create mask: 0 where !=8000 0000 + SSE_MOVAPS_XMM_to_XMM(rec_tmp2, rec_t); + SSE2_PCMPEQD_M128_to_XMM(rec_tmp2, (uptr)&const_clip[4]); + + //AND the mask w/ the edit values + SSE_ANDPS_XMM_to_XMM(rec_tmp1, rec_tmp2); + + //if v==8000 0000 && positive -> 8000 0000 + FFFF FFFF -> 7FFF FFFF + //if v==8000 0000 && negative -> 8000 0000 + 0 -> 8000 0000 + //if v!=8000 0000 -> v+0 (masked from the and) + + //Add the values as needed + SSE2_PADDD_XMM_to_XMM(rec_t, rec_tmp1); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FTOI 0/4/12/15 +//------------------------------------------------------------------ +static PCSX2_ALIGNED16(float FTIO_Temp1[4]) = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; +static PCSX2_ALIGNED16(float FTIO_Temp2[4]) = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; +void recVUMI_FTOI0(VURegs *VU, int info) +{ + int t1reg, t2reg; // Temp XMM regs + + if ( _Ft_ == 0 ) return; + + //SysPrintf ("recVUMI_FTOI0 \n"); + + if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + vuFloat_useEAX( info, EEREC_TEMP, 0xf ); // Clamp Infs and NaNs to pos/neg fmax (NaNs always to positive fmax) + SSE2_CVTTPS2DQ_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + + t1reg = _vuGetTempXMMreg(info); + + if( t1reg >= 0 ) { // If theres a temp XMM reg available + for (t2reg = 0; ( (t2reg == EEREC_S) || (t2reg == EEREC_T) || (t2reg == EEREC_TEMP) || (t2reg == t1reg) ); t2reg++) + ; // Find unused reg (For second temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp1, t2reg); // Backup XMM reg + + recVUMI_FTOI_Saturate(EEREC_S, EEREC_TEMP, t1reg, t2reg); // Saturate if Float->Int conversion returned illegal result + + SSE_MOVAPS_M128_to_XMM(t2reg, (uptr)FTIO_Temp1); // Restore XMM reg + _freeXMMreg(t1reg); // Free temp reg + } + else { // No temp reg available + for (t1reg = 0; ( (t1reg == EEREC_S) || (t1reg == EEREC_T) || (t1reg == EEREC_TEMP) ); t1reg++) + ; // Find unused reg (For first temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp1, t1reg); // Backup t1reg XMM reg + + for (t2reg = 0; ( (t2reg == EEREC_S) || (t2reg == EEREC_T) || (t2reg == EEREC_TEMP) || (t2reg == t1reg) ); t2reg++) + ; // Find unused reg (For second temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp2, t2reg); // Backup t2reg XMM reg + + recVUMI_FTOI_Saturate(EEREC_S, EEREC_TEMP, t1reg, t2reg); // Saturate if Float->Int conversion returned illegal result + + SSE_MOVAPS_M128_to_XMM(t1reg, (uptr)FTIO_Temp1); // Restore t1reg XMM reg + SSE_MOVAPS_M128_to_XMM(t2reg, (uptr)FTIO_Temp2); // Restore t2reg XMM reg + } + + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + } + else { + if (EEREC_T != EEREC_S) { + SSE_MOVAPS_XMM_to_XMM(EEREC_T, EEREC_S); + vuFloat_useEAX( info, EEREC_T, 0xf ); // Clamp Infs and NaNs to pos/neg fmax (NaNs always to positive fmax) + SSE2_CVTTPS2DQ_XMM_to_XMM(EEREC_T, EEREC_T); + + t1reg = _vuGetTempXMMreg(info); + + if( t1reg >= 0 ) { // If theres a temp XMM reg available + recVUMI_FTOI_Saturate(EEREC_S, EEREC_T, EEREC_TEMP, t1reg); // Saturate if Float->Int conversion returned illegal result + _freeXMMreg(t1reg); // Free temp reg + } + else { // No temp reg available + for (t1reg = 0; ( (t1reg == EEREC_S) || (t1reg == EEREC_T) || (t1reg == EEREC_TEMP) ); t1reg++) + ; // Find unused reg + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp1, t1reg); // Backup t1reg XMM reg + + recVUMI_FTOI_Saturate(EEREC_S, EEREC_T, EEREC_TEMP, t1reg); // Saturate if Float->Int conversion returned illegal result + + SSE_MOVAPS_M128_to_XMM(t1reg, (uptr)FTIO_Temp1); // Restore t1reg XMM reg + } + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + vuFloat_useEAX( info, EEREC_TEMP, 0xf ); // Clamp Infs and NaNs to pos/neg fmax (NaNs always to positive fmax) + SSE2_CVTTPS2DQ_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + + t1reg = _vuGetTempXMMreg(info); + + if( t1reg >= 0 ) { // If theres a temp XMM reg available + for (t2reg = 0; ( (t2reg == EEREC_S) || (t2reg == EEREC_T) || (t2reg == EEREC_TEMP) || (t2reg == t1reg)); t2reg++) + ; // Find unused reg (For second temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp1, t2reg); // Backup XMM reg + + recVUMI_FTOI_Saturate(EEREC_S, EEREC_TEMP, t1reg, t2reg); // Saturate if Float->Int conversion returned illegal result + + SSE_MOVAPS_M128_to_XMM(t2reg, (uptr)FTIO_Temp1); // Restore XMM reg + _freeXMMreg(t1reg); // Free temp reg + } + else { // No temp reg available + for (t1reg = 0; ( (t1reg == EEREC_S) || (t1reg == EEREC_T) || (t1reg == EEREC_TEMP) ); t1reg++) + ; // Find unused reg (For first temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp1, t1reg); // Backup t1reg XMM reg + + for (t2reg = 0; ( (t2reg == EEREC_S) || (t2reg == EEREC_T) || (t2reg == EEREC_TEMP) || (t2reg == t1reg) ); t2reg++) + ; // Find unused reg (For second temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp2, t2reg); // Backup t2reg XMM reg + + recVUMI_FTOI_Saturate(EEREC_S, EEREC_TEMP, t1reg, t2reg); // Saturate if Float->Int conversion returned illegal result + + SSE_MOVAPS_M128_to_XMM(t1reg, (uptr)FTIO_Temp1); // Restore t1reg XMM reg + SSE_MOVAPS_M128_to_XMM(t2reg, (uptr)FTIO_Temp2); // Restore t2reg XMM reg + } + + SSE_MOVAPS_XMM_to_XMM(EEREC_T, EEREC_TEMP); + } + } +} + +void recVUMI_FTOIX(VURegs *VU, int addr, int info) +{ + int t1reg, t2reg; // Temp XMM regs + + if ( _Ft_ == 0 ) return; + + //SysPrintf ("recVUMI_FTOIX \n"); + if (_X_Y_Z_W != 0xf) { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MULPS_M128_to_XMM(EEREC_TEMP, addr); + vuFloat_useEAX( info, EEREC_TEMP, 0xf ); // Clamp Infs and NaNs to pos/neg fmax (NaNs always to positive fmax) + SSE2_CVTTPS2DQ_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + + t1reg = _vuGetTempXMMreg(info); + + if( t1reg >= 0 ) { // If theres a temp XMM reg available + for (t2reg = 0; ( (t2reg == EEREC_S) || (t2reg == EEREC_T) || (t2reg == EEREC_TEMP) || (t2reg == t1reg)); t2reg++) + ; // Find unused reg (For second temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp1, t2reg); // Backup XMM reg + + recVUMI_FTOI_Saturate(EEREC_S, EEREC_TEMP, t1reg, t2reg); // Saturate if Float->Int conversion returned illegal result + + SSE_MOVAPS_M128_to_XMM(t2reg, (uptr)FTIO_Temp1); // Restore XMM reg + _freeXMMreg(t1reg); // Free temp reg + } + else { // No temp reg available + for (t1reg = 0; ( (t1reg == EEREC_S) || (t1reg == EEREC_T) || (t1reg == EEREC_TEMP) ); t1reg++) + ; // Find unused reg (For first temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp1, t1reg); // Backup t1reg XMM reg + + for (t2reg = 0; ( (t2reg == EEREC_S) || (t2reg == EEREC_T) || (t2reg == EEREC_TEMP) || (t2reg == t1reg) ); t2reg++) + ; // Find unused reg (For second temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp2, t2reg); // Backup t2reg XMM reg + + recVUMI_FTOI_Saturate(EEREC_S, EEREC_TEMP, t1reg, t2reg); // Saturate if Float->Int conversion returned illegal result + + SSE_MOVAPS_M128_to_XMM(t1reg, (uptr)FTIO_Temp1); // Restore t1reg XMM reg + SSE_MOVAPS_M128_to_XMM(t2reg, (uptr)FTIO_Temp2); // Restore t2reg XMM reg + } + + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + } + else { + if (EEREC_T != EEREC_S) { + SSE_MOVAPS_XMM_to_XMM(EEREC_T, EEREC_S); + SSE_MULPS_M128_to_XMM(EEREC_T, addr); + vuFloat_useEAX( info, EEREC_T, 0xf ); // Clamp Infs and NaNs to pos/neg fmax (NaNs always to positive fmax) + SSE2_CVTTPS2DQ_XMM_to_XMM(EEREC_T, EEREC_T); + + t1reg = _vuGetTempXMMreg(info); + + if( t1reg >= 0 ) { // If theres a temp XMM reg available + recVUMI_FTOI_Saturate(EEREC_S, EEREC_T, EEREC_TEMP, t1reg); // Saturate if Float->Int conversion returned illegal result + _freeXMMreg(t1reg); // Free temp reg + } + else { // No temp reg available + for (t1reg = 0; ( (t1reg == EEREC_S) || (t1reg == EEREC_T) || (t1reg == EEREC_TEMP) ); t1reg++) + ; // Find unused reg + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp1, t1reg); // Backup t1reg XMM reg + + recVUMI_FTOI_Saturate(EEREC_S, EEREC_T, EEREC_TEMP, t1reg); // Saturate if Float->Int conversion returned illegal result + + SSE_MOVAPS_M128_to_XMM(t1reg, (uptr)FTIO_Temp1); // Restore t1reg XMM reg + } + } + else { + SSE_MOVAPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MULPS_M128_to_XMM(EEREC_TEMP, addr); + vuFloat_useEAX( info, EEREC_TEMP, 0xf ); // Clamp Infs and NaNs to pos/neg fmax (NaNs always to positive fmax) + SSE2_CVTTPS2DQ_XMM_to_XMM(EEREC_TEMP, EEREC_TEMP); + + t1reg = _vuGetTempXMMreg(info); + + if( t1reg >= 0 ) { // If theres a temp XMM reg available + for (t2reg = 0; ( (t2reg == EEREC_S) || (t2reg == EEREC_T) || (t2reg == EEREC_TEMP) || (t2reg == t1reg)); t2reg++) + ; // Find unused reg (For second temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp1, t2reg); // Backup XMM reg + + recVUMI_FTOI_Saturate(EEREC_S, EEREC_TEMP, t1reg, t2reg); // Saturate if Float->Int conversion returned illegal result + + SSE_MOVAPS_M128_to_XMM(t2reg, (uptr)FTIO_Temp1); // Restore XMM reg + _freeXMMreg(t1reg); // Free temp reg + } + else { // No temp reg available + for (t1reg = 0; ( (t1reg == EEREC_S) || (t1reg == EEREC_T) || (t1reg == EEREC_TEMP) ); t1reg++) + ; // Find unused reg (For first temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp1, t1reg); // Backup t1reg XMM reg + + for (t2reg = 0; ( (t2reg == EEREC_S) || (t2reg == EEREC_T) || (t2reg == EEREC_TEMP) || (t2reg == t1reg) ); t2reg++) + ; // Find unused reg (For second temp reg) + SSE_MOVAPS_XMM_to_M128((uptr)FTIO_Temp2, t2reg); // Backup t2reg XMM reg + + recVUMI_FTOI_Saturate(EEREC_S, EEREC_TEMP, t1reg, t2reg); // Saturate if Float->Int conversion returned illegal result + + SSE_MOVAPS_M128_to_XMM(t1reg, (uptr)FTIO_Temp1); // Restore t1reg XMM reg + SSE_MOVAPS_M128_to_XMM(t2reg, (uptr)FTIO_Temp2); // Restore t2reg XMM reg + } + + SSE_MOVAPS_XMM_to_XMM(EEREC_T, EEREC_TEMP); + } + } +} + +void recVUMI_FTOI4( VURegs *VU, int info ) { recVUMI_FTOIX(VU, (uptr)&recMult_float_to_int4[0], info); } +void recVUMI_FTOI12( VURegs *VU, int info ) { recVUMI_FTOIX(VU, (uptr)&recMult_float_to_int12[0], info); } +void recVUMI_FTOI15( VURegs *VU, int info ) { recVUMI_FTOIX(VU, (uptr)&recMult_float_to_int15[0], info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ITOF 0/4/12/15 +//------------------------------------------------------------------ +void recVUMI_ITOF0( VURegs *VU, int info ) +{ + if ( _Ft_ == 0 ) return; + + //SysPrintf ("recVUMI_ITOF0 \n"); + if (_X_Y_Z_W != 0xf) { + SSE2_CVTDQ2PS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + vuFloat_useEAX( info, EEREC_TEMP, 15); // Clamp infinities + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + xmmregs[EEREC_T].mode |= MODE_WRITE; + } + else { + SSE2_CVTDQ2PS_XMM_to_XMM(EEREC_T, EEREC_S); + vuFloat2(EEREC_T, EEREC_TEMP, 15); // Clamp infinities + } +} + +void recVUMI_ITOFX(VURegs *VU, int addr, int info) +{ + if ( _Ft_ == 0 ) return; + + //SysPrintf ("recVUMI_ITOFX \n"); + if (_X_Y_Z_W != 0xf) { + SSE2_CVTDQ2PS_XMM_to_XMM(EEREC_TEMP, EEREC_S); + SSE_MULPS_M128_to_XMM(EEREC_TEMP, addr); + vuFloat_useEAX( info, EEREC_TEMP, 15); // Clamp infinities + VU_MERGE_REGS(EEREC_T, EEREC_TEMP); + xmmregs[EEREC_T].mode |= MODE_WRITE; + } + else { + SSE2_CVTDQ2PS_XMM_to_XMM(EEREC_T, EEREC_S); + SSE_MULPS_M128_to_XMM(EEREC_T, addr); + vuFloat2(EEREC_T, EEREC_TEMP, 15); // Clamp infinities + } +} + +void recVUMI_ITOF4( VURegs *VU, int info ) { recVUMI_ITOFX(VU, (uptr)&recMult_int_to_float4[0], info); } +void recVUMI_ITOF12( VURegs *VU, int info ) { recVUMI_ITOFX(VU, (uptr)&recMult_int_to_float12[0], info); } +void recVUMI_ITOF15( VURegs *VU, int info ) { recVUMI_ITOFX(VU, (uptr)&recMult_int_to_float15[0], info); } +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// CLIP +//------------------------------------------------------------------ +void recVUMI_CLIP(VURegs *VU, int info) +{ + int t1reg = EEREC_D; + int t2reg = EEREC_ACC; + int x86temp1, x86temp2; + + u32 clipaddr = VU_VI_ADDR(REG_CLIP_FLAG, 0); + u32 prevclipaddr = VU_VI_ADDR(REG_CLIP_FLAG, 2); + + if( clipaddr == 0 ) { // battle star has a clip right before fcset + SysPrintf("skipping vu clip\n"); + return; + } + + //Flush the clip flag before processing, incase of double clip commands (GoW) + + if( prevclipaddr != (uptr)&VU->VI[REG_CLIP_FLAG] ) { + MOV32MtoR(EAX, prevclipaddr); + MOV32RtoM((uptr)&VU->VI[REG_CLIP_FLAG], EAX); + } + + assert( clipaddr != 0 ); + assert( t1reg != t2reg && t1reg != EEREC_TEMP && t2reg != EEREC_TEMP ); + + x86temp1 = ALLOCTEMPX86(MODE_8BITREG); + x86temp2 = ALLOCTEMPX86(MODE_8BITREG); + + //if ( (x86temp1 == 0) || (x86temp2 == 0) ) SysPrintf("VU CLIP Allocation Error: EAX being allocated! \n"); + + _freeXMMreg(t1reg); // These should have been freed at allocation in eeVURecompileCode() + _freeXMMreg(t2reg); // but if they've been used since then, then free them. (just doing this incase :p (cottonvibes)) + + if( _Ft_ == 0 ) { + SSE_MOVAPS_M128_to_XMM(EEREC_TEMP, (uptr)&s_fones[0]); // all 1s + SSE_MOVAPS_M128_to_XMM(t1reg, (uptr)&s_fones[4]); + } + else { + _unpackVF_xyzw(EEREC_TEMP, EEREC_T, 3); + SSE_ANDPS_M128_to_XMM(EEREC_TEMP, (uptr)&const_clip[0]); + SSE_MOVAPS_XMM_to_XMM(t1reg, EEREC_TEMP); + SSE_ORPS_M128_to_XMM(t1reg, (uptr)&const_clip[4]); + } + + MOV32MtoR(EAX, prevclipaddr); + + SSE_CMPNLEPS_XMM_to_XMM(t1reg, EEREC_S); //-w, -z, -y, -x + SSE_CMPLTPS_XMM_to_XMM(EEREC_TEMP, EEREC_S); //+w, +z, +y, +x + + SHL32ItoR(EAX, 6); + + SSE_MOVAPS_XMM_to_XMM(t2reg, EEREC_TEMP); //t2 = +w, +z, +y, +x + SSE_UNPCKLPS_XMM_to_XMM(EEREC_TEMP, t1reg); //EEREC_TEMP = -y,+y,-x,+x + SSE_UNPCKHPS_XMM_to_XMM(t2reg, t1reg); //t2reg = -w,+w,-z,+z + SSE_MOVMSKPS_XMM_to_R32(x86temp2, EEREC_TEMP); // -y,+y,-x,+x + SSE_MOVMSKPS_XMM_to_R32(x86temp1, t2reg); // -w,+w,-z,+z + + AND8ItoR(x86temp1, 0x3); + SHL8ItoR(x86temp1, 4); + OR8RtoR(EAX, x86temp1); + AND8ItoR(x86temp2, 0xf); + OR8RtoR(EAX, x86temp2); + AND32ItoR(EAX, 0xffffff); + + MOV32RtoM(clipaddr, EAX); + + if (( !(info & (PROCESS_VU_SUPER|PROCESS_VU_COP2)) ) ) //Instantly update the flag if its called from elsewhere (unlikely, but ok) + MOV32RtoM((uptr)&VU->VI[REG_CLIP_FLAG], EAX); + + _freeX86reg(x86temp1); + _freeX86reg(x86temp2); +} diff --git a/pcsx2/x86/iVUops.h b/pcsx2/x86/iVUops.h new file mode 100644 index 0000000000..d4571bff93 --- /dev/null +++ b/pcsx2/x86/iVUops.h @@ -0,0 +1,57 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +#define REC_VUOP(VU, f) { \ + _freeXMMregs(/*&VU*/); \ + _freeMMXregs(); \ + SetFPUstate();) \ + MOV32ItoM((uptr)&VU.code, (u32)VU.code); \ + CALLFunc((uptr)VU##MI_##f); \ +} + +#define REC_VUOPs(VU, f) { \ + _freeXMMregs(); \ + _freeMMXregs(); \ + SetFPUstate();) \ + if (VU==&VU1) { \ + MOV32ItoM((uptr)&VU1.code, (u32)VU1.code); \ + CALLFunc((uptr)VU1MI_##f); \ + } \ + else { \ + MOV32ItoM((uptr)&VU0.code, (u32)VU0.code); \ + CALLFunc((uptr)VU0MI_##f); \ + } \ +} + +#define REC_VUOPFLAGS(VU, f) { \ + _freeXMMregs(/*&VU*/); \ + _freeMMXregs(); \ + SetFPUstate(); \ + MOV32ItoM((uptr)&VU.code, (u32)VU.code); \ + CALLFunc((uptr)VU##MI_##f); \ +} + +#define REC_VUBRANCH(VU, f) { \ + _freeXMMregs(/*&VU*/); \ + _freeMMXregs(); \ + SetFPUstate(); \ + MOV32ItoM((uptr)&VU.code, (u32)VU.code); \ + MOV32ItoM((uptr)&VU.VI[REG_TPC].UL, (u32)pc); \ + CALLFunc((uptr)VU##MI_##f); \ + branch = 1; \ +} diff --git a/pcsx2/x86/iVUzerorec.cpp b/pcsx2/x86/iVUzerorec.cpp new file mode 100644 index 0000000000..cf7f3b10f2 --- /dev/null +++ b/pcsx2/x86/iVUzerorec.cpp @@ -0,0 +1,4139 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// Super VU recompiler - author: zerofrog(@gmail.com) + +#include "PrecompiledHeader.h" + +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include "Common.h" + +#include "GS.h" +#include "R5900.h" +#include "VU.h" +#include "ix86/ix86.h" +#include "iR5900.h" + +#include "iVUzerorec.h" +#include "SamplProf.h" + +using namespace std; + +// temporary externs +extern void iDumpVU0Registers(); +extern void iDumpVU1Registers(); + +// SuperVURec optimization options, uncomment only for debugging purposes +#define SUPERVU_CACHING // vu programs are saved and queried via memcompare (should be no reason to disable this) +#define SUPERVU_WRITEBACKS // don't flush the writebacks after every block +#define SUPERVU_X86CACHING // use x86reg caching (faster) +#define SUPERVU_VIBRANCHDELAY // when integers are modified right before a branch that uses the integer, + // the old integer value is used in the branch + // fixes kh2 + +#define SUPERVU_PROPAGATEFLAGS // the correct behavior of VUs, for some reason superman breaks gfx with it on... + +#ifndef _DEBUG +#define SUPERVU_INTERCACHING // registers won't be flushed at block boundaries (faster) +#endif + +#define SUPERVU_CHECKCONDITION 0 // has to be 0!! + +#define VU_EXESIZE 0x00800000 + +#define _Imm11_ (s32)( (vucode & 0x400) ? (0xfffffc00 | (vucode & 0x3ff)) : (vucode & 0x3ff) ) +#define _UImm11_ (s32)(vucode & 0x7ff) + +#define _Ft_ ((VU->code >> 16) & 0x1F) // The rt part of the instruction register +#define _Fs_ ((VU->code >> 11) & 0x1F) // The rd part of the instruction register +#define _Fd_ ((VU->code >> 6) & 0x1F) // The sa part of the instruction register + +static const u32 QWaitTimes[] = { 6, 12 }; +static const u32 PWaitTimes[] = { 53, 43, 28, 23, 17, 11, 10 }; + +static u32 s_vuInfo; // info passed into rec insts + +static const u32 s_MemSize[2] = {VU0_MEMSIZE, VU1_MEMSIZE}; +static u8* s_recVUMem = NULL, *s_recVUPtr = NULL; + +// tables which are defined at the bottom of this massive file. +extern void (*recVU_UPPER_OPCODE[64])( VURegs* VU, s32 info ); +extern void (*recVU_LOWER_OPCODE[128])( VURegs* VU, s32 info ); + +#define INST_Q_READ 0x0001 // flush Q +#define INST_P_READ 0x0002 // flush P +#define INST_BRANCH_DELAY 0x0004 +#define INST_CLIP_WRITE 0x0040 // inst writes CLIP in the future +#define INST_STATUS_WRITE 0x0080 +#define INST_MAC_WRITE 0x0100 +#define INST_Q_WRITE 0x0200 +#define INST_CACHE_VI 0x0400 // write old vi value to s_VIBranchDelay + +// Let's tempt fate by defining two different constants with almost identical names +#define INST_DUMMY_ 0x8000 +#define INST_DUMMY 0x83c0 + +#define VFFREE_INVALID0 0x80000000 // (vffree[i]&0xf) is invalid + +#define FORIT(it, v) for(it = (v).begin(); it != (v).end(); ++(it)) + +#ifdef _DEBUG +u32 s_vucount=0; + +static u32 g_vu1lastrec = 0, skipparent = -1; +static u32 s_svulast = 0, s_vufnheader; +static u32 badaddrs[][2] = {0,0xffff}; +#endif + +union VURecRegs +{ + struct { + u16 reg; + u16 type; + }; + u32 id; +}; + +#define SUPERVU_XGKICKDELAY 1 // yes this is needed as default (wipeout) + +class VuBaseBlock; + +struct VuFunctionHeader +{ + struct RANGE + { + RANGE() : pmem(NULL) {} + + u16 start, size; + void* pmem; // all the mem + }; + + VuFunctionHeader() : pprogfunc(NULL), startpc(0xffffffff) {} + ~VuFunctionHeader() { + for(vector::iterator it = ranges.begin(); it != ranges.end(); ++it) { + free(it->pmem); + } + } + + // returns true if the checksum for the current mem is the same as this fn + bool IsSame(void* pmem); + + u32 startpc; + void* pprogfunc; + + vector ranges; +}; + +struct VuBlockHeader +{ + VuBaseBlock* pblock; + u32 delay; +}; + +// one vu inst (lower and upper) +class VuInstruction +{ +public: + VuInstruction() { memzero_obj(*this); nParentPc = -1; vicached = -1; } + + int nParentPc; // used for syncing with flag writes, -1 for no parent + + _vuopinfo info; + + _VURegsNum regs[2]; // [0] - lower, [1] - upper + u32 livevars[2]; // live variables right before this inst, [0] - inst, [1] - float + u32 addvars[2]; // live variables to add + u32 usedvars[2]; // set if var is used in the future including vars used in this inst + u32 keepvars[2]; + u16 pqcycles; // the number of cycles to stall if function writes to the regs + u16 type; // INST_ + + u32 pClipWrite, pMACWrite, pStatusWrite; // addrs to write the flags + u32 vffree[2]; + s8 vfwrite[2], vfread0[2], vfread1[2], vfacc[2]; + s8 vfflush[2]; // extra flush regs + s8 vicached; // if >= 0, then use the cached integer s_VIBranchDelay + + int SetCachedRegs(int upper, u32 vuxyz); + void Recompile(list::iterator& itinst, u32 vuxyz); +}; + +#define BLOCKTYPE_EOP 0x01 // at least one of the children of the block contains eop (or the block itself) +#define BLOCKTYPE_FUNCTION 0x02 +#define BLOCKTYPE_HASEOP 0x04 // last inst of block is an eop +#define BLOCKTYPE_MACFLAGS 0x08 +#define BLOCKTYPE_ANALYZED 0x40 +#define BLOCKTYPE_IGNORE 0x80 // special for recursive fns +#define BLOCKTYPE_ANALYZEDPARENT 0x100 + +// base block used when recompiling +class VuBaseBlock +{ +public: + typedef list LISTBLOCKS; + + VuBaseBlock(); + + // returns true if the leads to a EOP (ALL VU blocks must ret true) + void AssignVFRegs(); + void AssignVIRegs(int parent); + + list::iterator GetInstIterAtPc(int instpc); + void GetInstsAtPc(int instpc, list& listinsts); + + void Recompile(); + + u16 type; // BLOCKTYPE_ + u16 id; + u16 startpc; + u16 endpc; // first inst not in block + void* pcode; // x86 code pointer + void* pendcode; // end of the x86 code pointer + int cycles; + list insts; + list parents; + LISTBLOCKS blocks; // blocks branches to + u32* pChildJumps[4]; // addrs that need to be filled with the children's start addrs + // if highest bit is set, addr needs to be relational + u32 vuxyz; // corresponding bit is set if reg's xyz channels are used only + u32 vuxy; // corresponding bit is set if reg's xyz channels are used only + + _xmmregs startregs[XMMREGS], endregs[XMMREGS]; + int nStartx86, nEndx86; // indices into s_vecRegArray + + int allocX86Regs; + int prevFlagsOutOfBlock; +}; + +struct WRITEBACK +{ + WRITEBACK() : nParentPc(0), cycle(0) //, pStatusWrite(NULL), pMACWrite(NULL) + { + viwrite[0] = viwrite[1] = 0; + viread[0] = viread[1] = 0; + } + + void InitInst(VuInstruction* pinst, int cycle) const + { + u32 write = viwrite[0]|viwrite[1]; + pinst->type = ((write&(1<nParentPc = nParentPc; + pinst->info.cycle = cycle; + for(int i = 0; i < 2; ++i) { + pinst->regs[i].VIwrite = viwrite[i]; + pinst->regs[i].VIread = viread[i]; + } + } + + static int SortWritebacks(const WRITEBACK& w1, const WRITEBACK& w2) { + return w1.cycle < w2.cycle; + } + + int nParentPc; + int cycle; + u32 viwrite[2]; + u32 viread[2]; +}; + +struct VUPIPELINES +{ + fmacPipe fmac[8]; + fdivPipe fdiv; + efuPipe efu; + list< WRITEBACK > listWritebacks; +}; + +VuBaseBlock::VuBaseBlock() +{ + type = 0; endpc = 0; cycles = 0; pcode = NULL; id = 0; + memzero_obj(pChildJumps); + memzero_obj(startregs); + memzero_obj(endregs); + allocX86Regs = nStartx86 = nEndx86 = -1; + prevFlagsOutOfBlock = 0; +} + +#define SUPERVU_STACKSIZE 0x1000 + +static list s_listVUHeaders[2]; +static list* s_plistCachedHeaders[2]; +static VuFunctionHeader** recVUHeaders[2] = {NULL}; +static VuBlockHeader* recVUBlocks[2] = {NULL}; +static u8* recVUStack = NULL, *recVUStackPtr = NULL; +static vector<_x86regs> s_vecRegArray(128); + +static VURegs* VU = NULL; +static list s_listBlocks; +static u32 s_vu = 0; +static u32 s_UnconditionalDelay = 0; // 1 if there are two sequential branches and the last is unconditional +static u32 g_nLastBlockExecuted = 0; + +// Global functions +#ifdef __LINUX__ +extern "C" { +#endif +void* SuperVUGetProgram(u32 startpc, int vuindex); +void SuperVUCleanupProgram(u32 startpc, int vuindex); +#ifdef __LINUX__ +} +#endif +static VuFunctionHeader* SuperVURecompileProgram(u32 startpc, int vuindex); +static VuBaseBlock* SuperVUBuildBlocks(VuBaseBlock* parent, u32 startpc, const VUPIPELINES& pipes); +static void SuperVUInitLiveness(VuBaseBlock* pblock); +static void SuperVULivenessAnalysis(); +static void SuperVUEliminateDeadCode(); +static void SuperVUAssignRegs(); + +//void SuperVUFreeXMMreg(int xmmreg, int xmmtype, int reg); +#define SuperVUFreeXMMreg 0&& +void SuperVUFreeXMMregs(u32* livevars); + +static u32* SuperVUStaticAlloc(u32 size); +static void SuperVURecompile(); + +// allocate VU resources +void SuperVUAlloc(int vuindex) +{ + // The old -1 crap has been depreciated on this function. Please + // specify either 0 or 1, thanks. + jASSUME( vuindex >= 0 ); + + // upper 4 bits must be zero! + if( s_recVUMem == NULL ) + { + // upper 4 bits must be zero! + // Changed "first try base" to 0xb800000, since 0x0c000000 liked to fail a lot. (air) + s_recVUMem = SysMmapEx(0x0e000000, VU_EXESIZE, 0x10000000, "SuperVUAlloc"); + + if( s_recVUMem == NULL ) + { + throw Exception::OutOfMemory( + fmt_string( "SuperVU Error > failed to allocate recompiler memory (addr: 0x%x)", (u32)s_recVUMem ) + ); + } + + ProfilerRegisterSource( "VURec", s_recVUMem, VU_EXESIZE); + + if( recVUStack == NULL ) recVUStack = new u8[SUPERVU_STACKSIZE * 4]; + } + + if( vuindex >= 0 ) + { + jASSUME( s_recVUMem != NULL ); + + if( recVUHeaders[vuindex] == NULL ) + recVUHeaders[vuindex] = new VuFunctionHeader* [s_MemSize[vuindex]/8]; + if( recVUBlocks[vuindex] == NULL ) + recVUBlocks[vuindex] = new VuBlockHeader[s_MemSize[vuindex]/8]; + if( s_plistCachedHeaders[vuindex] == NULL ) + s_plistCachedHeaders[vuindex] = new list[s_MemSize[vuindex]/8]; + } +} + +// destroy VU resources +void SuperVUDestroy(int vuindex) +{ + list::iterator it; + + if( vuindex < 0 ) + { + SuperVUDestroy(0); + SuperVUDestroy(1); + ProfilerTerminateSource( "VURec" ); + SafeSysMunmap(s_recVUMem, VU_EXESIZE); + safe_delete_array( recVUStack ); + } + else + { + safe_delete_array( recVUHeaders[vuindex] ); + safe_delete_array( recVUBlocks[vuindex] ); + + if( s_plistCachedHeaders[vuindex] != NULL ) { + for(u32 j = 0; j < s_MemSize[vuindex]/8; ++j) { + FORIT(it, s_plistCachedHeaders[vuindex][j]) delete *it; + s_plistCachedHeaders[vuindex][j].clear(); + } + safe_delete_array( s_plistCachedHeaders[vuindex] ); + } + + FORIT(it, s_listVUHeaders[vuindex]) delete *it; + s_listVUHeaders[vuindex].clear(); + } +} + +// reset VU +void SuperVUReset(int vuindex) +{ +#ifdef _DEBUG + s_vucount = 0; +#endif + + if( s_recVUMem == NULL ) + return; + + //jASSUME( s_recVUMem != NULL ); + + if( vuindex < 0 ) + { + DbgCon::Status( "SuperVU reset > Resetting recompiler memory and structures." ); + memset_8<0xcd, VU_EXESIZE>(s_recVUMem); + memzero_ptr(recVUStack); + + s_recVUPtr = s_recVUMem; + } + else + { + DbgCon::Status( "SuperVU reset [VU%d] > Resetting the recs and junk", params vuindex ); + list::iterator it; + if( recVUHeaders[vuindex] ) memset( recVUHeaders[vuindex], 0, sizeof(VuFunctionHeader*) * (s_MemSize[vuindex]/8) ); + if( recVUBlocks[vuindex] ) memset( recVUBlocks[vuindex], 0, sizeof(VuBlockHeader) * (s_MemSize[vuindex]/8) ); + + if( s_plistCachedHeaders[vuindex] != NULL ) { + for(u32 j = 0; j < s_MemSize[vuindex]/8; ++j) { + FORIT(it, s_plistCachedHeaders[vuindex][j]) delete *it; + s_plistCachedHeaders[vuindex][j].clear(); + } + } + + FORIT(it, s_listVUHeaders[vuindex]) delete *it; + s_listVUHeaders[vuindex].clear(); + } +} + +// clear the block and any joining blocks +void __fastcall SuperVUClear(u32 startpc, u32 size, int vuindex) +{ + vector::iterator itrange; + list::iterator it = s_listVUHeaders[vuindex].begin(); + u32 endpc = startpc+(size+(8-(size&7))); // Adding this code to ensure size is always a multiple of 8, it can be simplified to startpc+size if size is always a multiple of 8 (cottonvibes) + while( it != s_listVUHeaders[vuindex].end() ) { + + // for every fn, check if it has code in the range + FORIT(itrange, (*it)->ranges) { + if( startpc < (u32)itrange->start+itrange->size && itrange->start < endpc ) + break; + } + + if( itrange != (*it)->ranges.end() ) { + recVUHeaders[vuindex][(*it)->startpc/8] = NULL; +#ifdef SUPERVU_CACHING + list* plist = &s_plistCachedHeaders[vuindex][(*it)->startpc/8]; + plist->push_back(*it); + if( plist->size() > 10 ) { + // list is too big, delete + delete plist->front(); + plist->pop_front(); + } + it = s_listVUHeaders[vuindex].erase(it); +#else + delete *it; + it = s_listVUHeaders[vuindex].erase(it); +#endif + } + else ++it; + } +} + +static VuFunctionHeader* s_pFnHeader = NULL; +static VuBaseBlock* s_pCurBlock = NULL; +static VuInstruction* s_pCurInst = NULL; +static u32 s_StatusRead = 0, s_MACRead = 0, s_ClipRead = 0; // read addrs +static u32 s_PrevStatusWrite = 0, s_PrevMACWrite = 0, s_PrevClipWrite = 0, s_PrevIWrite = 0; +static u32 s_WriteToReadQ = 0; + +static u32 s_VIBranchDelay = 0; //Value of register to use in a vi branch delayed situation + + +u32 s_TotalVUCycles; // total cycles since start of program execution + + +u32 SuperVUGetVIAddr(int reg, int read) +{ + assert( s_pCurInst != NULL ); + + switch(reg) { + case REG_STATUS_FLAG: + { + u32 addr = (read==2) ? s_PrevStatusWrite : (read ? s_StatusRead : s_pCurInst->pStatusWrite); + assert(!read || addr != 0); + return addr; + } + case REG_MAC_FLAG: + { + return (read==2) ? s_PrevMACWrite : (read ? s_MACRead : s_pCurInst->pMACWrite); + } + case REG_CLIP_FLAG: + { + u32 addr = (read==2) ? s_PrevClipWrite : (read ? s_ClipRead : s_pCurInst->pClipWrite); + assert( !read || addr != 0 ); + return addr; + } + case REG_Q: return (read || s_WriteToReadQ) ? (uptr)&VU->VI[REG_Q] : (uptr)&VU->q; + case REG_P: return read ? (uptr)&VU->VI[REG_P] : (uptr)&VU->p; + case REG_I: return s_PrevIWrite; + } + +#ifdef SUPERVU_VIBRANCHDELAY + if( (read != 0) && (s_pCurInst->regs[0].pipe == VUPIPE_BRANCH) && (s_pCurInst->vicached >= 0) && (s_pCurInst->vicached == reg) ) { + if (!CHECK_VUBRANCHHACK) { + return (uptr)&s_VIBranchDelay; // test for branch delays + } + //else SysPrintf("VIBRANCHDELAY hack! Please report if this breaks anything (rama)\n"); + } +#endif + + return (uptr)&VU->VI[reg]; +} + +void SuperVUDumpBlock(list& blocks, int vuindex) +{ + FILE *f; + char filename[ g_MaxPath ], str[256]; + u32 *mem; + u32 i; + +#ifdef _WIN32 + CreateDirectory("dumps", NULL); + sprintf_s( filename, g_MaxPath, "dumps\\svu%c_%.4X.txt", s_vu?'1':'0', s_pFnHeader->startpc ); +#else + mkdir("dumps", 0755); + sprintf( filename, "dumps/svu%c_%.4X.txt", s_vu?'1':'0', s_pFnHeader->startpc ); +#endif + //SysPrintf( "dump1 %x => %s\n", s_pFnHeader->startpc, filename ); + + f = fopen( filename, "w" ); + + fprintf(f, "Format: upper_inst lower_inst\ntype f:vf_live_vars vf_used_vars i:vi_live_vars vi_used_vars inst_cycle pq_inst\n"); + fprintf(f, "Type: %.2x - qread, %.2x - pread, %.2x - clip_write, %.2x - status_write\n" + "%.2x - mac_write, %.2x -qflush\n", + INST_Q_READ, INST_P_READ, INST_CLIP_WRITE, INST_STATUS_WRITE, INST_MAC_WRITE, INST_Q_WRITE); + fprintf(f, "XMM: Upper: read0 read1 write acc temp; Lower: read0 read1 write acc temp\n\n"); + + list::iterator itblock; + list::iterator itinst; + VuBaseBlock::LISTBLOCKS::iterator itchild; + + FORIT(itblock, blocks) { + fprintf(f, "block:%c %x-%x; children: ", ((*itblock)->type&BLOCKTYPE_HASEOP)?'*':' ', + (*itblock)->startpc, (*itblock)->endpc-8); + FORIT(itchild, (*itblock)->blocks) { + fprintf(f, "%x ", (*itchild)->startpc); + } + fprintf(f, "; vuxyz = %x, vuxy = %x\n", (*itblock)->vuxyz&(*itblock)->insts.front().usedvars[1], + (*itblock)->vuxy&(*itblock)->insts.front().usedvars[1]); + + itinst = (*itblock)->insts.begin(); + i = (*itblock)->startpc; + while(itinst != (*itblock)->insts.end() ) { + assert( i <= (*itblock)->endpc ); + if( itinst->type & INST_DUMMY ) { + if( itinst->nParentPc >= 0 && !(itinst->type&INST_DUMMY_)) { + // search for the parent + fprintf(f, "writeback 0x%x (%x)\n", itinst->type, itinst->nParentPc); + } + } + else { + mem = (u32*)&VU->Micro[i]; + char* pstr = disVU1MicroUF( mem[1], i+4 ); + fprintf(f, "%.4x: %-40s", i, pstr); + if( mem[1] & 0x80000000 ) fprintf(f, " I=%f(%.8x)\n", *(float*)mem, mem[0]); + else fprintf(f, "%s\n", disVU1MicroLF( mem[0], i )); + i += 8; + } + + ++itinst; + } + + fprintf(f, "\n"); + + _x86regs* pregs; + if( (*itblock)->nStartx86 >= 0 || (*itblock)->nEndx86 >= 0 ) { + fprintf(f, "X86: AX CX DX BX SP BP SI DI\n"); + } + + if( (*itblock)->nStartx86 >= 0 ) { + pregs = &s_vecRegArray[(*itblock)->nStartx86]; + fprintf(f, "STR: "); + for(i = 0; i < X86REGS; ++i) { + if( pregs[i].inuse ) fprintf(f, "%.2d ", pregs[i].reg); + else fprintf(f, "-1 "); + } + fprintf(f, "\n"); + } + + if( (*itblock)->nEndx86 >= 0 ) { + fprintf(f, "END: "); + pregs = &s_vecRegArray[(*itblock)->nEndx86]; + for(i = 0; i < X86REGS; ++i) { + if( pregs[i].inuse ) fprintf(f, "%.2d ", pregs[i].reg); + else fprintf(f, "-1 "); + } + fprintf(f, "\n"); + } + + itinst = (*itblock)->insts.begin(); + for ( i = (*itblock)->startpc; i < (*itblock)->endpc; ++itinst ) { + + if( itinst->type & INST_DUMMY ) { + } + else { + sprintf(str, "%.4x:%x f:%.8x_%.8x", i, itinst->type, itinst->livevars[1], itinst->usedvars[1]); + fprintf(f, "%-46s i:%.8x_%.8x c:%d pq:%d\n", str, + itinst->livevars[0], itinst->usedvars[0], (int)itinst->info.cycle, (int)itinst->pqcycles ); + + sprintf(str, "XMM r0:%d r1:%d w:%d a:%d t:%x;", + itinst->vfread0[1], itinst->vfread1[1], itinst->vfwrite[1], itinst->vfacc[1], itinst->vffree[1]); + fprintf(f, "%-46s r0:%d r1:%d w:%d a:%d t:%x\n", str, + itinst->vfread0[0], itinst->vfread1[0], itinst->vfwrite[0], itinst->vfacc[0], itinst->vffree[0]); + i += 8; + } + } + +#ifdef __LINUX__ + // dump the asm + if( (*itblock)->pcode != NULL ) { + char command[255]; + FILE* fasm = fopen( "mydump1", "wb" ); + //SysPrintf("writing: %x, %x\n", (*itblock)->startpc, (uptr)(*itblock)->pendcode - (uptr)(*itblock)->pcode); + fwrite( (*itblock)->pcode, 1, (uptr)(*itblock)->pendcode - (uptr)(*itblock)->pcode, fasm ); + fclose( fasm ); + sprintf( command, "objdump -D --target=binary --architecture=i386 -M intel mydump1 > tempdump"); + system( command ); + fasm = fopen("tempdump", "r"); + // read all of it and write it to f + fseek(fasm, 0, SEEK_END); + vector vbuffer(ftell(fasm)); + fseek(fasm, 0, SEEK_SET); + fread(&vbuffer[0], vbuffer.size(), 1, fasm); + + fprintf(f, "\n\n"); + fwrite(&vbuffer[0], vbuffer.size(), 1, f); + fclose(fasm); + } +#endif + + fprintf(f, "\n---------------\n"); + } + + fclose( f ); +} + +LARGE_INTEGER svubase, svufinal; +static u64 svutime; + +// uncomment to count svu exec time +//#define SUPERVU_COUNT + +// Private methods +void* SuperVUGetProgram(u32 startpc, int vuindex) +{ + assert( startpc < s_MemSize[vuindex] ); + assert( (startpc%8) == 0 ); + assert( recVUHeaders[vuindex] != NULL ); + VuFunctionHeader** pheader = &recVUHeaders[vuindex][startpc/8]; + + if( *pheader == NULL ) { +#ifdef _DEBUG +// if( vuindex ) VU1.VI[REG_TPC].UL = startpc; +// else VU0.VI[REG_TPC].UL = startpc; +// __Log("VU: %x\n", startpc); +// iDumpVU1Registers(); +// vudump |= 2; +#endif + + // measure run time + //QueryPerformanceCounter(&svubase); + +#ifdef SUPERVU_CACHING + void* pmem = (vuindex&1) ? VU1.Micro : VU0.Micro; + // check if program exists in cache + list::iterator it; + FORIT(it, s_plistCachedHeaders[vuindex][startpc/8]) { + if( (*it)->IsSame(pmem) ) { + // found, transfer to regular lists + void* pfn = (*it)->pprogfunc; + recVUHeaders[vuindex][startpc/8] = *it; + s_listVUHeaders[vuindex].push_back(*it); + s_plistCachedHeaders[vuindex][startpc/8].erase(it); + return pfn; + } + } +#endif + + *pheader = SuperVURecompileProgram(startpc, vuindex); + + if( *pheader == NULL ) { + assert( s_TotalVUCycles > 0 ); + if( vuindex ) VU1.VI[REG_TPC].UL = startpc; + else VU0.VI[REG_TPC].UL = startpc; + return (void*)SuperVUEndProgram; + } + + //QueryPerformanceCounter(&svufinal); + //svutime += (u32)(svufinal.QuadPart-svubase.QuadPart); + + assert( (*pheader)->pprogfunc != NULL ); + } + //else assert( (*pheader)->IsSame((vuindex&1) ? VU1.Micro : VU0.Micro) ); + + assert( (*pheader)->startpc == startpc ); + + return (*pheader)->pprogfunc; +} + +bool VuFunctionHeader::IsSame(void* pmem) +{ +#ifdef SUPERVU_CACHING + //u32 checksum[2]; + vector::iterator it; + FORIT(it, ranges) { + //memxor_mmx(checksum, (u8*)pmem+it->start, it->size); + //if( checksum[0] != it->checksum[0] || checksum[1] != it->checksum[1] ) + // return false; + // memcmp_mmx doesn't work on x86-64 machines +#if defined(_MSC_VER) + if( memcmp_mmx((u8*)pmem+it->start, it->pmem, it->size) ) +#else + if( memcmp((u8*)pmem+it->start, it->pmem, it->size) ) +#endif + return false; + } +#endif + return true; +} + +list::iterator VuBaseBlock::GetInstIterAtPc(int instpc) +{ + assert( instpc >= 0 ); + + u32 curpc = startpc; + list::iterator it; + for(it = insts.begin(); it != insts.end(); ++it) { + if( it->type & INST_DUMMY ) + continue; + + if( curpc == instpc ) + break; + curpc += 8; + } + + if( it != insts.end() ) + return it; + + assert( 0 ); + return insts.begin(); +} + +void VuBaseBlock::GetInstsAtPc(int instpc, list& listinsts) +{ + assert( instpc >= 0 ); + + listinsts.clear(); + + u32 curpc = startpc; + list::iterator it; + for(it = insts.begin(); it != insts.end(); ++it) { + if( it->type & INST_DUMMY ) + continue; + + if( curpc == instpc ) + break; + curpc += 8; + } + + if( it != insts.end() ) { + listinsts.push_back(&(*it)); + return; + } + + // look for the pc in other blocks + for(list::iterator itblock = s_listBlocks.begin(); itblock != s_listBlocks.end(); ++itblock) { + if( *itblock == this ) + continue; + + if( instpc >= (*itblock)->startpc && instpc < (*itblock)->endpc ) { + listinsts.push_back(&(*(*itblock)->GetInstIterAtPc(instpc))); + } + } + + assert(listinsts.size()>0); +} + +static VuFunctionHeader* SuperVURecompileProgram(u32 startpc, int vuindex) +{ + assert( vuindex < 2 ); + assert( s_recVUPtr != NULL ); + //SysPrintf("svu%c rec: %x\n", '0'+vuindex, startpc); + + // if recPtr reached the mem limit reset whole mem + if ( ( (uptr)s_recVUPtr - (uptr)s_recVUMem ) >= VU_EXESIZE-0x40000 ) { + //SysPrintf("SuperVU reset mem\n"); + SuperVUReset(-1); + SuperVUReset(0); + SuperVUReset(1); + if( s_TotalVUCycles > 0 ) { + // already executing, so return NULL + return NULL; + } + } + + list::iterator itblock; + + s_vu = vuindex; + VU = s_vu ? &VU1 : &VU0; + s_pFnHeader = new VuFunctionHeader(); + s_listVUHeaders[vuindex].push_back(s_pFnHeader); + s_pFnHeader->startpc = startpc; + + memset( recVUBlocks[s_vu], 0, sizeof(VuBlockHeader) * (s_MemSize[s_vu]/8) ); + + // analyze the global graph + s_listBlocks.clear(); + VUPIPELINES pipes; + memzero_obj(pipes.fmac); + memzero_obj(pipes.fdiv); + memzero_obj(pipes.efu); + SuperVUBuildBlocks(NULL, startpc, pipes); + + // fill parents + VuBaseBlock::LISTBLOCKS::iterator itchild; + FORIT(itblock, s_listBlocks) { + FORIT(itchild, (*itblock)->blocks) + (*itchild)->parents.push_back(*itblock); + + //(*itblock)->type &= ~(BLOCKTYPE_IGNORE|BLOCKTYPE_ANALYZED); + } + + assert( s_listBlocks.front()->startpc == startpc ); + s_listBlocks.front()->type |= BLOCKTYPE_FUNCTION; + + FORIT(itblock, s_listBlocks) { + SuperVUInitLiveness(*itblock); + } + + SuperVULivenessAnalysis(); + SuperVUEliminateDeadCode(); + SuperVUAssignRegs(); + +#ifdef _DEBUG + if( (s_vu && (vudump&1)) || (!s_vu && (vudump&16)) ) + SuperVUDumpBlock(s_listBlocks, s_vu); +#endif + + // code generation + x86SetPtr(s_recVUPtr); + branch = 0; + + SuperVURecompile(); + + s_recVUPtr = x86Ptr; + + // set the function's range + VuFunctionHeader::RANGE r; + s_pFnHeader->ranges.reserve(s_listBlocks.size()); + + FORIT(itblock, s_listBlocks) { + r.start = (*itblock)->startpc; + r.size = (*itblock)->endpc-(*itblock)->startpc; +#ifdef SUPERVU_CACHING + //memxor_mmx(r.checksum, &VU->Micro[r.start], r.size); + r.pmem = malloc(r.size); + memcpy_fast(r.pmem, &VU->Micro[r.start], r.size); +#endif + s_pFnHeader->ranges.push_back(r); + } + +#if defined(_DEBUG) && defined(__LINUX__) + // dump at the end to capture the actual code + if( (s_vu && (vudump&1)) || (!s_vu && (vudump&16)) ) + SuperVUDumpBlock(s_listBlocks, s_vu); +#endif + + // destroy + for(list::iterator itblock = s_listBlocks.begin(); itblock != s_listBlocks.end(); ++itblock) { + delete *itblock; + } + s_listBlocks.clear(); + + assert( s_recVUPtr < s_recVUMem+VU_EXESIZE ); + + return s_pFnHeader; +} + +static int _recbranchAddr(u32 vucode) { + s32 bpc = pc + (_Imm11_ << 3); +/* + if ( bpc < 0 ) { + SysPrintf("zerorec branch warning: bpc < 0 ( %x ); Using unsigned imm11\n", bpc); + bpc = pc + (_UImm11_ << 3); + }*/ + bpc &= (s_MemSize[s_vu]-1); + + return bpc; +} + +// return inst that flushes everything +static VuInstruction SuperVUFlushInst() +{ + VuInstruction inst; + // don't need to raed q/p + inst.type = INST_DUMMY_;//|INST_Q_READ|INST_P_READ; + return inst; +} + +void SuperVUAddWritebacks(VuBaseBlock* pblock, const list& listWritebacks) +{ +#ifdef SUPERVU_WRITEBACKS + // regardless of repetition, add the pipes (for selfloops) + list::const_iterator itwriteback = listWritebacks.begin(); + list::iterator itinst = pblock->insts.begin(), itinst2; + + while(itwriteback != listWritebacks.end()) { + if( itinst != pblock->insts.end() && (itinst->info.cycle < itwriteback->cycle || (itinst->type&INST_DUMMY) ) ) { + ++itinst; + continue; + } + + itinst2 = pblock->insts.insert(itinst, VuInstruction()); + itwriteback->InitInst(&(*itinst2), vucycle); + ++itwriteback; + } +#endif +} + +static VuBaseBlock* SuperVUBuildBlocks(VuBaseBlock* parent, u32 startpc, const VUPIPELINES& pipes) +{ + // check if block already exists + //SysPrintf("startpc %x\n", startpc); + startpc &= (s_vu ? 0x3fff : 0xfff); + VuBlockHeader* pbh = &recVUBlocks[s_vu][startpc/8]; + + if ( pbh->pblock != NULL ) { + + VuBaseBlock* pblock = pbh->pblock; + list::iterator itinst; + + if( pblock->startpc == startpc ) { + SuperVUAddWritebacks(pblock, pipes.listWritebacks); + return pblock; + } + + // have to divide the blocks, pnewblock is first block + assert( startpc > pblock->startpc ); + assert( startpc < pblock->endpc ); + + u32 dummyinst = (startpc-pblock->startpc)>>3; + + // count inst non-dummy insts + itinst = pblock->insts.begin(); + int cycleoff = 0; + + while(dummyinst > 0) { + if( itinst->type & INST_DUMMY ) + ++itinst; + else { + cycleoff = itinst->info.cycle; + ++itinst; + --dummyinst; + } + } + + // NOTE: still leaves insts with their writebacks in different blocks + while( itinst->type & INST_DUMMY ) + ++itinst; + + // the difference in cycles between dummy insts (naruto utlimate ninja) + int cyclediff = 0; + if( parent == pblock ) + cyclediff = itinst->info.cycle-cycleoff; + cycleoff = itinst->info.cycle; + + // new block + VuBaseBlock* pnewblock = new VuBaseBlock(); + s_listBlocks.push_back(pnewblock); + + pnewblock->startpc = startpc; + pnewblock->endpc = pblock->endpc; + pnewblock->cycles = pblock->cycles-cycleoff+cyclediff; + + pnewblock->blocks.splice(pnewblock->blocks.end(), pblock->blocks); + pnewblock->insts.splice(pnewblock->insts.end(), pblock->insts, itinst, pblock->insts.end()); + pnewblock->type = pblock->type; + + // any writebacks in the next 3 cycles also belong to original block +// for(itinst = pnewblock->insts.begin(); itinst != pnewblock->insts.end(); ) { +// if( (itinst->type & INST_DUMMY) && itinst->nParentPc >= 0 && itinst->nParentPc < (int)startpc ) { +// +// if( !(itinst->type & INST_Q_WRITE) ) +// pblock->insts.push_back(*itinst); +// itinst = pnewblock->insts.erase(itinst); +// continue; +// } +// +// ++itinst; +// } + + pbh = &recVUBlocks[s_vu][startpc/8]; + for(u32 inst = startpc; inst < pblock->endpc; inst += 8) { + if( pbh->pblock == pblock ) + pbh->pblock = pnewblock; + ++pbh; + } + + FORIT(itinst, pnewblock->insts) + itinst->info.cycle -= cycleoff; + + SuperVUAddWritebacks(pnewblock, pipes.listWritebacks); + + // old block + pblock->blocks.push_back(pnewblock); + pblock->endpc = startpc; + pblock->cycles = cycleoff; + pblock->type &= BLOCKTYPE_MACFLAGS; + //pblock->insts.push_back(SuperVUFlushInst()); //don't need + + return pnewblock; + } + + VuBaseBlock* pblock = new VuBaseBlock(); + s_listBlocks.push_back(pblock); + + int i = 0; + branch = 0; + pc = startpc; + pblock->startpc = startpc; + + // clear stalls (might be a prob) + memcpy(VU->fmac, pipes.fmac, sizeof(pipes.fmac)); + memcpy(&VU->fdiv, &pipes.fdiv, sizeof(pipes.fdiv)); + memcpy(&VU->efu, &pipes.efu, sizeof(pipes.efu)); +// memset(VU->fmac, 0, sizeof(VU->fmac)); +// memset(&VU->fdiv, 0, sizeof(VU->fdiv)); +// memset(&VU->efu, 0, sizeof(VU->efu)); + + vucycle = 0; + + u8 macflags = 0; + + list< WRITEBACK > listWritebacks; + list< WRITEBACK >::iterator itwriteback; + list::iterator itinst; + u32 hasSecondBranch = 0; + u32 needFullStatusFlag = 0; + +#ifdef SUPERVU_WRITEBACKS + listWritebacks = pipes.listWritebacks; +#endif + + // first analysis pass for status flags + while(1) { + u32* ptr = (u32*)&VU->Micro[pc]; + pc += 8; + int prevbranch = branch; + + if( ptr[1] & 0x40000000 ) + branch = 1; + + if( !(ptr[1] & 0x80000000) ) { // not I + switch( ptr[0]>>25 ) { + case 0x24: // jr + case 0x25: // jalr + case 0x20: // B + case 0x21: // BAL + case 0x28: // IBEQ + case 0x2f: // IBGEZ + case 0x2d: // IBGTZ + case 0x2e: // IBLEZ + case 0x2c: // IBLTZ + case 0x29: // IBNE + branch = 1; + break; + + case 0x14: // fseq + case 0x17: // fsor + //needFullStatusFlag = 2; + break; + + case 0x16: // fsand + if( (ptr[0]&0xc0) ) { + // sometimes full sticky bits are needed (simple series 2000 - oane chapara) + //SysPrintf("needSticky: %x-%x\n", s_pFnHeader->startpc, startpc); + needFullStatusFlag = 2; + } + break; + } + } + + if( prevbranch ) + break; + + if( pc >= s_MemSize[s_vu] ) { + Console::Error( "inf vu0 prog %x",params startpc); + break; + } + } + + // second full pass + pc = startpc; + branch = 0; + VuInstruction* pprevinst=NULL, *ppprevinst=NULL, *pinst = NULL; + + while(1) { + + if( pc == s_MemSize[s_vu] ) { + branch |= 8; + break; + } + + if( !branch && pbh->pblock != NULL ) { + pblock->blocks.push_back(pbh->pblock); + break; + } + + int prevbranch = branch; + + if( !prevbranch ) { + pbh->pblock = pblock; + } + else assert( prevbranch || pbh->pblock == NULL); + + pblock->insts.push_back(VuInstruction()); + + ppprevinst = pprevinst; + pprevinst = pinst; + pinst = &pblock->insts.back(); + SuperVUAnalyzeOp(VU, &pinst->info, pinst->regs); + +#ifdef SUPERVU_VIBRANCHDELAY + if( pinst->regs[0].pipe == VUPIPE_BRANCH && pblock->insts.size() > 1 ) { + + if( pprevinst != NULL && pprevinst->info.cycle+1==pinst->info.cycle && + (pprevinst->regs[0].pipe == VUPIPE_IALU||pprevinst->regs[0].pipe == VUPIPE_FMAC) && ((pprevinst->regs[0].VIwrite & pinst->regs[0].VIread) & 0xffff) + && !(pprevinst->regs[0].VIread&((1<Micro[pc-16]; + + // check for the previous instruction. If that has the same register used, then have a 2 cycle delay! + // (monsterhouse has sqi vi05, sqi vi05, ibeq vi05, vi03). The ibeq should read the vi05 before the first sqi + if( ppprevinst != NULL && ppprevinst->info.cycle+2==pinst->info.cycle && (ppprevinst->regs[0].pipe == VUPIPE_FMAC||ppprevinst->regs[0].pipe == VUPIPE_IALU) && + ((ppprevinst->regs[0].VIwrite & pinst->regs[0].VIread) & 0xffff) && + ((ppprevinst->regs[0].VIwrite & pinst->regs[0].VIread) & 0xffff) == ((ppprevinst->regs[0].VIwrite & pprevinst->regs[0].VIread) & 0xffff) && + !(ppprevinst->regs[0].VIread&((1<startpc); + + // ignore if prev instruction is ILW or ILWR (xenosaga 2) + lowercode = *(int*)&VU->Micro[pc-24]; + pdelayinst = ppprevinst; + } + + + //SysPrintf("vurec: %x\n", pc); + // ignore if prev instruction is ILW or ILWR (xenosaga 2) + if( (lowercode>>25) != 4 // ILW + && !((lowercode>>25) == 0x40 && (lowercode&0x3ff)==0x3fe) ) { // ILWR + + //SysPrintf("branchdelay: %x: %x\n", s_pFnHeader->startpc, pc-8); + + // share the same register + pdelayinst->type |= INST_CACHE_VI; + + // find the correct register + u32 mask = pdelayinst->regs[0].VIwrite & pinst->regs[0].VIread; + for(int i = 0; i < 16; ++i) { + if( mask & (1<vicached = i; + break; + } + } + + pinst->vicached = pdelayinst->vicached; + } + } + } +#endif + + if( prevbranch ) { + if( pinst->regs[0].pipe == VUPIPE_BRANCH ) + hasSecondBranch = 1; + pinst->type |= INST_BRANCH_DELAY; + } + + // check write back + for(itwriteback = listWritebacks.begin(); itwriteback != listWritebacks.end(); ) { + if( pinst->info.cycle >= itwriteback->cycle ) { + itinst = pblock->insts.insert(--pblock->insts.end(), VuInstruction()); + itwriteback->InitInst(&(*itinst), pinst->info.cycle); + itwriteback = listWritebacks.erase(itwriteback); + } + else ++itwriteback; + } + + // add new writebacks + WRITEBACK w; + const u32 allflags = (1<regs[j].VIwrite & allflags; + + if( pinst->info.macflag & VUOP_WRITE ) w.viwrite[1] |= (1<info.statusflag & VUOP_WRITE ) w.viwrite[1] |= (1<info.macflag|pinst->info.statusflag) & VUOP_READ ) + macflags = 1; + if( pinst->regs[0].VIread & ((1<regs[1].pipe == VUPIPE_FMAC && (pinst->regs[1].VFwrite==0&&!(pinst->regs[1].VIwrite&(1<regs[0].VIread |= (1<VIwrite |= lregs->VIwrite & (1<info.statusflag&VUOP_WRITE)&&!(pinst->regs[0].VIwrite&(1<regs[j].VIread & allflags; + + if( (pinst->regs[j].VIread&(1<regs[j].VIwrite&(1<regs[j].VIread &= ~(1<regs[j].VIread&(1<regs[j].VIwrite&(1<regs[j].VIread &= ~(1<regs[j].VIwrite &= ~allflags; + } + + if( pinst->info.macflag & VUOP_READ) w.viread[1] |= 1<info.statusflag & VUOP_READ) w.viread[1] |= 1<info.cycle+4; + listWritebacks.push_back(w); + } + + if( pinst->info.q&VUOP_READ ) pinst->type |= INST_Q_READ; + if( pinst->info.p&VUOP_READ ) pinst->type |= INST_P_READ; + + if( pinst->info.q&VUOP_WRITE ) { + pinst->pqcycles = QWaitTimes[pinst->info.pqinst]+1; + + memset(&w, 0, sizeof(w)); + w.nParentPc = pc-8; + w.cycle = pinst->info.cycle+pinst->pqcycles; + w.viwrite[0] = 1<info.p&VUOP_WRITE ) + pinst->pqcycles = PWaitTimes[pinst->info.pqinst]+1; + + if( prevbranch ) { + break; + } + + // make sure there is always a branch + // sensible soccer overflows on vu0, so increase the limit... + if( (s_vu==1 && i >= 0x799) || (s_vu==0 && i >= 0x201) ) { + Console::Error("VuRec base block doesn't terminate!"); + assert(0); + break; + } + + i++; + pbh++; + } + + if( macflags ) + pblock->type |= BLOCKTYPE_MACFLAGS; + + pblock->endpc = pc; + u32 lastpc = pc; + + pblock->cycles = vucycle; + +#ifdef SUPERVU_WRITEBACKS + if( !branch || (branch&8) ) +#endif + { + // flush writebacks + if( listWritebacks.size() > 0 ) { + listWritebacks.sort(WRITEBACK::SortWritebacks); + for(itwriteback = listWritebacks.begin(); itwriteback != listWritebacks.end(); ++itwriteback) { + if( itwriteback->viwrite[0] & (1<insts.push_back(VuInstruction()); + itwriteback->InitInst(&pblock->insts.back(), vucycle); + } + + listWritebacks.clear(); + } + } + + if( !branch ) + return pblock; + + if( branch & 8 ) { + // what if also a jump? + pblock->type |= BLOCKTYPE_EOP|BLOCKTYPE_HASEOP; + + // add an instruction to flush p and q (if written) + pblock->insts.push_back(SuperVUFlushInst()); + return pblock; + } + + // it is a (cond) branch or a jump + u32 vucode = *(u32*)(VU->Micro+lastpc-16); + int bpc = _recbranchAddr(vucode)-8; + + VUPIPELINES newpipes; + memcpy(newpipes.fmac, VU->fmac, sizeof(newpipes.fmac)); + memcpy(&newpipes.fdiv, &VU->fdiv, sizeof(newpipes.fdiv)); + memcpy(&newpipes.efu, &VU->efu, sizeof(newpipes.efu)); + + for(i = 0; i < 8; ++i) newpipes.fmac[i].sCycle -= vucycle; + newpipes.fdiv.sCycle -= vucycle; + newpipes.efu.sCycle -= vucycle; + + if( listWritebacks.size() > 0 ) { + // flush all when jumping, send down the pipe when in branching + bool bFlushWritebacks = (vucode>>25)==0x24||(vucode>>25)==0x25;//||(vucode>>25)==0x20||(vucode>>25)==0x21; + + listWritebacks.sort(WRITEBACK::SortWritebacks); + for(itwriteback = listWritebacks.begin(); itwriteback != listWritebacks.end(); ++itwriteback) { + if( itwriteback->viwrite[0] & (1<cycle < vucycle || bFlushWritebacks ) { + pblock->insts.push_back(VuInstruction()); + itwriteback->InitInst(&pblock->insts.back(), vucycle); + } + else { + newpipes.listWritebacks.push_back(*itwriteback); + newpipes.listWritebacks.back().cycle -= vucycle; + } + } + } + + if( newpipes.listWritebacks.size() > 0 ) // other blocks might read the mac flags + pblock->type |= BLOCKTYPE_MACFLAGS; + + u32 firstbranch = vucode>>25; + switch(firstbranch) { + case 0x24: // jr + pblock->type |= BLOCKTYPE_EOP; // jump out of procedure, since not returning, set EOP + pblock->insts.push_back(SuperVUFlushInst()); + firstbranch = 0xff; //Non-Conditional Jump + break; + + case 0x25: // jalr + { + // linking, so will return to procedure + pblock->insts.push_back(SuperVUFlushInst()); + + VuBaseBlock* pjumpblock = SuperVUBuildBlocks(pblock, lastpc, newpipes); + + // update pblock since could have changed + pblock = recVUBlocks[s_vu][lastpc/8-2].pblock; + assert( pblock != NULL ); + + pblock->blocks.push_back(pjumpblock); + firstbranch = 0xff; //Non-Conditional Jump + break; + } + case 0x20: // B + { + VuBaseBlock* pbranchblock = SuperVUBuildBlocks(pblock, bpc, newpipes); + + // update pblock since could have changed + pblock = recVUBlocks[s_vu][lastpc/8-2].pblock; + assert( pblock != NULL ); + + pblock->blocks.push_back(pbranchblock); + firstbranch = 0xff; //Non-Conditional Jump + break; + } + case 0x21: // BAL + { + VuBaseBlock* pbranchblock = SuperVUBuildBlocks(pblock, bpc, newpipes); + + // update pblock since could have changed + pblock = recVUBlocks[s_vu][lastpc/8-2].pblock; + assert( pblock != NULL ); + pblock->blocks.push_back(pbranchblock); + firstbranch = 0xff; //Non-Conditional Jump + break; + } + case 0x28: // IBEQ + case 0x2f: // IBGEZ + case 0x2d: // IBGTZ + case 0x2e: // IBLEZ + case 0x2c: // IBLTZ + case 0x29: // IBNE + { + VuBaseBlock* pbranchblock = SuperVUBuildBlocks(pblock, bpc, newpipes); + + // update pblock since could have changed + pblock = recVUBlocks[s_vu][lastpc/8-2].pblock; + assert( pblock != NULL ); + pblock->blocks.push_back(pbranchblock); + + // if has a second branch that is B or BAL, skip this + u32 secondbranch = (*(u32*)(VU->Micro+lastpc-8))>>25; + if( !hasSecondBranch || (secondbranch != 0x21 && secondbranch != 0x20) ) { + pbranchblock = SuperVUBuildBlocks(pblock, lastpc, newpipes); + + pblock = recVUBlocks[s_vu][lastpc/8-2].pblock; + pblock->blocks.push_back(pbranchblock); + } + + break; + } + default: + assert(pblock->blocks.size() == 1); + break; + } + + pblock = recVUBlocks[s_vu][lastpc/8-2].pblock; + +#ifdef SUPERVU_VIBRANCHDELAY +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///// NOTE! This could still be a hack for KH2/GoW, but until we know how it properly works, this will do for now./// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + if( hasSecondBranch && firstbranch != 0xff ) { //check the previous jump was conditional and there is a second branch +#else + if( hasSecondBranch) { +#endif + + u32 vucode = *(u32*)(VU->Micro+lastpc-8); + pc = lastpc; + int bpc = _recbranchAddr(vucode); + + switch(vucode>>25) { + case 0x24: // jr + Console::Error("svurec bad jr jump!"); + assert(0); + break; + + case 0x25: // jalr + { + Console::Error("svurec bad jalr jump!"); + assert(0); + break; + } + case 0x20: // B + { + VuBaseBlock* pbranchblock = SuperVUBuildBlocks(pblock, bpc, newpipes); + + // update pblock since could have changed + pblock = recVUBlocks[s_vu][lastpc/8-2].pblock; + + pblock->blocks.push_back(pbranchblock); + break; + } + case 0x21: // BAL + { + VuBaseBlock* pbranchblock = SuperVUBuildBlocks(pblock, bpc, newpipes); + + // replace instead of pushing a new block + pblock = recVUBlocks[s_vu][lastpc/8-2].pblock; + pblock->blocks.push_back(pbranchblock); + break; + } + case 0x28: // IBEQ + case 0x2f: // IBGEZ + case 0x2d: // IBGTZ + case 0x2e: // IBLEZ + case 0x2c: // IBLTZ + case 0x29: // IBNE + { + VuBaseBlock* pbranchblock = SuperVUBuildBlocks(pblock, bpc, newpipes); + + // update pblock since could have changed + pblock = recVUBlocks[s_vu][lastpc/8-2].pblock; + pblock->blocks.push_back(pbranchblock); + + // only add the block if the previous branch doesn't include the next instruction (ie, if a direct jump) + if( firstbranch == 0x24 || firstbranch == 0x25 || firstbranch == 0x20 || firstbranch == 0x21 ) { + pbranchblock = SuperVUBuildBlocks(pblock, lastpc, newpipes); + + pblock = recVUBlocks[s_vu][lastpc/8-2].pblock; + pblock->blocks.push_back(pbranchblock); + } + + break; + } + + jNO_DEFAULT; + } + } + + return recVUBlocks[s_vu][startpc/8].pblock; +} + +static void SuperVUInitLiveness(VuBaseBlock* pblock) +{ + list::iterator itinst, itnext; + + assert( pblock->insts.size() > 0 ); + + for(itinst = pblock->insts.begin(); itinst != pblock->insts.end(); ++itinst) { + + if( itinst->type & INST_DUMMY_ ) { + itinst->addvars[0] = itinst->addvars[1] = 0xffffffff; + itinst->livevars[0] = itinst->livevars[1] = 0xffffffff; + itinst->keepvars[0] = itinst->keepvars[1] = 0xffffffff; + itinst->usedvars[0] = itinst->usedvars[1] = 0; + } + else { + itinst->addvars[0] = itinst->regs[0].VIread | itinst->regs[1].VIread; + itinst->addvars[1] = (itinst->regs[0].VFread0 ? (1 << itinst->regs[0].VFread0) : 0) | + (itinst->regs[0].VFread1 ? (1 << itinst->regs[0].VFread1) : 0) | + (itinst->regs[1].VFread0 ? (1 << itinst->regs[1].VFread0) : 0) | + (itinst->regs[1].VFread1 ? (1 << itinst->regs[1].VFread1) : 0); + + // vf0 is not handled by VFread + if( !itinst->regs[0].VFread0 && (itinst->regs[0].VIread & (1<addvars[1] |= 1; + if( !itinst->regs[1].VFread0 && (itinst->regs[1].VIread & (1<addvars[1] |= 1; + if( !itinst->regs[0].VFread1 && (itinst->regs[0].VIread & (1<regs[0].VFr1xyzw != 0xff ) itinst->addvars[1] |= 1; + if( !itinst->regs[1].VFread1 && (itinst->regs[1].VIread & (1<regs[1].VFr1xyzw != 0xff ) itinst->addvars[1] |= 1; + + + u32 vfwrite = 0; + if( itinst->regs[0].VFwrite != 0 ) { + if( itinst->regs[0].VFwxyzw != 0xf ) itinst->addvars[1] |= 1<regs[0].VFwrite; + else vfwrite |= 1<regs[0].VFwrite; + } + if( itinst->regs[1].VFwrite != 0 ) { + if( itinst->regs[1].VFwxyzw != 0xf ) itinst->addvars[1] |= 1<regs[1].VFwrite; + else vfwrite |= 1<regs[1].VFwrite; + } + if( (itinst->regs[1].VIwrite & (1<regs[1].VFwxyzw != 0xf ) + itinst->addvars[1] |= 1<regs[0].VIwrite|itinst->regs[1].VIwrite); + + itinst->usedvars[0] = itinst->addvars[0]|viwrite; + itinst->usedvars[1] = itinst->addvars[1]|vfwrite; + +// itinst->addvars[0] &= ~viwrite; +// itinst->addvars[1] &= ~vfwrite; + itinst->keepvars[0] = ~viwrite; + itinst->keepvars[1] = ~vfwrite; + } + } + + itinst = --pblock->insts.end(); + while( itinst != pblock->insts.begin() ) { + itnext = itinst; --itnext; + + itnext->usedvars[0] |= itinst->usedvars[0]; + itnext->usedvars[1] |= itinst->usedvars[1]; + + itinst = itnext; + } +} + +u32 COMPUTE_LIVE(u32 R, u32 K, u32 L) +{ + u32 live = R | ((L)&(K)); + // special process mac and status flags + // only propagate liveness if doesn't write to the flag + if( !(L&(1<::reverse_iterator itblock; + list::iterator itinst, itnext; + VuBaseBlock::LISTBLOCKS::iterator itchild; + + u32 livevars[2]; + + do { + changed = FALSE; + for(itblock = s_listBlocks.rbegin(); itblock != s_listBlocks.rend(); ++itblock) { + + u32 newlive; + VuBaseBlock* pb = *itblock; + + // the last inst relies on the neighbor's insts + itinst = --pb->insts.end(); + + if( pb->blocks.size() > 0 ) { + livevars[0] = 0; livevars[1] = 0; + for( itchild = pb->blocks.begin(); itchild != pb->blocks.end(); ++itchild) { + VuInstruction& front = (*itchild)->insts.front(); + livevars[0] |= front.livevars[0]; + livevars[1] |= front.livevars[1]; + } + + newlive = COMPUTE_LIVE(itinst->addvars[0], itinst->keepvars[0], livevars[0]); + + // should propagate status flags whose parent insts are not in this block +// if( itinst->nParentPc >= 0 && (itinst->type & (INST_STATUS_WRITE|INST_MAC_WRITE)) ) +// newlive |= livevars[0]&((1<livevars[0] != newlive ) { + changed = TRUE; + itinst->livevars[0] = newlive; + } + + newlive = COMPUTE_LIVE(itinst->addvars[1], itinst->keepvars[1], livevars[1]); + if( itinst->livevars[1] != newlive ) { + changed = TRUE; + itinst->livevars[1] = newlive; + } + } + + while( itinst != pb->insts.begin() ) { + + itnext = itinst; --itnext; + + newlive = COMPUTE_LIVE(itnext->addvars[0], itnext->keepvars[0], itinst->livevars[0]); + + // should propagate status flags whose parent insts are not in this block +// if( itnext->nParentPc >= 0 && (itnext->type & (INST_STATUS_WRITE|INST_MAC_WRITE)) && !(itinst->type & (INST_STATUS_WRITE|INST_MAC_WRITE)) ) +// newlive |= itinst->livevars[0]&((1<livevars[0] != newlive ) { + changed = TRUE; + itnext->livevars[0] = newlive; + itnext->livevars[1] = COMPUTE_LIVE(itnext->addvars[1], itnext->keepvars[1], itinst->livevars[1]); + } + else { + newlive = COMPUTE_LIVE(itnext->addvars[1], itnext->keepvars[1], itinst->livevars[1]); + if( itnext->livevars[1] != newlive ) { + changed = TRUE; + itnext->livevars[1] = newlive; + } + } + + itinst = itnext; + } + +// if( (livevars[0] | itinst->livevars[0]) != itinst->livevars[0] ) { +// changed = TRUE; +// itinst->livevars[0] |= livevars[0]; +// } +// if( (livevars[1] | itinst->livevars[1]) != itinst->livevars[1] ) { +// changed = TRUE; +// itinst->livevars[1] |= livevars[1]; +// } +// +// while( itinst != pb->insts.begin() ) { +// +// itnext = itinst; --itnext; +// if( (itnext->livevars[0] | (itinst->livevars[0] & itnext->keepvars[0])) != itnext->livevars[0] ) { +// changed = TRUE; +// itnext->livevars[0] |= itinst->livevars[0] & itnext->keepvars[0]; +// itnext->livevars[1] |= itinst->livevars[1] & itnext->keepvars[1]; +// } +// else if( (itnext->livevars[1] | (itinst->livevars[1] & itnext->keepvars[1])) != itnext->livevars[1] ) { +// changed = TRUE; +// itnext->livevars[1] |= itinst->livevars[1] & itnext->keepvars[1]; +// } +// +// itinst = itnext; +// } + } + + } while(changed); +} + +static void SuperVUEliminateDeadCode() +{ + list::iterator itblock; + VuBaseBlock::LISTBLOCKS::iterator itchild; + list::iterator itinst, itnext; + list listParents; + list::iterator itparent; + + FORIT(itblock, s_listBlocks) { + +#ifdef _DEBUG + u32 startpc = (*itblock)->startpc; + u32 curpc = startpc; +#endif + + itnext = (*itblock)->insts.begin(); + itinst = itnext++; + while(itnext != (*itblock)->insts.end() ) { + if( itinst->type & (INST_CLIP_WRITE|INST_MAC_WRITE|INST_STATUS_WRITE) ) { + u32 live0 = itnext->livevars[0]; + if( itinst->nParentPc >= 0 && itnext->nParentPc >= 0 && itinst->nParentPc != itnext->nParentPc ) { // superman returns + // take the live vars from the next next inst + list::iterator itnextnext = itnext; ++itnextnext; + if( itnextnext != (*itblock)->insts.end() ) { + live0 = itnextnext->livevars[0]; + } + } + + itinst->regs[0].VIwrite &= live0; + itinst->regs[1].VIwrite &= live0; + + u32 viwrite = itinst->regs[0].VIwrite|itinst->regs[1].VIwrite; + + (*itblock)->GetInstsAtPc(itinst->nParentPc, listParents); + int removetype = 0; + + FORIT(itparent, listParents) { + VuInstruction* parent = *itparent; + + if( viwrite & (1<regs[0].VIwrite |= (itinst->regs[0].VIwrite&(1<regs[1].VIwrite |= (itinst->regs[1].VIwrite&(1<info.macflag && (itinst->type & INST_MAC_WRITE) ) { + if( !(viwrite&(1<info.macflag = 0; + // parent->regs[0].VIwrite &= ~(1<regs[1].VIwrite &= ~(1<regs[0].VIwrite & (1<regs[1].VIwrite & (1<regs[1].pipe == VUPIPE_FMAC && (parent->regs[1].VFwrite == 0&&!(parent->regs[1].VIwrite&(1<regs[0].VIwrite |= ((1<regs[1].VIwrite |= ((1<regs[0].VIwrite |= (itinst->regs[0].VIwrite&(1<regs[1].VIwrite |= (itinst->regs[1].VIwrite&(1<info.statusflag && (itinst->type & INST_STATUS_WRITE)) { + if( !(viwrite&(1<info.statusflag = 0; + // parent->regs[0].VIwrite &= ~(1<regs[1].VIwrite &= ~(1<regs[0].VIwrite & (1<regs[1].VIwrite & (1<regs[1].pipe == VUPIPE_FMAC && (parent->regs[1].VFwrite == 0&&!(parent->regs[1].VIwrite&(1<regs[0].VIwrite |= ((1<regs[1].VIwrite |= ((1<regs[0].VIwrite |= (itinst->regs[0].VIwrite&(1<regs[1].VIwrite |= (itinst->regs[1].VIwrite&(1<type &= ~removetype; + if( itinst->type == 0 ) { + itnext = (*itblock)->insts.erase(itinst); + itinst = itnext++; + continue; + } + } +#ifdef _DEBUG + else curpc += 8; +#endif + itinst = itnext; + ++itnext; + } + + if( itinst->type & INST_DUMMY ) { + // last inst with the children + u32 mask = 0; + for(itchild = (*itblock)->blocks.begin(); itchild != (*itblock)->blocks.end(); ++itchild) { + mask |= (*itchild)->insts.front().livevars[0]; + } + itinst->regs[0].VIwrite &= mask; + itinst->regs[1].VIwrite &= mask; + u32 viwrite = itinst->regs[0].VIwrite|itinst->regs[1].VIwrite; + + if( itinst->nParentPc >= 0 ) { + + (*itblock)->GetInstsAtPc(itinst->nParentPc, listParents); + int removetype = 0; + + FORIT(itparent, listParents) { + VuInstruction* parent = *itparent; + + if( viwrite & (1<regs[0].VIwrite |= (itinst->regs[0].VIwrite&(1<regs[1].VIwrite |= (itinst->regs[1].VIwrite&(1<info.macflag && (itinst->type & INST_MAC_WRITE) ) { + if( !(viwrite&(1<info.macflag = 0; +#ifndef SUPERVU_WRITEBACKS + assert( !(parent->regs[0].VIwrite & (1<regs[1].VIwrite & (1<regs[0].VIwrite |= (itinst->regs[0].VIwrite&(1<regs[1].VIwrite |= (itinst->regs[1].VIwrite&(1<info.statusflag && (itinst->type & INST_STATUS_WRITE)) { + if( !(viwrite&(1<info.statusflag = 0; +#ifndef SUPERVU_WRITEBACKS + assert( !(parent->regs[0].VIwrite & (1<regs[1].VIwrite & (1<regs[0].VIwrite |= (itinst->regs[0].VIwrite&(1<regs[1].VIwrite |= (itinst->regs[1].VIwrite&(1<type &= ~removetype; + if( itinst->type == 0 ) { + (*itblock)->insts.erase(itinst); + } + } + } + } +} + +void VuBaseBlock::AssignVFRegs() +{ + int i; + VuBaseBlock::LISTBLOCKS::iterator itchild; + list::iterator itblock; + list::iterator itinst, itnext, itinst2; + + // init the start regs + if( type & BLOCKTYPE_ANALYZED ) return; // nothing changed + memcpy(xmmregs, startregs, sizeof(xmmregs)); + + if( type & BLOCKTYPE_ANALYZED ) { + // check if changed + for(i = 0; i < XMMREGS; ++i) { + if( xmmregs[i].inuse != startregs[i].inuse ) + break; + if( xmmregs[i].inuse && (xmmregs[i].reg != startregs[i].reg || xmmregs[i].type != startregs[i].type) ) + break; + } + + if( i == XMMREGS ) return; // nothing changed + } + + u8* oldX86 = x86Ptr; + + FORIT(itinst, insts) { + + if( itinst->type & INST_DUMMY ) + continue; + + // reserve, go from upper to lower + int lastwrite = -1; + + for(i = 1; i >= 0; --i) { + _VURegsNum* regs = itinst->regs+i; + + // redo the counters so that the proper regs are released + for(int j = 0; j < XMMREGS; ++j) { + if( xmmregs[j].inuse ) { + if( xmmregs[j].type == XMMTYPE_VFREG ) { + int count = 0; + itinst2 = itinst; + + if( i ) { + if( itinst2->regs[0].VFread0 == xmmregs[j].reg || itinst2->regs[0].VFread1 == xmmregs[j].reg || itinst2->regs[0].VFwrite == xmmregs[j].reg ) { + itinst2 = insts.end(); + break; + } + else { + ++count; + ++itinst2; + } + } + + while(itinst2 != insts.end() ) { + if( itinst2->regs[0].VFread0 == xmmregs[j].reg || itinst2->regs[0].VFread1 == xmmregs[j].reg || itinst2->regs[0].VFwrite == xmmregs[j].reg || + itinst2->regs[1].VFread0 == xmmregs[j].reg || itinst2->regs[1].VFread1 == xmmregs[j].reg || itinst2->regs[1].VFwrite == xmmregs[j].reg) + break; + + ++count; + ++itinst2; + } + xmmregs[j].counter = 1000-count; + } + else { + assert( xmmregs[j].type == XMMTYPE_ACC ); + + int count = 0; + itinst2 = itinst; + + if( i ) ++itinst2; // acc isn't used in lower insts + + while(itinst2 != insts.end() ) { + assert( !((itinst2->regs[0].VIread|itinst2->regs[0].VIwrite) & (1<regs[1].VIread|itinst2->regs[1].VIwrite) & (1<VFread0 ) _addNeededVFtoXMMreg(regs->VFread0); + if( regs->VFread1 ) _addNeededVFtoXMMreg(regs->VFread1); + if( regs->VFwrite ) _addNeededVFtoXMMreg(regs->VFwrite); + if( regs->VIread & (1<VIread & (1<vfread0[i] = itinst->vfread1[i] = itinst->vfwrite[i] = itinst->vfacc[i] = -1; + itinst->vfflush[i] = -1; + + if( regs->VFread0 ) itinst->vfread0[i] = _allocVFtoXMMreg(VU, -1, regs->VFread0, 0); + else if( regs->VIread & (1<vfread0[i] = _allocVFtoXMMreg(VU, -1, 0, 0); + if( regs->VFread1 ) itinst->vfread1[i] = _allocVFtoXMMreg(VU, -1, regs->VFread1, 0); + else if( (regs->VIread & (1<VFr1xyzw != 0xff ) itinst->vfread1[i] = _allocVFtoXMMreg(VU, -1, 0, 0); + if( regs->VIread & (1<vfacc[i] = _allocACCtoXMMreg(VU, -1, 0); + + int reusereg = -1; // 0 - VFwrite, 1 - VFAcc + + if( regs->VFwrite ) { + assert( !(regs->VIwrite&(1<VFwxyzw == 0xf ) { + itinst->vfwrite[i] = _checkXMMreg(XMMTYPE_VFREG, regs->VFwrite, 0); + if( itinst->vfwrite[i] < 0 ) reusereg = 0; + } + else { + itinst->vfwrite[i] = _allocVFtoXMMreg(VU, -1, regs->VFwrite, 0); + } + } + else if( regs->VIwrite & (1<VFwxyzw == 0xf ) { + itinst->vfacc[i] = _checkXMMreg(XMMTYPE_ACC, 0, 0); + if( itinst->vfacc[i] < 0 ) reusereg = 1; + } + else { + itinst->vfacc[i] = _allocACCtoXMMreg(VU, -1, 0); + } + } + + if( reusereg >= 0 ) { + // reuse + itnext = itinst; itnext++; + + u8 type = reusereg ? XMMTYPE_ACC : XMMTYPE_VFREG; + u8 reg = reusereg ? 0 : regs->VFwrite; + + if( itinst->vfacc[i] >= 0 && lastwrite != itinst->vfacc[i] && + (itnext == insts.end() || ((regs->VIread&(1<usedvars[0]&(1<livevars[0]&(1<livevars[0]&(1<vfacc[i]); + xmmregs[itinst->vfacc[i]].inuse = 1; + xmmregs[itinst->vfacc[i]].reg = reg; + xmmregs[itinst->vfacc[i]].type = type; + xmmregs[itinst->vfacc[i]].mode = 0; + itinst->vfwrite[i] = itinst->vfacc[i]; + } + else if( itinst->vfread0[i] >= 0 && lastwrite != itinst->vfread0[i] && + (itnext == insts.end() || (regs->VFread0 > 0 && (!(itnext->usedvars[1]&(1<VFread0)) || !(itnext->livevars[1]&(1<VFread0))))) ) { + + if(itnext == insts.end() || (itnext->livevars[1]®s->VFread0)) _freeXMMreg(itinst->vfread0[i]); + xmmregs[itinst->vfread0[i]].inuse = 1; + xmmregs[itinst->vfread0[i]].reg = reg; + xmmregs[itinst->vfread0[i]].type = type; + xmmregs[itinst->vfread0[i]].mode = 0; + if( reusereg ) itinst->vfacc[i] = itinst->vfread0[i]; + else itinst->vfwrite[i] = itinst->vfread0[i]; + } + else if( itinst->vfread1[i] >= 0 && lastwrite != itinst->vfread1[i] && + (itnext == insts.end() || (regs->VFread1 > 0 && (!(itnext->usedvars[1]&(1<VFread1)) || !(itnext->livevars[1]&(1<VFread1))))) ) { + + if(itnext == insts.end() || (itnext->livevars[1]®s->VFread1)) _freeXMMreg(itinst->vfread1[i]); + xmmregs[itinst->vfread1[i]].inuse = 1; + xmmregs[itinst->vfread1[i]].reg = reg; + xmmregs[itinst->vfread1[i]].type = type; + xmmregs[itinst->vfread1[i]].mode = 0; + if( reusereg ) itinst->vfacc[i] = itinst->vfread1[i]; + else itinst->vfwrite[i] = itinst->vfread1[i]; + } + else { + if( reusereg ) itinst->vfacc[i] = _allocACCtoXMMreg(VU, -1, 0); + else itinst->vfwrite[i] = _allocVFtoXMMreg(VU, -1, regs->VFwrite, 0); + } + } + + if( itinst->vfwrite[i] >= 0 ) lastwrite = itinst->vfwrite[i]; + else if( itinst->vfacc[i] >= 0 ) lastwrite = itinst->vfacc[i]; + + // always alloc at least 1 temp reg + int free0 = (i||regs->VFwrite||regs->VFread0||regs->VFread1||(regs->VIwrite&(1<vfwrite[1] >= 0 && (itinst->vfread0[0]==itinst->vfwrite[1]||itinst->vfread1[0]==itinst->vfwrite[1]) ) { + itinst->vfflush[i] = _allocTempXMMreg(XMMT_FPS, -1); + } + + if( i == 1 && (regs->VIwrite & (1<VIwrite & (1<vfflush[i] >= 0 ) _freeXMMreg(itinst->vfflush[i]); + if( free0 >= 0 ) _freeXMMreg(free0); + + itinst->vffree[i] = (free0&0xf)|(free1<<8)|(free2<<16); + if( free0 == -1 ) itinst->vffree[i] |= VFFREE_INVALID0; + + _clearNeededXMMregs(); + } + } + + assert( x86Ptr == oldX86 ); + u32 analyzechildren = !(type&BLOCKTYPE_ANALYZED); + type |= BLOCKTYPE_ANALYZED; + + //memset(endregs, 0, sizeof(endregs)); + + if( analyzechildren ) { + FORIT(itchild, blocks) (*itchild)->AssignVFRegs(); + } +} + +struct MARKOVBLANKET +{ + list parents; + list children; +}; + +static MARKOVBLANKET s_markov; + +void VuBaseBlock::AssignVIRegs(int parent) +{ + const int maxregs = 6; + + if( parent ) { + if( (type&BLOCKTYPE_ANALYZEDPARENT) ) + return; + + type |= BLOCKTYPE_ANALYZEDPARENT; + s_markov.parents.push_back(this); + for(LISTBLOCKS::iterator it = blocks.begin(); it != blocks.end(); ++it) { + (*it)->AssignVIRegs(0); + } + return; + } + + if( (type&BLOCKTYPE_ANALYZED) ) + return; + + // child + assert( allocX86Regs == -1 ); + allocX86Regs = s_vecRegArray.size(); + s_vecRegArray.resize(allocX86Regs+X86REGS); + + _x86regs* pregs = &s_vecRegArray[allocX86Regs]; + memset(pregs, 0, sizeof(_x86regs)*X86REGS); + + assert( parents.size() > 0 ); + + list::iterator itparent; + u32 usedvars = insts.front().usedvars[0]; + u32 livevars = insts.front().livevars[0]; + + if( parents.size() > 0 ) { + u32 usedvars2 = 0xffffffff; + FORIT(itparent, parents) usedvars2 &= (*itparent)->insts.front().usedvars[0]; + usedvars |= usedvars2; + } + + usedvars &= livevars; + + // currently order doesn't matter + int num = 0; + + if( usedvars ) { + for(int i = 1; i < 16; ++i) { + if( usedvars & (1<= maxregs ) break; + } + } + } + + if( num < maxregs) { + livevars &= ~usedvars; + livevars &= insts.back().usedvars[0]; + + if( livevars ) { + for(int i = 1; i < 16; ++i) { + if( livevars & (1<= maxregs) break; + } + } + } + } + + s_markov.children.push_back(this); + type |= BLOCKTYPE_ANALYZED; + FORIT(itparent, parents) { + (*itparent)->AssignVIRegs(1); + } +} + +static void SuperVUAssignRegs() +{ + list::iterator itblock, itblock2; + + FORIT(itblock, s_listBlocks) (*itblock)->type &= ~BLOCKTYPE_ANALYZED; + s_listBlocks.front()->AssignVFRegs(); + + // VI assignments, find markov blanket for each node in the graph + // then allocate regs based on the commonly used ones +#ifdef SUPERVU_X86CACHING + FORIT(itblock, s_listBlocks) (*itblock)->type &= ~(BLOCKTYPE_ANALYZED|BLOCKTYPE_ANALYZEDPARENT); + s_vecRegArray.resize(0); + u8 usedregs[16]; + + // note: first block always has to start with no alloc regs + bool bfirst = true; + + FORIT(itblock, s_listBlocks) { + + if( !((*itblock)->type & BLOCKTYPE_ANALYZED) ) { + + if( (*itblock)->parents.size() == 0 ) { + (*itblock)->type |= BLOCKTYPE_ANALYZED; + bfirst = false; + continue; + } + + s_markov.children.clear(); + s_markov.parents.clear(); + (*itblock)->AssignVIRegs(0); + + // assign the regs + int regid = s_vecRegArray.size(); + s_vecRegArray.resize(regid+X86REGS); + + _x86regs* mergedx86 = &s_vecRegArray[regid]; + memset(mergedx86, 0, sizeof(_x86regs)*X86REGS); + + if( !bfirst ) { + *(u32*)usedregs = *((u32*)usedregs+1) = *((u32*)usedregs+2) = *((u32*)usedregs+3) = 0; + + FORIT(itblock2, s_markov.children) { + assert( (*itblock2)->allocX86Regs >= 0 ); + _x86regs* pregs = &s_vecRegArray[(*itblock2)->allocX86Regs]; + for(int i = 0; i < X86REGS; ++i) { + if( pregs[i].inuse && pregs[i].reg < 16) { + //assert( pregs[i].reg < 16); + usedregs[pregs[i].reg]++; + } + } + } + + int num = 1; + for(int i = 0; i < 16; ++i) { + if( usedregs[i] == s_markov.children.size() ) { + // use + mergedx86[num].inuse = 1; + mergedx86[num].reg = i; + mergedx86[num].type = (s_vu?X86TYPE_VU1:0)|X86TYPE_VI; + mergedx86[num].mode = MODE_READ; + if( ++num >= X86REGS ) + break; + if( num == ESP ) + ++num; + } + } + + FORIT(itblock2, s_markov.children) { + assert( (*itblock2)->nStartx86 == -1 ); + (*itblock2)->nStartx86 = regid; + } + + FORIT(itblock2, s_markov.parents) { + assert( (*itblock2)->nEndx86 == -1 ); + (*itblock2)->nEndx86 = regid; + } + } + + bfirst = false; + } + } +#endif +} + +////////////////// +// Recompilation +////////////////// + +// cycles in which the last Q,P regs were finished (written to VU->VI[]) +// the write occurs before the instruction is executed at that cycle +// compare with s_TotalVUCycles +// if less than 0, already flushed +int s_writeQ, s_writeP; + +// declare the saved registers +uptr s_vu1esp, s_callstack;//, s_vu1esp +uptr s_vu1ebp, s_vuebx, s_vuedi, s_vu1esi; + +static int s_recWriteQ, s_recWriteP; // wait times during recompilation +static int s_needFlush; // first bit - Q, second bit - P, third bit - Q has been written, fourth bit - P has been written + +static int s_JumpX86; +static int s_ScheduleXGKICK = 0, s_XGKICKReg = -1; + +void recVUMI_XGKICK_( VURegs *VU ); + +void SuperVUCleanupProgram(u32 startpc, int vuindex) +{ +#ifdef SUPERVU_COUNT + QueryPerformanceCounter(&svufinal); + svutime += (u32)(svufinal.QuadPart-svubase.QuadPart); +#endif + + assert( s_vu1esp == 0 ); + + VU = vuindex ? &VU1 : &VU0; + VU->cycle += s_TotalVUCycles; + if( (int)s_writeQ > 0 ) VU->VI[REG_Q] = VU->q; + if( (int)s_writeP > 0 ) { + assert(VU == &VU1); + VU1.VI[REG_P] = VU1.p; // only VU1 + } + + //memset(recVUStack, 0, SUPERVU_STACKSIZE * 4); + + // Clear allocation info to prevent bad data being used in other parts of pcsx2; doing this just incase (cottonvibes) + _initXMMregs(); + _initMMXregs(); + _initX86regs(); +} + +#if defined(_MSC_VER) + +// entry point of all vu programs from emulator calls +__declspec(naked) void SuperVUExecuteProgram(u32 startpc, int vuindex) +{ +#ifdef SUPERVU_COUNT + QueryPerformanceCounter(&svubase); +#endif + __asm { + mov eax, dword ptr [esp] + mov s_TotalVUCycles, 0 // necessary to be here! + add esp, 4 + mov s_callstack, eax + call SuperVUGetProgram + + // save cpu state + mov s_vu1ebp, ebp + mov s_vu1esi, esi // have to save even in Release + mov s_vuedi, edi // have to save even in Release + mov s_vuebx, ebx + } +#ifdef _DEBUG + __asm { + mov s_vu1esp, esp + } +#endif + + __asm { + //stmxcsr s_ssecsr + ldmxcsr g_sseVUMXCSR + + // init vars + mov s_writeQ, 0xffffffff + mov s_writeP, 0xffffffff + + jmp eax + } +} + +// exit point of all vu programs +__declspec(naked) static void SuperVUEndProgram() +{ + __asm { + // restore cpu state + ldmxcsr g_sseMXCSR + + mov ebp, s_vu1ebp + mov esi, s_vu1esi + mov edi, s_vuedi + mov ebx, s_vuebx + } + +#ifdef _DEBUG + __asm { + sub s_vu1esp, esp + } +#endif + + __asm { + call SuperVUCleanupProgram + jmp s_callstack // so returns correctly + } +} + +#endif + +// Flushes P/Q regs +void SuperVUFlush(int p, int wait) +{ + u8* pjmp[3]; + if( !(s_needFlush&(1<info.cycle < recwait ) return; + + if( recwait == 0 ) { + // write didn't happen this block + MOV32MtoR(EAX, p ? (uptr)&s_writeP : (uptr)&s_writeQ); + OR32RtoR(EAX, EAX); + pjmp[0] = JS8(0); + + if( s_pCurInst->info.cycle ) SUB32ItoR(EAX, s_pCurInst->info.cycle); + + // if writeQ <= total+offset + if( !wait ) { // only write back if time is up + CMP32MtoR(EAX, (uptr)&s_TotalVUCycles); + pjmp[1] = JG8(0); + } + else { + // add (writeQ-total-offset) to s_TotalVUCycles + // necessary? + CMP32MtoR(EAX, (uptr)&s_TotalVUCycles); + pjmp[2] = JLE8(0); + MOV32RtoM((uptr)&s_TotalVUCycles, EAX); + x86SetJ8(pjmp[2]); + } + } + else if( wait && s_pCurInst->info.cycle < recwait ) { + ADD32ItoM((uptr)&s_TotalVUCycles, recwait); + } + + MOV32MtoR(EAX, SuperVUGetVIAddr(p?REG_P:REG_Q, 0)); + MOV32ItoM(p ? (uptr)&s_writeP : (uptr)&s_writeQ, 0x80000000); + MOV32RtoM(SuperVUGetVIAddr(p?REG_P:REG_Q, 1), EAX); + + if( recwait == 0 ) { + if( !wait ) x86SetJ8(pjmp[1]); + x86SetJ8(pjmp[0]); + } + + if( wait || (!p && recwait == 0 && s_pCurInst->info.cycle >= 12) || (!p && recwait > 0 && s_pCurInst->info.cycle >= recwait ) ) + s_needFlush &= ~(1<::iterator itblock; + + FORIT(itblock, s_listBlocks) (*itblock)->type &= ~BLOCKTYPE_ANALYZED; + s_listBlocks.front()->Recompile(); + // make sure everything compiled + FORIT(itblock, s_listBlocks) assert( ((*itblock)->type & BLOCKTYPE_ANALYZED) && (*itblock)->pcode != NULL ); + + // link all blocks + FORIT(itblock, s_listBlocks) { + VuBaseBlock::LISTBLOCKS::iterator itchild; + + assert( (*itblock)->blocks.size() <= ARRAYSIZE((*itblock)->pChildJumps) ); + + int i = 0; + FORIT(itchild, (*itblock)->blocks) { + + if( (u32)(uptr)(*itblock)->pChildJumps[i] == 0xffffffff ) + continue; + + if( (*itblock)->pChildJumps[i] == NULL ) { + VuBaseBlock* pchild = *itchild; + + if( pchild->type & BLOCKTYPE_HASEOP) { + assert( pchild->blocks.size() == 0); + + AND32ItoM( (uptr)&VU0.VI[ REG_VPU_STAT ].UL, s_vu?~0x100:~0x001 ); // E flag + AND32ItoM( (uptr)&VU->vifRegs->stat, ~0x4 ); + + MOV32ItoM((uptr)&VU->VI[REG_TPC], pchild->endpc); + JMP32( (uptr)SuperVUEndProgram - ( (uptr)x86Ptr + 5 )); + } + // only other case is when there are two branches + else assert( (*itblock)->insts.back().regs[0].pipe == VUPIPE_BRANCH ); + + continue; + } + + if( (u32)(uptr)(*itblock)->pChildJumps[i] & 0x80000000 ) { + // relative + assert( (uptr)(*itblock)->pChildJumps[i] <= 0xffffffff); + (*itblock)->pChildJumps[i] = (u32*)((uptr)(*itblock)->pChildJumps[i] & 0x7fffffff); + *(*itblock)->pChildJumps[i] = (uptr)(*itchild)->pcode - ((uptr)(*itblock)->pChildJumps[i] + 4); + } + else *(*itblock)->pChildJumps[i] = (uptr)(*itchild)->pcode; + + ++i; + } + } + + s_pFnHeader->pprogfunc = s_listBlocks.front()->pcode; +} + +// debug + + +u32 s_saveecx, s_saveedx, s_saveebx, s_saveesi, s_saveedi, s_saveebp; +u32 g_curdebugvu; + +//float vuDouble(u32 f); + +#if defined(_MSC_VER) +__declspec(naked) static void svudispfn() +{ + __asm { + mov g_curdebugvu, eax + mov s_saveecx, ecx + mov s_saveedx, edx + mov s_saveebx, ebx + mov s_saveesi, esi + mov s_saveedi, edi + mov s_saveebp, ebp + } +#else +#ifdef __LINUX__ +extern "C" { +#endif +void svudispfn(); +#ifdef __LINUX__ +} +#endif + +void svudispfntemp() +{ +#endif + +#ifdef _DEBUG + static u32 i; + + if( ((vudump&8) && g_curdebugvu) || ((vudump&0x80) && !g_curdebugvu) ) { //&& g_vu1lastrec != g_vu1last ) { + + if( skipparent != g_vu1lastrec ) { + for(i = 0; i < ARRAYSIZE(badaddrs); ++i) { + if( s_svulast == badaddrs[i][1] && g_vu1lastrec == badaddrs[i][0] ) + break; + } + + if( i == ARRAYSIZE(badaddrs) ) + { + //static int curesp; + //__asm mov curesp, esp + //Console::WriteLn("tVU: %x %x %x", s_svulast, s_vucount, s_vufnheader); + if( g_curdebugvu ) iDumpVU1Registers(); + else iDumpVU0Registers(); + s_vucount++; + } + } + + g_vu1lastrec = s_svulast; + } +#endif + +#if defined(_MSC_VER) + __asm { + mov ecx, s_saveecx + mov edx, s_saveedx + mov ebx, s_saveebx + mov esi, s_saveesi + mov edi, s_saveedi + mov ebp, s_saveebp + ret + } +#endif +} + +// frees all regs taking into account the livevars +void SuperVUFreeXMMregs(u32* livevars) +{ + for(int i = 0; i < XMMREGS; ++i) { + if( xmmregs[i].inuse ) { + // same reg + if( (xmmregs[i].mode & MODE_WRITE) ) { + +#ifdef SUPERVU_INTERCACHING + if( xmmregs[i].type == XMMTYPE_VFREG ) { + if( !(livevars[1] & (1<VF[xmmregs[i].reg] : (uptr)&VU->ACC; + + if( xmmregs[i].mode & MODE_VUZ ) { + SSE_MOVHPS_XMM_to_M64(addr, (x86SSERegType)i); + SSE_SHUFPS_M128_to_XMM((x86SSERegType)i, addr, 0xc4); + } + else SSE_MOVHPS_M64_to_XMM((x86SSERegType)i, addr+8); + + xmmregs[i].mode &= ~MODE_VUXYZ; + } + + _freeXMMreg(i); + } + } + } + + //_freeXMMregs(); +} + +void SuperVUTestVU0Condition(u32 incstack) +{ + if( s_vu && !SUPERVU_CHECKCONDITION ) return; // vu0 only + + CMP32ItoM((uptr)&s_TotalVUCycles, 512); // sometimes games spin on vu0, so be careful with this value + // woody hangs if too high + + if( incstack ) { + u8* ptr = JB8(0); + + ADD32ItoR(ESP, incstack); + //CALLFunc((u32)timeout); + JMP32( (uptr)SuperVUEndProgram - ( (uptr)x86Ptr + 5 )); + + x86SetJ8(ptr); + } + else JAE32( (uptr)SuperVUEndProgram - ( (uptr)x86Ptr + 6 ) ); +} + +void VuBaseBlock::Recompile() +{ + if( type & BLOCKTYPE_ANALYZED ) return; + + x86Align(16); + pcode = x86Ptr; + +#ifdef _DEBUG + MOV32ItoM((uptr)&s_vufnheader, s_pFnHeader->startpc); + MOV32ItoM((uptr)&VU->VI[REG_TPC], startpc); + MOV32ItoM((uptr)&s_svulast, startpc); + list::iterator itparent; + for(itparent = parents.begin(); itparent != parents.end(); ++itparent) { + if( (*itparent)->blocks.size()==1 && (*itparent)->blocks.front()->startpc == startpc && + ((*itparent)->insts.size() < 2 || (----(*itparent)->insts.end())->regs[0].pipe != VUPIPE_BRANCH) ) { + MOV32ItoM((uptr)&skipparent, (*itparent)->startpc); + break; + } + } + + if( itparent == parents.end() ) MOV32ItoM((uptr)&skipparent, -1); + + MOV32ItoR(EAX, s_vu); + CALLFunc((uptr)svudispfn); +#endif + + s_pCurBlock = this; + s_needFlush = 3; + pc = startpc; + branch = 0; + s_recWriteQ = s_recWriteP = 0; + s_XGKICKReg = -1; + s_ScheduleXGKICK = 0; + + s_ClipRead = s_PrevClipWrite = (uptr)&VU->VI[REG_CLIP_FLAG]; + s_StatusRead = s_PrevStatusWrite = (uptr)&VU->VI[REG_STATUS_FLAG]; + s_MACRead = s_PrevMACWrite = (uptr)&VU->VI[REG_MAC_FLAG]; + s_PrevIWrite = (uptr)&VU->VI[REG_I]; + s_JumpX86 = 0; + s_UnconditionalDelay = 0; + + memcpy(xmmregs, startregs, sizeof(xmmregs)); +#ifdef SUPERVU_X86CACHING + if( nStartx86 >= 0 ) + memcpy(x86regs, &s_vecRegArray[nStartx86], sizeof(x86regs)); + else _initX86regs(); +#else + _initX86regs(); +#endif + + list::iterator itinst; + FORIT(itinst, insts) { + s_pCurInst = &(*itinst); + if( s_JumpX86 > 0 ) { + if( !x86regs[s_JumpX86].inuse ) { + // load + s_JumpX86 = _allocX86reg(-1, X86TYPE_VUJUMP, 0, MODE_READ); + } + x86regs[s_JumpX86].needed = 1; + } + + if( s_ScheduleXGKICK && s_XGKICKReg > 0 ) { + assert( x86regs[s_XGKICKReg].inuse ); + x86regs[s_XGKICKReg].needed = 1; + } + itinst->Recompile(itinst, vuxyz); + + if( s_ScheduleXGKICK > 0 ) { + if( s_ScheduleXGKICK-- == 1 ) { + recVUMI_XGKICK_(VU); + } + } + } + assert( pc == endpc ); + assert( s_ScheduleXGKICK == 0 ); + + // flush flags + if( s_PrevClipWrite != (uptr)&VU->VI[REG_CLIP_FLAG] ) { + MOV32MtoR(EAX, s_PrevClipWrite); + MOV32RtoM((uptr)&VU->VI[REG_CLIP_FLAG], EAX); + } + if( s_PrevStatusWrite != (uptr)&VU->VI[REG_STATUS_FLAG] ) { + MOV32MtoR(EAX, s_PrevStatusWrite); + MOV32RtoM((uptr)&VU->VI[REG_STATUS_FLAG], EAX); + } + if( s_PrevMACWrite != (uptr)&VU->VI[REG_MAC_FLAG] ) { + MOV32MtoR(EAX, s_PrevMACWrite); + MOV32RtoM((uptr)&VU->VI[REG_MAC_FLAG], EAX); + } +// if( s_StatusRead != (uptr)&VU->VI[REG_STATUS_FLAG] ) { +// // only lower 8 bits valid! +// MOVZX32M8toR(EAX, s_StatusRead); +// MOV32RtoM((uptr)&VU->VI[REG_STATUS_FLAG], EAX); +// } +// if( s_MACRead != (uptr)&VU->VI[REG_MAC_FLAG] ) { +// // only lower 8 bits valid! +// MOVZX32M8toR(EAX, s_MACRead); +// MOV32RtoM((uptr)&VU->VI[REG_MAC_FLAG], EAX); +// } + if( s_PrevIWrite != (uptr)&VU->VI[REG_I] ) { + MOV32ItoM((uptr)&VU->VI[REG_I], *(u32*)s_PrevIWrite); // never changes + } + + ADD32ItoM((uptr)&s_TotalVUCycles, cycles); + + // compute branches, jumps, eop + if( type & BLOCKTYPE_HASEOP ) { + // end + _freeXMMregs(); + _freeX86regs(); + AND32ItoM( (uptr)&VU0.VI[ REG_VPU_STAT ].UL, s_vu?~0x100:~0x001 ); // E flag + AND32ItoM( (uptr)&VU->vifRegs->stat, ~0x4 ); + if( !branch ) MOV32ItoM((uptr)&VU->VI[REG_TPC], endpc); + JMP32( (uptr)SuperVUEndProgram - ( (uptr)x86Ptr + 5 )); + } + else { + + u32 livevars[2] = {0}; + + list::iterator lastinst = GetInstIterAtPc(endpc-8); + lastinst++; + + if( lastinst != insts.end() ) { + livevars[0] = lastinst->livevars[0]; + livevars[1] = lastinst->livevars[1]; + } + else { + // take from children + if( blocks.size() > 0 ) { + LISTBLOCKS::iterator itchild; + FORIT(itchild, blocks) { + livevars[0] |= (*itchild)->insts.front().livevars[0]; + livevars[1] |= (*itchild)->insts.front().livevars[1]; + } + } + else { + livevars[0] = ~0; + livevars[1] = ~0; + } + } + + SuperVUFreeXMMregs(livevars); + + // get rid of any writes, otherwise _freeX86regs will write + x86regs[s_JumpX86].mode &= ~MODE_WRITE; + + if( branch == 1 ) { + if( !x86regs[s_JumpX86].inuse ) { + assert( x86regs[s_JumpX86].type == X86TYPE_VUJUMP ); + s_JumpX86 = 0xffffffff; // notify to jump from g_recWriteback + } + } + + // align VI regs +#ifdef SUPERVU_X86CACHING + if( nEndx86 >= 0 ) { + _x86regs* endx86 = &s_vecRegArray[nEndx86]; + for(int i = 0; i < X86REGS; ++i) { + if( endx86[i].inuse ) { + + if( s_JumpX86 == i && x86regs[s_JumpX86].inuse ) { + x86regs[s_JumpX86].inuse = 0; + x86regs[EAX].inuse = 1; + MOV32RtoR(EAX, s_JumpX86); + s_JumpX86 = EAX; + } + + if( x86regs[i].inuse ) { + if( x86regs[i].type == endx86[i].type && x86regs[i].reg == endx86[i].reg ) { + _freeX86reg(i); + // will continue to use it + continue; + } + + if( x86regs[i].type == (X86TYPE_VI|(s_vu?X86TYPE_VU1:0)) ) { +#ifdef SUPERVU_INTERCACHING + if( livevars[0] & (1<startpc); + + switch(branch) { + case 1: // branch, esi has new prog + + SuperVUTestVU0Condition(0); + + if( s_JumpX86 == 0xffffffff ) + JMP32M((uptr)&g_recWriteback); + else + JMPR(s_JumpX86); + + break; + case 4: // jalr + pChildJumps[0] = (u32*)0xffffffff; + // fall through + + case 0x10: // jump, esi has new vupc + { + _freeXMMregs(); + _freeX86regs(); + + SuperVUTestVU0Condition(8); + + // already onto stack + CALLFunc((uptr)SuperVUGetProgram); + ADD32ItoR(ESP, 8); + JMPR(EAX); + break; + } + + case 0x13: // jr with uncon branch, uncond branch takes precendence (dropship) + { +// s32 delta = (s32)(VU->code & 0x400 ? 0xfffffc00 | (VU->code & 0x3ff) : VU->code & 0x3ff) << 3; +// ADD32ItoRmOffset(ESP, delta, 0); + + ADD32ItoR(ESP, 8); // restore + pChildJumps[0] = (u32*)((uptr)JMP32(0)|0x80000000); + break; + } + case 0: + case 3: // unconditional branch + pChildJumps[s_UnconditionalDelay] = (u32*)((uptr)JMP32(0)|0x80000000); + break; + + default: + DevCon::Error("Bad branch %x\n", params branch); + assert(0); + break; + } + } + + pendcode = x86Ptr; + type |= BLOCKTYPE_ANALYZED; + + LISTBLOCKS::iterator itchild; + FORIT(itchild, blocks) { + (*itchild)->Recompile(); + } +} + +#define GET_VUXYZMODE(reg) 0//((vuxyz&(1<<(reg)))?MODE_VUXYZ:0) + +int VuInstruction::SetCachedRegs(int upper, u32 vuxyz) +{ + if( vfread0[upper] >= 0 ) { + SuperVUFreeXMMreg(vfread0[upper], XMMTYPE_VFREG, regs[upper].VFread0); + _allocVFtoXMMreg(VU, vfread0[upper], regs[upper].VFread0, MODE_READ|GET_VUXYZMODE(regs[upper].VFread0)); + } + if( vfread1[upper] >= 0 ) { + SuperVUFreeXMMreg(vfread1[upper], XMMTYPE_VFREG, regs[upper].VFread1); + _allocVFtoXMMreg(VU, vfread1[upper], regs[upper].VFread1, MODE_READ|GET_VUXYZMODE(regs[upper].VFread1)); + } + if( vfacc[upper] >= 0 && (regs[upper].VIread&(1<= 0 ) { + assert( regs[upper].VFwrite > 0); + SuperVUFreeXMMreg(vfwrite[upper], XMMTYPE_VFREG, regs[upper].VFwrite); + _allocVFtoXMMreg(VU, vfwrite[upper], regs[upper].VFwrite, + MODE_WRITE|(regs[upper].VFwxyzw != 0xf?MODE_READ:0)|GET_VUXYZMODE(regs[upper].VFwrite)); + } + if( vfacc[upper] >= 0 && (regs[upper].VIwrite&(1<= 0 ) info |= PROCESS_EE_SET_S(vfread0[upper]); + if( vfread1[upper] >= 0 ) info |= PROCESS_EE_SET_T(vfread1[upper]); + if( vfacc[upper] >= 0 ) info |= PROCESS_VU_SET_ACC(vfacc[upper]); + if( vfwrite[upper] >= 0 ) { + if( regs[upper].VFwrite == _Ft_ && vfread1[upper] < 0 ) { + info |= PROCESS_EE_SET_T(vfwrite[upper]); + } + else { + assert( regs[upper].VFwrite == _Fd_ ); + info |= PROCESS_EE_SET_D(vfwrite[upper]); + } + } + + if( !(vffree[upper]&VFFREE_INVALID0) ) { + SuperVUFreeXMMreg(vffree[upper]&0xf, XMMTYPE_TEMP, 0); + _allocTempXMMreg(XMMT_FPS, vffree[upper]&0xf); + } + info |= PROCESS_VU_SET_TEMP(vffree[upper]&0xf); + + if( vfflush[upper] >= 0 ) { + SuperVUFreeXMMreg(vfflush[upper], XMMTYPE_TEMP, 0); + _allocTempXMMreg(XMMT_FPS, vfflush[upper]); + } + + if( upper && (regs[upper].VIwrite & (1 << REG_CLIP_FLAG)) ) { + // CLIP inst, need two extra temp registers, put it EEREC_D and EEREC_ACC + assert( vfwrite[upper] == -1 ); + SuperVUFreeXMMreg((vffree[upper]>>8)&0xf, XMMTYPE_TEMP, 0); + _allocTempXMMreg(XMMT_FPS, (vffree[upper]>>8)&0xf); + info |= PROCESS_EE_SET_D((vffree[upper]>>8)&0xf); + + SuperVUFreeXMMreg((vffree[upper]>>16)&0xf, XMMTYPE_TEMP, 0); + _allocTempXMMreg(XMMT_FPS, (vffree[upper]>>16)&0xf); + info |= PROCESS_EE_SET_ACC((vffree[upper]>>16)&0xf); + + _freeXMMreg((vffree[upper]>>8)&0xf); // don't need anymore + _freeXMMreg((vffree[upper]>>16)&0xf); // don't need anymore + } + else if( regs[upper].VIwrite & (1<>8)&0xf, XMMTYPE_TEMP, 0); + _allocTempXMMreg(XMMT_FPS, (vffree[upper]>>8)&0xf); + info |= PROCESS_EE_SET_D((vffree[upper]>>8)&0xf); + _freeXMMreg((vffree[upper]>>8)&0xf); // don't need anymore + } + + if( vfflush[upper] >= 0 ) _freeXMMreg(vfflush[upper]); + if( !(vffree[upper]&VFFREE_INVALID0) ) + _freeXMMreg(vffree[upper]&0xf); // don't need anymore + + if( (regs[0].VIwrite|regs[1].VIwrite) & ((1<::iterator& itinst, u32 vuxyz) +{ + static PCSX2_ALIGNED16(VECTOR _VF); + static PCSX2_ALIGNED16(VECTOR _VFc); + u32 *ptr; + u8* pjmp; + int vfregstore=0; + + assert( s_pCurInst == this); + s_WriteToReadQ = 0; + + ptr = (u32*)&VU->Micro[ pc ]; + + if( type & INST_Q_READ ) + SuperVUFlush(0, (ptr[0] == 0x800003bf)||!!(regs[0].VIwrite & (1<startpc || nParentPc >= (int)pc) ) { + +// if( !s_vu ) { +// for(int j = 0; j < ARRAYSIZE(badaddrs); ++j) { +// if( badaddrs[j] == nParentPc ) +// goto NoParent; +// } +// } + + list::iterator itblock; + FORIT(itblock, s_listBlocks) { + if( nParentPc >= (*itblock)->startpc && nParentPc < (*itblock)->endpc ) { + pparentinst = &(*(*itblock)->GetInstIterAtPc(nParentPc)); + //if( !s_vu ) SysPrintf("%x ", nParentPc); + if( find(s_pCurBlock->parents.begin(), s_pCurBlock->parents.end(), *itblock) != s_pCurBlock->parents.end() ) + nParentCheckForExecution = (*itblock)->startpc; + break; + } + } + + assert( pparentinst != NULL ); + } +#endif + + if( type & INST_CLIP_WRITE ) { + if( nParentPc < s_pCurBlock->startpc || nParentPc >= (int)pc ) + // reading from out of this block, so already flushed to mem + s_ClipRead = (uptr)&VU->VI[REG_CLIP_FLAG]; + else { + s_ClipRead = s_pCurBlock->GetInstIterAtPc(nParentPc)->pClipWrite; + if (s_ClipRead == 0) SysPrintf("super ClipRead allocation error! \n"); + } + } + + // before modifying, check if they will ever be read + if( s_pCurBlock->type & BLOCKTYPE_MACFLAGS ) { + + u8 outofblock=0; + if( type & INST_STATUS_WRITE ) { + + if( nParentPc < s_pCurBlock->startpc || nParentPc >= (int)pc ) { + + // reading from out of this block, so already flushed to mem + if( pparentinst != NULL ) { //&& pparentinst->pStatusWrite != NULL ) { + + // might not have processed it yet, so reserve a mem loc + if( pparentinst->pStatusWrite == 0 ) { + pparentinst->pStatusWrite = (uptr)SuperVUStaticAlloc(4); + //MOV32ItoM(pparentinst->pStatusWrite, 0); + } + +// if( s_pCurBlock->prevFlagsOutOfBlock && s_StatusRead != NULL ) { +// // or instead since don't now which parent we came from +// MOV32MtoR(EAX, pparentinst->pStatusWrite); +// OR32RtoM(s_StatusRead, EAX); +// MOV32ItoM(pparentinst->pStatusWrite, 0); +// } + + if( nParentCheckForExecution >= 0 ) { + + // don't now which parent we came from, so have to check +// uptr tempstatus = (uptr)SuperVUStaticAlloc(4); +// if( s_StatusRead != NULL ) +// MOV32MtoR(EAX, s_StatusRead); +// else +// MOV32MtoR(EAX, (uptr)&VU->VI[REG_STATUS_FLAG]); +// s_StatusRead = tempstatus; + if( s_StatusRead == 0 ) + s_StatusRead = (uptr)&VU->VI[REG_STATUS_FLAG]; + + CMP32ItoM((uptr)&g_nLastBlockExecuted, nParentCheckForExecution); + u8* jptr = JNE8(0); + MOV32MtoR(EAX, pparentinst->pStatusWrite); + MOV32ItoM(pparentinst->pStatusWrite, 0); + MOV32RtoM(s_StatusRead, EAX); + x86SetJ8(jptr); + } + else { + uptr tempstatus = (uptr)SuperVUStaticAlloc(4); + MOV32MtoR(EAX, pparentinst->pStatusWrite); + MOV32RtoM(tempstatus, EAX); + MOV32ItoM(pparentinst->pStatusWrite, 0); + s_StatusRead = tempstatus; + } + + outofblock = 2; + } + else + s_StatusRead = (uptr)&VU->VI[REG_STATUS_FLAG]; + } + else { + s_StatusRead = s_pCurBlock->GetInstIterAtPc(nParentPc)->pStatusWrite; + if (s_StatusRead == 0) SysPrintf("super StatusRead allocation error! \n"); +// if( pc >= (u32)s_pCurBlock->endpc-8 ) { +// // towards the end, so variable might be leaded to another block (silent hill 4) +// uptr tempstatus = (uptr)SuperVUStaticAlloc(4); +// MOV32MtoR(EAX, s_StatusRead); +// MOV32RtoM(tempstatus, EAX); +// MOV32ItoM(s_StatusRead, 0); +// s_StatusRead = tempstatus; +// } + } + } + if( type & INST_MAC_WRITE ) { + + if( nParentPc < s_pCurBlock->startpc || nParentPc >= (int)pc ) { + // reading from out of this block, so already flushed to mem + + if( pparentinst != NULL ) {//&& pparentinst->pMACWrite != NULL ) { + // necessary for (katamari) + // towards the end, so variable might be leaked to another block (silent hill 4) + + // might not have processed it yet, so reserve a mem loc + if( pparentinst->pMACWrite == 0 ) { + pparentinst->pMACWrite = (uptr)SuperVUStaticAlloc(4); + //MOV32ItoM(pparentinst->pMACWrite, 0); + } + +// if( s_pCurBlock->prevFlagsOutOfBlock && s_MACRead != NULL ) { +// // or instead since don't now which parent we came from +// MOV32MtoR(EAX, pparentinst->pMACWrite); +// OR32RtoM(s_MACRead, EAX); +// MOV32ItoM(pparentinst->pMACWrite, 0); +// } + if( nParentCheckForExecution >= 0 ) { + + // don't now which parent we came from, so have to check +// uptr tempmac = (uptr)SuperVUStaticAlloc(4); +// if( s_MACRead != NULL ) +// MOV32MtoR(EAX, s_MACRead); +// else +// MOV32MtoR(EAX, (uptr)&VU->VI[REG_MAC_FLAG]); +// s_MACRead = tempmac; + + if( s_MACRead == 0 ) + s_MACRead = (uptr)&VU->VI[REG_MAC_FLAG]; + + CMP32ItoM((uptr)&g_nLastBlockExecuted, nParentCheckForExecution); + u8* jptr = JNE8(0); + MOV32MtoR(EAX, pparentinst->pMACWrite); + MOV32ItoM(pparentinst->pMACWrite, 0); + MOV32RtoM(s_MACRead, EAX); + x86SetJ8(jptr); + } + else { + uptr tempMAC = (uptr)SuperVUStaticAlloc(4); + MOV32MtoR(EAX, pparentinst->pMACWrite); + MOV32RtoM(tempMAC, EAX); + MOV32ItoM(pparentinst->pMACWrite, 0); + s_MACRead = tempMAC; + } + + outofblock = 2; + } + else + s_MACRead = (uptr)&VU->VI[REG_MAC_FLAG]; + +// if( pc >= (u32)s_pCurBlock->endpc-8 ) { +// // towards the end, so variable might be leaked to another block (silent hill 4) +// uptr tempMAC = (uptr)SuperVUStaticAlloc(4); +// MOV32MtoR(EAX, s_MACRead); +// MOV32RtoM(tempMAC, EAX); +// MOV32ItoM(s_MACRead, 0); +// s_MACRead = tempMAC; +// } + } + else { + s_MACRead = s_pCurBlock->GetInstIterAtPc(nParentPc)->pMACWrite; + } + } + + s_pCurBlock->prevFlagsOutOfBlock = outofblock; + } + else if( pparentinst != NULL) { + // make sure to reset the mac and status flags! (katamari) + if( pparentinst->pStatusWrite) + MOV32ItoM(pparentinst->pStatusWrite, 0); + if( pparentinst->pMACWrite) + MOV32ItoM(pparentinst->pMACWrite, 0); + } + + assert( s_ClipRead != 0 ); + assert( s_MACRead != 0 ); + assert( s_StatusRead != 0 ); + + return; + } + + s_pCurBlock->prevFlagsOutOfBlock = 0; + +#ifdef _DEBUG + MOV32ItoR(EAX, pc); +#endif + + assert( !(type & (INST_CLIP_WRITE|INST_STATUS_WRITE|INST_MAC_WRITE)) ); + pc += 8; + + list::const_iterator itinst2; + + if( (regs[0].VIwrite|regs[1].VIwrite) & ((1<type & BLOCKTYPE_MACFLAGS ) { + if( pMACWrite == 0 ) { + pMACWrite = (uptr)SuperVUStaticAlloc(4); + //MOV32ItoM(pMACWrite, 0); + } + if( pStatusWrite == 0 ) { + pStatusWrite = (uptr)SuperVUStaticAlloc(4); + //MOV32ItoM(pStatusWrite, 0); + } + } + else { + assert( s_StatusRead == (uptr)&VU->VI[REG_STATUS_FLAG] ); + assert( s_MACRead == (uptr)&VU->VI[REG_MAC_FLAG] ); + pMACWrite = s_MACRead; + pStatusWrite = s_StatusRead; + } + } + + if( (pClipWrite == 0) && ((regs[0].VIwrite|regs[1].VIwrite) & (1<insts.end() ) { + if( (itinst2->regs[0].VIread|itinst2->regs[0].VIwrite|itinst2->regs[1].VIread|itinst2->regs[1].VIwrite) && (1<flags, VUFLAG_MFLAGSET); + } + if (ptr[1] & 0x10000000) { // D flag + TEST32ItoM((uptr)&VU0.VI[REG_FBRST].UL, s_vu?0x400:0x004); + u8* ptr = JZ8(0); + OR32ItoM((uptr)&VU0.VI[REG_VPU_STAT].UL, s_vu?0x200:0x002); + _callFunctionArg1((uptr)hwIntcIrq, MEM_CONSTTAG, s_vu?INTC_VU1:INTC_VU0); + x86SetJ8(ptr); + } + if (ptr[1] & 0x08000000) { // T flag + TEST32ItoM((uptr)&VU0.VI[REG_FBRST].UL, s_vu?0x800:0x008); + u8* ptr = JZ8(0); + OR32ItoM((uptr)&VU0.VI[REG_VPU_STAT].UL, s_vu?0x400:0x004); + _callFunctionArg1((uptr)hwIntcIrq, MEM_CONSTTAG, s_vu?INTC_VU1:INTC_VU0); + x86SetJ8(ptr); + } + + // check upper flags + if (ptr[1] & 0x80000000) { // I flag + + assert( !(regs[0].VIwrite & ((1<code = ptr[1]; + s_vuInfo = SetCachedRegs(1, vuxyz); + if( s_JumpX86 > 0 ) x86regs[s_JumpX86].needed = 1; + if( s_ScheduleXGKICK && s_XGKICKReg > 0 ) x86regs[s_XGKICKReg].needed = 1; + + recVU_UPPER_OPCODE[ VU->code & 0x3f ]( VU, s_vuInfo ); + + s_PrevIWrite = (uptr)ptr; + _clearNeededXMMregs(); + _clearNeededX86regs(); + } + else { + if( regs[0].VIwrite & (1<insts.end()); + u32* codeptr2 = ptr+2; + + while(itinst2 != s_pCurBlock->insts.end() ) { + if( !(itinst2->type & INST_DUMMY) && ((itinst2->regs[0].VIwrite&(1<type & INST_Q_WRITE) && itinst2->nParentPc == pc-8 ) { + break; + } + if( itinst2->type & INST_Q_READ ) { + cacheq = 1; + break; + } + if( itinst2->type & INST_DUMMY ) { + ++itinst2; + continue; + } + codeptr2 += 2; + ++itinst2; + } + + if( itinst2 == s_pCurBlock->insts.end() ) + cacheq = 1; + + int x86temp = -1; + if( cacheq ) + x86temp = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); + + // new is written so flush old + // if type & INST_Q_READ, already flushed + if( !(type & INST_Q_READ) && s_recWriteQ == 0 ) MOV32MtoR(EAX, (uptr)&s_writeQ); + + if( cacheq ) + MOV32MtoR(x86temp, (uptr)&s_TotalVUCycles); + + if( !(type & INST_Q_READ) ) { + if( s_recWriteQ == 0 ) { + OR32RtoR(EAX, EAX); + pjmp = JS8(0); + MOV32MtoR(EAX, SuperVUGetVIAddr(REG_Q, 0)); + MOV32RtoM(SuperVUGetVIAddr(REG_Q, 1), EAX); + x86SetJ8(pjmp); + } + else if( s_needFlush & 1 ) { + MOV32MtoR(EAX, SuperVUGetVIAddr(REG_Q, 0)); + MOV32RtoM(SuperVUGetVIAddr(REG_Q, 1), EAX); + s_needFlush &= ~1; + } + } + + // write new Q + if( cacheq ) { + assert(s_pCurInst->pqcycles>1); + ADD32ItoR(x86temp, s_pCurInst->info.cycle+s_pCurInst->pqcycles); + MOV32RtoM((uptr)&s_writeQ, x86temp); + s_needFlush |= 1; + } + else { + // won't be writing back + s_WriteToReadQ = 1; + s_needFlush &= ~1; + MOV32ItoM((uptr)&s_writeQ, 0x80000001); + } + + s_recWriteQ = s_pCurInst->info.cycle+s_pCurInst->pqcycles; + + if( x86temp >= 0 ) + _freeX86reg(x86temp); + } + + if( regs[0].VIwrite & (1<pqcycles>1); + ADD32ItoR(x86temp, s_pCurInst->info.cycle+s_pCurInst->pqcycles); + MOV32RtoM((uptr)&s_writeP, x86temp); + s_needFlush |= 2; + + s_recWriteP = s_pCurInst->info.cycle+s_pCurInst->pqcycles; + + _freeX86reg(x86temp); + } + + if( ptr[0] == 0x800003bf ) // waitq + SuperVUFlush(0, 1); + + if( ptr[0] == 0x800007bf ) // waitp + SuperVUFlush(1, 1); + +#ifdef PCSX2_DEVBUILD + if ( regs[1].VIread & regs[0].VIwrite & ~((1<startpc); + } +#endif + + u32 modewrite = 0; + if( vfwrite[1] >= 0 && xmmregs[vfwrite[1]].inuse && xmmregs[vfwrite[1]].type == XMMTYPE_VFREG && xmmregs[vfwrite[1]].reg == regs[1].VFwrite ) + modewrite = xmmregs[vfwrite[1]].mode & MODE_WRITE; + + VU->code = ptr[1]; + s_vuInfo = SetCachedRegs(1, vuxyz); + + if (vfwrite[1] >= 0) { + assert( regs[1].VFwrite > 0 ); + + if (vfwrite[0] == vfwrite[1]) { + //SysPrintf("*PCSX2*: Warning, VF write to the same reg in both lower/upper cycle %x\n", s_pCurBlock->startpc); + } + + if (vfread0[0] == vfwrite[1] || vfread1[0] == vfwrite[1] ) { + assert( regs[0].VFread0 == regs[1].VFwrite || regs[0].VFread1 == regs[1].VFwrite ); + assert( vfflush[0] >= 0 ); + if( modewrite ) { + SSE_MOVAPS_XMM_to_M128((uptr)&VU->VF[regs[1].VFwrite], (x86SSERegType)vfwrite[1]); + } + vfregstore = 1; + } + } + + if( s_JumpX86 > 0 ) x86regs[s_JumpX86].needed = 1; + if( s_ScheduleXGKICK && s_XGKICKReg > 0 ) x86regs[s_XGKICKReg].needed = 1; + + recVU_UPPER_OPCODE[ VU->code & 0x3f ]( VU, s_vuInfo ); + _clearNeededXMMregs(); + _clearNeededX86regs(); + + // necessary because status can be set by both upper and lower + if( regs[1].VIwrite & (1<code = ptr[0]; + s_vuInfo = SetCachedRegs(0, vuxyz); + + if( vfregstore ) { + // load + SSE_MOVAPS_M128_to_XMM(vfflush[0], (uptr)&VU->VF[regs[1].VFwrite]); + + assert( xmmregs[vfwrite[1]].mode & MODE_WRITE ); + + // replace with vfflush + if( _Fs_ == regs[1].VFwrite ) { + s_vuInfo &= ~PROCESS_EE_SET_S(0xf); + s_vuInfo |= PROCESS_EE_SET_S(vfflush[0]); + } + if( _Ft_ == regs[1].VFwrite ) { + s_vuInfo &= ~PROCESS_EE_SET_T(0xf); + s_vuInfo |= PROCESS_EE_SET_T(vfflush[0]); + } + + xmmregs[vfflush[0]].mode |= MODE_NOFLUSH|MODE_WRITE; // so that lower inst doesn't flush + } + + // notify vuinsts that upper inst is a fmac + if( regs[1].pipe == VUPIPE_FMAC ) + s_vuInfo |= PROCESS_VU_SET_FMAC(); + + if( s_JumpX86 > 0 ) x86regs[s_JumpX86].needed = 1; + if( s_ScheduleXGKICK && s_XGKICKReg > 0 ) x86regs[s_XGKICKReg].needed = 1; + +#ifdef SUPERVU_VIBRANCHDELAY + if ( (!CHECK_VUBRANCHHACK) && (type & INST_CACHE_VI) ) { + assert( vicached >= 0 ); + int cachedreg = _allocX86reg(-1, X86TYPE_VI|(s_vu?X86TYPE_VU1:0), vicached, MODE_READ); + MOV32RtoM((uptr)&s_VIBranchDelay, cachedreg); + } +#endif + + // check if inst before branch and the write is the same as the read in the branch (wipeout) +// int oldreg=0; +// if( pc == s_pCurBlock->endpc-16 ) { +// itinst2 = itinst; ++itinst2; +// if( itinst2->regs[0].pipe == VUPIPE_BRANCH && (itinst->regs[0].VIwrite&itinst2->regs[0].VIread) ) { +// +// CALLFunc((u32)branchfn); +// assert( itinst->regs[0].VIwrite & 0xffff ); +// SysPrintf("vi write before branch\n"); +// for(s_CacheVIReg = 0; s_CacheVIReg < 16; ++s_CacheVIReg) { +// if( itinst->regs[0].VIwrite & (1<endpc-8 && s_CacheVIReg >= 0 ) { +// assert( s_CacheVIX86 > 0 && x86regs[s_CacheVIX86].inuse && x86regs[s_CacheVIX86].reg == s_CacheVIReg && x86regs[s_CacheVIX86].type == X86TYPE_VITEMP ); +// +// oldreg = _allocX86reg(-1, X86TYPE_VI|(s_vu?X86TYPE_VU1:0), s_CacheVIReg, MODE_READ); +// x86regs[s_CacheVIX86].needed = 1; +// assert( x86regs[oldreg].mode & MODE_WRITE ); +// +// x86regs[s_CacheVIX86].type = X86TYPE_VI|(s_vu?X86TYPE_VU1:0); +// x86regs[oldreg].type = X86TYPE_VITEMP; +// } + + recVU_LOWER_OPCODE[ VU->code >> 25 ]( VU, s_vuInfo ); + +// if( pc == s_pCurBlock->endpc-8 && s_CacheVIReg >= 0 ) { +// // revert +// x86regs[s_CacheVIX86].inuse = 0; +// x86regs[oldreg].type = X86TYPE_VI|(s_vu?X86TYPE_VU1:0); +// } + + _clearNeededXMMregs(); + _clearNeededX86regs(); + } + + // clip is always written so ok + if( (regs[0].VIwrite|regs[1].VIwrite) & (1<code); + int curjump = 0; + + if( s_pCurInst->type & INST_BRANCH_DELAY ) { + assert( (branch&0x17)!=0x10 && (branch&0x17)!=4 ); // no jump handlig for now + + if( (branch & 0x7) == 3 ) { + // previous was a direct jump + curjump = 1; + } + else if( branch & 1 ) curjump = 2; + } + + assert( s_JumpX86 > 0 ); + + if( (s_pCurBlock->type & BLOCKTYPE_HASEOP) || s_vu == 0 || SUPERVU_CHECKCONDITION) + MOV32ItoM(SuperVUGetVIAddr(REG_TPC, 0), bpc); + MOV32ItoR(s_JumpX86, 0); + s_pCurBlock->pChildJumps[curjump] = (u32*)x86Ptr-1; + + if( !(s_pCurInst->type & INST_BRANCH_DELAY) ) { + j8Ptr[1] = JMP8(0); + x86SetJ8( j8Ptr[ 0 ] ); + + if( (s_pCurBlock->type & BLOCKTYPE_HASEOP) || s_vu == 0 || SUPERVU_CHECKCONDITION ) + MOV32ItoM(SuperVUGetVIAddr(REG_TPC, 0), pc+8); + MOV32ItoR(s_JumpX86, 0); + s_pCurBlock->pChildJumps[curjump+1] = (u32*)x86Ptr-1; + + x86SetJ8( j8Ptr[ 1 ] ); + } + else + x86SetJ8( j8Ptr[ 0 ] ); + + branch |= 1; +} + +// supervu specific insts +void recVUMI_IBQ_prep() +{ + int fsreg, ftreg; + + if( _Fs_ == 0 ) { +#ifdef SUPERVU_VIBRANCHDELAY + if( s_pCurInst->vicached >= 0 && s_pCurInst->vicached == _Ft_ ) { + ftreg = -1; + } + else +#endif + { + ftreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Ft_, MODE_READ); + } + + s_JumpX86 = _allocX86reg(-1, X86TYPE_VUJUMP, 0, MODE_WRITE); + + if( ftreg >= 0 ) { + CMP16ItoR( ftreg, 0 ); + } + else CMP16ItoM(SuperVUGetVIAddr(_Ft_, 1), 0); + } + else if( _Ft_ == 0 ) { +#ifdef SUPERVU_VIBRANCHDELAY + if( s_pCurInst->vicached >= 0 && s_pCurInst->vicached == _Fs_ ) { + fsreg = -1; + } + else +#endif + { + fsreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_READ); + } + + s_JumpX86 = _allocX86reg(-1, X86TYPE_VUJUMP, 0, MODE_WRITE); + + if( fsreg >= 0 ) { + CMP16ItoR( fsreg, 0 ); + } + else CMP16ItoM(SuperVUGetVIAddr(_Fs_, 1), 0); + + } + else { + _addNeededX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Ft_); + +#ifdef SUPERVU_VIBRANCHDELAY + if( s_pCurInst->vicached >= 0 && s_pCurInst->vicached == _Fs_ ) { + fsreg = -1; + } + else +#endif + { + fsreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_READ); + } + +#ifdef SUPERVU_VIBRANCHDELAY + if( s_pCurInst->vicached >= 0 && s_pCurInst->vicached == _Ft_ ) { + ftreg = -1; + + if( fsreg <= 0 ) { + // allocate fsreg + if( s_pCurInst->vicached >= 0 && s_pCurInst->vicached == _Fs_ ) { + fsreg = _allocX86reg(-1, X86TYPE_TEMP, 0, MODE_READ|MODE_WRITE); + MOV32MtoR(fsreg, SuperVUGetVIAddr(_Fs_, 1)); + } + else + fsreg = _allocX86reg(-1, X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_READ); + } + } + else +#endif + { + ftreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Ft_, MODE_READ); + } + + s_JumpX86 = _allocX86reg(-1, X86TYPE_VUJUMP, 0, MODE_WRITE); + + if( fsreg >= 0 ) { + if( ftreg >= 0 ) { + CMP16RtoR( fsreg, ftreg ); + } + else CMP16MtoR(fsreg, SuperVUGetVIAddr(_Ft_, 1)); + } + else if( ftreg >= 0 ) { + CMP16MtoR(ftreg, SuperVUGetVIAddr(_Fs_, 1)); + } + else { + fsreg = _allocX86reg(-1, X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_READ); + CMP16MtoR(fsreg, SuperVUGetVIAddr(_Ft_, 1)); + } + } +} + +void recVUMI_IBEQ( VURegs* vuu, s32 info ) +{ + recVUMI_IBQ_prep(); + j8Ptr[ 0 ] = JNE8( 0 ); + recVUMI_BranchHandle(); +} + +void recVUMI_IBGEZ( VURegs* vuu, s32 info ) +{ + int fsreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_READ); + s_JumpX86 = _allocX86reg(-1, X86TYPE_VUJUMP, 0, MODE_WRITE); + + if( fsreg >= 0 ) { + OR16RtoR(fsreg, fsreg); + j8Ptr[ 0 ] = JS8( 0 ); + } + else { + CMP16ItoM( SuperVUGetVIAddr(_Fs_, 1), 0x0 ); + j8Ptr[ 0 ] = JL8( 0 ); + } + + recVUMI_BranchHandle(); +} + +void recVUMI_IBGTZ( VURegs* vuu, s32 info ) +{ + int fsreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_READ); + s_JumpX86 = _allocX86reg(-1, X86TYPE_VUJUMP, 0, MODE_WRITE); + + if( fsreg >= 0 ) { + CMP16ItoR(fsreg, 0); + j8Ptr[ 0 ] = JLE8( 0 ); + } + else { + CMP16ItoM( SuperVUGetVIAddr(_Fs_, 1), 0x0 ); + j8Ptr[ 0 ] = JLE8( 0 ); + } + recVUMI_BranchHandle(); +} + +void recVUMI_IBLEZ( VURegs* vuu, s32 info ) +{ + int fsreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_READ); + s_JumpX86 = _allocX86reg(-1, X86TYPE_VUJUMP, 0, MODE_WRITE); + + if( fsreg >= 0 ) { + CMP16ItoR(fsreg, 0); + j8Ptr[ 0 ] = JG8( 0 ); + } + else { + CMP16ItoM( SuperVUGetVIAddr(_Fs_, 1), 0x0 ); + j8Ptr[ 0 ] = JG8( 0 ); + } + recVUMI_BranchHandle(); +} + +void recVUMI_IBLTZ( VURegs* vuu, s32 info ) +{ + int fsreg = _checkX86reg(X86TYPE_VI|(VU==&VU1?X86TYPE_VU1:0), _Fs_, MODE_READ); + s_JumpX86 = _allocX86reg(-1, X86TYPE_VUJUMP, 0, MODE_WRITE); + + if( fsreg >= 0 ) { + OR16RtoR(fsreg, fsreg); + j8Ptr[ 0 ] = JNS8( 0 ); + } + else { + CMP16ItoM( SuperVUGetVIAddr(_Fs_, 1), 0x0 ); + j8Ptr[ 0 ] = JGE8( 0 ); + } + recVUMI_BranchHandle(); +} + +void recVUMI_IBNE( VURegs* vuu, s32 info ) +{ + recVUMI_IBQ_prep(); + j8Ptr[ 0 ] = JE8( 0 ); + recVUMI_BranchHandle(); +} + +void recVUMI_B( VURegs* vuu, s32 info ) +{ + // supervu will take care of the rest + int bpc = _recbranchAddr(VU->code); + if( (s_pCurBlock->type & BLOCKTYPE_HASEOP) || s_vu == 0 || SUPERVU_CHECKCONDITION) + MOV32ItoM(SuperVUGetVIAddr(REG_TPC, 0), bpc); + + // loops to self, so check condition + if( bpc == s_pCurBlock->startpc && (s_vu == 0 || SUPERVU_CHECKCONDITION) ) { + SuperVUTestVU0Condition(0); + } + + if( s_pCurBlock->blocks.size() > 1 ) { + s_JumpX86 = _allocX86reg(-1, X86TYPE_VUJUMP, 0, MODE_WRITE); + MOV32ItoR(s_JumpX86, 0); + s_pCurBlock->pChildJumps[(s_pCurInst->type & INST_BRANCH_DELAY)?1:0] = (u32*)x86Ptr-1; + s_UnconditionalDelay = 1; + } + + branch |= 3; +} + +void recVUMI_BAL( VURegs* vuu, s32 info ) +{ + int bpc = _recbranchAddr(VU->code); + if( (s_pCurBlock->type & BLOCKTYPE_HASEOP) || s_vu == 0 || SUPERVU_CHECKCONDITION ) + MOV32ItoM(SuperVUGetVIAddr(REG_TPC, 0), bpc); + + // loops to self, so check condition + if( bpc == s_pCurBlock->startpc && (s_vu == 0 || SUPERVU_CHECKCONDITION) ) { + SuperVUTestVU0Condition(0); + } + + if ( _Ft_ ) { + _deleteX86reg(X86TYPE_VI|(s_vu?X86TYPE_VU1:0), _Ft_, 2); + MOV16ItoM( SuperVUGetVIAddr(_Ft_, 0), (pc+8)>>3 ); + } + + if( s_pCurBlock->blocks.size() > 1 ) { + s_JumpX86 = _allocX86reg(-1, X86TYPE_VUJUMP, 0, MODE_WRITE); + MOV32ItoR(s_JumpX86, 0); + s_pCurBlock->pChildJumps[(s_pCurInst->type & INST_BRANCH_DELAY)?1:0] = (u32*)x86Ptr-1; + s_UnconditionalDelay = 1; + } + + branch |= 3; +} + +void recVUMI_JR( VURegs* vuu, s32 info ) +{ + int fsreg = _allocX86reg(-1, X86TYPE_VI|(s_vu?X86TYPE_VU1:0), _Fs_, MODE_READ); + LEA32RStoR(EAX, fsreg, 3); + CWDE(); + + if( (s_pCurBlock->type & BLOCKTYPE_HASEOP) || s_vu == 0 ) MOV32RtoM(SuperVUGetVIAddr(REG_TPC, 0), EAX); + + if( !(s_pCurBlock->type & BLOCKTYPE_HASEOP) ) { + PUSH32I(s_vu); + PUSH32R(EAX); + } + branch |= 0x10; // 0x08 is reserved +} + +void recVUMI_JALR( VURegs* vuu, s32 info ) +{ + _addNeededX86reg(X86TYPE_VI|(s_vu?X86TYPE_VU1:0), _Ft_); + + int fsreg = _allocX86reg(-1, X86TYPE_VI|(s_vu?X86TYPE_VU1:0), _Fs_, MODE_READ); + LEA32RStoR(EAX, fsreg, 3); + CWDE(); // necessary, charlie and chocolate factory gives bad addrs, but graphics are ok + + if ( _Ft_ ) { + _deleteX86reg(X86TYPE_VI|(s_vu?X86TYPE_VU1:0), _Ft_, 2); + MOV16ItoM( SuperVUGetVIAddr(_Ft_, 0), (pc+8)>>3 ); + } + + if( (s_pCurBlock->type & BLOCKTYPE_HASEOP) || s_vu == 0 ) MOV32RtoM(SuperVUGetVIAddr(REG_TPC, 0), EAX); + + if( !(s_pCurBlock->type & BLOCKTYPE_HASEOP) ) { + PUSH32I(s_vu); + PUSH32R(EAX); + } + + branch |= 4; +} + +#ifdef SUPERVU_COUNT +void StopSVUCounter() +{ + QueryPerformanceCounter(&svufinal); + svutime += (u32)(svufinal.QuadPart-svubase.QuadPart); +} + +void StartSVUCounter() +{ + QueryPerformanceCounter(&svubase); +} +#endif + +#ifdef PCSX2_DEVBUILD +void vu1xgkick(u32* pMem, u32 addr) +{ + assert( addr < 0x4000 ); +#ifdef SUPERVU_COUNT + StopSVUCounter(); +#endif + + GSGIFTRANSFER1(pMem, addr); + +#ifdef SUPERVU_COUNT + StartSVUCounter(); +#endif +} +#endif + +void recVUMI_XGKICK_( VURegs *VU ) +{ + assert( s_XGKICKReg > 0 && x86regs[s_XGKICKReg].inuse && x86regs[s_XGKICKReg].type == X86TYPE_VITEMP); + + x86regs[s_XGKICKReg].inuse = 0; // so free doesn't flush + _freeX86regs(); + _freeXMMregs(); + + PUSH32R(s_XGKICKReg); + PUSH32I((uptr)VU->Mem); + + //CALLFunc((u32)countfn); + + if( mtgsThread != NULL ) { + CALLFunc((uptr)VU1XGKICK_MTGSTransfer); + ADD32ItoR(ESP, 8); + } + else { +#ifdef PCSX2_DEVBUILD + CALLFunc((uptr)vu1xgkick); + ADD32ItoR(ESP, 8); +#else + CALLFunc((uptr)GSgifTransfer1); +#endif + } + + s_ScheduleXGKICK = 0; +} + +void recVUMI_XGKICK( VURegs *VU, int info ) +{ + if( s_ScheduleXGKICK ) { + // second xgkick, so launch the first + recVUMI_XGKICK_(VU); + } + + int fsreg = _allocX86reg(X86ARG2, X86TYPE_VI|(s_vu?X86TYPE_VU1:0), _Fs_, MODE_READ); + _freeX86reg(fsreg); // flush + x86regs[fsreg].inuse = 1; + x86regs[fsreg].type = X86TYPE_VITEMP; + x86regs[fsreg].needed = 1; + x86regs[fsreg].mode = MODE_WRITE|MODE_READ; + SHL32ItoR(fsreg, 4); + AND32ItoR(fsreg, 0x3fff); + s_XGKICKReg = fsreg; + + if( !SUPERVU_XGKICKDELAY || pc == s_pCurBlock->endpc ) { + recVUMI_XGKICK_(VU); + } + else { + if( g_VUGameFixes & VUFIX_XGKICKDELAY2 ) + s_ScheduleXGKICK = min((u32)4, (s_pCurBlock->endpc-pc)/8); + else + s_ScheduleXGKICK = 2; + } +} + +void recVU_UPPER_FD_00( VURegs* VU, s32 info ); +void recVU_UPPER_FD_01( VURegs* VU, s32 info ); +void recVU_UPPER_FD_10( VURegs* VU, s32 info ); +void recVU_UPPER_FD_11( VURegs* VU, s32 info ); +void recVULowerOP( VURegs* VU, s32 info ); +void recVULowerOP_T3_00( VURegs* VU, s32 info ); +void recVULowerOP_T3_01( VURegs* VU, s32 info ); +void recVULowerOP_T3_10( VURegs* VU, s32 info ); +void recVULowerOP_T3_11( VURegs* VU, s32 info ); +void recVUunknown( VURegs* VU, s32 info ); + +void (*recVU_LOWER_OPCODE[128])( VURegs* VU, s32 info ) = { + recVUMI_LQ , recVUMI_SQ , recVUunknown , recVUunknown, + recVUMI_ILW , recVUMI_ISW , recVUunknown , recVUunknown, + recVUMI_IADDIU, recVUMI_ISUBIU, recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUMI_FCEQ , recVUMI_FCSET , recVUMI_FCAND, recVUMI_FCOR, /* 0x10 */ + recVUMI_FSEQ , recVUMI_FSSET , recVUMI_FSAND, recVUMI_FSOR, + recVUMI_FMEQ , recVUunknown , recVUMI_FMAND, recVUMI_FMOR, + recVUMI_FCGET , recVUunknown , recVUunknown , recVUunknown, + recVUMI_B , recVUMI_BAL , recVUunknown , recVUunknown, /* 0x20 */ + recVUMI_JR , recVUMI_JALR , recVUunknown , recVUunknown, + recVUMI_IBEQ , recVUMI_IBNE , recVUunknown , recVUunknown, + recVUMI_IBLTZ , recVUMI_IBGTZ , recVUMI_IBLEZ, recVUMI_IBGEZ, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, /* 0x30 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVULowerOP , recVUunknown , recVUunknown , recVUunknown, /* 0x40*/ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, /* 0x50 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, /* 0x60 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, /* 0x70 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, +}; + +void (*recVULowerOP_T3_00_OPCODE[32])(VURegs* VU, s32 info) = { + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUMI_MOVE , recVUMI_LQI , recVUMI_DIV , recVUMI_MTIR, + recVUMI_RNEXT , recVUunknown , recVUunknown , recVUunknown, /* 0x10 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUMI_MFP , recVUMI_XTOP , recVUMI_XGKICK, + recVUMI_ESADD , recVUMI_EATANxy, recVUMI_ESQRT, recVUMI_ESIN, +}; + +void (*recVULowerOP_T3_01_OPCODE[32])(VURegs* VU, s32 info) = { + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUMI_MR32 , recVUMI_SQI , recVUMI_SQRT , recVUMI_MFIR, + recVUMI_RGET , recVUunknown , recVUunknown , recVUunknown, /* 0x10 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUMI_XITOP, recVUunknown, + recVUMI_ERSADD, recVUMI_EATANxz, recVUMI_ERSQRT, recVUMI_EATAN, +}; + +void (*recVULowerOP_T3_10_OPCODE[32])(VURegs* VU, s32 info) = { + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUMI_LQD , recVUMI_RSQRT, recVUMI_ILWR, + recVUMI_RINIT , recVUunknown , recVUunknown , recVUunknown, /* 0x10 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUMI_ELENG , recVUMI_ESUM , recVUMI_ERCPR, recVUMI_EEXP, +}; + +void (*recVULowerOP_T3_11_OPCODE[32])(VURegs* VU, s32 info) = { + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUMI_SQD , recVUMI_WAITQ, recVUMI_ISWR, + recVUMI_RXOR , recVUunknown , recVUunknown , recVUunknown, /* 0x10 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUMI_ERLENG, recVUunknown , recVUMI_WAITP, recVUunknown, +}; + +void (*recVULowerOP_OPCODE[64])(VURegs* VU, s32 info) = { + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, /* 0x10 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, /* 0x20 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUMI_IADD , recVUMI_ISUB , recVUMI_IADDI, recVUunknown, /* 0x30 */ + recVUMI_IAND , recVUMI_IOR , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVULowerOP_T3_00, recVULowerOP_T3_01, recVULowerOP_T3_10, recVULowerOP_T3_11, +}; + +void (*recVU_UPPER_OPCODE[64])(VURegs* VU, s32 info) = { + recVUMI_ADDx , recVUMI_ADDy , recVUMI_ADDz , recVUMI_ADDw, + recVUMI_SUBx , recVUMI_SUBy , recVUMI_SUBz , recVUMI_SUBw, + recVUMI_MADDx , recVUMI_MADDy , recVUMI_MADDz , recVUMI_MADDw, + recVUMI_MSUBx , recVUMI_MSUBy , recVUMI_MSUBz , recVUMI_MSUBw, + recVUMI_MAXx , recVUMI_MAXy , recVUMI_MAXz , recVUMI_MAXw, /* 0x10 */ + recVUMI_MINIx , recVUMI_MINIy , recVUMI_MINIz , recVUMI_MINIw, + recVUMI_MULx , recVUMI_MULy , recVUMI_MULz , recVUMI_MULw, + recVUMI_MULq , recVUMI_MAXi , recVUMI_MULi , recVUMI_MINIi, + recVUMI_ADDq , recVUMI_MADDq , recVUMI_ADDi , recVUMI_MADDi, /* 0x20 */ + recVUMI_SUBq , recVUMI_MSUBq , recVUMI_SUBi , recVUMI_MSUBi, + recVUMI_ADD , recVUMI_MADD , recVUMI_MUL , recVUMI_MAX, + recVUMI_SUB , recVUMI_MSUB , recVUMI_OPMSUB, recVUMI_MINI, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, /* 0x30 */ + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVUunknown , recVUunknown , recVUunknown , recVUunknown, + recVU_UPPER_FD_00, recVU_UPPER_FD_01, recVU_UPPER_FD_10, recVU_UPPER_FD_11, +}; + +void (*recVU_UPPER_FD_00_TABLE[32])(VURegs* VU, s32 info) = { + recVUMI_ADDAx, recVUMI_SUBAx , recVUMI_MADDAx, recVUMI_MSUBAx, + recVUMI_ITOF0, recVUMI_FTOI0, recVUMI_MULAx , recVUMI_MULAq , + recVUMI_ADDAq, recVUMI_SUBAq, recVUMI_ADDA , recVUMI_SUBA , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , +}; + +void (*recVU_UPPER_FD_01_TABLE[32])(VURegs* VU, s32 info) = { + recVUMI_ADDAy , recVUMI_SUBAy , recVUMI_MADDAy, recVUMI_MSUBAy, + recVUMI_ITOF4 , recVUMI_FTOI4 , recVUMI_MULAy , recVUMI_ABS , + recVUMI_MADDAq, recVUMI_MSUBAq, recVUMI_MADDA , recVUMI_MSUBA , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , +}; + +void (*recVU_UPPER_FD_10_TABLE[32])(VURegs* VU, s32 info) = { + recVUMI_ADDAz , recVUMI_SUBAz , recVUMI_MADDAz, recVUMI_MSUBAz, + recVUMI_ITOF12, recVUMI_FTOI12, recVUMI_MULAz , recVUMI_MULAi , + recVUMI_ADDAi, recVUMI_SUBAi , recVUMI_MULA , recVUMI_OPMULA, + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , +}; + +void (*recVU_UPPER_FD_11_TABLE[32])(VURegs* VU, s32 info) = { + recVUMI_ADDAw , recVUMI_SUBAw , recVUMI_MADDAw, recVUMI_MSUBAw, + recVUMI_ITOF15, recVUMI_FTOI15, recVUMI_MULAw , recVUMI_CLIP , + recVUMI_MADDAi, recVUMI_MSUBAi, recVUunknown , recVUMI_NOP , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , + recVUunknown , recVUunknown , recVUunknown , recVUunknown , +}; + +void recVU_UPPER_FD_00( VURegs* VU, s32 info ) +{ + recVU_UPPER_FD_00_TABLE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} + +void recVU_UPPER_FD_01( VURegs* VU, s32 info ) +{ + recVU_UPPER_FD_01_TABLE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} + +void recVU_UPPER_FD_10( VURegs* VU, s32 info ) +{ + recVU_UPPER_FD_10_TABLE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} + +void recVU_UPPER_FD_11( VURegs* VU, s32 info ) +{ + recVU_UPPER_FD_11_TABLE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} + +void recVULowerOP( VURegs* VU, s32 info ) +{ + recVULowerOP_OPCODE[ VU->code & 0x3f ]( VU, info ); +} + +void recVULowerOP_T3_00( VURegs* VU, s32 info ) +{ + recVULowerOP_T3_00_OPCODE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} + +void recVULowerOP_T3_01( VURegs* VU, s32 info ) +{ + recVULowerOP_T3_01_OPCODE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} + +void recVULowerOP_T3_10( VURegs* VU, s32 info ) +{ + recVULowerOP_T3_10_OPCODE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} + +void recVULowerOP_T3_11( VURegs* VU, s32 info ) +{ + recVULowerOP_T3_11_OPCODE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} + +void recVUunknown( VURegs* VU, s32 info ) +{ + SysPrintf("Unknown SVU micromode opcode called\n"); +} diff --git a/pcsx2/x86/iVUzerorec.h b/pcsx2/x86/iVUzerorec.h new file mode 100644 index 0000000000..c62a166d9f --- /dev/null +++ b/pcsx2/x86/iVUzerorec.h @@ -0,0 +1,50 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// Super VU recompiler - author: zerofrog(@gmail.com) + +#ifndef VU1_SUPER_RECOMPILER +#define VU1_SUPER_RECOMPILER + +#include "iVUmicro.h" + +extern void SuperVUAlloc(int vuindex); // global VU resources are automatically allocated if necessary. +extern void SuperVUDestroy(int vuindex); // if vuindex is -1, destroys everything +extern void SuperVUReset(int vuindex); // if vuindex is -1, resets everything + +//Using assembly code from an external file. +#ifdef __LINUX__ +extern "C" { +#endif +extern void SuperVUExecuteProgram(u32 startpc, int vuindex); +extern void SuperVUEndProgram(); +extern void svudispfntemp(); +#ifdef __LINUX__ +} +#endif +extern void __fastcall SuperVUClear(u32 startpc, u32 size, int vuindex); + +// read = 0, will write to reg +// read = 1, will read from reg +// read = 2, addr of previously written reg (used for status and clip flags) +extern u32 SuperVUGetVIAddr(int reg, int read); + +// if p == 0, flush q else flush p; if wait is != 0, waits for p/q +extern void SuperVUFlush(int p, int wait); + +#endif diff --git a/pcsx2/x86/iVif.cpp b/pcsx2/x86/iVif.cpp new file mode 100644 index 0000000000..3eeba0b22b --- /dev/null +++ b/pcsx2/x86/iVif.cpp @@ -0,0 +1,139 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "ix86/ix86.h" +#include "Vif.h" +#include "VUmicro.h" + +// sse2 highly optimized vif (~200 separate functions are built) zerofrog(@gmail.com) +extern u32 g_vif1Masks[48], g_vif0Masks[48]; +extern u32 g_vif1HasMask3[4], g_vif0HasMask3[4]; + +//static const u32 writearr[4] = { 0xffffffff, 0, 0, 0 }; +//static const u32 rowarr[4] = { 0, 0xffffffff, 0, 0 }; +//static const u32 colarr[4] = { 0, 0, 0xffffffff, 0 }; +//static const u32 updatearr[4] = {0xffffffff, 0xffffffff, 0xffffffff, 0 }; + +// arranged in writearr, rowarr, colarr, updatearr +static PCSX2_ALIGNED16(u32 s_maskarr[16][4]) = { + 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, + 0xffff0000, 0x0000ffff, 0x00000000, 0xffffffff, + 0xffff0000, 0x00000000, 0x0000ffff, 0xffffffff, + 0xffff0000, 0x00000000, 0x00000000, 0xffff0000, + 0x0000ffff, 0xffff0000, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff, + 0x00000000, 0xffff0000, 0x00000000, 0xffff0000, + 0x0000ffff, 0x00000000, 0xffff0000, 0xffffffff, + 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffff0000, 0xffff0000, + 0x0000ffff, 0x00000000, 0x00000000, 0x0000ffff, + 0x00000000, 0x0000ffff, 0x00000000, 0x0000ffff, + 0x00000000, 0x00000000, 0x0000ffff, 0x0000ffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +extern u8 s_maskwrite[256]; + +extern "C" PCSX2_ALIGNED16(u32 s_TempDecompress[4]) = {0}; + +#if defined(_MSC_VER) + +#include +#include + +void SetNewMask(u32* vif1masks, u32* hasmask, u32 mask, u32 oldmask) +{ + u32 i; + u32 prev = 0; + FreezeXMMRegs(1); + for(i = 0; i < 4; ++i, mask >>= 8, oldmask >>= 8, vif1masks += 16) { + + prev |= s_maskwrite[mask&0xff];//((mask&3)==3)||((mask&0xc)==0xc)||((mask&0x30)==0x30)||((mask&0xc0)==0xc0); + hasmask[i] = prev; + + if( (mask&0xff) != (oldmask&0xff) ) { + __m128i r0, r1, r2, r3; + r0 = _mm_load_si128((__m128i*)&s_maskarr[mask&15][0]); + r2 = _mm_unpackhi_epi16(r0, r0); + r0 = _mm_unpacklo_epi16(r0, r0); + + r1 = _mm_load_si128((__m128i*)&s_maskarr[(mask>>4)&15][0]); + r3 = _mm_unpackhi_epi16(r1, r1); + r1 = _mm_unpacklo_epi16(r1, r1); + + _mm_storel_pi((__m64*)&vif1masks[0], *(__m128*)&r0); + _mm_storel_pi((__m64*)&vif1masks[2], *(__m128*)&r1); + _mm_storeh_pi((__m64*)&vif1masks[4], *(__m128*)&r0); + _mm_storeh_pi((__m64*)&vif1masks[6], *(__m128*)&r1); + + _mm_storel_pi((__m64*)&vif1masks[8], *(__m128*)&r2); + _mm_storel_pi((__m64*)&vif1masks[10], *(__m128*)&r3); + _mm_storeh_pi((__m64*)&vif1masks[12], *(__m128*)&r2); + _mm_storeh_pi((__m64*)&vif1masks[14], *(__m128*)&r3); + } + } + FreezeXMMRegs(0); +} + + +#else // gcc + +void SetNewMask(u32* vif1masks, u32* hasmask, u32 mask, u32 oldmask) +{ + u32 i; + u32 prev = 0; + FreezeXMMRegs(1); + + for(i = 0; i < 4; ++i, mask >>= 8, oldmask >>= 8, vif1masks += 16) { + + prev |= s_maskwrite[mask&0xff];//((mask&3)==3)||((mask&0xc)==0xc)||((mask&0x30)==0x30)||((mask&0xc0)==0xc0); + hasmask[i] = prev; + + if( (mask&0xff) != (oldmask&0xff) ) { + u8* p0 = (u8*)&s_maskarr[mask&15][0]; + u8* p1 = (u8*)&s_maskarr[(mask>>4)&15][0]; + + __asm__(".intel_syntax\n" + "movaps %%xmm0, [%0]\n" + "movaps %%xmm1, [%1]\n" + "movaps %%xmm2, %%xmm0\n" + "punpcklwd %%xmm0, %%xmm0\n" + "punpckhwd %%xmm2, %%xmm2\n" + "movaps %%xmm3, %%xmm1\n" + "punpcklwd %%xmm1, %%xmm1\n" + "punpckhwd %%xmm3, %%xmm3\n" + "movq [%2], %%xmm0\n" + "movq [%2+8], %%xmm1\n" + "movhps [%2+16], %%xmm0\n" + "movhps [%2+24], %%xmm1\n" + "movq [%2+32], %%xmm2\n" + "movq [%2+40], %%xmm3\n" + "movhps [%2+48], %%xmm2\n" + "movhps [%2+56], %%xmm3\n" + ".att_syntax\n" : : "r"(p0), "r"(p1), "r"(vif1masks) ); + } + } + FreezeXMMRegs(0); +} + +#endif diff --git a/pcsx2/x86/ir5900tables.cpp b/pcsx2/x86/ir5900tables.cpp new file mode 100644 index 0000000000..f9ee0a3fac --- /dev/null +++ b/pcsx2/x86/ir5900tables.cpp @@ -0,0 +1,1049 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// Holds instruction tables for the r5900 recompiler + +#include "PrecompiledHeader.h" + +#include "Common.h" +#include "Memory.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "iR5900AritImm.h" +#include "iR5900Arit.h" +#include "iR5900MultDiv.h" +#include "iR5900Shift.h" +#include "iR5900Branch.h" +#include "iR5900Jump.h" +#include "iR5900LoadStore.h" +#include "iR5900Move.h" +#include "iMMI.h" +#include "iFPU.h" +#include "iCOP0.h" + +// Use this to call into interpreter functions that require an immediate branchtest +// to be done afterward (anything that throws an exception or enables interrupts, etc). +void recBranchCall( void (*func)() ) +{ + // In order to make sure a branch test is performed, the nextBranchCycle is set + // to the current cpu cycle. + + MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code ); + MOV32MtoR( EAX, (uptr)&cpuRegs.cycle ); + MOV32ItoM( (uptr)&cpuRegs.pc, pc ); + MOV32RtoM( (uptr)&g_nextBranchCycle, EAX ); + + // Might as well flush everything -- it'll all get flushed when the + // recompiler inserts the branchtest anyway. + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (uptr)func ); + branch = 2; +} + +void recCall( void (*func)(), int delreg ) +{ + MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (uptr)&cpuRegs.pc, pc ); + + iFlushCall(FLUSH_EVERYTHING); + if( delreg > 0 ) _deleteEEreg(delreg, 0); + CALLFunc( (uptr)func ); +} + +using namespace R5900::Dynarec::OpcodeImpl; + +#ifdef PCSX2_VM_COISSUE +// coissued insts +void (*recBSC_co[64] )() = { + recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, + recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, + recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, recNULL, + recNULL, recNULL, recLDL_co, recLDR_co, recNULL, recNULL, recLQ_co, recSQ_co, + recLB_co, recLH_co, recLWL_co, recLW_co, recLBU_co, recLHU_co, recLWR_co, recLWU_co, + recSB_co, recSH_co, recSWL_co, recSW_co, recSDL_co, recSDR_co, recSWR_co, recNULL, + recNULL, recLWC1_co, recNULL, recNULL, recNULL, recNULL, recLQC2_co, recLD_co, + recNULL, recSWC1_co, recNULL, recNULL, recNULL, recNULL, recSQC2_co, recSD_co +}; +#endif + + +//////////////////////////////////////////////// +// Back-Prob Function Tables - Gathering Info // +//////////////////////////////////////////////// +BSCPropagate::BSCPropagate( EEINST& previous, EEINST& pinstance ) : + prev( previous ) +, pinst( pinstance ) +{ +} + +void BSCPropagate::rpropSetRead( int reg, int mask ) +{ + if( !(pinst.regs[reg] & EEINST_USED) ) + pinst.regs[reg] |= EEINST_LASTUSE; + prev.regs[reg] |= EEINST_LIVE0|(mask)|EEINST_USED; + pinst.regs[reg] |= EEINST_USED; + if( reg ) pinst.info = ((mask)&(EEINST_MMX|EEINST_XMM)); + _recFillRegister(pinst, XMMTYPE_GPRREG, reg, 0); +} + +template< int live > +void BSCPropagate::rpropSetWrite0( int reg, int mask ) +{ + prev.regs[reg] &= ~((mask)|live|EEINST_XMM|EEINST_MMX); + if( !(pinst.regs[reg] & EEINST_USED) ) + pinst.regs[reg] |= EEINST_LASTUSE; + pinst.regs[reg] |= EEINST_USED; + prev.regs[reg] |= EEINST_USED; + _recFillRegister(pinst, XMMTYPE_GPRREG, reg, 1); +} + +__forceinline void BSCPropagate::rpropSetWrite( int reg, int mask ) +{ + rpropSetWrite0( reg, mask ); +} + +__forceinline void BSCPropagate::rpropSetFast( int write1, int read1, int read2, int mask ) +{ + if( write1 ) { rpropSetWrite(write1,mask); } + if( read1 ) { rpropSetRead(read1,mask); } + if( read2 ) { rpropSetRead(read2,mask); } +} + +template< int lo, int hi > +void BSCPropagate::rpropSetLOHI( int write1, int read1, int read2, int mask ) +{ + if( write1 ) { rpropSetWrite(write1,mask); } + if( lo & MODE_WRITE ) { rpropSetWrite(XMMGPR_LO,mask); } + if( hi & MODE_WRITE ) { rpropSetWrite(XMMGPR_HI,mask); } + if( read1 ) { rpropSetRead(read1,mask); } + if( read2 ) { rpropSetRead(read2,mask); } + if( lo & MODE_READ ) { rpropSetRead(XMMGPR_LO,mask); } + if( hi & MODE_READ ) { rpropSetRead(XMMGPR_HI,mask); } +} + +// FPU regs +void BSCPropagate::rpropSetFPURead( int reg, int mask ) +{ + if( !(pinst.fpuregs[reg] & EEINST_USED) ) + pinst.fpuregs[reg] |= EEINST_LASTUSE; + prev.fpuregs[reg] |= EEINST_LIVE0|(mask)|EEINST_USED; + pinst.fpuregs[reg] |= EEINST_USED; + if( reg ) pinst.info = ((mask)&(EEINST_MMX|EEINST_XMM)); + if( reg == XMMFPU_ACC ) _recFillRegister(pinst, XMMTYPE_FPACC, 0, 0); + else _recFillRegister(pinst, XMMTYPE_FPREG, reg, 0); +} + +template< int live > +void BSCPropagate::rpropSetFPUWrite0( int reg, int mask ) +{ + prev.fpuregs[reg] &= ~((mask)|live|EEINST_XMM|EEINST_MMX); + if( !(pinst.fpuregs[reg] & EEINST_USED) ) + pinst.fpuregs[reg] |= EEINST_LASTUSE; + pinst.fpuregs[reg] |= EEINST_USED; + prev.fpuregs[reg] |= EEINST_USED; + if( reg == XMMFPU_ACC ) _recFillRegister(pinst, XMMTYPE_FPACC, 0, 1); + else _recFillRegister(pinst, XMMTYPE_FPREG, reg, 1); +} + +__forceinline void BSCPropagate::rpropSetFPUWrite( int reg, int mask ) +{ + rpropSetFPUWrite0( reg, mask ); +} + + +#define EEINST_REALXMM EEINST_XMM + +//SLL, NULL, SRL, SRA, SLLV, NULL, SRLV, SRAV, +//JR, JALR, MOVZ, MOVN, SYSCALL, BREAK, NULL, SYNC, +//MFHI, MTHI, MFLO, MTLO, DSLLV, NULL, DSRLV, DSRAV, +//MULT, MULTU, DIV, DIVU, NULL, NULL, NULL, NULL, +//ADD, ADDU, SUB, SUBU, AND, OR, XOR, NOR, +//MFSA, MTSA, SLT, SLTU, DADD, DADDU, DSUB, DSUBU, +//TGE, TGEU, TLT, TLTU, TEQ, NULL, TNE, NULL, +//DSLL, NULL, DSRL, DSRA, DSLL32, NULL, DSRL32, DSRA32 + +__forceinline void BSCPropagate::rpropSPECIAL() +{ + switch(_Funct_) { + case 0: // SLL + case 2: // SRL + case 3: // SRA + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_MMX); + break; + + case 4: // sllv + case 6: // srlv + case 7: // srav + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_MMX); + rpropSetRead(_Rt_, EEINST_MMX); + break; + + case 8: // JR + rpropSetRead(_Rs_, 0); + break; + case 9: // JALR + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rs_, 0); + break; + + case 10: // movz + case 11: // movn + // do not write _Rd_! + rpropSetRead(_Rs_, EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rd_, EEINST_LIVE1); + pinst.info |= EEINST_MMX; + _recFillRegister(pinst, XMMTYPE_GPRREG, _Rd_, 1); + break; + + case 12: // syscall + case 13: // break + _recClearInst(&prev); + prev.info = 0; + break; + case 15: // sync + break; + + case 16: // mfhi + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(XMMGPR_HI, (pinst.regs[_Rd_]&(EEINST_MMX|EEINST_REALXMM))|EEINST_LIVE1); + break; + case 17: // mthi + rpropSetWrite(XMMGPR_HI, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1); + break; + case 18: // mflo + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(XMMGPR_LO, (pinst.regs[_Rd_]&(EEINST_MMX|EEINST_REALXMM))|EEINST_LIVE1); + break; + case 19: // mtlo + rpropSetWrite(XMMGPR_LO, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1); + break; + + case 20: // dsllv + case 22: // dsrlv + case 23: // dsrav + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_MMX); + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_MMX); + break; + + //case 24: // mult + // // can do unsigned mult only if HI isn't used + + // //using this allocation for temp causes the emu to crash + // //temp = (pinst.regs[XMMGPR_HI]&(EEINST_LIVE0|EEINST_LIVE1))?0:EEINST_MMX; + // temp = 0; + // rpropSetWrite(XMMGPR_LO, EEINST_LIVE1); + // rpropSetWrite(XMMGPR_HI, EEINST_LIVE1); + // rpropSetWrite(_Rd_, EEINST_LIVE1); + // + // // fixme - temp is always 0, so I doubt the next three lines are right. (arcum42) + + // // Yep, its wrong. Using always 0 causes the wrong damage calculations in Soul Nomad. + // rpropSetRead(_Rs_, temp); + // rpropSetRead(_Rt_, temp); + // pinst.info |= temp; + // break; + + //case 25: // multu + // rpropSetWrite(XMMGPR_LO, EEINST_LIVE1); + // rpropSetWrite(XMMGPR_HI, EEINST_LIVE1); + // rpropSetWrite(_Rd_, EEINST_LIVE1); + // rpropSetRead(_Rs_, EEINST_MMX); + // rpropSetRead(_Rt_, EEINST_MMX); + // pinst.info |= EEINST_MMX; + // break; + + + case 24: // mult + case 25: // multu + rpropSetWrite(XMMGPR_LO, EEINST_LIVE1); + rpropSetWrite(XMMGPR_HI, EEINST_LIVE1); + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_USED); //EEINST_MMX crashes on init, EEINST_XMM fails on Tales of Abyss, so EEINST_USED (rama) + rpropSetRead(_Rt_, EEINST_USED); + // did we ever try EEINST_LIVE1 or 0 in the place of EEINST_USED? I think of those might be more correct here (Air) + + pinst.info |= EEINST_USED; + break; + + case 26: // div + case 27: // divu + rpropSetWrite(XMMGPR_LO, EEINST_LIVE1); + rpropSetWrite(XMMGPR_HI, EEINST_LIVE1); + rpropSetRead(_Rs_, 0); + rpropSetRead(_Rt_, 0); + //pinst.info |= EEINST_REALXMM|EEINST_MMX; + break; + + case 32: // add + case 33: // addu + case 34: // sub + case 35: // subu + rpropSetWrite(_Rd_, EEINST_LIVE1); + if( _Rs_ ) rpropSetRead(_Rs_, 0); + if( _Rt_ ) rpropSetRead(_Rt_, 0); + pinst.info |= EEINST_MMX; + break; + + case 36: // and + case 37: // or + case 38: // xor + case 39: // nor + // if rd == rs or rt, keep live1 + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rs_, (pinst.regs[_Rd_]&EEINST_LIVE1)|EEINST_MMX); + rpropSetRead(_Rt_, (pinst.regs[_Rd_]&EEINST_LIVE1)|EEINST_MMX); + break; + + case 40: // mfsa + rpropSetWrite(_Rd_, EEINST_LIVE1); + break; + case 41: // mtsa + rpropSetRead(_Rs_, EEINST_LIVE1|EEINST_MMX); + break; + + case 42: // slt + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE1); + pinst.info |= EEINST_MMX; + break; + + case 43: // sltu + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE1); + pinst.info |= EEINST_MMX; + break; + + case 44: // dadd + case 45: // daddu + case 46: // dsub + case 47: // dsubu + rpropSetWrite(_Rd_, EEINST_LIVE1); + if( _Rs_ == 0 || _Rt_ == 0 ) { + // just a copy, so don't force mmx + rpropSetRead(_Rs_, (pinst.regs[_Rd_]&EEINST_LIVE1)); + rpropSetRead(_Rt_, (pinst.regs[_Rd_]&EEINST_LIVE1)); + } + else { + rpropSetRead(_Rs_, (pinst.regs[_Rd_]&EEINST_LIVE1)|EEINST_MMX); + rpropSetRead(_Rt_, (pinst.regs[_Rd_]&EEINST_LIVE1)|EEINST_MMX); + } + pinst.info |= EEINST_MMX; + break; + + // traps + case 48: case 49: case 50: case 51: case 52: case 54: + rpropSetRead(_Rs_, EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE1); + break; + + case 56: // dsll + case 58: // dsrl + case 59: // dsra + case 62: // dsrl32 + case 63: // dsra32 + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_MMX); + pinst.info |= EEINST_MMX; + break; + + case 60: // dsll32 + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_MMX); + pinst.info |= EEINST_MMX; + break; + + default: + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1|EEINST_MMX); + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_MMX); + break; + } +} + +//BLTZ, BGEZ, BLTZL, BGEZL, NULL, NULL, NULL, NULL, +//TGEI, TGEIU, TLTI, TLTIU, TEQI, NULL, TNEI, NULL, +//BLTZAL, BGEZAL, BLTZALL, BGEZALL, NULL, NULL, NULL, NULL, +//MTSAB, MTSAH, NULL, NULL, NULL, NULL, NULL, NULL, +__forceinline void BSCPropagate::rpropREGIMM() +{ + switch(_Rt_) { + case 0: // bltz + case 1: // bgez + rpropSetRead(_Rs_, EEINST_LIVE1); + pinst.info |= EEINST_MMX; + pinst.info |= EEINST_REALXMM; + break; + + case 2: // bltzl + case 3: // bgezl + // reset since don't know which path to go + _recClearInst(&prev); + prev.info = 0; + rpropSetRead(_Rs_, EEINST_LIVE1); + pinst.info |= EEINST_MMX; + pinst.info |= EEINST_REALXMM; + break; + + // traps + case 8: + case 9: + case 10: + case 11: + case 12: + case 14: + rpropSetRead(_Rs_, EEINST_LIVE1); + break; + + case 16: // bltzal + case 17: // bgezal + // do not write 31 + rpropSetRead(_Rs_, EEINST_LIVE1); + break; + + case 18: // bltzall + case 19: // bgezall + // reset since don't know which path to go + _recClearInst(&prev); + prev.info = 0; + // do not write 31 + rpropSetRead(_Rs_, EEINST_LIVE1); + break; + + case 24: // mtsab + case 25: // mtsah + rpropSetRead(_Rs_, 0); + break; + default: + assert(0); + break; + } +} + +//MFC0, NULL, NULL, NULL, MTC0, NULL, NULL, NULL, +//COP0BC0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +//COP0C0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +//NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +__forceinline void BSCPropagate::rpropCP0() +{ + switch(_Rs_) { + case 0: // mfc0 + rpropSetWrite(_Rt_, EEINST_LIVE1|EEINST_REALXMM); + break; + case 4: + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_REALXMM); + break; + case 8: // cop0bc0 + _recClearInst(&prev); + prev.info = 0; + break; + case 16: // cop0c0 + _recClearInst(&prev); + prev.info = 0; + break; + } +} + +#define _Ft_ _Rt_ +#define _Fs_ _Rd_ +#define _Fd_ _Sa_ + +//ADD_S, SUB_S, MUL_S, DIV_S, SQRT_S, ABS_S, MOV_S, NEG_S, +//NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +//NULL, NULL, NULL, NULL, NULL, NULL, RSQRT_S, NULL, +//ADDA_S, SUBA_S, MULA_S, NULL, MADD_S, MSUB_S, MADDA_S, MSUBA_S, +//NULL, NULL, NULL, NULL, CVT_W, NULL, NULL, NULL, +//MAX_S, MIN_S, NULL, NULL, NULL, NULL, NULL, NULL, +//C_F, NULL, C_EQ, NULL, C_LT, NULL, C_LE, NULL, +//NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +__forceinline void BSCPropagate::rpropCP1() +{ + switch(_Rs_) { + case 0: // mfc1 + rpropSetWrite(_Rt_, EEINST_LIVE1|EEINST_REALXMM); + rpropSetFPURead(_Fs_, EEINST_REALXMM); + break; + case 2: // cfc1 + rpropSetWrite(_Rt_, EEINST_LIVE1|EEINST_REALXMM|EEINST_MMX); + break; + case 4: // mtc1 + rpropSetFPUWrite(_Fs_, EEINST_REALXMM); + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_REALXMM); + break; + case 6: // ctc1 + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_REALXMM|EEINST_MMX); + break; + case 8: // bc1 + // reset since don't know which path to go + _recClearInst(&prev); + prev.info = 0; + break; + case 16: + // floating point ops + pinst.info |= EEINST_REALXMM; + switch( _Funct_ ) { + case 0: // add.s + case 1: // sub.s + case 2: // mul.s + case 3: // div.s + case 22: // rsqrt.s + case 40: // max.s + case 41: // min.s + rpropSetFPUWrite(_Fd_, EEINST_REALXMM); + rpropSetFPURead(_Fs_, EEINST_REALXMM); + rpropSetFPURead(_Ft_, EEINST_REALXMM); + break; + case 4: // sqrt.s + rpropSetFPUWrite(_Fd_, EEINST_REALXMM); + rpropSetFPURead(_Ft_, EEINST_REALXMM); + break; + case 5: // abs.s + case 6: // mov.s + case 7: // neg.s + case 36: // cvt.w + rpropSetFPUWrite(_Fd_, EEINST_REALXMM); + rpropSetFPURead(_Fs_, EEINST_REALXMM); + break; + case 24: // adda.s + case 25: // suba.s + case 26: // mula.s + rpropSetFPUWrite(XMMFPU_ACC, EEINST_REALXMM); + rpropSetFPURead(_Fs_, EEINST_REALXMM); + rpropSetFPURead(_Ft_, EEINST_REALXMM); + break; + case 28: // madd.s + case 29: // msub.s + rpropSetFPUWrite(_Fd_, EEINST_REALXMM); + rpropSetFPURead(XMMFPU_ACC, EEINST_REALXMM); + rpropSetFPURead(_Fs_, EEINST_REALXMM); + rpropSetFPURead(_Ft_, EEINST_REALXMM); + break; + + case 30: // madda.s + case 31: // msuba.s + rpropSetFPUWrite(XMMFPU_ACC, EEINST_REALXMM); + rpropSetFPURead(XMMFPU_ACC, EEINST_REALXMM); + rpropSetFPURead(_Fs_, EEINST_REALXMM); + rpropSetFPURead(_Ft_, EEINST_REALXMM); + break; + + case 48: // c.f + case 50: // c.eq + case 52: // c.lt + case 54: // c.le + rpropSetFPURead(_Fs_, EEINST_REALXMM); + rpropSetFPURead(_Ft_, EEINST_REALXMM); + break; + default: assert(0); + } + break; + case 20: + assert( _Funct_ == 32 ); // CVT.S.W + rpropSetFPUWrite(_Fd_, EEINST_REALXMM); + rpropSetFPURead(_Fs_, EEINST_REALXMM); + break; + default: + assert(0); + } +} + +#undef _Ft_ +#undef _Fs_ +#undef _Fd_ + +__forceinline void BSCPropagate::rpropCP2() +{ + switch(_Rs_) { + case 1: // qmfc2 + rpropSetWrite(_Rt_, EEINST_LIVE2|EEINST_LIVE1); + break; + + case 2: // cfc2 + rpropSetWrite(_Rt_, EEINST_LIVE1); + break; + + case 5: // qmtc2 + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_LIVE2); + break; + + case 6: // ctc2 + rpropSetRead(_Rt_, 0); + break; + + case 8: // bc2 + break; + + default: + // vu macro mode insts + pinst.info |= EEINSTINFO_COP2; + break; + } +} + +//MADD, MADDU, NULL, NULL, PLZCW, NULL, NULL, NULL, +//MMI0, MMI2, NULL, NULL, NULL, NULL, NULL, NULL, +//MFHI1, MTHI1, MFLO1, MTLO1, NULL, NULL, NULL, NULL, +//MULT1, MULTU1, DIV1, DIVU1, NULL, NULL, NULL, NULL, +//MADD1, MADDU1, NULL, NULL, NULL, NULL, NULL, NULL, +//MMI1 , MMI3, NULL, NULL, NULL, NULL, NULL, NULL, +//PMFHL, PMTHL, NULL, NULL, PSLLH, NULL, PSRLH, PSRAH, +//NULL, NULL, NULL, NULL, PSLLW, NULL, PSRLW, PSRAW, +__forceinline void BSCPropagate::rpropMMI() +{ + switch(cpuRegs.code&0x3f) { + case 0: // madd + case 1: // maddu + rpropSetLOHI(_Rd_, _Rs_, _Rt_, EEINST_LIVE1); + break; + case 4: // plzcw + rpropSetFast(_Rd_, _Rs_, 0, EEINST_LIVE1); + break; + case 8: rpropMMI0(); break; + case 9: rpropMMI2(); break; + + case 16: // mfhi1 + { + rpropSetWrite(_Rd_, EEINST_LIVE1); + int temp = ((pinst.regs[_Rd_]&(EEINST_MMX|EEINST_REALXMM))?EEINST_MMX:EEINST_REALXMM); + rpropSetRead(XMMGPR_HI, temp|EEINST_LIVE2); + break; + } + case 17: // mthi1 + rpropSetWrite0<0>(XMMGPR_HI, EEINST_LIVE2); + rpropSetRead(_Rs_, EEINST_LIVE1); + break; + case 18: // mflo1 + { + rpropSetWrite(_Rd_, EEINST_LIVE1); + int temp = ((pinst.regs[_Rd_]&(EEINST_MMX|EEINST_REALXMM))?EEINST_MMX:EEINST_REALXMM); + rpropSetRead(XMMGPR_LO, temp|EEINST_LIVE2); + break; + } + case 19: // mtlo1 + rpropSetWrite0<0>(XMMGPR_LO, EEINST_LIVE2); + rpropSetRead(_Rs_, EEINST_LIVE1); + break; + + case 24: // mult1 + { + int temp = (pinst.regs[XMMGPR_HI]&(EEINST_LIVE2))?0:EEINST_MMX; + rpropSetWrite0<0>(XMMGPR_LO, EEINST_LIVE2); + rpropSetWrite0<0>(XMMGPR_HI, EEINST_LIVE2); + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rs_, temp); + rpropSetRead(_Rt_, temp); + pinst.info |= temp; + break; + } + case 25: // multu1 + rpropSetWrite0<0>(XMMGPR_LO, EEINST_LIVE2); + rpropSetWrite0<0>(XMMGPR_HI, EEINST_LIVE2); + rpropSetWrite(_Rd_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_MMX); + rpropSetRead(_Rt_, EEINST_MMX); + pinst.info |= EEINST_MMX; + break; + + case 26: // div1 + case 27: // divu1 + rpropSetWrite0<0>(XMMGPR_LO, EEINST_LIVE2); + rpropSetWrite0<0>(XMMGPR_HI, EEINST_LIVE2); + rpropSetRead(_Rs_, 0); + rpropSetRead(_Rt_, 0); + //pinst.info |= EEINST_REALXMM|EEINST_MMX; + break; + + case 32: // madd1 + case 33: // maddu1 + rpropSetWrite0<0>(XMMGPR_LO, EEINST_LIVE2); + rpropSetWrite0<0>(XMMGPR_HI, EEINST_LIVE2); + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE1); + rpropSetRead(XMMGPR_LO, EEINST_LIVE2); + rpropSetRead(XMMGPR_HI, EEINST_LIVE2); + break; + + case 40: rpropMMI1(); break; + case 41: rpropMMI3(); break; + + case 48: // pmfhl + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(XMMGPR_LO, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + rpropSetRead(XMMGPR_HI, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + + case 49: // pmthl + rpropSetWrite(XMMGPR_LO, EEINST_LIVE2|EEINST_LIVE1); + rpropSetWrite(XMMGPR_HI, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + + default: + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE1|EEINST_LIVE2|EEINST_REALXMM); + break; + } +} + +//recPADDW, PSUBW, PCGTW, PMAXW, +//PADDH, PSUBH, PCGTH, PMAXH, +//PADDB, PSUBB, PCGTB, NULL, +//NULL, NULL, NULL, NULL, +//PADDSW, PSUBSW, PEXTLW, PPACW, +//PADDSH, PSUBSH, PEXTLH, PPACH, +//PADDSB, PSUBSB, PEXTLB, PPACB, +//NULL, NULL, PEXT5, PPAC5, +__forceinline void BSCPropagate::rpropMMI0() +{ + switch((cpuRegs.code>>6)&0x1f) { + case 16: // paddsw + case 17: // psubsw + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE1|EEINST_LIVE2); + break; + + case 18: // pextlw + case 22: // pextlh + case 26: // pextlb + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1|EEINST_REALXMM); + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_REALXMM); + break; + + case 30: // pext5 + case 31: // ppac5 + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE1|EEINST_LIVE2); + break; + + default: + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE1|EEINST_LIVE2|EEINST_REALXMM); + break; + } +} + +__forceinline void BSCPropagate::rpropMMI1() +{ + switch((cpuRegs.code>>6)&0x1f) { + case 1: // pabsw + case 5: // pabsh + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + + case 17: // psubuw + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE1|EEINST_LIVE2); + break; + + case 18: // pextuw + case 22: // pextuh + case 26: // pextub + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE2|EEINST_REALXMM); + rpropSetRead(_Rt_, EEINST_LIVE2|EEINST_REALXMM); + break; + + default: + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE1|EEINST_LIVE2|EEINST_REALXMM); + break; + } +} + +__forceinline void BSCPropagate::rpropMMI2() +{ + switch((cpuRegs.code>>6)&0x1f) { + case 0: // pmaddw + case 4: // pmsubw + rpropSetLOHI + (_Rd_, _Rs_, _Rt_, EEINST_LIVE2|EEINST_LIVE1); + break; + case 2: // psllvw + case 3: // psllvw + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE2); + break; + case 8: // pmfhi + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(XMMGPR_HI, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + case 9: // pmflo + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(XMMGPR_HI, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + case 10: // pinth + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE2|EEINST_REALXMM); + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_REALXMM); + break; + case 12: // pmultw, + rpropSetLOHI + (_Rd_, _Rs_, _Rt_, EEINST_LIVE2|EEINST_LIVE1); + break; + case 13: // pdivw + rpropSetLOHI + (0, _Rs_, _Rt_, EEINST_LIVE2|EEINST_LIVE1); + break; + case 14: // pcpyld + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1|EEINST_REALXMM); + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_REALXMM); + break; + case 16: // pmaddh + case 17: // phmadh + case 20: // pmsubh + case 21: // phmsbh + rpropSetLOHI + (_Rd_, _Rs_, _Rt_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + + case 26: // pexeh + case 27: // prevh + case 30: // pexew + case 31: // prot3w + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + + case 28: // pmulth + rpropSetLOHI + (_Rd_, _Rs_, _Rt_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + case 29: // pdivbw + rpropSetLOHI + (_Rd_, _Rs_, _Rt_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + + default: + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE1|EEINST_LIVE2|EEINST_REALXMM); + break; + } +} + +__forceinline void BSCPropagate::rpropMMI3() +{ + switch((cpuRegs.code>>6)&0x1f) { + case 0: // pmadduw + rpropSetLOHI + (_Rd_, _Rs_, _Rt_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM ); + break; + case 3: // psravw + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE2); + break; + + case 8: // pmthi + rpropSetWrite(XMMGPR_HI, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + case 9: // pmtlo + rpropSetWrite(XMMGPR_LO, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + case 12: // pmultuw, + rpropSetLOHI + (_Rd_, _Rs_, _Rt_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + case 13: // pdivuw + rpropSetLOHI + (0, _Rs_, _Rt_, EEINST_LIVE2|EEINST_LIVE1); + break; + case 14: // pcpyud + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE2|EEINST_REALXMM); + rpropSetRead(_Rt_, EEINST_LIVE2|EEINST_REALXMM); + break; + + case 26: // pexch + case 27: // pcpyh + case 30: // pexcw + rpropSetWrite(_Rd_, EEINST_LIVE2|EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE2|EEINST_LIVE1|EEINST_REALXMM); + break; + + default: + rpropSetFast(_Rd_, _Rs_, _Rt_, EEINST_LIVE1|EEINST_LIVE2|EEINST_REALXMM); + break; + } +} + +//SPECIAL, REGIMM, J, JAL, BEQ, BNE, BLEZ, BGTZ, +//ADDI, ADDIU, SLTI, SLTIU, ANDI, ORI, XORI, LUI, +//COP0, COP1, COP2, NULL, BEQL, BNEL, BLEZL, BGTZL, +//DADDI, DADDIU, LDL, LDR, MMI, NULL, LQ, SQ, +//LB, LH, LWL, LW, LBU, LHU, LWR, LWU, +//SB, SH, SWL, SW, SDL, SDR, SWR, CACHE, +//NULL, LWC1, NULL, PREF, NULL, NULL, LQC2, LD, +//NULL, SWC1, NULL, NULL, NULL, NULL, SQC2, SD +void BSCPropagate::rprop() +{ + switch(cpuRegs.code >> 26) { + case 0: rpropSPECIAL(); break; + case 1: rpropREGIMM(); break; + case 2: // j + break; + case 3: // jal + rpropSetWrite(31, EEINST_LIVE1); + break; + case 4: // beq + case 5: // bne + rpropSetRead(_Rs_, EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE1); + pinst.info |= EEINST_REALXMM|EEINST_MMX; + break; + + case 20: // beql + case 21: // bnel + // reset since don't know which path to go + _recClearInst(&prev); + prev.info = 0; + rpropSetRead(_Rs_, EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE1); + pinst.info |= EEINST_REALXMM|EEINST_MMX; + break; + + case 6: // blez + case 7: // bgtz + rpropSetRead(_Rs_, EEINST_LIVE1); + break; + + case 22: // blezl + case 23: // bgtzl + // reset since don't know which path to go + _recClearInst(&prev); + prev.info = 0; + rpropSetRead(_Rs_, EEINST_LIVE1); + break; + + case 24: // daddi + case 25: // daddiu + rpropSetWrite(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1 | (_Rs_!=0?EEINST_MMX:0)); // This looks like what ZeroFrog wanted; ToDo: Needs checking! (cottonvibes) + break; + + case 8: // addi + case 9: // addiu + rpropSetWrite(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rs_, 0); + pinst.info |= EEINST_MMX; + break; + + case 10: // slti + rpropSetWrite(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1); + pinst.info |= EEINST_MMX; + break; + case 11: // sltiu + rpropSetWrite(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1); + pinst.info |= EEINST_MMX; + break; + + case 12: // andi + rpropSetWrite(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rs_, (_Rs_!=_Rt_?EEINST_MMX:0)); + pinst.info |= EEINST_MMX; + break; + case 13: // ori + rpropSetWrite(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1|(_Rs_!=_Rt_?EEINST_MMX:0)); + pinst.info |= EEINST_MMX; + break; + case 14: // xori + rpropSetWrite(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1|(_Rs_!=_Rt_?EEINST_MMX:0)); + pinst.info |= EEINST_MMX; + break; + + case 15: // lui + rpropSetWrite(_Rt_, EEINST_LIVE1); + break; + + case 16: rpropCP0(); break; + case 17: rpropCP1(); break; + case 18: rpropCP2(); break; + + // loads + case 34: // lwl + case 38: // lwr + case 26: // ldl + case 27: // ldr + rpropSetWrite(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rs_, 0); + break; + + case 32: case 33: case 35: case 36: case 37: case 39: + case 55: // LD + rpropSetWrite(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rs_, 0); + break; + + case 28: rpropMMI(); break; + + case 30: // lq + rpropSetWrite(_Rt_, EEINST_LIVE1|EEINST_LIVE2); + rpropSetRead(_Rs_, 0); + pinst.info |= EEINST_MMX; + pinst.info |= EEINST_REALXMM; + break; + + case 31: // sq + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_LIVE2|EEINST_REALXMM); + rpropSetRead(_Rs_, 0); + pinst.info |= EEINST_MMX; + pinst.info |= EEINST_REALXMM; + break; + + // 4 byte stores + case 40: case 41: case 42: case 43: case 46: + rpropSetRead(_Rt_, 0); + rpropSetRead(_Rs_, 0); + pinst.info |= EEINST_MMX; + pinst.info |= EEINST_REALXMM; + break; + + case 44: // sdl + case 45: // sdr + case 63: // sd + rpropSetRead(_Rt_, EEINST_LIVE1|EEINST_MMX|EEINST_REALXMM); + rpropSetRead(_Rs_, 0); + break; + + case 49: // lwc1 + rpropSetFPUWrite(_Rt_, EEINST_REALXMM); + rpropSetRead(_Rs_, 0); + break; + + case 57: // swc1 + rpropSetFPURead(_Rt_, EEINST_REALXMM); + rpropSetRead(_Rs_, 0); + break; + + case 54: // lqc2 + case 62: // sqc2 + rpropSetRead(_Rs_, 0); + break; + + default: + rpropSetWrite(_Rt_, EEINST_LIVE1); + rpropSetRead(_Rs_, EEINST_LIVE1|(_Rs_!=0?EEINST_MMX:0)); + break; + } +} // End namespace OpcodeImpl diff --git a/pcsx2/x86/ix86-32/aR5900-32.S b/pcsx2/x86/ix86-32/aR5900-32.S new file mode 100644 index 0000000000..a6988d8377 --- /dev/null +++ b/pcsx2/x86/ix86-32/aR5900-32.S @@ -0,0 +1,210 @@ +// iR5900.c assembly routines +// zerofrog(@gmail.com) +.intel_syntax + +.extern cpuRegs +.extern recRecompile +//.extern recLUT +.extern lbase +.extern s_pCurBlock_ltime + +#define BLOCKTYPE_STARTPC 4 // startpc offset +#define BLOCKTYPE_DELAYSLOT 1 // if bit set, delay slot + +#define BASEBLOCK_SIZE 2 // in dwords +#define PCOFFSET 0x2a8 // this must always match what Pcsx2 displays at startup + +#define REG_BLOCK %esi + +////////////////////////////////////////////////////////////////////////// +// Recompiles the next block, and links the old block directly to it. +// This is a on-shot execution for ny block which uses it. Once the block +// has been statically linked to the new block, this function will be bypassed +// +.globl Dispatcher +Dispatcher: + # EDX contains the jump addr to modify + push %edx + + # calc PC_GETBLOCK + # ((BASEBLOCK*)(recLUT[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff))) + + mov %eax,dword ptr [cpuRegs+PCOFFSET] + mov %ecx,%eax // ecx is the BLOCK address + mov %esi,%eax // esi is the PC address (leave unmodified!) + shr %eax,0x10 + and %ecx,0xFFFF + mov %edx,dword ptr [recLUT] + mov %eax,dword ptr [%edx+%eax*4] + lea %ecx,[%eax+%ecx*2] + + // check if startpc == cpuRegs.pc + //and %ecx, 0x5fffffff // remove higher bits + cmp %esi, dword ptr [%ecx+BLOCKTYPE_STARTPC] + je Dispatcher_CheckPtr + + // recompile + push %ecx + push %esi // pc + call recRecompile + add %esp, 4 + pop %ecx // ecx is now the REG_BLOCK +Dispatcher_CheckPtr: + mov %eax, dword ptr [%ecx] + +#ifdef _DEBUG + test %eax, %eax + jnz Dispatcher_CallFn + // throw an exception + int 10 + +Dispatcher_CallFn: +#endif + //and %eax, 0x0fffffff + shl %eax, 4 + pop %ecx // x86Ptr to mod + mov %edx, %eax + sub %edx, %ecx + sub %edx, 4 + mov dword ptr [%ecx], %edx + + jmp %eax + +////////////////////////////////////////////////////////////////////////// +// edx - baseblock->startpc +// stack - x86Ptr +.globl DispatcherClear +DispatcherClear: + // EDX contains the current pc + mov dword ptr [cpuRegs + PCOFFSET], %edx + + // calc PC_GETBLOCK + # ((BASEBLOCK*)(recLUT[((u32)(x)) >> 16] + (sizeof(BASEBLOCK)/4)*((x) & 0xffff))) + mov %eax, %edx + mov REG_BLOCK, %edx + shr %eax, 16 + and REG_BLOCK, 0xffff + shl %eax, 2 + add %eax, dword ptr [recLUT] + shl REG_BLOCK, 1 + add REG_BLOCK, dword ptr [%eax] + + cmp %edx, dword ptr [REG_BLOCK + 4] + jne DispatcherClear_Recompile + + add %esp, 4 // ignore stack + mov %eax, dword ptr [REG_BLOCK] + +#ifdef _DEBUG + test %eax, %eax + jnz DispatcherClear_CallFn + # throw an exception + int 10 + +DispatcherClear_CallFn: +#endif + + //and %eax, 0x0fffffff + shl %eax, 4 + jmp %eax + +DispatcherClear_Recompile: + push REG_BLOCK + push %edx + call recRecompile + add %esp, 4 // pop old param + pop REG_BLOCK + mov %eax, dword ptr [REG_BLOCK] + + pop %ecx // old fnptr + + //and %eax, 0x0fffffff + shl %eax, 4 + mov byte ptr [%ecx], 0xe9 // jmp32 + mov %edx, %eax + sub %edx, %ecx + sub %edx, 5 + mov dword ptr [%ecx+1], %edx + + jmp %eax + +////////////////////////////////////////////////////////////////////////// +// called when jumping to variable pc address +// This is basically the same as Dispatcher but without the part at the end +// that modifies the block's jmp instruction. (ie, no static block linking) + +.globl DispatcherReg +DispatcherReg: + + mov %eax,dword ptr [cpuRegs+PCOFFSET] + mov %ecx,%eax // ecx will be the BLOCK + mov %esi,%eax // esi is the PC address (leave unmodified!) + shr %eax,0x10 + and %ecx,0xFFFF + mov %edx,dword ptr [recLUT] + mov %eax,dword ptr [%edx+%eax*4] + lea %ecx,[%eax+%ecx*2] + + // check if startpc == cpuRegs.pc + //and %ecx, 0x5fffffff // remove higher bits + cmp %esi, dword ptr [%ecx+BLOCKTYPE_STARTPC] + je DispatcherReg_CheckPtr + + // recompile + push %ecx // block + push %esi // pc + call recRecompile + add %esp, 4 + pop %ecx // block +DispatcherReg_CheckPtr: + mov %eax, dword ptr [%ecx] + +#ifdef _DEBUG + test %eax, %eax + jnz DispatcherReg_CallFn + // throw an exception + int 10 + +DispatcherReg_CallFn: +#endif + //and %eax, 0x0fffffff + shl %eax, 4 + jmp %eax + + +.globl _StartPerfCounter +_StartPerfCounter: + + push %eax + push %ebx + push %ecx + + rdtsc + mov dword ptr [lbase], %eax + mov dword ptr [lbase + 4], %edx + + pop %ecx + pop %ebx + pop %eax + ret + +.globl _StopPerfCounter +_StopPerfCounter: + + push %eax + push %ebx + push %ecx + + rdtsc + + sub %eax, dword ptr [lbase] + sbb %edx, dword ptr [lbase + 4] + mov %ecx, s_pCurBlock_ltime + add %eax, dword ptr [%ecx] + adc %edx, dword ptr [%ecx + 4] + mov dword ptr [%ecx], %eax + mov dword ptr [%ecx + 4], %edx + pop %ecx + pop %ebx + pop %eax + ret diff --git a/pcsx2/x86/ix86-32/aVif_proc-32.asm b/pcsx2/x86/ix86-32/aVif_proc-32.asm new file mode 100644 index 0000000000..12c8b969b4 --- /dev/null +++ b/pcsx2/x86/ix86-32/aVif_proc-32.asm @@ -0,0 +1,1839 @@ + +.686 +.model flat, c +.mmx +.xmm + + +extern _vifRegs:ptr +extern _vifMaskRegs:ptr +extern _vifRow:ptr +extern s_TempDecompress:ptr + + +.code + +UNPACK_Write0_Regular macro r0, CL, DEST_OFFSET, MOVDQA + MOVDQA [edi+DEST_OFFSET], r0 + endm + +UNPACK_Write1_Regular macro r0, CL, DEST_OFFSET, MOVDQA + MOVDQA [edi], r0 + add edi, ecx + endm + +UNPACK_Write0_Mask macro r0, CL, DEST_OFFSET, MOVDQA + UNPACK_Write0_Regular r0, CL, DEST_OFFSET, MOVDQA + endm + +UNPACK_Write1_Mask macro r0, CL, DEST_OFFSET, MOVDQA + UNPACK_Write1_Regular r0, CL, DEST_OFFSET, MOVDQA + endm + + +UNPACK_Write0_WriteMask macro r0, CL, DEST_OFFSET, MOVDQA + + movdqa xmm3, [eax + 64*(CL) + 48] + pand r0, xmm3 + pandn xmm3, [edi] + por r0, xmm3 + MOVDQA [edi], r0 + add edi, 16 + endm + + +UNPACK_Write1_WriteMask macro r0, CL, DEST_OFFSET, MOVDQA + + movdqa xmm3, [eax + 64*(0) + 48] + pand r0, xmm3 + pandn xmm3, [edi] + por r0, xmm3 + MOVDQA [edi], r0 + add edi, ecx + endm + +UNPACK_Mask_SSE_0 macro r0 + pand r0, xmm3 + por r0, xmm5 + endm + + + + +UNPACK_Mask_SSE_1 macro r0 + + pand r0, xmm3 + por r0, xmm5 + pand xmm3, xmm6 + paddd r0, xmm3 + endm + + + +UNPACK_Mask_SSE_2 macro r0 + + + pand r0, xmm3 + pand xmm3, xmm6 + paddd xmm6, r0 + por r0, xmm5 + paddd r0, xmm3 + endm + +UNPACK_WriteMask_SSE_0 macro r0 + UNPACK_Mask_SSE_0 r0 + endm +UNPACK_WriteMask_SSE_1 macro r0 + UNPACK_Mask_SSE_1 r0 + endm +UNPACK_WriteMask_SSE_2 macro r0 + UNPACK_Mask_SSE_2 r0 + endm + +UNPACK_Regular_SSE_0 macro r0 + endm + +UNPACK_Regular_SSE_1 macro r0 + paddd r0, xmm6 + endm + +UNPACK_Regular_SSE_2 macro r0 + paddd r0, xmm6 + movdqa xmm6, r0 + endm + + +UNPACK_Setup_Mask_SSE macro CL + mov eax, [_vifMaskRegs] + movdqa xmm4, [eax + 64*(CL) + 16] + movdqa xmm5, [eax + 64*(CL) + 32] + movdqa xmm3, [eax + 64*(CL)] + pand xmm4, xmm6 + pand xmm5, xmm7 + por xmm5, xmm4 + endm + +UNPACK_Start_Setup_Mask_SSE_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm + +UNPACK_Start_Setup_Mask_SSE_1 macro CL + mov eax, [_vifMaskRegs] + movdqa xmm4, [eax + 64*(CL) + 16] + movdqa xmm5, [eax + 64*(CL) + 32] + pand xmm4, xmm6 + pand xmm5, xmm7 + por xmm5, xmm4 + endm + +UNPACK_Start_Setup_Mask_SSE_2 macro CL + endm + +UNPACK_Setup_Mask_SSE_0_1 macro CL + endm +UNPACK_Setup_Mask_SSE_1_1 macro CL + mov eax, [_vifMaskRegs] + movdqa xmm3, [eax + 64*(0)] + endm + + +UNPACK_Setup_Mask_SSE_2_1 macro CL + + mov eax, [_vifMaskRegs] + movdqa xmm4, [eax + 64*(0) + 16] + movdqa xmm5, [eax + 64*(0) + 32] + movdqa xmm3, [eax + 64*(0)] + pand xmm4, xmm6 + pand xmm5, xmm7 + por xmm5, xmm4 + endm + +UNPACK_Setup_Mask_SSE_0_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm +UNPACK_Setup_Mask_SSE_1_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm +UNPACK_Setup_Mask_SSE_2_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm + + +UNPACK_Setup_WriteMask_SSE_0_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm +UNPACK_Setup_WriteMask_SSE_1_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm +UNPACK_Setup_WriteMask_SSE_2_0 macro CL + UNPACK_Setup_Mask_SSE CL + endm +UNPACK_Setup_WriteMask_SSE_0_1 macro CL + UNPACK_Setup_Mask_SSE_1_1 CL + endm + +UNPACK_Setup_WriteMask_SSE_1_1 macro CL + UNPACK_Setup_Mask_SSE_1_1 CL + endm + +UNPACK_Setup_WriteMask_SSE_2_1 macro CL + UNPACK_Setup_Mask_SSE_2_1 CL + endm + +UNPACK_Start_Setup_WriteMask_SSE_0 macro CL + UNPACK_Start_Setup_Mask_SSE_1 CL + endm +UNPACK_Start_Setup_WriteMask_SSE_1 macro CL + UNPACK_Start_Setup_Mask_SSE_1 CL + endm +UNPACK_Start_Setup_WriteMask_SSE_2 macro CL + UNPACK_Start_Setup_Mask_SSE_2 CL + endm + +UNPACK_Start_Setup_Regular_SSE_0 macro CL + endm +UNPACK_Start_Setup_Regular_SSE_1 macro CL + endm +UNPACK_Start_Setup_Regular_SSE_2 macro CL + endm +UNPACK_Setup_Regular_SSE_0_0 macro CL + endm +UNPACK_Setup_Regular_SSE_1_0 macro CL + endm +UNPACK_Setup_Regular_SSE_2_0 macro CL + endm +UNPACK_Setup_Regular_SSE_0_1 macro CL + endm +UNPACK_Setup_Regular_SSE_1_1 macro CL + endm +UNPACK_Setup_Regular_SSE_2_1 macro CL + endm + +UNPACK_INC_DST_0_Regular macro qw + add edi, (16*qw) + endm +UNPACK_INC_DST_1_Regular macro qw + endm +UNPACK_INC_DST_0_Mask macro qw + add edi, (16*qw) + endm +UNPACK_INC_DST_1_Mask macro qw + endm +UNPACK_INC_DST_0_WriteMask macro qw + endm +UNPACK_INC_DST_1_WriteMask macro qw + endm + + +UNPACK4_SSE macro CL, TOTALCL, MaskType, ModeType + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+0 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm0, CL, 0, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+1 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm1 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm1, CL+1, 16, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+2 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm2 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm2, CL+2, 32, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+3 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm7 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm7, CL+3, 48, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 4 + endm + + +UNPACK3_SSE macro CL, TOTALCL, MaskType, ModeType + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm0, CL, 0, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+1 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm1 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm1, CL+1, 16, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+2 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm2 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm2, CL+2, 32, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 3 + endm + +UNPACK2_SSE macro CL, TOTALCL, MaskType, ModeType + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm0, CL, 0, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+1 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm1 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm1, CL+1, 16, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 2 + endm + +UNPACK1_SSE macro CL, TOTALCL, MaskType, ModeType + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm0, CL, 0, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 1 + endm + + + +UNPACK_S_32SSE_4x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA xmm7, [esi] + + pshufd xmm0, xmm7, 0 + pshufd xmm1, xmm7, 055h + pshufd xmm2, xmm7, 0aah + pshufd xmm7, xmm7, 0ffh + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 16 + endm + +UNPACK_S_32SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_4x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_S_32SSE_4 macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_4x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +UNPACK_S_32SSE_3x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA xmm2, [esi] + + pshufd xmm0, xmm2, 0 + pshufd xmm1, xmm2, 055h + pshufd xmm2, xmm2, 0aah + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 12 + endm + +UNPACK_S_32SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_3x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_S_32SSE_3 macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_3x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +UNPACK_S_32SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq xmm1, QWORD PTR [esi] + + pshufd xmm0, xmm1, 0 + pshufd xmm1, xmm1, 055h + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + +UNPACK_S_32SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_32SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd xmm0, dword ptr [esi] + pshufd xmm0, xmm0, 0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 4 + endm + +UNPACK_S_32SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_32SSE_1 CL, TOTALCL, MaskType, ModeType + endm + + +UNPACK_S_16SSE_4 macro CL, TOTALCL, MaskType, ModeType + movq xmm7, QWORD PTR [esi] + punpcklwd xmm7, xmm7 + UNPACK_RIGHTSHIFT xmm7, 16 + + pshufd xmm0, xmm7, 0 + pshufd xmm1, xmm7, 055h + pshufd xmm2, xmm7, 0aah + pshufd xmm7, xmm7, 0ffh + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + +UNPACK_S_16SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_16SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_16SSE_3 macro CL, TOTALCL, MaskType, ModeType + movq xmm2, QWORD PTR [esi] + punpcklwd xmm2, xmm2 + UNPACK_RIGHTSHIFT xmm2, 16 + + pshufd xmm0, xmm2, 0 + pshufd xmm1, xmm2, 055h + pshufd xmm2, xmm2, 0aah + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + add esi, 6 + endm + +UNPACK_S_16SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_16SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_16SSE_2 macro CL, TOTALCL, MaskType, ModeType + movd xmm1, dword ptr [esi] + punpcklwd xmm1, xmm1 + UNPACK_RIGHTSHIFT xmm1, 16 + + pshufd xmm0, xmm1, 0 + pshufd xmm1, xmm1, 055h + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 4 + endm + +UNPACK_S_16SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_16SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_16SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd xmm0, dword ptr [esi] + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 16 + pshufd xmm0, xmm0, 0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 2 + endm + +UNPACK_S_16SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_16SSE_1 CL, TOTALCL, MaskType, ModeType + endm + + +UNPACK_S_8SSE_4 macro CL, TOTALCL, MaskType, ModeType + movd xmm7, dword ptr [esi] + punpcklbw xmm7, xmm7 + punpcklwd xmm7, xmm7 + UNPACK_RIGHTSHIFT xmm7, 24 + + pshufd xmm0, xmm7, 0 + pshufd xmm1, xmm7, 055h + pshufd xmm2, xmm7, 0aah + pshufd xmm7, xmm7, 0ffh + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 4 + endm + +UNPACK_S_8SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_8SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_8SSE_3 macro CL, TOTALCL, MaskType, ModeType + movd xmm2, dword ptr [esi] + punpcklbw xmm2, xmm2 + punpcklwd xmm2, xmm2 + UNPACK_RIGHTSHIFT xmm2, 24 + + pshufd xmm0, xmm2, 0 + pshufd xmm1, xmm2, 055h + pshufd xmm2, xmm2, 0aah + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 3 + endm + +UNPACK_S_8SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_8SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_8SSE_2 macro CL, TOTALCL, MaskType, ModeType + movd xmm1, dword ptr [esi] + punpcklbw xmm1, xmm1 + punpcklwd xmm1, xmm1 + UNPACK_RIGHTSHIFT xmm1, 24 + + pshufd xmm0, xmm1, 0 + pshufd xmm1, xmm1, 055h + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 2 + endm + +UNPACK_S_8SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_8SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_S_8SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd xmm0, dword ptr [esi] + punpcklbw xmm0, xmm0 + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 24 + pshufd xmm0, xmm0, 0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + inc esi + endm + +UNPACK_S_8SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_S_8SSE_1 CL, TOTALCL, MaskType, ModeType + endm + + +UNPACK_V2_32SSE_4A macro CL, TOTALCL, MaskType, ModeType + MOVDQA xmm0, [esi] + MOVDQA xmm2, [esi+16] + + pshufd xmm1, xmm0, 0eeh + pshufd xmm7, xmm2, 0eeh + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 32 + endm + +UNPACK_V2_32SSE_4 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + movq xmm1, QWORD PTR [esi+8] + movq xmm2, QWORD PTR [esi+16] + movq xmm7, QWORD PTR [esi+24] + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 32 + endm + +UNPACK_V2_32SSE_3A macro CL, TOTALCL, MaskType, ModeType + MOVDQA xmm0, [esi] + movq xmm2, QWORD PTR [esi+16] + pshufd xmm1, xmm0, 0eeh + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 24 + endm + +UNPACK_V2_32SSE_3 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + movq xmm1, QWORD PTR [esi+8] + movq xmm2, QWORD PTR [esi+16] + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 24 + endm + +UNPACK_V2_32SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + movq xmm1, QWORD PTR [esi+8] + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 16 + endm + +UNPACK_V2_32SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_32SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V2_32SSE_1 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + +UNPACK_V2_32SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_32SSE_1 CL, TOTALCL, MaskType, ModeType + endm + + +UNPACK_V2_16SSE_4A macro CL, TOTALCL, MaskType, ModeType + punpcklwd xmm0, [esi] + punpckhwd xmm2, [esi] + + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm2, 16 + + punpckhqdq xmm1, xmm0 + punpckhqdq xmm7, xmm2 + + punpcklqdq xmm0, xmm0 + punpcklqdq xmm2, xmm2 + punpckhqdq xmm1, xmm1 + punpckhqdq xmm7, xmm7 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + add esi, 16 + endm + +UNPACK_V2_16SSE_4 macro CL, TOTALCL, MaskType, ModeType + pxor xmm0, xmm0 + pxor xmm2, xmm2 + movdqu xmm0, [esi] + + punpckhwd xmm2, xmm0 + punpcklwd xmm0, xmm0 + + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm2, 16 + + punpckhqdq xmm1, xmm0 + punpckhqdq xmm7, xmm2 + + punpcklqdq xmm0, xmm0 + punpcklqdq xmm2, xmm2 + punpckhqdq xmm1, xmm1 + punpckhqdq xmm7, xmm7 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 16 + endm + +UNPACK_V2_16SSE_3A macro CL, TOTALCL, MaskType, ModeType + punpcklwd xmm0, [esi] + punpckhwd xmm2, [esi] + + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm2, 16 + + + punpckhqdq xmm1, xmm0 + + punpcklqdq xmm0, xmm0 + punpcklqdq xmm2, xmm2 + punpckhqdq xmm1, xmm1 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 12 + endm + +UNPACK_V2_16SSE_3 macro CL, TOTALCL, MaskType, ModeType + movdqu xmm0, [esi] + + punpckhwd xmm2, xmm0 + punpcklwd xmm0, xmm0 + + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm2, 16 + + + punpckhqdq xmm1, xmm0 + + punpcklqdq xmm0, xmm0 + punpcklqdq xmm2, xmm2 + punpckhqdq xmm1, xmm1 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 12 + endm + +UNPACK_V2_16SSE_2A macro CL, TOTALCL, MaskType, ModeType + punpcklwd xmm0, [esi] + UNPACK_RIGHTSHIFT xmm0, 16 + + + punpckhqdq xmm1, xmm0 + + punpcklqdq xmm0, xmm0 + punpckhqdq xmm1, xmm1 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + +UNPACK_V2_16SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 16 + + + punpckhqdq xmm1, xmm0 + + punpcklqdq xmm0, xmm0 + punpckhqdq xmm1, xmm1 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + +UNPACK_V2_16SSE_1A macro CL, TOTALCL, MaskType, ModeType + punpcklwd xmm0, [esi] + UNPACK_RIGHTSHIFT xmm0, 16 + + punpcklqdq xmm0, xmm0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 4 + endm + +UNPACK_V2_16SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd xmm0, dword ptr [esi] + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 16 + + punpcklqdq xmm0, xmm0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 4 + endm + + +UNPACK_V2_8SSE_4 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + + punpcklbw xmm0, xmm0 + punpckhwd xmm2, xmm0 + punpcklwd xmm0, xmm0 + + UNPACK_RIGHTSHIFT xmm0, 24 + UNPACK_RIGHTSHIFT xmm2, 24 + + punpckhqdq xmm1, xmm0 + punpckhqdq xmm7, xmm2 + + punpcklqdq xmm0, xmm0 + punpcklqdq xmm2, xmm2 + punpckhqdq xmm1, xmm1 + punpckhqdq xmm7, xmm7 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + +UNPACK_V2_8SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_8SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V2_8SSE_3 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + + punpcklbw xmm0, xmm0 + punpckhwd xmm2, xmm0 + punpcklwd xmm0, xmm0 + + UNPACK_RIGHTSHIFT xmm0, 24 + UNPACK_RIGHTSHIFT xmm2, 24 + + punpckhqdq xmm1, xmm0 + + punpcklqdq xmm0, xmm0 + punpcklqdq xmm2, xmm2 + punpckhqdq xmm1, xmm1 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 6 + endm + +UNPACK_V2_8SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_8SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V2_8SSE_2 macro CL, TOTALCL, MaskType, ModeType + movd xmm0, dword ptr [esi] + punpcklbw xmm0, xmm0 + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 24 + + punpckhqdq xmm1, xmm0 + + punpcklqdq xmm0, xmm0 + punpckhqdq xmm1, xmm1 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 4 + endm + +UNPACK_V2_8SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_8SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V2_8SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd xmm0, dword ptr [esi] + punpcklbw xmm0, xmm0 + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 24 + punpcklqdq xmm0, xmm0 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 2 + endm + +UNPACK_V2_8SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V2_8SSE_1 CL, TOTALCL, MaskType, ModeType + endm + + +UNPACK_V3_32SSE_4x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA xmm0, [esi] + movdqu xmm1, [esi+12] + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+0 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm0, CL, 0, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+1 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm1 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm1, CL+1, 16, movdqa + + + MOVDQA xmm7, [esi+32] + movdqu xmm2, [esi+24] + psrldq xmm7, 4 + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+2 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm2 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm2, CL+2, 32, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+3 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm7 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm7, CL+3, 48, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 4 + + add esi, 48 + endm + +UNPACK_V3_32SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_4x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_V3_32SSE_4 macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_4x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +UNPACK_V3_32SSE_3x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA xmm0, [esi] + movdqu xmm1, [esi+12] + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm0 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm0, CL, 0, movdqa + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+1 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm1 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm1, CL+1, 16, movdqa + + movdqu xmm2, [esi+24] + + @CatStr(UNPACK_Setup_, MaskType, _SSE_, ModeType, _, TOTALCL) CL+2 + @CatStr(UNPACK_, MaskType, _SSE_, ModeType) xmm2 + @CatStr(UNPACK_Write, TOTALCL, _, MaskType) xmm2, CL+2, 32, movdqa + + @CatStr(UNPACK_INC_DST_, TOTALCL, _, MaskType) 3 + + add esi, 36 + endm + +UNPACK_V3_32SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_3x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_V3_32SSE_3 macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_3x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +UNPACK_V3_32SSE_2x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA xmm0, [esi] + movdqu xmm1, [esi+12] + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 24 + endm + +UNPACK_V3_32SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_2x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_V3_32SSE_2 macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_2x CL, TOTALCL, MaskType, ModeType, movdqu + endm + +UNPACK_V3_32SSE_1x macro CL, TOTALCL, MaskType, ModeType, MOVDQA + MOVDQA xmm0, [esi] + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 12 + endm + +UNPACK_V3_32SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_1x CL, TOTALCL, MaskType, ModeType, movdqa + endm +UNPACK_V3_32SSE_1 macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_32SSE_1x CL, TOTALCL, MaskType, ModeType, movdqu + endm + + +UNPACK_V3_16SSE_4 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + movq xmm1, QWORD PTR [esi+6] + + punpcklwd xmm0, xmm0 + movq xmm2, QWORD PTR [esi+12] + punpcklwd xmm1, xmm1 + UNPACK_RIGHTSHIFT xmm0, 16 + movq xmm7, QWORD PTR [esi+18] + UNPACK_RIGHTSHIFT xmm1, 16 + punpcklwd xmm2, xmm2 + punpcklwd xmm7, xmm7 + + UNPACK_RIGHTSHIFT xmm2, 16 + UNPACK_RIGHTSHIFT xmm7, 16 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 24 + endm + +UNPACK_V3_16SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_16SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_16SSE_3 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + movq xmm1, QWORD PTR [esi+6] + + punpcklwd xmm0, xmm0 + movq xmm2, QWORD PTR [esi+12] + punpcklwd xmm1, xmm1 + UNPACK_RIGHTSHIFT xmm0, 16 + punpcklwd xmm2, xmm2 + + UNPACK_RIGHTSHIFT xmm1, 16 + UNPACK_RIGHTSHIFT xmm2, 16 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 18 + endm + +UNPACK_V3_16SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_16SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_16SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + movq xmm1, QWORD PTR [esi+6] + + punpcklwd xmm0, xmm0 + punpcklwd xmm1, xmm1 + + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm1, 16 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 12 + endm + +UNPACK_V3_16SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_16SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_16SSE_1 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 16 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 6 + endm + +UNPACK_V3_16SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_16SSE_1 CL, TOTALCL, MaskType, ModeType + endm + + +UNPACK_V3_8SSE_4 macro CL, TOTALCL, MaskType, ModeType + movq xmm1, QWORD PTR [esi] + movq xmm7, QWORD PTR [esi+6] + + punpcklbw xmm1, xmm1 + punpcklbw xmm7, xmm7 + punpcklwd xmm0, xmm1 + psrldq xmm1, 6 + punpcklwd xmm2, xmm7 + psrldq xmm7, 6 + punpcklwd xmm1, xmm1 + UNPACK_RIGHTSHIFT xmm0, 24 + punpcklwd xmm7, xmm7 + + UNPACK_RIGHTSHIFT xmm2, 24 + UNPACK_RIGHTSHIFT xmm1, 24 + UNPACK_RIGHTSHIFT xmm7, 24 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 12 + endm + +UNPACK_V3_8SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_8SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_8SSE_3 macro CL, TOTALCL, MaskType, ModeType + movd xmm0, dword ptr [esi] + movd xmm1, dword ptr [esi+3] + + punpcklbw xmm0, xmm0 + movd xmm2, dword ptr [esi+6] + punpcklbw xmm1, xmm1 + punpcklwd xmm0, xmm0 + punpcklbw xmm2, xmm2 + + punpcklwd xmm1, xmm1 + punpcklwd xmm2, xmm2 + + UNPACK_RIGHTSHIFT xmm0, 24 + UNPACK_RIGHTSHIFT xmm1, 24 + UNPACK_RIGHTSHIFT xmm2, 24 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 9 + endm + +UNPACK_V3_8SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_8SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_8SSE_2 macro CL, TOTALCL, MaskType, ModeType + movd xmm0, dword ptr [esi] + movd xmm1, dword ptr [esi+3] + + punpcklbw xmm0, xmm0 + punpcklbw xmm1, xmm1 + + punpcklwd xmm0, xmm0 + punpcklwd xmm1, xmm1 + + UNPACK_RIGHTSHIFT xmm0, 24 + UNPACK_RIGHTSHIFT xmm1, 24 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 6 + endm + +UNPACK_V3_8SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_8SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V3_8SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd xmm0, dword ptr [esi] + punpcklbw xmm0, xmm0 + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 24 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 3 + endm + +UNPACK_V3_8SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V3_8SSE_1 CL, TOTALCL, MaskType, ModeType + endm + + +UNPACK_V4_32SSE_4A macro CL, TOTALCL, MaskType, ModeType + movdqa xmm0, [esi] + movdqa xmm1, [esi+16] + movdqa xmm2, [esi+32] + movdqa xmm7, [esi+48] + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 64 + endm + +UNPACK_V4_32SSE_4 macro CL, TOTALCL, MaskType, ModeType + movdqu xmm0, [esi] + movdqu xmm1, [esi+16] + movdqu xmm2, [esi+32] + movdqu xmm7, [esi+48] + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 64 + endm + +UNPACK_V4_32SSE_3A macro CL, TOTALCL, MaskType, ModeType + movdqa xmm0, [esi] + movdqa xmm1, [esi+16] + movdqa xmm2, [esi+32] + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 48 + endm + +UNPACK_V4_32SSE_3 macro CL, TOTALCL, MaskType, ModeType + movdqu xmm0, [esi] + movdqu xmm1, [esi+16] + movdqu xmm2, [esi+32] + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 48 + endm + +UNPACK_V4_32SSE_2A macro CL, TOTALCL, MaskType, ModeType + movdqa xmm0, [esi] + movdqa xmm1, [esi+16] + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 32 + endm + +UNPACK_V4_32SSE_2 macro CL, TOTALCL, MaskType, ModeType + movdqu xmm0, [esi] + movdqu xmm1, [esi+16] + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 32 + endm + +UNPACK_V4_32SSE_1A macro CL, TOTALCL, MaskType, ModeType + movdqa xmm0, [esi] + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 16 + endm + +UNPACK_V4_32SSE_1 macro CL, TOTALCL, MaskType, ModeType + movdqu xmm0, [esi] + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 16 + endm + + +UNPACK_V4_16SSE_4A macro CL, TOTALCL, MaskType, ModeType + + punpcklwd xmm0, [esi] + punpckhwd xmm1, [esi] + punpcklwd xmm2, [esi+16] + punpckhwd xmm7, [esi+16] + + UNPACK_RIGHTSHIFT xmm1, 16 + UNPACK_RIGHTSHIFT xmm7, 16 + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm2, 16 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 32 + endm + +UNPACK_V4_16SSE_4 macro CL, TOTALCL, MaskType, ModeType + movdqu xmm0, [esi] + movdqu xmm2, [esi+16] + + punpckhwd xmm1, xmm0 + punpckhwd xmm7, xmm2 + punpcklwd xmm0, xmm0 + punpcklwd xmm2, xmm2 + + UNPACK_RIGHTSHIFT xmm1, 16 + UNPACK_RIGHTSHIFT xmm7, 16 + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm2, 16 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 32 + endm + +UNPACK_V4_16SSE_3A macro CL, TOTALCL, MaskType, ModeType + punpcklwd xmm0, [esi] + punpckhwd xmm1, [esi] + punpcklwd xmm2, [esi+16] + + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm1, 16 + UNPACK_RIGHTSHIFT xmm2, 16 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 24 + endm + +UNPACK_V4_16SSE_3 macro CL, TOTALCL, MaskType, ModeType + movdqu xmm0, [esi] + movq xmm2, QWORD PTR [esi+16] + + punpckhwd xmm1, xmm0 + punpcklwd xmm0, xmm0 + punpcklwd xmm2, xmm2 + + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm1, 16 + UNPACK_RIGHTSHIFT xmm2, 16 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 24 + endm + +UNPACK_V4_16SSE_2A macro CL, TOTALCL, MaskType, ModeType + punpcklwd xmm0, [esi] + punpckhwd xmm1, [esi] + + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm1, 16 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 16 + endm + +UNPACK_V4_16SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + movq xmm1, QWORD PTR [esi+8] + + punpcklwd xmm0, xmm0 + punpcklwd xmm1, xmm1 + + UNPACK_RIGHTSHIFT xmm0, 16 + UNPACK_RIGHTSHIFT xmm1, 16 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 16 + endm + +UNPACK_V4_16SSE_1A macro CL, TOTALCL, MaskType, ModeType + punpcklwd xmm0, [esi] + UNPACK_RIGHTSHIFT xmm0, 16 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + +UNPACK_V4_16SSE_1 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 16 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + + +UNPACK_V4_8SSE_4A macro CL, TOTALCL, MaskType, ModeType + punpcklbw xmm0, [esi] + punpckhbw xmm2, [esi] + + punpckhwd xmm1, xmm0 + punpckhwd xmm7, xmm2 + punpcklwd xmm0, xmm0 + punpcklwd xmm2, xmm2 + + UNPACK_RIGHTSHIFT xmm1, 24 + UNPACK_RIGHTSHIFT xmm7, 24 + UNPACK_RIGHTSHIFT xmm0, 24 + UNPACK_RIGHTSHIFT xmm2, 24 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 16 + endm + +UNPACK_V4_8SSE_4 macro CL, TOTALCL, MaskType, ModeType + movdqu xmm0, [esi] + + punpckhbw xmm2, xmm0 + punpcklbw xmm0, xmm0 + + punpckhwd xmm7, xmm2 + punpckhwd xmm1, xmm0 + punpcklwd xmm2, xmm2 + punpcklwd xmm0, xmm0 + + UNPACK_RIGHTSHIFT xmm7, 24 + UNPACK_RIGHTSHIFT xmm2, 24 + + UNPACK_RIGHTSHIFT xmm0, 24 + UNPACK_RIGHTSHIFT xmm1, 24 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 16 + endm + +UNPACK_V4_8SSE_3A macro CL, TOTALCL, MaskType, ModeType + punpcklbw xmm0, [esi] + punpckhbw xmm2, [esi] + + punpckhwd xmm1, xmm0 + punpcklwd xmm0, xmm0 + punpcklwd xmm2, xmm2 + + UNPACK_RIGHTSHIFT xmm1, 24 + UNPACK_RIGHTSHIFT xmm0, 24 + UNPACK_RIGHTSHIFT xmm2, 24 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 12 + endm + +UNPACK_V4_8SSE_3 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + movd xmm2, dword ptr [esi+8] + + punpcklbw xmm0, xmm0 + punpcklbw xmm2, xmm2 + + punpckhwd xmm1, xmm0 + punpcklwd xmm2, xmm2 + punpcklwd xmm0, xmm0 + + UNPACK_RIGHTSHIFT xmm1, 24 + UNPACK_RIGHTSHIFT xmm0, 24 + UNPACK_RIGHTSHIFT xmm2, 24 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 12 + endm + +UNPACK_V4_8SSE_2A macro CL, TOTALCL, MaskType, ModeType + punpcklbw xmm0, [esi] + + punpckhwd xmm1, xmm0 + punpcklwd xmm0, xmm0 + + UNPACK_RIGHTSHIFT xmm1, 24 + UNPACK_RIGHTSHIFT xmm0, 24 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + +UNPACK_V4_8SSE_2 macro CL, TOTALCL, MaskType, ModeType + movq xmm0, QWORD PTR [esi] + + punpcklbw xmm0, xmm0 + + punpckhwd xmm1, xmm0 + punpcklwd xmm0, xmm0 + + UNPACK_RIGHTSHIFT xmm1, 24 + UNPACK_RIGHTSHIFT xmm0, 24 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + +UNPACK_V4_8SSE_1A macro CL, TOTALCL, MaskType, ModeType + punpcklbw xmm0, [esi] + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 24 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 4 + endm + +UNPACK_V4_8SSE_1 macro CL, TOTALCL, MaskType, ModeType + movd xmm0, dword ptr [esi] + punpcklbw xmm0, xmm0 + punpcklwd xmm0, xmm0 + UNPACK_RIGHTSHIFT xmm0, 24 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 4 + endm + + +DECOMPRESS_RGBA macro OFFSET + mov bl, al + shl bl, 3 + mov byte ptr [s_TempDecompress+OFFSET], bl + + mov bx, ax + shr bx, 2 + and bx, 0f8h + mov byte ptr [s_TempDecompress+OFFSET+1], bl + + mov bx, ax + shr bx, 7 + and bx, 0f8h + mov byte ptr [s_TempDecompress+OFFSET+2], bl + mov bx, ax + shr bx, 8 + and bx, 080h + mov byte ptr [s_TempDecompress+OFFSET+3], bl + endm + +UNPACK_V4_5SSE_4 macro CL, TOTALCL, MaskType, ModeType + mov eax, dword ptr [esi] + DECOMPRESS_RGBA 0 + + shr eax, 16 + DECOMPRESS_RGBA 4 + + mov eax, dword ptr [esi+4] + DECOMPRESS_RGBA 8 + + shr eax, 16 + DECOMPRESS_RGBA 12 + + ;; have to use movaps instead of movdqa + movaps xmm0, [s_TempDecompress] + + punpckhbw xmm2, xmm0 + punpcklbw xmm0, xmm0 + + punpckhwd xmm7, xmm2 + punpckhwd xmm1, xmm0 + punpcklwd xmm0, xmm0 + punpcklwd xmm2, xmm2 + + psrld xmm0, 24 + psrld xmm1, 24 + psrld xmm2, 24 + psrld xmm7, 24 + + UNPACK4_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 8 + endm + +UNPACK_V4_5SSE_4A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V4_5SSE_4 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V4_5SSE_3 macro CL, TOTALCL, MaskType, ModeType + mov eax, dword ptr [esi] + DECOMPRESS_RGBA 0 + + shr eax, 16 + DECOMPRESS_RGBA 4 + + mov eax, dword ptr [esi] + DECOMPRESS_RGBA 8 + + ;; have to use movaps instead of movdqa + movaps xmm0, [s_TempDecompress] + + punpckhbw xmm2, xmm0 + punpcklbw xmm0, xmm0 + + punpckhwd xmm1, xmm0 + punpcklwd xmm0, xmm0 + punpcklwd xmm2, xmm2 + + psrld xmm0, 24 + psrld xmm1, 24 + psrld xmm2, 24 + + UNPACK3_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 6 + endm + +UNPACK_V4_5SSE_3A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V4_5SSE_3 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V4_5SSE_2 macro CL, TOTALCL, MaskType, ModeType + mov eax, dword ptr [esi] + DECOMPRESS_RGBA 0 + + shr eax, 16 + DECOMPRESS_RGBA 4 + + movq xmm0, QWORD PTR [s_TempDecompress] + + punpcklbw xmm0, xmm0 + + punpckhwd xmm1, xmm0 + punpcklwd xmm0, xmm0 + + psrld xmm0, 24 + psrld xmm1, 24 + + UNPACK2_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 4 + endm + +UNPACK_V4_5SSE_2A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V4_5SSE_2 CL, TOTALCL, MaskType, ModeType + endm + +UNPACK_V4_5SSE_1 macro CL, TOTALCL, MaskType, ModeType + mov ax, word ptr [esi] + DECOMPRESS_RGBA 0 + + movd xmm0, DWORD PTR [s_TempDecompress] + punpcklbw xmm0, xmm0 + punpcklwd xmm0, xmm0 + + psrld xmm0, 24 + + UNPACK1_SSE CL, TOTALCL, MaskType, ModeType + + add esi, 2 + endm + +UNPACK_V4_5SSE_1A macro CL, TOTALCL, MaskType, ModeType + UNPACK_V4_5SSE_1 CL, TOTALCL, MaskType, ModeType + endm + + +SAVE_ROW_REG_BASE macro + mov eax, [_vifRow] + movdqa [eax], xmm6 + mov eax, [_vifRegs] + movss dword ptr [eax+0100h], xmm6 + psrldq xmm6, 4 + movss dword ptr [eax+0110h], xmm6 + psrldq xmm6, 4 + movss dword ptr [eax+0120h], xmm6 + psrldq xmm6, 4 + movss dword ptr [eax+0130h], xmm6 + endm + +SAVE_NO_REG macro + endm + +INIT_ARGS macro + mov edi, dword ptr [esp+4+12] + mov esi, dword ptr [esp+8+12] + mov edx, dword ptr [esp+12+12] + endm + +INC_STACK macro reg + add esp, 4 + endm + + + + + +defUNPACK_SkippingWrite macro name, MaskType, ModeType, qsize, sign, SAVE_ROW_REG +@CatStr(UNPACK_SkippingWrite_, name, _, sign, _, MaskType, _, ModeType) proc public + push edi + push esi + push ebx + + INIT_ARGS + mov eax, [_vifRegs] + movzx ecx, byte ptr [eax + 040h] + movzx ebx, byte ptr [eax + 041h] + sub ecx, ebx + shl ecx, 4 + + cmp ebx, 1 + je @CatStr(name, _, sign, _, MaskType, _, ModeType, _WL1) + cmp ebx, 2 + je @CatStr(name, _, sign, _, MaskType, _, ModeType, _WL2) + cmp ebx, 3 + je @CatStr(name, _, sign, _, MaskType, _, ModeType, _WL3) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _WL4) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _WL1): + @CatStr(UNPACK_Start_Setup_, MaskType, _SSE_, ModeType) 0 + + cmp edx, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Done3) + + add ecx, 16 + + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Align16): + + test esi, 15 + jz @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_UnpackAligned) + + @CatStr(UNPACK_, name, SSE_1) 0, 1, MaskType, ModeType + + cmp edx, (2*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_DoneWithDec) + sub edx, qsize + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Align16) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_UnpackAligned): + + cmp edx, (2*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack1) + cmp edx, (3*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack2) + cmp edx, (4*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack3) + prefetchnta [esi + 64] + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack4): + @CatStr(UNPACK_, name, SSE_4A) 0, 1, MaskType, ModeType + + cmp edx, (8*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_DoneUnpack4) + sub edx, (4*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack4) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_DoneUnpack4): + + sub edx, (4*qsize) + cmp edx, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Done3) + cmp edx, (2*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack1) + cmp edx, (3*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack2) + + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack3): + @CatStr(UNPACK_, name, SSE_3A) 0, 1, MaskType, ModeType + + sub edx, (3*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Done3) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack2): + @CatStr(UNPACK_, name, SSE_2A) 0, 1, MaskType, ModeType + + sub edx, (2*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Done3) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Unpack1): + @CatStr(UNPACK_, name, SSE_1A) 0, 1, MaskType, ModeType +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_DoneWithDec): + sub edx, qsize +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C1_Done3): + SAVE_ROW_REG + mov eax, edx + pop ebx + pop esi + pop edi + + ret + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _WL2): + cmp edx, (2*qsize) + + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done3) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Unpack): + @CatStr(UNPACK_, name, SSE_2) 0, 0, MaskType, ModeType + + + add edi, ecx + cmp edx, (4*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done2) + sub edx, (2*qsize) + + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Unpack) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done2): + sub edx, (2*qsize) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done3): + cmp edx, qsize + + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done4) + + + @CatStr(UNPACK_, name, SSE_1) 0, 0, MaskType, ModeType + + sub edx, qsize +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C2_Done4): + + SAVE_ROW_REG + mov eax, edx + pop ebx + pop esi + pop edi + + ret + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _WL3): + cmp edx, (3*qsize) + + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done5) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Unpack): + @CatStr(UNPACK_, name, SSE_3) 0, 0, MaskType, ModeType + + add edi, ecx + cmp edx, (6*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done2) + sub edx, (3*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Unpack) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done2): + sub edx, (3*qsize) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done5): + cmp edx, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done4) + + cmp edx, (2*qsize) + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done3) + + @CatStr(UNPACK_, name, SSE_2) 0, 0, MaskType, ModeType + + sub edx, (2*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done4) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done3): + sub edx, qsize + @CatStr(UNPACK_, name, SSE_1) 0, 0, MaskType, ModeType +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C3_Done4): + SAVE_ROW_REG + mov eax, edx + pop ebx + pop esi + pop edi + + ret + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _WL4): + sub ebx, 3 + push ecx + cmp edx, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done) + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack): + cmp edx, (3*qsize) + jge @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack3) + cmp edx, (2*qsize) + jge @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack2) + + @CatStr(UNPACK_, name, SSE_1) 0, 0, MaskType, ModeType + + + sub edx, qsize + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack2): + @CatStr(UNPACK_, name, SSE_2) 0, 0, MaskType, ModeType + + + sub edx, (2*qsize) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack3): + @CatStr(UNPACK_, name, SSE_3) 0, 0, MaskType, ModeType + + + sub edx, (3*qsize) + mov ecx, ebx + +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_UnpackX): + + + cmp edx, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done) + + @CatStr(UNPACK_, name, SSE_1) 3, 0, MaskType, ModeType + + sub edx, qsize + cmp ecx, 1 + je @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_DoneLoop) + sub ecx, 1 + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_UnpackX) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_DoneLoop): + add edi, [esp] + cmp edx, qsize + jl @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done) + jmp @CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Unpack) +@CatStr(name, _, sign, _, MaskType, _, ModeType, _C4_Done): + + SAVE_ROW_REG + INC_STACK() + mov eax, edx + pop ebx + pop esi + pop edi + + ret +@CatStr(UNPACK_SkippingWrite_, name, _, sign, _, MaskType, _, ModeType endp) +endm + +UNPACK_RIGHTSHIFT macro reg, shift + psrld reg, shift + endm + +defUNPACK_SkippingWrite2 macro name, qsize + defUNPACK_SkippingWrite name, Regular, 0, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, Regular, 1, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, Regular, 2, qsize, u, SAVE_ROW_REG_BASE + defUNPACK_SkippingWrite name, Mask, 0, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, Mask, 1, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, Mask, 2, qsize, u, SAVE_ROW_REG_BASE + defUNPACK_SkippingWrite name, WriteMask, 0, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, WriteMask, 1, qsize, u, SAVE_NO_REG + defUNPACK_SkippingWrite name, WriteMask, 2, qsize, u, SAVE_ROW_REG_BASE + endm + +defUNPACK_SkippingWrite2 S_32, 4 +defUNPACK_SkippingWrite2 S_16, 2 +defUNPACK_SkippingWrite2 S_8, 1 +defUNPACK_SkippingWrite2 V2_32, 8 +defUNPACK_SkippingWrite2 V2_16, 4 +defUNPACK_SkippingWrite2 V2_8, 2 +defUNPACK_SkippingWrite2 V3_32, 12 +defUNPACK_SkippingWrite2 V3_16, 6 +defUNPACK_SkippingWrite2 V3_8, 3 +defUNPACK_SkippingWrite2 V4_32, 16 +defUNPACK_SkippingWrite2 V4_16, 8 +defUNPACK_SkippingWrite2 V4_8, 4 +defUNPACK_SkippingWrite2 V4_5, 2 + +UNPACK_RIGHTSHIFT macro reg, shift + psrad reg, shift + endm + + +defUNPACK_SkippingWrite2a macro name, qsize + defUNPACK_SkippingWrite name, Mask, 0, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, Regular, 0, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, Regular, 1, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, Regular, 2, qsize, s, SAVE_ROW_REG_BASE + defUNPACK_SkippingWrite name, Mask, 1, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, Mask, 2, qsize, s, SAVE_ROW_REG_BASE + defUNPACK_SkippingWrite name, WriteMask, 0, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, WriteMask, 1, qsize, s, SAVE_NO_REG + defUNPACK_SkippingWrite name, WriteMask, 2, qsize, s, SAVE_ROW_REG_BASE + endm + +defUNPACK_SkippingWrite2a S_16, 2 +defUNPACK_SkippingWrite2a S_8, 1 +defUNPACK_SkippingWrite2a V2_16, 4 +defUNPACK_SkippingWrite2a V2_8, 2 +defUNPACK_SkippingWrite2a V3_16, 6 +defUNPACK_SkippingWrite2a V3_8, 3 +defUNPACK_SkippingWrite2a V4_16, 8 +defUNPACK_SkippingWrite2a V4_8, 4 + +end diff --git a/pcsx2/x86/ix86-32/iCore-32.cpp b/pcsx2/x86/ix86-32/iCore-32.cpp new file mode 100644 index 0000000000..3b23bdf060 --- /dev/null +++ b/pcsx2/x86/ix86-32/iCore-32.cpp @@ -0,0 +1,1112 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Misc.h" +#include "iR5900.h" +#include "Vif.h" +#include "VU.h" +#include "ix86/ix86.h" +#include "iR3000A.h" + +#include + +using namespace std; + +u16 x86FpuState, iCWstate; +u16 g_mmxAllocCounter = 0; + +// X86 caching +int g_x86checknext; + +// use special x86 register allocation for ia32 + +void _initX86regs() { + memzero_obj(x86regs); + g_x86AllocCounter = 0; + g_x86checknext = 0; +} + +u32 _x86GetAddr(int type, int reg) +{ + switch(type&~X86TYPE_VU1) { + case X86TYPE_GPR: return (u32)&cpuRegs.GPR.r[reg]; + case X86TYPE_VI: { + //assert( reg < 16 || reg == REG_R ); + return (type&X86TYPE_VU1)?(u32)&VU1.VI[reg]:(u32)&VU0.VI[reg]; + } + case X86TYPE_MEMOFFSET: return 0; + case X86TYPE_VIMEMOFFSET: return 0; + case X86TYPE_VUQREAD: return (type&X86TYPE_VU1)?(u32)&VU1.VI[REG_Q]:(u32)&VU0.VI[REG_Q]; + case X86TYPE_VUPREAD: return (type&X86TYPE_VU1)?(u32)&VU1.VI[REG_P]:(u32)&VU0.VI[REG_P]; + case X86TYPE_VUQWRITE: return (type&X86TYPE_VU1)?(u32)&VU1.q:(u32)&VU0.q; + case X86TYPE_VUPWRITE: return (type&X86TYPE_VU1)?(u32)&VU1.p:(u32)&VU0.p; + case X86TYPE_PSX: return (u32)&psxRegs.GPR.r[reg]; + case X86TYPE_PCWRITEBACK: + return (u32)&g_recWriteback; + case X86TYPE_VUJUMP: + return (u32)&g_recWriteback; + + jNO_DEFAULT; + } + + return 0; +} + +int _getFreeX86reg(int mode) +{ + int i, tempi; + u32 bestcount = 0x10000; + + int maxreg = (mode&MODE_8BITREG)?4:X86REGS; + + for (i=0; i= maxreg ) continue; + if( (mode&MODE_NOFRAME) && reg==EBP ) continue; + + if (x86regs[reg].inuse == 0) { + g_x86checknext = (reg+1)%X86REGS; + return reg; + } + } + + tempi = -1; + for (i=1; i= 0 && reg < 32 ); + +// if( X86_ISVI(type) ) +// assert( reg < 16 || reg == REG_R ); + + // don't alloc EAX and ESP,EBP if MODE_NOFRAME + int oldmode = mode; + int noframe = mode&MODE_NOFRAME; + int maxreg = (mode&MODE_8BITREG)?4:X86REGS; + mode &= ~(MODE_NOFRAME|MODE_8BITREG); + int readfromreg = -1; + + if( type != X86TYPE_TEMP ) { + + if( maxreg < X86REGS ) { + // make sure reg isn't in the higher regs + + for(i = maxreg; i < X86REGS; ++i) { + if (!x86regs[i].inuse || x86regs[i].type != type || x86regs[i].reg != reg) continue; + + if( mode & MODE_READ ) { + readfromreg = i; + x86regs[i].inuse = 0; + break; + } + else if( mode & MODE_WRITE ) { + x86regs[i].inuse = 0; + break; + } + } + } + + for (i=1; i= maxreg) ) { + if( x86regs[i].mode & MODE_READ ) + readfromreg = i; + //if( xmmregs[i].mode & MODE_WRITE ) mode |= MODE_WRITE; + mode |= x86regs[i].mode&MODE_WRITE; + x86regs[i].inuse = 0; + break; + } + + if( x86reg >= 0 ) { + // requested specific reg, so return that instead + if( i != x86reg ) { + if( x86regs[i].mode & MODE_READ ) readfromreg = i; + //if( x86regs[i].mode & MODE_WRITE ) mode |= MODE_WRITE; + mode |= x86regs[i].mode&MODE_WRITE; + x86regs[i].inuse = 0; + break; + } + } + + if( type != X86TYPE_TEMP && !(x86regs[i].mode & MODE_READ) && (mode&MODE_READ)) { + + if( type == X86TYPE_GPR ) _flushConstReg(reg); + + if( X86_ISVI(type) && reg < 16 ) MOVZX32M16toR(i, _x86GetAddr(type, reg)); + else MOV32MtoR(i, _x86GetAddr(type, reg)); + + x86regs[i].mode |= MODE_READ; + } + + x86regs[i].needed = 1; + x86regs[i].mode|= mode; + return i; + } + } + + if (x86reg == -1) { + x86reg = _getFreeX86reg(oldmode); + } + else { + _freeX86reg(x86reg); + } + + x86regs[x86reg].type = type; + x86regs[x86reg].reg = reg; + x86regs[x86reg].mode = mode; + x86regs[x86reg].needed = 1; + x86regs[x86reg].inuse = 1; + + if( mode & MODE_READ ) { + if( readfromreg >= 0 ) MOV32RtoR(x86reg, readfromreg); + else { + if( type == X86TYPE_GPR ) { + + if( reg == 0 ) { + XOR32RtoR(x86reg, x86reg); + } + else { + _flushConstReg(reg); + _deleteMMXreg(MMX_GPR+reg, 1); + _deleteGPRtoXMMreg(reg, 1); + + _eeMoveGPRtoR(x86reg, reg); + + _deleteMMXreg(MMX_GPR+reg, 0); + _deleteGPRtoXMMreg(reg, 0); + } + } + else { + if( X86_ISVI(type) && reg < 16 ) { + if( reg == 0 ) XOR32RtoR(x86reg, x86reg); + else MOVZX32M16toR(x86reg, _x86GetAddr(type, reg)); + } + else MOV32MtoR(x86reg, _x86GetAddr(type, reg)); + } + } + } + + return x86reg; +} + +int _checkX86reg(int type, int reg, int mode) +{ + int i; + + for (i=0; i= 0 && x86reg < X86REGS ); + + if( x86regs[x86reg].inuse && (x86regs[x86reg].mode&MODE_WRITE) ) { + x86regs[x86reg].mode &= ~MODE_WRITE; + + if( X86_ISVI(x86regs[x86reg].type) && x86regs[x86reg].reg < 16 ) { + MOV16RtoM(_x86GetAddr(x86regs[x86reg].type, x86regs[x86reg].reg), x86reg); + } + else + MOV32RtoM(_x86GetAddr(x86regs[x86reg].type, x86regs[x86reg].reg), x86reg); + } + + x86regs[x86reg].inuse = 0; +} + +void _freeX86regs() { + int i; + + for (i=0; i= MMX_GPR && reg < MMX_GPR+32 ) return &cpuRegs.GPR.r[reg&31]; + if( reg >= MMX_FPU && reg < MMX_FPU+32 ) return &fpuRegs.fpr[reg&31]; + if( reg >= MMX_COP0 && reg < MMX_COP0+32 ) return &cpuRegs.CP0.r[reg&31]; + + assert( 0 ); + return NULL; +} + +int _getFreeMMXreg() +{ + int i, tempi; + u32 bestcount = 0x10000; + + for (i=0; i= MMX_GPR && mmxregs[i].reg < MMX_GPR+34 ) { + if( !(g_pCurInstInfo->regs[mmxregs[i].reg-MMX_GPR] & (EEINST_LIVE0|EEINST_LIVE1)) ) { + _freeMMXreg(i); + return i; + } + if( !(g_pCurInstInfo->regs[mmxregs[i].reg-MMX_GPR]&EEINST_USED) ) { + _freeMMXreg(i); + return i; + } + } + } + + // check for future xmm usage + for (i=0; i= MMX_GPR && mmxregs[i].reg < MMX_GPR+34 ) { + if( !(g_pCurInstInfo->regs[mmxregs[i].reg] & EEINST_MMX) ) { + _freeMMXreg(i); + return i; + } + } + } + + tempi = -1; + for (i=0; i= 0 ) { + SSE_MOVHPS_XMM_to_M64((u32)_MMXGetAddr(reg)+8, xmmreg); + if( mode & MODE_READ ) + SSE2_MOVDQ2Q_XMM_to_MM(mmxreg, xmmreg); + + if( xmmregs[xmmreg].mode & MODE_WRITE ) + mmxregs[mmxreg].mode |= MODE_WRITE; + + // don't flush + xmmregs[xmmreg].inuse = 0; + } + else { + if( MMX_ISGPR(reg) ) { + if(mode&(MODE_READHALF|MODE_READ)) _flushConstReg(reg-MMX_GPR); + } + + if( (mode & MODE_READHALF) || (MMX_IS32BITS(reg)&&(mode&MODE_READ)) ) { + MOVDMtoMMX(mmxreg, (u32)_MMXGetAddr(reg)); + } + else if( mode & MODE_READ ) { + MOVQMtoR(mmxreg, (u32)_MMXGetAddr(reg)); + } + } + } + + return mmxreg; +} + +int _checkMMXreg(int reg, int mode) +{ + int i; + for (i=0; i= MMX_GPR && mmxregs[i].reg < MMX_GPR+34 ) { + if( !EEINST_ISLIVE64(mmxregs[i].reg-MMX_GPR) ) { + return 1; + } + } + } + + // check for dead regs + for (i=0; i= MMX_GPR && mmxregs[i].reg < MMX_GPR+34 ) { + if( !(g_pCurInstInfo->regs[mmxregs[i].reg-MMX_GPR]&EEINST_USED) ) { + return 1; + } + } + } + + return 0; +} + +void _freeMMXreg(int mmxreg) +{ + assert( mmxreg < MMXREGS ); + if (!mmxregs[mmxreg].inuse) return; + + if (mmxregs[mmxreg].mode & MODE_WRITE ) { + + if( mmxregs[mmxreg].reg >= MMX_GPR && mmxregs[mmxreg].reg < MMX_GPR+32 ) + assert( !(g_cpuHasConstReg & (1<<(mmxregs[mmxreg].reg-MMX_GPR))) ); + + assert( mmxregs[mmxreg].reg != MMX_GPR ); + + if( MMX_IS32BITS(mmxregs[mmxreg].reg) ) + MOVDMMXtoM((u32)_MMXGetAddr(mmxregs[mmxreg].reg), mmxreg); + else + MOVQRtoM((u32)_MMXGetAddr(mmxregs[mmxreg].reg), mmxreg); + + SetMMXstate(); + } + + mmxregs[mmxreg].mode &= ~MODE_WRITE; + mmxregs[mmxreg].inuse = 0; +} + +void _moveMMXreg(int mmxreg) +{ + int i; + if( !mmxregs[mmxreg].inuse ) return; + + for (i=0; i>16)&0x1f].UL[0]); + } + else if( IS_PSXCONSTREG(arg) ) { + PUSH32I(g_psxConstRegs[(arg>>16)&0x1f]); + } + else if( IS_MEMORYREG(arg) ) PUSH32M(argmem); + else { + assert( (arg&0xfff0) == 0 ); + // assume it is a GPR reg + PUSH32R(arg&0xf); + } +} + +__forceinline void _callFunctionArg1(uptr fn, u32 arg1, uptr arg1mem) +{ + _callPushArg(arg1, arg1mem); + CALLFunc((uptr)fn); + ADD32ItoR(ESP, 4); +} + +__forceinline void _callFunctionArg2(uptr fn, u32 arg1, u32 arg2, uptr arg1mem, uptr arg2mem) +{ + _callPushArg(arg2, arg2mem); + _callPushArg(arg1, arg1mem); + CALLFunc((uptr)fn); + ADD32ItoR(ESP, 8); +} + +__forceinline void _callFunctionArg3(uptr fn, u32 arg1, u32 arg2, u32 arg3, uptr arg1mem, uptr arg2mem, uptr arg3mem) +{ + _callPushArg(arg3, arg3mem); + _callPushArg(arg2, arg2mem); + _callPushArg(arg1, arg1mem); + CALLFunc((uptr)fn); + ADD32ItoR(ESP, 12); +} + +void _recPushReg(int mmreg) +{ + if( IS_XMMREG(mmreg) ) { + SUB32ItoR(ESP, 4); + SSEX_MOVD_XMM_to_Rm(ESP, mmreg&0xf); + } + else if( IS_MMXREG(mmreg) ) { + SUB32ItoR(ESP, 4); + MOVD32MMXtoRm(ESP, mmreg&0xf); + } + else if( IS_EECONSTREG(mmreg) ) { + PUSH32I(g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]); + } + else if( IS_PSXCONSTREG(mmreg) ) { + PUSH32I(g_psxConstRegs[(mmreg>>16)&0x1f]); + } + else { + assert( (mmreg&0xfff0) == 0 ); + PUSH32R(mmreg); + } +} + +void _signExtendSFtoM(u32 mem) +{ + LAHF(); + SAR16ItoR(EAX, 15); + CWDE(); + MOV32RtoM(mem, EAX ); +} + +int _signExtendMtoMMX(x86MMXRegType to, u32 mem) +{ + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVDMtoMMX(t0reg, mem); + MOVQRtoR(to, t0reg); + PSRADItoR(t0reg, 31); + PUNPCKLDQRtoR(to, t0reg); + _freeMMXreg(t0reg); + return to; +} + +int _signExtendGPRMMXtoMMX(x86MMXRegType to, u32 gprreg, x86MMXRegType from, u32 gprfromreg) +{ + assert( to >= 0 && from >= 0 ); + if( !EEINST_ISLIVE1(gprreg) ) { + EEINST_RESETHASLIVE1(gprreg); + if( to != from ) MOVQRtoR(to, from); + return to; + } + + if( to == from ) return _signExtendGPRtoMMX(to, gprreg, 0); + if( !(g_pCurInstInfo->regs[gprfromreg]&EEINST_LASTUSE) ) { + if( EEINST_ISLIVE64(gprfromreg) ) { + MOVQRtoR(to, from); + return _signExtendGPRtoMMX(to, gprreg, 0); + } + } + + // from is free for use + SetMMXstate(); + + if( g_pCurInstInfo->regs[gprreg] & EEINST_MMX ) { + + if( EEINST_ISLIVE64(gprfromreg) ) { + _freeMMXreg(from); + } + + MOVQRtoR(to, from); + PSRADItoR(from, 31); + PUNPCKLDQRtoR(to, from); + return to; + } + else { + MOVQRtoR(to, from); + MOVDMMXtoM((u32)&cpuRegs.GPR.r[gprreg].UL[0], from); + PSRADItoR(from, 31); + MOVDMMXtoM((u32)&cpuRegs.GPR.r[gprreg].UL[1], from); + mmxregs[to].inuse = 0; + return -1; + } + + assert(0); +} + +int _signExtendGPRtoMMX(x86MMXRegType to, u32 gprreg, int shift) +{ + assert( to >= 0 && shift >= 0 ); + if( !EEINST_ISLIVE1(gprreg) ) { + if( shift > 0 ) PSRADItoR(to, shift); + EEINST_RESETHASLIVE1(gprreg); + return to; + } + + SetMMXstate(); + + if( g_pCurInstInfo->regs[gprreg] & EEINST_MMX ) { + if( _hasFreeMMXreg() ) { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t0reg, to); + PSRADItoR(to, 31); + if( shift > 0 ) PSRADItoR(t0reg, shift); + PUNPCKLDQRtoR(t0reg, to); + + // swap mmx regs.. don't ask + mmxregs[t0reg] = mmxregs[to]; + mmxregs[to].inuse = 0; + return t0reg; + } + else { + // will be used in the future as mmx + if( shift > 0 ) PSRADItoR(to, shift); + MOVDMMXtoM((u32)&cpuRegs.GPR.r[gprreg].UL[0], to); + PSRADItoR(to, 31); + MOVDMMXtoM((u32)&cpuRegs.GPR.r[gprreg].UL[1], to); + + // read again + MOVQMtoR(to, (u32)&cpuRegs.GPR.r[gprreg].UL[0]); + mmxregs[to].mode &= ~MODE_WRITE; + return to; + } + } + else { + if( shift > 0 ) PSRADItoR(to, shift); + MOVDMMXtoM((u32)&cpuRegs.GPR.r[gprreg].UL[0], to); + PSRADItoR(to, 31); + MOVDMMXtoM((u32)&cpuRegs.GPR.r[gprreg].UL[1], to); + mmxregs[to].inuse = 0; + return -1; + } + + assert(0); +} + +int _allocCheckGPRtoMMX(EEINST* pinst, int reg, int mode) +{ + if( pinst->regs[reg] & EEINST_MMX ) return _allocMMXreg(-1, MMX_GPR+reg, mode); + + return _checkMMXreg(MMX_GPR+reg, mode); +} + +// fixme - yay stupid? This sucks, and is used form iCOp2.cpp only. +// Surely there is a better way! +void _recMove128MtoM(u32 to, u32 from) +{ + MOV32MtoR(EAX, from); + MOV32MtoR(EDX, from+4); + MOV32RtoM(to, EAX); + MOV32RtoM(to+4, EDX); + MOV32MtoR(EAX, from+8); + MOV32MtoR(EDX, from+12); + MOV32RtoM(to+8, EAX); + MOV32RtoM(to+12, EDX); +} + +// fixme - see above function! +void _recMove128RmOffsettoM(u32 to, u32 offset) +{ + MOV32RmtoROffset(EAX, ECX, offset); + MOV32RmtoROffset(EDX, ECX, offset+4); + MOV32RtoM(to, EAX); + MOV32RtoM(to+4, EDX); + MOV32RmtoROffset(EAX, ECX, offset+8); + MOV32RmtoROffset(EDX, ECX, offset+12); + MOV32RtoM(to+8, EAX); + MOV32RtoM(to+12, EDX); +} + +// fixme - see above function again! +void _recMove128MtoRmOffset(u32 offset, u32 from) +{ + MOV32MtoR(EAX, from); + MOV32MtoR(EDX, from+4); + MOV32RtoRmOffset(ECX, EAX, offset); + MOV32RtoRmOffset(ECX, EDX, offset+4); + MOV32MtoR(EAX, from+8); + MOV32MtoR(EDX, from+12); + MOV32RtoRmOffset(ECX, EAX, offset+8); + MOV32RtoRmOffset(ECX, EDX, offset+12); +} + +static PCSX2_ALIGNED16(u32 s_ones[2]) = {0xffffffff, 0xffffffff}; + +void LogicalOpRtoR(x86MMXRegType to, x86MMXRegType from, int op) +{ + switch(op) { + case 0: PANDRtoR(to, from); break; + case 1: PORRtoR(to, from); break; + case 2: PXORRtoR(to, from); break; + case 3: + PORRtoR(to, from); + PXORMtoR(to, (u32)&s_ones[0]); + break; + } +} + +void LogicalOpMtoR(x86MMXRegType to, u32 from, int op) +{ + switch(op) { + case 0: PANDMtoR(to, from); break; + case 1: PORMtoR(to, from); break; + case 2: PXORMtoR(to, from); break; + case 3: + PORRtoR(to, from); + PXORMtoR(to, (u32)&s_ones[0]); + break; + } +} + +void LogicalOp32RtoM(u32 to, x86IntRegType from, int op) +{ + switch(op) { + case 0: AND32RtoM(to, from); break; + case 1: OR32RtoM(to, from); break; + case 2: XOR32RtoM(to, from); break; + case 3: OR32RtoM(to, from); break; + } +} + +void LogicalOp32MtoR(x86IntRegType to, u32 from, int op) +{ + switch(op) { + case 0: AND32MtoR(to, from); break; + case 1: OR32MtoR(to, from); break; + case 2: XOR32MtoR(to, from); break; + case 3: OR32MtoR(to, from); break; + } +} + +void LogicalOp32ItoR(x86IntRegType to, u32 from, int op) +{ + switch(op) { + case 0: AND32ItoR(to, from); break; + case 1: OR32ItoR(to, from); break; + case 2: XOR32ItoR(to, from); break; + case 3: OR32ItoR(to, from); break; + } +} + +void LogicalOp32ItoM(u32 to, u32 from, int op) +{ + switch(op) { + case 0: AND32ItoM(to, from); break; + case 1: OR32ItoM(to, from); break; + case 2: XOR32ItoM(to, from); break; + case 3: OR32ItoM(to, from); break; + } +} diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp new file mode 100644 index 0000000000..0a688a1730 --- /dev/null +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -0,0 +1,2062 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "Memory.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "iR5900AritImm.h" +#include "iR5900Arit.h" +#include "iR5900MultDiv.h" +#include "iR5900Shift.h" +#include "iR5900Branch.h" +#include "iR5900Jump.h" +#include "iR5900LoadStore.h" +#include "iR5900Move.h" +#include "iMMI.h" +#include "iFPU.h" +#include "iCOP0.h" +#include "iVUmicro.h" +#include "VU.h" +#include "VUmicro.h" + +#include "iVUzerorec.h" +#include "vtlb.h" + +#include "SamplProf.h" + +using namespace R5900; + +// used to disable register freezing during cpuBranchTests (registers +// are safe then since they've been completely flushed) +bool g_EEFreezeRegs = false; + +// I can't find where the Linux recRecompile is defined. Is it used anymore? +// If so, namespacing might break it. :/ (air) +#ifdef __LINUX__ +extern "C" { +#endif +void recRecompile( u32 startpc ); +#ifdef __LINUX__ +} +#endif + +u32 maxrecmem = 0; +uptr *recLUT = NULL; + +u32 s_nBlockCycles = 0; // cycles of current block recompiling +//u8* dyna_block_discard_recmem=0; + +u32 pc; // recompiler pc +int branch; // set for branch + +PCSX2_ALIGNED16(GPR_reg64 g_cpuConstRegs[32]) = {0}; +u32 g_cpuHasConstReg = 0, g_cpuFlushedConstReg = 0; +u32 s_saveConstGPRreg = 0; +GPR_reg64 s_ConstGPRreg; + +//////////////////////////////////////////////////////////////// +// Static Private Variables - R5900 Dynarec + +#define X86 +static const int RECSTACK_SIZE = 0x00010000; +static const int EE_NUMBLOCKS = (1<<15); + +static u8 *recMem = NULL; // the recompiled blocks will be here +static u8* recStack = NULL; // stack mem +static BASEBLOCK *recRAM = NULL; // and the ptr to the blocks here +static BASEBLOCK *recROM = NULL; // and here +static BASEBLOCK *recROM1 = NULL; // also here +static BASEBLOCKEX *recBlocks = NULL; +static u8* recPtr = NULL, *recStackPtr = NULL; +static EEINST* s_pInstCache = NULL; +static u32 s_nInstCacheSize = 0; + +static BASEBLOCK* s_pCurBlock = NULL; +static BASEBLOCKEX* s_pCurBlockEx = NULL; +const BASEBLOCK* s_pDispatchBlock = NULL; +static u32 s_nEndBlock = 0; // what pc the current block ends +static u32 s_nHasDelay = 0; + +static u32 s_nNextBlock = 0; // next free block in recBlocks + +// save states for branches +static u16 s_savex86FpuState, s_saveiCWstate; +static u32 s_saveHasConstReg = 0, s_saveFlushedConstReg = 0, s_saveRegHasLive1 = 0, s_saveRegHasSignExt = 0; +static EEINST* s_psaveInstInfo = NULL; + +static u32 s_savenBlockCycles = 0; + +#ifdef _DEBUG +static u32 dumplog = 0; +#else +#define dumplog 0 +#endif + +#ifdef PCSX2_DEVBUILD +// and not sure what these might have once been used for... (air) +//static const char *txt0 = "EAX = %x : ECX = %x : EDX = %x\n"; +//static const char *txt0RC = "EAX = %x : EBX = %x : ECX = %x : EDX = %x : ESI = %x : EDI = %x\n"; +//static const char *txt1 = "REG[%d] = %x_%x\n"; +//static const char *txt2 = "M32 = %x\n"; +#endif + +static void iBranchTest(u32 newpc, bool noDispatch=false); + +BASEBLOCKEX* PC_GETBLOCKEX(BASEBLOCK* p) +{ +// BASEBLOCKEX* pex = *(BASEBLOCKEX**)(p+1); +// if( pex >= recBlocks && pex < recBlocks+EE_NUMBLOCKS ) +// return pex; + + // otherwise, use the sorted list + return GetBaseBlockEx(p->startpc, 0); +} + +//////////////////////////////////////////////////// +static void iDumpBlock( int startpc, u8 * ptr ) +{ + FILE *f; + char filename[ g_MaxPath ]; + u32 i, j; + EEINST* pcur; + u8 used[34]; + u8 fpuused[33]; + int numused, count, fpunumused; + + Console::Status( "dump1 %x:%x, %x", params startpc, pc, cpuRegs.cycle ); +#ifdef _WIN32 + CreateDirectory("dumps", NULL); + sprintf_s( filename, g_MaxPath, "dumps\\dump%.8X.txt", startpc); +#else + mkdir("dumps", 0755); + sprintf( filename, "dumps/dump%.8X.txt", startpc); +#endif + + fflush( stdout ); +// f = fopen( "dump1", "wb" ); +// fwrite( ptr, 1, (u32)x86Ptr - (u32)ptr, f ); +// fclose( f ); +// +// sprintf( command, "objdump -D --target=binary --architecture=i386 dump1 > %s", filename ); +// system( command ); + + f = fopen( filename, "w" ); + + std::string output; + + if( disR5900GetSym(startpc) != NULL ) + fprintf(f, "%s\n", disR5900GetSym(startpc)); + for ( i = startpc; i < s_nEndBlock; i += 4 ) { + disR5900Fasm( output, PSMu32( i ), i ); + fprintf( f, output.c_str() ); + } + + // write the instruction info + + fprintf(f, "\n\nlive0 - %x, live1 - %x, live2 - %x, lastuse - %x\nmmx - %x, xmm - %x, used - %x\n", + EEINST_LIVE0, EEINST_LIVE1, EEINST_LIVE2, EEINST_LASTUSE, EEINST_MMX, EEINST_XMM, EEINST_USED); + + memzero_obj(used); + numused = 0; + for(i = 0; i < ARRAYSIZE(s_pInstCache->regs); ++i) { + if( s_pInstCache->regs[i] & EEINST_USED ) { + used[i] = 1; + numused++; + } + } + + memzero_obj(fpuused); + fpunumused = 0; + for(i = 0; i < ARRAYSIZE(s_pInstCache->fpuregs); ++i) { + if( s_pInstCache->fpuregs[i] & EEINST_USED ) { + fpuused[i] = 1; + fpunumused++; + } + } + + fprintf(f, " "); + for(i = 0; i < ARRAYSIZE(s_pInstCache->regs); ++i) { + if( used[i] ) fprintf(f, "%2d ", i); + } + for(i = 0; i < ARRAYSIZE(s_pInstCache->fpuregs); ++i) { + if( fpuused[i] ) fprintf(f, "%2d ", i); + } + fprintf(f, "\n"); + + fprintf(f, " "); + for(i = 0; i < ARRAYSIZE(s_pInstCache->regs); ++i) { + if( used[i] ) fprintf(f, "%s ", disRNameGPR[i]); + } + for(i = 0; i < ARRAYSIZE(s_pInstCache->fpuregs); ++i) { + if( fpuused[i] ) fprintf(f, "%s ", i<32?"FR":"FA"); + } + fprintf(f, "\n"); + + pcur = s_pInstCache+1; + for( i = 0; i < (s_nEndBlock-startpc)/4; ++i, ++pcur) { + fprintf(f, "%2d: %2.2x ", i+1, pcur->info); + + count = 1; + for(j = 0; j < ARRAYSIZE(s_pInstCache->regs); j++) { + if( used[j] ) { + fprintf(f, "%2.2x%s", pcur->regs[j], ((count%8)&&countfpuregs); j++) { + if( fpuused[j] ) { + fprintf(f, "%2.2x%s", pcur->fpuregs[j], ((count%8)&&count>26) { + case 26: // ldl + case 27: // ldr + case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: + case 55: // LD + case 30: // lq + return ((tempcode>>21)&0x1f)==((tempcode>>16)&0x1f); // rs==rt + } + return 0; +} + +static u8 _eeIsLoadStoreCoIssue(u32 firstcode, u32 secondcode) +{ + switch(firstcode>>26) { + case 34: // lwl + return (secondcode>>26)==38; + case 38: // lwr + return (secondcode>>26)==34; + case 42: // swl + return (secondcode>>26)==46; + case 46: // swr + return (secondcode>>26)==42; + case 26: // ldl + return (secondcode>>26)==27; + case 27: // ldr + return (secondcode>>26)==26; + case 44: // sdl + return (secondcode>>26)==45; + case 45: // sdr + return (secondcode>>26)==44; + + case 32: case 33: case 35: case 36: case 37: case 39: + case 55: // LD + + // stores + case 40: case 41: case 43: + case 63: // sd + return (secondcode>>26)==(firstcode>>26); + + case 30: // lq + case 31: // sq + case 49: // lwc1 + case 57: // swc1 + case 54: // lqc2 + case 62: // sqc2 + return (secondcode>>26)==(firstcode>>26); + } + return 0; +} + +static u8 _eeIsLoadStoreCoX(u32 tempcode) +{ + switch( tempcode>>26 ) { + case 30: case 31: case 49: case 57: case 55: case 63: + return 1; + } + return 0; +} + +void _eeFlushAllUnused() +{ + int i; + for(i = 0; i < 34; ++i) { + if( pc < s_nEndBlock ) { + if( (g_pCurInstInfo[1].regs[i]&EEINST_USED) ) + continue; + } + else if( (g_pCurInstInfo[0].regs[i]&EEINST_USED) ) + continue; + + if( i < 32 && GPR_IS_CONST1(i) ) _flushConstReg(i); + else { + _deleteMMXreg(MMX_GPR+i, 1); + _deleteGPRtoXMMreg(i, 1); + } + } + + //TODO when used info is done for FPU and VU0 + for(i = 0; i < XMMREGS; ++i) { + if( xmmregs[i].inuse && xmmregs[i].type != XMMTYPE_GPRREG ) + _freeXMMreg(i); + } +} + +u32* _eeGetConstReg(int reg) +{ + assert( GPR_IS_CONST1( reg ) ); + + if( g_cpuFlushedConstReg & (1<= 0 && (xmmregs[mmreg].mode&MODE_WRITE)) { + SSE2_MOVD_XMM_to_R(to, mmreg); + } + else if( (mmreg = _checkMMXreg(MMX_GPR+fromgpr, MODE_READ)) >= 0 && (mmxregs[mmreg].mode&MODE_WRITE) ) { + MOVD32MMXtoR(to, mmreg); + SetMMXstate(); + } + else { + MOV32MtoR(to, (int)&cpuRegs.GPR.r[ fromgpr ].UL[ 0 ] ); + } + } +} + +void _eeMoveGPRtoM(u32 to, int fromgpr) +{ + if( GPR_IS_CONST1(fromgpr) ) + MOV32ItoM( to, g_cpuConstRegs[fromgpr].UL[0] ); + else { + int mmreg; + + if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, fromgpr, MODE_READ)) >= 0 ) { + SSEX_MOVD_XMM_to_M32(to, mmreg); + } + else if( (mmreg = _checkMMXreg(MMX_GPR+fromgpr, MODE_READ)) >= 0 ) { + MOVDMMXtoM(to, mmreg); + SetMMXstate(); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ fromgpr ].UL[ 0 ] ); + MOV32RtoM(to, EAX ); + } + } +} + +void _eeMoveGPRtoRm(x86IntRegType to, int fromgpr) +{ + if( GPR_IS_CONST1(fromgpr) ) + MOV32ItoRmOffset( to, g_cpuConstRegs[fromgpr].UL[0], 0 ); + else { + int mmreg; + + if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, fromgpr, MODE_READ)) >= 0 ) { + SSEX_MOVD_XMM_to_Rm(to, mmreg); + } + else if( (mmreg = _checkMMXreg(MMX_GPR+fromgpr, MODE_READ)) >= 0 ) { + MOVD32MMXtoRm(to, mmreg); + SetMMXstate(); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ fromgpr ].UL[ 0 ] ); + MOV32RtoRm(to, EAX ); + } + } +} + +int _flushXMMunused() +{ + int i; + for (i=0; iregs[xmmregs[i].reg]&EEINST_USED) ) { + if( !_recIsRegWritten(g_pCurInstInfo+1, (s_nEndBlock-pc)/4, XMMTYPE_GPRREG, xmmregs[i].reg) ) { + _freeXMMreg(i); + xmmregs[i].inuse = 1; + return 1; + } + } + } + + return 0; +} + +int _flushMMXunused() +{ + int i; + for (i=0; iregs[mmxregs[i].reg-MMX_GPR]&EEINST_USED) ) { + if( !_recIsRegWritten(g_pCurInstInfo+1, (s_nEndBlock-pc)/4, XMMTYPE_GPRREG, mmxregs[i].reg-MMX_GPR) ) { + _freeMMXreg(i); + mmxregs[i].inuse = 1; + return 1; + } + } + } + + return 0; +} + +int _flushUnusedConstReg() +{ + int i; + for(i = 1; i < 32; ++i) { + if( (g_cpuHasConstReg & (1< failed to allocate recompiler memory." ); + + // Goal: Allocate BASEBLOCKs for every possible branch target in PS2 memory. + // Any 4-byte aligned address makes a valid branch target as per MIPS design (all instructions are + // always 4 bytes long). + + if( m_recBlockAlloc == NULL ) + m_recBlockAlloc = (u8*) _aligned_malloc( m_recBlockAllocSize, 4096 ); + + if( m_recBlockAlloc == NULL ) + throw Exception::OutOfMemory( "R5900-32 Init > Failed to allocate memory for BASEBLOCK tables." ); + + u8* curpos = m_recBlockAlloc; + recRAM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Base / 4) * sizeof(BASEBLOCK); + recROM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom / 4) * sizeof(BASEBLOCK); + recROM1 = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom1 / 4) * sizeof(BASEBLOCK); + recBlocks = (BASEBLOCKEX*)curpos; curpos += sizeof(BASEBLOCKEX)*EE_NUMBLOCKS; + recStack = (u8*)curpos; + + if( s_pInstCache == NULL ) + { + s_nInstCacheSize = 128; + s_pInstCache = (EEINST*)malloc( sizeof(EEINST) * s_nInstCacheSize ); + } + + if( s_pInstCache == NULL ) + throw Exception::OutOfMemory( "R5900-32 Init > failed to allocate memory for pInstCache." ); + + // No errors.. Proceed with initialization: + + ProfilerRegisterSource( "EERec", recMem, REC_CACHEMEM+0x1000 ); + + x86FpuState = FPU_STATE; +} + +//////////////////////////////////////////////////// +void recResetEE( void ) +{ + DbgCon::Status( "iR5900-32 > Resetting recompiler memory and structures." ); + + s_nNextBlock = 0; + maxrecmem = 0; + + memset_8<0xcd, REC_CACHEMEM>(recMem); + memzero_ptr( m_recBlockAlloc ); + + if( s_pInstCache ) + memset( s_pInstCache, 0, sizeof(EEINST)*s_nInstCacheSize ); + + ResetBaseBlockEx(0); + mmap_ResetBlockTracking(); + +#ifdef _MSC_VER + __asm emms; +#else + __asm__("emms"); +#endif + + memzero_ptr<0x010000 * sizeof(uptr)>( recLUT ); + + for ( int i = 0x0000; i < 0x0200; i++ ) + { + recLUT[ i + 0x0000 ] = (uptr)&recRAM[ i << 14 ]; + recLUT[ i + 0x2000 ] = (uptr)&recRAM[ i << 14 ]; + recLUT[ i + 0x3000 ] = (uptr)&recRAM[ i << 14 ]; + } + + for ( int i = 0x0000; i < 0x0040; i++ ) + { + recLUT[ i + 0x1fc0 ] = (uptr)&recROM[ i << 14 ]; + recLUT[ i + 0x9fc0 ] = (uptr)&recROM[ i << 14 ]; + recLUT[ i + 0xbfc0 ] = (uptr)&recROM[ i << 14 ]; + } + + for ( int i = 0x0000; i < 0x0004; i++ ) + { + recLUT[ i + 0x1e00 ] = (uptr)&recROM1[ i << 14 ]; + recLUT[ i + 0x9e00 ] = (uptr)&recROM1[ i << 14 ]; + recLUT[ i + 0xbe00 ] = (uptr)&recROM1[ i << 14 ]; + } + + memcpy_fast( recLUT + 0x8000, recLUT, 0x2000 * sizeof(uptr) ); + memcpy_fast( recLUT + 0xa000, recLUT, 0x2000 * sizeof(uptr) ); + + // drk||Raziel says this is useful but I'm not sure why. Something to do with forward jumps. + // Anyways, it causes random crashing for some reasom, possibly because of memory + // corrupition elsewhere in the recs. I can't reproduce the problem here though, + // so a fix will have to wait until later. -_- (air) + + //x86SetPtr(recMem+REC_CACHEMEM); + //dyna_block_discard_recmem=(u8*)x86Ptr; + //JMP32( (uptr)&dyna_block_discard - ( (u32)x86Ptr + 5 )); + + x86SetPtr(recMem); + + recPtr = recMem; + recStackPtr = recStack; + x86FpuState = FPU_STATE; + iCWstate = 0; + + branch = 0; + SetCPUState(Config.sseMXCSR, Config.sseVUMXCSR); +} + +static void recShutdown( void ) +{ + ProfilerTerminateSource( "EERec" ); + ResetBaseBlockEx(0); + + SafeSysMunmap( recMem, REC_CACHEMEM ); + safe_aligned_free( recLUT ); + safe_aligned_free( m_recBlockAlloc ); + recRAM = recROM = recROM1 = NULL; + recBlocks = NULL; + recStack = NULL; + + safe_free( s_pInstCache ); + s_nInstCacheSize = 0; + + x86Shutdown(); +} + +#pragma warning(disable:4731) // frame pointer register 'ebp' modified by inline assembly code + +void recStep( void ) { +} + +static __forceinline bool recEventTest() +{ +#ifdef PCSX2_DEVBUILD + // dont' remove this check unless doing an official release + if( g_globalXMMSaved || g_globalMMXSaved) + DevCon::Error("Pcsx2 Foopah! Frozen regs have not been restored!!!"); + assert( !g_globalXMMSaved && !g_globalMMXSaved); +#endif + + // Perform counters, ints, and IOP updates: + bool retval = _cpuBranchTest_Shared(); + +#ifdef PCSX2_DEVBUILD + assert( !g_globalXMMSaved && !g_globalMMXSaved); +#endif + return retval; +} + +//////////////////////////////////////////////////// + +static u32 g_lastpc = 0; +u32 g_EEDispatchTemp; + +#ifdef _MSC_VER + +// jumped to when invalid pc address +// EDX contains the jump addr to modify +static __naked void Dispatcher() +{ + // EDX contains the jump addr to modify + __asm push edx + + // calc PC_GETBLOCK + s_pDispatchBlock = PC_GETBLOCK(cpuRegs.pc); + + if( s_pDispatchBlock->startpc != cpuRegs.pc ) + recRecompile(cpuRegs.pc); + + __asm + { + mov eax, s_pDispatchBlock + mov eax, dword ptr [eax] + } + +#ifdef _DEBUG + __asm mov g_EEDispatchTemp, eax + assert( g_EEDispatchTemp ); +#endif + + __asm { + //and eax, 0x0fffffff + shl eax, 4 + pop ecx // x86Ptr to mod + mov edx, eax + sub edx, ecx + sub edx, 4 + mov dword ptr [ecx], edx + + jmp eax + } +} + +// edx - baseblock->startpc +// stack - x86Ptr +static __naked void DispatcherClear() +{ + // EDX contains the current pc + __asm mov cpuRegs.pc, edx + __asm push edx + + // calc PC_GETBLOCK + s_pDispatchBlock = PC_GETBLOCK(cpuRegs.pc); + + if( s_pDispatchBlock != NULL && s_pDispatchBlock->startpc == cpuRegs.pc ) + { + assert( s_pDispatchBlock->GetFnptr() != 0 ); + + // already modded the code, jump to the new place + __asm { + pop edx + mov eax, s_pDispatchBlock + add esp, 4 // ignore stack + mov eax, dword ptr [eax] + //and eax, 0x0fffffff + shl eax, 4 + jmp eax + } + } + + __asm { + call recRecompile + add esp, 4 // pop old param + mov eax, s_pDispatchBlock + mov eax, dword ptr [eax] + + pop ecx // old fnptr + + //and eax, 0x0fffffff + shl eax, 4 + mov byte ptr [ecx], 0xe9 // jmp32 + mov edx, eax + sub edx, ecx + sub edx, 5 + mov dword ptr [ecx+1], edx + + jmp eax + } +} + +// called when jumping to variable pc address +static __naked void DispatcherReg() +{ + s_pDispatchBlock = PC_GETBLOCK(cpuRegs.pc); + + if( s_pDispatchBlock->startpc != cpuRegs.pc ) + recRecompile(cpuRegs.pc); + + __asm + { + mov eax, s_pDispatchBlock + mov eax, dword ptr [eax] + } + +#ifdef _DEBUG + __asm mov g_EEDispatchTemp, eax + assert( g_EEDispatchTemp ); +#endif + + __asm { + //and eax, 0x0fffffff + shl eax, 4 + jmp eax + } +} + +__forceinline void recExecute() +{ + // Optimization note : Compared pushad against manually pushing the regs one-by-one. + // Manually pushing is faster, especially on Core2's and such. :) + do { + g_EEFreezeRegs = true; + __asm + { + push ebx + push esi + push edi + push ebp + + call DispatcherReg + + pop ebp + pop edi + pop esi + pop ebx + } + g_EEFreezeRegs = false; + } + while( !recEventTest() ); +} + +static void recExecuteBlock() +{ + g_EEFreezeRegs = true; + __asm + { + push ebx + push esi + push edi + push ebp + + call DispatcherReg + + pop ebp + pop edi + pop esi + pop ebx + } + g_EEFreezeRegs = false; + recEventTest(); +} + +#else // _MSC_VER +// Linux uses an assembly version of these routines. +extern "C" { +extern void Dispatcher(); +extern void DispatcherClear(); +extern void DispatcherReg(); +} +__forceinline void recExecute() +{ + // Optimization note : Compared pushad against manually pushing the regs one-by-one. + // Manually pushing is faster, especially on Core2's and such. :) + do { + g_EEFreezeRegs = true; + __asm__ + ( + ".intel_syntax\n" + "push %ebx\n" + "push %esi\n" + "push %edi\n" + "push %ebp\n" + + "call DispatcherReg\n" + + "pop %ebp\n" + "pop %edi\n" + "pop %esi\n" + "pop %ebx\n" + ".att_syntax\n" + ); + g_EEFreezeRegs = false; + } + while( !recEventTest() ); +} + +static void recExecuteBlock() +{ + g_EEFreezeRegs = true; + __asm__ + ( + ".intel_syntax\n" + "push %ebx\n" + "push %esi\n" + "push %edi\n" + "push %ebp\n" + + "call DispatcherReg\n" + + "pop %ebp\n" + "pop %edi\n" + "pop %esi\n" + "pop %ebx\n" + ".att_syntax\n" + ); + g_EEFreezeRegs = false; + recEventTest(); +} +#endif + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + +//////////////////////////////////////////////////// +void recSYSCALL( void ) { + MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (uptr)&cpuRegs.pc, pc ); + iFlushCall(FLUSH_NODESTROY); + CALLFunc( (uptr)R5900::Interpreter::OpcodeImpl::SYSCALL ); + + CMP32ItoM((uptr)&cpuRegs.pc, pc); + j8Ptr[0] = JE8(0); + ADD32ItoM((uptr)&cpuRegs.cycle, eeScaleBlockCycles()); + JMP32((uptr)DispatcherReg - ( (uptr)x86Ptr + 5 )); + x86SetJ8(j8Ptr[0]); + //branch = 2; +} + +//////////////////////////////////////////////////// +void recBREAK( void ) { + MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (uptr)&cpuRegs.pc, pc ); + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (uptr)R5900::Interpreter::OpcodeImpl::BREAK ); + + CMP32ItoM((uptr)&cpuRegs.pc, pc); + j8Ptr[0] = JE8(0); + ADD32ItoM((uptr)&cpuRegs.cycle, eeScaleBlockCycles()); + RET(); + x86SetJ8(j8Ptr[0]); + //branch = 2; +} + +} } } // end namespace R5900::Dynarec::OpcodeImpl + +//////////////////////////////////////////////////// +void recClear( u32 Addr, u32 Size ) +{ + u32 i; + for(i = 0; i < Size; ++i, Addr+=4) { + REC_CLEARM(Addr); + } +} + +static const int EE_MIN_BLOCK_BYTES = 15; + +void recClearMem(BASEBLOCK* p) +{ + BASEBLOCKEX* pexblock; + BASEBLOCK* pstart; + int lastdelay; + + // necessary since recompiler doesn't call femms/emms +#ifdef _MSC_VER + if (cpucaps.has3DNOWInstructionExtensions) __asm femms; + else __asm emms; +#else + if( cpucaps.has3DNOWInstructionExtensions )__asm__("femms"); + else __asm__("emms"); +#endif + + assert( p != NULL ); + + if( p->uType & BLOCKTYPE_DELAYSLOT ) { + recClearMem(p-1); + if( p->GetFnptr() == 0 ) + return; + } + + assert( p->GetFnptr() != 0 ); + assert( p->startpc ); + + x86Ptr = (u8*)p->GetFnptr(); + + // there is a small problem: mem can be ored with 0xa<<28 or 0x8<<28, and don't know which + MOV32ItoR(EDX, p->startpc); + PUSH32I((u32)x86Ptr); // will be replaced by JMP32 + JMP32((u32)DispatcherClear - ( (u32)x86Ptr + 5 )); + assert( x86Ptr == (u8*)p->GetFnptr() + EE_MIN_BLOCK_BYTES ); + + pstart = PC_GETBLOCK(p->startpc); + pexblock = PC_GETBLOCKEX(pstart); + assert( pexblock->startpc == pstart->startpc ); + + if( pexblock->startpc != pstart->startpc ) { + // some bug with ffx after beating a big snake in sewers + RemoveBaseBlockEx(pexblock, 0); + pexblock->size = 0; + pexblock->startpc = 0; + return; + } + + // don't delete if last is delay + lastdelay = pexblock->size; + if( pstart[pexblock->size-1].uType & BLOCKTYPE_DELAYSLOT ) { + assert( pstart[pexblock->size-1].GetFnptr() != pstart->GetFnptr() ); + if( pstart[pexblock->size-1].GetFnptr() != 0 ) { + pstart[pexblock->size-1].uType = 0; + --lastdelay; + } + } + + memset(pstart, 0, lastdelay*sizeof(BASEBLOCK)); + + RemoveBaseBlockEx(pexblock, 0); + pexblock->size = 0; + pexblock->startpc = 0; +} + +void REC_CLEARM( u32 mem ) +{ + if ((mem) < maxrecmem && recLUT[(mem) >> 16]) { + BASEBLOCK* p = PC_GETBLOCK(mem); + if( *(u32*)p ) recClearMem(p); + } +} + +// check for end of bios +void CheckForBIOSEnd() +{ + MOV32MtoR(EAX, (int)&cpuRegs.pc); + + CMP32ItoR(EAX, 0x00200008); + j8Ptr[0] = JE8(0); + + CMP32ItoR(EAX, 0x00100008); + j8Ptr[1] = JE8(0); + + // return + j8Ptr[2] = JMP8(0); + + x86SetJ8( j8Ptr[0] ); + x86SetJ8( j8Ptr[1] ); + + // bios end + RET2(); + + x86SetJ8( j8Ptr[2] ); +} + +static int *s_pCode; + +void SetBranchReg( u32 reg ) +{ + branch = 1; + + if( reg != 0xffffffff ) { +// if( GPR_IS_CONST1(reg) ) +// MOV32ItoM( (uptr)&cpuRegs.pc, g_cpuConstRegs[reg].UL[0] ); +// else { +// int mmreg; +// +// if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, reg, MODE_READ)) >= 0 ) { +// SSE_MOVSS_XMM_to_M32((u32)&cpuRegs.pc, mmreg); +// } +// else if( (mmreg = _checkMMXreg(MMX_GPR+reg, MODE_READ)) >= 0 ) { +// MOVDMMXtoM((u32)&cpuRegs.pc, mmreg); +// SetMMXstate(); +// } +// else { +// MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ reg ].UL[ 0 ] ); +// MOV32RtoM((u32)&cpuRegs.pc, EAX); +// } +// } + _allocX86reg(ESI, X86TYPE_PCWRITEBACK, 0, MODE_WRITE); + _eeMoveGPRtoR(ESI, reg); + + recompileNextInstruction(1); + + if( x86regs[ESI].inuse ) { + assert( x86regs[ESI].type == X86TYPE_PCWRITEBACK ); + MOV32RtoM((int)&cpuRegs.pc, ESI); + x86regs[ESI].inuse = 0; + } + else { + MOV32MtoR(EAX, (u32)&g_recWriteback); + MOV32RtoM((int)&cpuRegs.pc, EAX); + } + } + +// CMP32ItoM((u32)&cpuRegs.pc, 0); +// j8Ptr[5] = JNE8(0); +// CALLFunc((uptr)tempfn); +// x86SetJ8( j8Ptr[5] ); + + iFlushCall(FLUSH_EVERYTHING); + + iBranchTest(0xffffffff); +} + +void SetBranchImm( u32 imm ) +{ + branch = 1; + + assert( imm ); + + // end the current block + MOV32ItoM( (uptr)&cpuRegs.pc, imm ); + iFlushCall(FLUSH_EVERYTHING); + + iBranchTest(imm); +} + +void SaveBranchState() +{ + s_savex86FpuState = x86FpuState; + s_saveiCWstate = iCWstate; + s_savenBlockCycles = s_nBlockCycles; + s_saveConstGPRreg = 0xffffffff; // indicate searching + s_saveHasConstReg = g_cpuHasConstReg; + s_saveFlushedConstReg = g_cpuFlushedConstReg; + s_psaveInstInfo = g_pCurInstInfo; + s_saveRegHasLive1 = g_cpuRegHasLive1; + s_saveRegHasSignExt = g_cpuRegHasSignExt; + + // save all mmx regs + memcpy_fast(s_saveMMXregs, mmxregs, sizeof(mmxregs)); + memcpy_fast(s_saveXMMregs, xmmregs, sizeof(xmmregs)); +} + +void LoadBranchState() +{ + x86FpuState = s_savex86FpuState; + iCWstate = s_saveiCWstate; + s_nBlockCycles = s_savenBlockCycles; + + if( s_saveConstGPRreg != 0xffffffff ) { + assert( s_saveConstGPRreg > 0 ); + + // make sure right GPR was saved + assert( g_cpuHasConstReg == s_saveHasConstReg || (g_cpuHasConstReg ^ s_saveHasConstReg) == (1<> 3; + + uint scalarLow, scalarMid, scalarHigh; + + // Note: larger blocks get a smaller scalar, to help keep + // them from becoming "too fat" and delaying branch tests. + + switch( CHECK_EE_CYCLERATE ) + { + case 0: return s_nBlockCycles >> 3; + + case 1: // Sync hack x1.5! + scalarLow = 5; + scalarMid = 7; + scalarHigh = 5; + break; + + case 2: // Sync hack x2 + scalarLow = 7; + scalarMid = 9; + scalarHigh = 7; + break; + + case 3: // Sync hack x3 + scalarLow = 10; + scalarMid = 19; + scalarHigh = 10; + break; + + jNO_DEFAULT + } + + s_nBlockCycles *= + (s_nBlockCycles <= (10<<3)) ? scalarLow : + ((s_nBlockCycles > (21<<3)) ? scalarHigh : scalarMid ); + + return s_nBlockCycles >> (3+2); +} + +// Generates dynarec code for Event tests followed by a block dispatch (branch). +// Parameters: +// newpc - address to jump to at the end of the block. If newpc == 0xffffffff then +// the jump is assumed to be to a register (dynamic). For any other value the +// jump is assumed to be static, in which case the block will be "hardlinked" after +// the first time it's dispatched. +// +// noDispatch - When set true, the jump to Dispatcher. Used by the recs +// for blocks which perform exception checks without branching (it's enabled by +// setting "branch = 2"; +static void iBranchTest(u32 newpc, bool noDispatch) +{ +#ifdef _DEBUG + //CALLFunc((uptr)testfpu); +#endif + u32* ptr; + + if( bExecBIOS ) CheckForBIOSEnd(); + + MOV32MtoR(EAX, (uptr)&cpuRegs.cycle); + if( !noDispatch && newpc != 0xffffffff ) + { + // Optimization note: Instructions order to pair EDX with EAX's load above. + + // Load EDX with the address of the JS32 jump below. + // We do this because the the Dispatcher will use this info to modify + // the JS instruction later on with the address of the block it's jumping + // to; creating a static link of blocks that doesn't require the overhead + // of a dispatcher. + MOV32ItoR(EDX, 0); + ptr = (u32*)(x86Ptr-4); + } + + // Check the Event scheduler if our "cycle target" has been reached. + // Equiv code to: + // cpuRegs.cycle += blockcycles; + // if( cpuRegs.cycle > g_nextBranchCycle ) { DoEvents(); } + ADD32ItoR(EAX, eeScaleBlockCycles()); + MOV32RtoM((uptr)&cpuRegs.cycle, EAX); // update cycles + SUB32MtoR(EAX, (uptr)&g_nextBranchCycle); + + if( newpc != 0xffffffff ) + { + // This is the jump instruction which gets modified by Dispatcher. + *ptr = (u32)JS32((u32)Dispatcher - ( (u32)x86Ptr + 6 )); + } + else if( !noDispatch ) + { + // This instruction is a dynamic link, so it's never modified. + JS32((uptr)DispatcherReg - ( (uptr)x86Ptr + 6 )); + } + + RET2(); +} + +static void checkcodefn() +{ + int pctemp; + +#ifdef _MSC_VER + __asm mov pctemp, eax; +#else + __asm__("movl %%eax, %0" : "=m"(pctemp) ); +#endif + + Console::Error("code changed! %x", params pctemp); + assert(0); +} + +//#ifdef _DEBUG +//#define CHECK_XMMCHANGED() CALLFunc((uptr)checkxmmchanged); +//#else +//#define CHECK_XMMCHANGED() +//#endif +// +//static void checkxmmchanged() +//{ +// assert( !g_globalMMXSaved ); +// assert( !g_globalXMMSaved ); +//} + +u32 recompileCodeSafe(u32 temppc) +{ + BASEBLOCK* pblock = PC_GETBLOCK(temppc); + + if( pblock->GetFnptr() != 0 && pblock->startpc != s_pCurBlock->startpc ) { + if( pc == pblock->startpc ) + return 0; + } + + return 1; +} + +void recompileNextInstruction(int delayslot) +{ + static u8 s_bFlushReg = 1; + int i, count; + + BASEBLOCK* pblock = PC_GETBLOCK(pc); + + // need *ppblock != s_pCurBlock because of branches + if( pblock->GetFnptr() != 0 && pblock->startpc != s_pCurBlock->startpc ) { + + if( !delayslot && pc == pblock->startpc ) { + // code already in place, so jump to it and exit recomp + assert( PC_GETBLOCKEX(pblock)->startpc == pblock->startpc ); + + iFlushCall(FLUSH_EVERYTHING); + MOV32ItoM((uptr)&cpuRegs.pc, pc); + +// if( pexblock->pOldFnptr ) { +// // code already in place, so jump to it and exit recomp +// JMP32((u32)pexblock->pOldFnptr - ((u32)x86Ptr + 5)); +// branch = 3; +// return; +// } + + JMP32((uptr)pblock->GetFnptr() - ((uptr)x86Ptr + 5)); + branch = 3; + return; + } + else { + + if( !(delayslot && pblock->startpc == pc) ) { + u8* oldX86 = x86Ptr; + //__Log("clear block %x\n", pblock->startpc); + recClearMem(pblock); + x86Ptr = oldX86; + if( delayslot ) + Console::Notice("delay slot %x", params pc); + } + } + } + + if( delayslot ) + pblock->uType = BLOCKTYPE_DELAYSLOT; + + s_pCode = (int *)PSM( pc ); + assert(s_pCode); + +#ifdef _DEBUG + MOV32ItoR(EAX, pc); +#endif + + cpuRegs.code = *(int *)s_pCode; + pc += 4; + +//#ifdef _DEBUG +// CMP32ItoM((u32)s_pCode, cpuRegs.code); +// j8Ptr[0] = JE8(0); +// MOV32ItoR(EAX, pc); +// CALLFunc((uptr)checkcodefn); +// x86SetJ8( j8Ptr[ 0 ] ); +// +// if( !delayslot ) { +// CMP32ItoM((u32)&cpuRegs.pc, s_pCurBlockEx->startpc); +// j8Ptr[0] = JB8(0); +// CMP32ItoM((u32)&cpuRegs.pc, pc); +// j8Ptr[1] = JA8(0); +// j8Ptr[2] = JMP8(0); +// x86SetJ8( j8Ptr[ 0 ] ); +// x86SetJ8( j8Ptr[ 1 ] ); +// PUSH32I(s_pCurBlockEx->startpc); +// ADD32ItoR(ESP, 4); +// x86SetJ8( j8Ptr[ 2 ] ); +// } +//#endif + + g_pCurInstInfo++; + + // reorder register priorities +// for(i = 0; i < X86REGS; ++i) { +// if( x86regs[i].inuse ) { +// if( count > 0 ) mmxregs[i].counter = 1000-count; +// else mmxregs[i].counter = 0; +// } +// } + + for(i = 0; i < MMXREGS; ++i) { + if( mmxregs[i].inuse ) { + assert( MMX_ISGPR(mmxregs[i].reg) ); + count = _recIsRegWritten(g_pCurInstInfo, (s_nEndBlock-pc)/4 + 1, XMMTYPE_GPRREG, mmxregs[i].reg-MMX_GPR); + if( count > 0 ) mmxregs[i].counter = 1000-count; + else mmxregs[i].counter = 0; + } + } + + for(i = 0; i < XMMREGS; ++i) { + if( xmmregs[i].inuse ) { + count = _recIsRegWritten(g_pCurInstInfo, (s_nEndBlock-pc)/4 + 1, xmmregs[i].type, xmmregs[i].reg); + if( count > 0 ) xmmregs[i].counter = 1000-count; + else xmmregs[i].counter = 0; + } + } + + const OPCODE& opcode = GetCurrentInstruction(); + + // peephole optimizations +#ifdef PCSX2_VM_COISSUE + if( g_pCurInstInfo->info & EEINSTINFO_COREC ) { + + if( g_pCurInstInfo->numpeeps > 1 ) { + switch(_Opcode_) { + case 30: recLQ_coX(g_pCurInstInfo->numpeeps); break; + case 31: recSQ_coX(g_pCurInstInfo->numpeeps); break; + case 49: recLWC1_coX(g_pCurInstInfo->numpeeps); break; + case 57: recSWC1_coX(g_pCurInstInfo->numpeeps); break; + case 55: recLD_coX(g_pCurInstInfo->numpeeps); break; + case 63: recSD_coX(g_pCurInstInfo->numpeeps, 1); break; //not sure if should be set to 1 or 0; looks like "1" handles alignment, so i'm going with that for now + + jNO_DEFAULT + } + pc += g_pCurInstInfo->numpeeps*4; + s_nBlockCycles += (g_pCurInstInfo->numpeeps+1) * opcode.cycles; + g_pCurInstInfo += g_pCurInstInfo->numpeeps; + } + else { + recBSC_co[_Opcode_](); + pc += 4; + g_pCurInstInfo++; + s_nBlockCycles += opcode.cycles*2; + } + } + else +#endif + { + //assert( !(g_pCurInstInfo->info & EEINSTINFO_NOREC) ); + + // if this instruction is a jump or a branch, exit right away + if( delayslot ) { + switch(_Opcode_) { + case 1: + switch(_Rt_) { + case 0: case 1: case 2: case 3: case 0x10: case 0x11: case 0x12: case 0x13: + Console::Notice("branch %x in delay slot!", params cpuRegs.code); + _clearNeededX86regs(); + _clearNeededMMXregs(); + _clearNeededXMMregs(); + return; + } + break; + + case 2: case 3: case 4: case 5: case 6: case 7: case 0x14: case 0x15: case 0x16: case 0x17: + Console::Notice("branch %x in delay slot!", params cpuRegs.code); + _clearNeededX86regs(); + _clearNeededMMXregs(); + _clearNeededXMMregs(); + return; + } + } + opcode.recompile(); + s_nBlockCycles += opcode.cycles; + } + + if( !delayslot ) { + if( s_bFlushReg ) { + //if( !_flushUnusedConstReg() ) { + int flushed = 0; + if( _getNumMMXwrite() > 3 ) flushed = _flushMMXunused(); + if( !flushed && _getNumXMMwrite() > 2 ) _flushXMMunused(); + s_bFlushReg = !flushed; +// } +// else s_bFlushReg = 0; + } + else s_bFlushReg = 1; + } + else s_bFlushReg = 1; + + //CHECK_XMMCHANGED(); + _clearNeededX86regs(); + _clearNeededMMXregs(); + _clearNeededXMMregs(); + +// _freeXMMregs(); +// _freeMMXregs(); +// _flushCachedRegs(); +// g_cpuHasConstReg = 1; +} + +extern u32 psxdump; + +static void printfn() +{ + static int lastrec = 0; + static int curcount = 0; + const int skip = 0; + + assert( !g_globalMMXSaved ); + assert( !g_globalXMMSaved ); + + if( (dumplog&2) && g_lastpc != 0x81fc0 ) {//&& lastrec != g_lastpc ) { + curcount++; + + if( curcount > skip ) { + iDumpRegisters(g_lastpc, 1); + curcount = 0; + } + + lastrec = g_lastpc; + } +} + +u32 s_recblocks[] = {0}; + +void badespfn() { + Console::Error("Bad esp!"); + assert(0); +} + +void __fastcall dyna_block_discard(u32 start,u32 sz) +{ + Console::WriteLn("dyna_block_discard %08X , count %d", params start,sz); + Cpu->Clear(start,sz); +} + +void recRecompile( const u32 startpc ) +{ + u32 i = 0; + u32 branchTo; + u32 willbranch3 = 0; + u32* ptr; + u32 usecop2; + +#ifdef _DEBUG + //dumplog |= 4; + if( dumplog & 4 ) + iDumpRegisters(startpc, 0); +#endif + + assert( startpc ); + + // if recPtr reached the mem limit reset whole mem + if ( ( (uptr)recPtr - (uptr)recMem ) >= REC_CACHEMEM-0x40000 || dumplog == 0xffffffff) { + DevCon::WriteLn( "EE Recompiler data reset" ); + recResetEE(); + } + if ( ( (uptr)recStackPtr - (uptr)recStack ) >= RECSTACK_SIZE-0x100 ) { + DevCon::WriteLn("EE recompiler stack reset"); + recResetEE(); + } + + s_pCurBlock = PC_GETBLOCK(startpc); + + if( s_pCurBlock->GetFnptr() ) { + // clear if already taken + assert( s_pCurBlock->startpc < startpc ); + recClearMem(s_pCurBlock); + } + + if( s_pCurBlock->startpc == startpc ) { + s_pCurBlockEx = PC_GETBLOCKEX(s_pCurBlock); + assert( s_pCurBlockEx->startpc == startpc ); + } + else { + s_pCurBlockEx = NULL; + for(i = 0; i < EE_NUMBLOCKS; ++i) { + if( recBlocks[(i+s_nNextBlock)%EE_NUMBLOCKS].size == 0 ) { + s_pCurBlockEx = recBlocks+(i+s_nNextBlock)%EE_NUMBLOCKS; + s_nNextBlock = (i+s_nNextBlock+1)%EE_NUMBLOCKS; + break; + } + } + + if( s_pCurBlockEx == NULL ) { + //SysPrintf("ee reset (blocks)\n"); + recResetEE(); + s_nNextBlock = 0; + s_pCurBlockEx = recBlocks; + } + + s_pCurBlockEx->startpc = startpc; + } + + x86SetPtr( recPtr ); + x86Align(16); + recPtr = x86Ptr; + s_pCurBlock->SetFnptr( (uptr)x86Ptr ); + s_pCurBlock->startpc = startpc; + + branch = 0; + + // reset recomp state variables + s_nBlockCycles = 0; + pc = startpc; + x86FpuState = FPU_STATE; + iCWstate = 0; + s_saveConstGPRreg = 0; + g_cpuHasConstReg = g_cpuFlushedConstReg = 1; + g_cpuPrevRegHasLive1 = g_cpuRegHasLive1 = 0xffffffff; + g_cpuPrevRegHasSignExt = g_cpuRegHasSignExt = 0; + assert( g_cpuConstRegs[0].UD[0] == 0 ); + + _initX86regs(); + _initXMMregs(); + _initMMXregs(); + +#ifdef _DEBUG + // for debugging purposes + MOV32ItoM((uptr)&g_lastpc, pc); + CALLFunc((uptr)printfn); + +// CMP32MtoR(EBP, (uptr)&s_uSaveEBP); +// j8Ptr[0] = JE8(0); +// CALLFunc((uptr)badespfn); +// x86SetJ8(j8Ptr[0]); +#endif + + // go until the next branch + i = startpc; + s_nEndBlock = 0xffffffff; + s_nHasDelay = 0; + + while(1) { + BASEBLOCK* pblock = PC_GETBLOCK(i); + if( pblock->GetFnptr() != 0 && pblock->startpc != s_pCurBlock->startpc ) { + + if( i == pblock->startpc ) { + // branch = 3 + willbranch3 = 1; + s_nEndBlock = i; + break; + } + } + //HUH ? PSM ? whut ? THIS IS VIRTUAL ACCESS GOD DAMMIT + cpuRegs.code = *(int *)PSM(i); + + switch(cpuRegs.code >> 26) { + case 0: // special + + if( _Funct_ == 8 || _Funct_ == 9 ) { // JR, JALR + s_nEndBlock = i + 8; + s_nHasDelay = 1; + goto StartRecomp; + } + + break; + case 1: // regimm + + if( _Rt_ < 4 || (_Rt_ >= 16 && _Rt_ < 20) ) { + // branches + if( _Rt_ == 2 || _Rt_ == 3 || _Rt_ == 18 || _Rt_ == 19 ) s_nHasDelay = 1; + else s_nHasDelay = 2; + + branchTo = _Imm_ * 4 + i + 4; + if( branchTo > startpc && branchTo < i ) s_nEndBlock = branchTo; + else s_nEndBlock = i+8; + + goto StartRecomp; + } + + break; + + case 2: // J + case 3: // JAL + s_nHasDelay = 1; + s_nEndBlock = i + 8; + goto StartRecomp; + + // branches + case 4: case 5: case 6: case 7: + case 20: case 21: case 22: case 23: + + if( (cpuRegs.code >> 26) >= 20 ) s_nHasDelay = 1; + else s_nHasDelay = 2; + + branchTo = _Imm_ * 4 + i + 4; + if( branchTo > startpc && branchTo < i ) s_nEndBlock = branchTo; + else s_nEndBlock = i+8; + + goto StartRecomp; + + case 16: // cp0 + if( _Rs_ == 16 ) { + if( _Funct_ == 24 ) { // eret + s_nEndBlock = i+4; + goto StartRecomp; + } + } + // Fall through! + // COP0's branch opcodes line up with COP1 and COP2's + + case 17: // cp1 + case 18: // cp2 + if( _Rs_ == 8 ) { + // BC1F, BC1T, BC1FL, BC1TL + // BC2F, BC2T, BC2FL, BC2TL + if( _Rt_ >= 2 ) s_nHasDelay = 1; + else s_nHasDelay = 2; + + branchTo = _Imm_ * 4 + i + 4; + if( branchTo > startpc && branchTo < i ) s_nEndBlock = branchTo; + else s_nEndBlock = i+8; + + goto StartRecomp; + } + break; + } + + i += 4; + } + +StartRecomp: + + // rec info // + { + EEINST* pcur; + + if( s_nInstCacheSize < (s_nEndBlock-startpc)/4+1 ) { + free(s_pInstCache); + s_nInstCacheSize = (s_nEndBlock-startpc)/4+10; + s_pInstCache = (EEINST*)malloc(sizeof(EEINST)*s_nInstCacheSize); + assert( s_pInstCache != NULL ); + } + + pcur = s_pInstCache + (s_nEndBlock-startpc)/4; + _recClearInst(pcur); + pcur->info = 0; + + for(i = s_nEndBlock; i > startpc; i -= 4 ) { + cpuRegs.code = *(int *)PSM(i-4); + pcur[-1] = pcur[0]; + + BSCPropagate bsc( pcur[-1], pcur[0] ); + bsc.rprop(); + pcur--; + } + } + + // analyze instructions // + { + usecop2 = 0; + g_pCurInstInfo = s_pInstCache; + + for(i = startpc; i < s_nEndBlock; i += 4) { + g_pCurInstInfo++; + cpuRegs.code = *(u32*)PSM(i); + + // cop2 // + if( g_pCurInstInfo->info & EEINSTINFO_COP2 ) { + + if( !usecop2 ) { + // init + vucycle = 0; + usecop2 = 1; + } + + VU0.code = cpuRegs.code; + _vuRegsCOP22( &VU0, &g_pCurInstInfo->vuregs ); + continue; + } + + // fixme - This should be based on the cycle count of the current EE + // instruction being analyzed. + if( usecop2 ) vucycle++; + + // peephole optimizations // +#ifdef PCSX2_VM_COISSUE + if( i < s_nEndBlock-4 && recompileCodeSafe(i) ) { + u32 curcode = cpuRegs.code; + u32 nextcode = *(u32*)PSM(i+4); + if( _eeIsLoadStoreCoIssue(curcode, nextcode) && recBSC_co[curcode>>26] != NULL ) { + + // rs has to be the same, and cannot be just written + if( ((curcode >> 21) & 0x1F) == ((nextcode >> 21) & 0x1F) && !_eeLoadWritesRs(curcode) ) { + + if( _eeIsLoadStoreCoX(curcode) && ((nextcode>>16)&0x1f) != ((curcode>>21)&0x1f) ) { + // see how many stores there are + u32 j; + // use xmmregs since only supporting lwc1,lq,swc1,sq + for(j = i+8; j < s_nEndBlock && j < i+4*XMMREGS; j += 4 ) { + u32 nncode = *(u32*)PSM(j); + if( (nncode>>26) != (curcode>>26) || ((curcode>>21)&0x1f) != ((nncode>>21)&0x1f) || + _eeLoadWritesRs(nncode)) + break; + } + + if( j > i+8 ) { + u32 num = (j-i)>>2; // number of stores that can coissue + assert( num <= XMMREGS ); + + g_pCurInstInfo[0].numpeeps = num-1; + g_pCurInstInfo[0].info |= EEINSTINFO_COREC; + + while(i < j-4) { + g_pCurInstInfo++; + g_pCurInstInfo[0].info |= EEINSTINFO_NOREC; + i += 4; + } + + continue; + } + + // fall through + } + + // unaligned loadstores + + // if LWL, check if LWR and that offsets are +3 away + switch(curcode >> 26) { + case 0x22: // LWL + if( (nextcode>>26) != 0x26 || ((s16)nextcode)+3 != (s16)curcode ) + continue; + break; + case 0x26: // LWR + if( (nextcode>>26) != 0x22 || ((s16)nextcode) != (s16)curcode+3 ) + continue; + break; + + case 0x2a: // SWL + if( (nextcode>>26) != 0x2e || ((s16)nextcode)+3 != (s16)curcode ) + continue; + break; + case 0x2e: // SWR + if( (nextcode>>26) != 0x2a || ((s16)nextcode) != (s16)curcode+3 ) + continue; + break; + + case 0x1a: // LDL + if( (nextcode>>26) != 0x1b || ((s16)nextcode)+7 != (s16)curcode ) + continue; + break; + case 0x1b: // LWR + if( (nextcode>>26) != 0x1aa || ((s16)nextcode) != (s16)curcode+7 ) + continue; + break; + + case 0x2c: // SWL + if( (nextcode>>26) != 0x2d || ((s16)nextcode)+7 != (s16)curcode ) + continue; + break; + case 0x2d: // SWR + if( (nextcode>>26) != 0x2c || ((s16)nextcode) != (s16)curcode+7 ) + continue; + break; + } + + // good enough + g_pCurInstInfo[0].info |= EEINSTINFO_COREC; + g_pCurInstInfo[0].numpeeps = 1; + g_pCurInstInfo[1].info |= EEINSTINFO_NOREC; + g_pCurInstInfo++; + i += 4; + continue; + } + } + } +#endif // end peephole + } + // This *is* important because g_pCurInstInfo is checked a bit later on and + // if it's not equal to s_pInstCache it handles recompilation differently. + // ... but the empty if() conditional inside the for loop is still amusing. >_< + if( usecop2 ) { + // add necessary mac writebacks + g_pCurInstInfo = s_pInstCache; + + for(i = startpc; i < s_nEndBlock-4; i += 4) { + g_pCurInstInfo++; + + if( g_pCurInstInfo->info & EEINSTINFO_COP2 ) { + } + } + } + } + +#ifdef _DEBUG + // dump code + for(i = 0; i < ARRAYSIZE(s_recblocks); ++i) { + if( startpc == s_recblocks[i] ) { + iDumpBlock(startpc, recPtr); + } + } + + if( (dumplog & 1) ) //|| usecop2 ) + iDumpBlock(startpc, recPtr); +#endif + + u32 sz=(s_nEndBlock-startpc)>>2; +#ifdef lulz + /* + Block checking (ADDED BY RAZ-TEMP) + */ + + MOV32ItoR(ECX,startpc); + MOV32ItoR(EDX,sz); + +#endif + + u32 inpage_offs=startpc&0xFFF; + u32 inpage_ptr=startpc; + u32 inpage_sz=sz*4; + + MOV32ItoR(ECX,startpc); + MOV32ItoR(EDX,sz); + + while(inpage_sz) + { + int PageType=mmap_GetRamPageInfo((u32*)PSM(inpage_ptr)); + u32 pgsz=std::min(0x1000-inpage_offs,inpage_sz); + + if(PageType!=-1) + { + if (PageType==0) + { + //MOV32ItoR(EAX,*pageVer); + //CMP32MtoR(EAX,(uptr)pageVer); + //JNE32(((u32)dyna_block_discard_recmem)- ( (u32)x86Ptr + 6 )); + + mmap_MarkCountedRamPage(PSM(inpage_ptr),inpage_ptr&~0xFFF); + } + else + { + u32 lpc=inpage_ptr; + u32 stg=pgsz; + while(stg>0) + { + // was dyna_block_discard_recmem. See note in recResetEE for details. + CMP32ItoM((uptr)PSM(lpc),*(u32*)PSM(lpc)); + JNE32(((u32)&dyna_block_discard)- ( (u32)x86Ptr + 6 )); + + stg-=4; + lpc+=4; + } + DbgCon::WriteLn("Manual block @ %08X : %08X %d %d %d %d", params + startpc,inpage_ptr,pgsz,0x1000-inpage_offs,inpage_sz,sz*4); + } + } + inpage_ptr+=pgsz; + inpage_sz-=pgsz; + inpage_offs=inpage_ptr&0xFFF; + } + + // finally recompile // + g_pCurInstInfo = s_pInstCache; + while (!branch && pc < s_nEndBlock) { + recompileNextInstruction(0); + } + +#ifdef _DEBUG + if( (dumplog & 1) ) + iDumpBlock(startpc, recPtr); +#endif + + assert( (pc-startpc)>>2 <= 0xffff ); + s_pCurBlockEx->size = (pc-startpc)>>2; + + for(i = 1; i < (u32)s_pCurBlockEx->size-1; ++i) { + s_pCurBlock[i].SetFnptr( s_pCurBlock->GetFnptr() ); + s_pCurBlock[i].startpc = s_pCurBlock->startpc; + } + + // don't overwrite if delay slot + if( i < (u32)s_pCurBlockEx->size && !(s_pCurBlock[i].uType & BLOCKTYPE_DELAYSLOT) ) { + s_pCurBlock[i].SetFnptr( s_pCurBlock->GetFnptr() ); + s_pCurBlock[i].startpc = s_pCurBlock->startpc; + } + + // set the block ptr + AddBaseBlockEx(s_pCurBlockEx, 0); +// if( p[1].startpc == p[0].startpc + 4 ) { +// assert( p[1].GetFnptr() != 0 ); +// // already fn in place, so add to list +// AddBaseBlockEx(s_pCurBlockEx, 0); +// } +// else +// *(BASEBLOCKEX**)(p+1) = pex; +// } + + //PC_SETBLOCKEX(s_pCurBlock, s_pCurBlockEx); + + if( !(pc&0x10000000) ) + maxrecmem = std::max( (pc&~0xa0000000), maxrecmem ); + + if( branch == 2 ) + { + // Branch type 2 - This is how I "think" this works (air): + // Performs a branch/event test but does not actually "break" the block. + // This allows exceptions to be raised, and is thus sufficient for + // certain types of things like SYSCALL, EI, etc. but it is not sufficient + // for actual branching instructions. + + iFlushCall(FLUSH_EVERYTHING); + iBranchTest(0xffffffff, true); + } + else + { + assert( branch != 3 ); + if( branch ) + assert( !willbranch3 ); + else + ADD32ItoM((int)&cpuRegs.cycle, eeScaleBlockCycles() ); + + if( willbranch3 ) { + BASEBLOCK* pblock = PC_GETBLOCK(s_nEndBlock); + assert( pc == s_nEndBlock ); + iFlushCall(FLUSH_EVERYTHING); + MOV32ItoM((uptr)&cpuRegs.pc, pc); + JMP32((uptr)pblock->GetFnptr() - ((uptr)x86Ptr + 5)); + branch = 3; + } + else if( !branch ) { + // didn't branch, but had to stop + MOV32ItoM( (uptr)&cpuRegs.pc, pc ); + + iFlushCall(FLUSH_EVERYTHING); + + ptr = JMP32(0); + } + } + + assert( x86Ptr >= (u8*)s_pCurBlock->GetFnptr() + EE_MIN_BLOCK_BYTES ); + assert( x86Ptr < recMem+REC_CACHEMEM ); + assert( recStackPtr < recStack+RECSTACK_SIZE ); + assert( x86FpuState == 0 ); + + recPtr = x86Ptr; + + assert( (g_cpuHasConstReg&g_cpuFlushedConstReg) == g_cpuHasConstReg ); + + if( !branch ) { + BASEBLOCK* pcurblock = s_pCurBlock; + u32 nEndBlock = s_nEndBlock; + s_pCurBlock = PC_GETBLOCK(pc); + assert( ptr != NULL ); + + if( s_pCurBlock->startpc != pc ) + recRecompile(pc); + + if( pcurblock->startpc == startpc ) { + assert( pcurblock->GetFnptr() ); + assert( s_pCurBlock->startpc == nEndBlock ); + *ptr = s_pCurBlock->GetFnptr() - ( (u32)ptr + 4 ); + } + else { + recRecompile(startpc); + assert( pcurblock->GetFnptr() != 0 ); + } + } +} + +R5900cpu recCpu = { + recAlloc, + recResetEE, + recStep, + recExecute, + recExecuteBlock, + recClear, + recShutdown +}; diff --git a/pcsx2/x86/ix86-32/iR5900Arit.cpp b/pcsx2/x86/ix86-32/iR5900Arit.cpp new file mode 100644 index 0000000000..d0f6dd4396 --- /dev/null +++ b/pcsx2/x86/ix86-32/iR5900Arit.cpp @@ -0,0 +1,1993 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" + + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl +{ + +/********************************************************* +* Register arithmetic * +* Format: OP rd, rs, rt * +*********************************************************/ + +// NOTE: The reason ADD/SUB/etc are so large is because they are very commonly +// used and recompiler needs to cover ALL possible usages to minimize code size (zerofrog) + +#ifndef ARITHMETIC_RECOMPILE + +namespace Interp = R5900::Interpreter::OpcodeImpl; + +REC_FUNC(ADD); +REC_FUNC(ADDU); +REC_FUNC(DADD); +REC_FUNC(DADDU); +REC_FUNC(SUB); +REC_FUNC(SUBU); +REC_FUNC(DSUB); +REC_FUNC(DSUBU); +REC_FUNC(AND); +REC_FUNC(OR); +REC_FUNC(XOR); +REC_FUNC(NOR); +REC_FUNC(SLT); +REC_FUNC(SLTU); + +#elif defined(EE_CONST_PROP) + +//// ADD +void recADD_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].SL[0] + g_cpuConstRegs[_Rt_].SL[0]; +} + +void recADD_constv(int info, int creg, int vreg) +{ + assert( !(info&PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + int mmreg = vreg == _Rt_ ? EEREC_T : EEREC_S; + + if( g_cpuConstRegs[ creg ].UL[0] ) { + u32* ptempmem; + + ptempmem = _eeGetConstReg(creg); + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + PADDDMtoR(EEREC_D, (u32)ptempmem); + + _signExtendGPRtoMMX(EEREC_D, _Rd_, 0); + } + else { + // just move and sign extend + if( EEINST_HASLIVE1(vreg) ) { + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + } + else { + _signExtendGPRMMXtoMMX(EEREC_D, _Rd_, mmreg, vreg); + } + } + } + else { + + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) && (!EEINST_ISLIVE1(_Rd_) || !g_cpuConstRegs[ creg ].UL[0]) ) { + int mmreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + + if( EEINST_HASLIVE1(vreg) && EEINST_ISLIVE1(_Rd_) ) { + MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[ vreg ].UL[ 0 ]); + } + else { + if( g_cpuConstRegs[creg].UL[0] ) { + MOVDMtoMMX(mmreg, (u32)_eeGetConstReg(creg)); + PADDDMtoR(mmreg, (u32)&cpuRegs.GPR.r[ vreg ].UL[ 0 ]); + } + else { + if( EEINST_ISLIVE1(_Rd_) ) { + _signExtendMtoMMX(mmreg, (u32)&cpuRegs.GPR.r[ vreg ].UL[ 0 ]); + } + else { + MOVDMtoMMX(mmreg, (u32)&cpuRegs.GPR.r[ vreg ].UL[ 0 ]); + EEINST_RESETHASLIVE1(_Rd_); + } + } + } + } + else { + if( _Rd_ == vreg ) { + ADD32ItoM((int)&cpuRegs.GPR.r[_Rd_].UL[ 0 ], g_cpuConstRegs[creg].UL[0]); + if( EEINST_ISLIVE1(_Rd_) ) _signExtendSFtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ]); + else EEINST_RESETHASLIVE1(_Rd_); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ vreg ].UL[ 0 ] ); + if( g_cpuConstRegs[ creg ].UL[0] ) + ADD32ItoR( EAX, g_cpuConstRegs[ creg ].UL[0] ); + + if( EEINST_ISLIVE1(_Rd_) ) { + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + } + } + } + } +} + +// s is constant +void recADD_consts(int info) +{ + recADD_constv(info, _Rs_, _Rt_); + EEINST_SETSIGNEXT(_Rd_); + EEINST_SETSIGNEXT(_Rt_); +} + +// t is constant +void recADD_constt(int info) +{ + recADD_constv(info, _Rt_, _Rs_); + EEINST_SETSIGNEXT(_Rd_); + EEINST_SETSIGNEXT(_Rs_); +} + +// nothing is constant +void recADD_(int info) +{ + assert( !(info&PROCESS_EE_XMM) ); + EEINST_SETSIGNEXT(_Rd_); + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + + if( info & PROCESS_EE_MMX ) { + + if( EEREC_D == EEREC_S ) PADDDRtoR(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) PADDDRtoR(EEREC_D, EEREC_S); + else { + MOVQRtoR(EEREC_D, EEREC_T); + PADDDRtoR(EEREC_D, EEREC_S); + } + + if( EEINST_ISLIVE1(_Rd_) ) { + // sign extend + _signExtendGPRtoMMX(EEREC_D, _Rd_, 0); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + } + } + else { + + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) && !EEINST_ISLIVE1(_Rd_) ) { + int mmreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + + EEINST_RESETHASLIVE1(_Rd_); + MOVDMtoMMX(mmreg, (int)&cpuRegs.GPR.r[_Rs_].UL[0] ); + PADDDMtoR(mmreg, (int)&cpuRegs.GPR.r[_Rt_].UL[0] ); + } + else { + if( _Rd_ == _Rs_ ) { + if( _Rd_ == _Rt_ ) SHL32ItoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], 1); // mult by 2 + else { + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + ADD32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], ECX); + } + + if( EEINST_ISLIVE1(_Rd_) ) _signExtendSFtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ]); + else EEINST_RESETHASLIVE1(_Rd_); + + return; + } + else if( _Rd_ == _Rt_ ) { + EEINST_RESETHASLIVE1(_Rd_); + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + ADD32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], ECX); + + if( EEINST_ISLIVE1(_Rd_) ) _signExtendSFtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ]); + else EEINST_RESETHASLIVE1(_Rd_); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if( _Rs_ != _Rt_ ) ADD32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + else SHL32ItoR(EAX, 1); // mult by 2 + + if( EEINST_ISLIVE1(_Rd_) ) { + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + } + } + } + } +} + +EERECOMPILE_CODE0(ADD, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + +//// ADDU +void recADDU( void ) +{ + recADD( ); +} + +//// DADD +void recDADD_const( void ) +{ + g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].SD[0] + g_cpuConstRegs[_Rt_].SD[0]; +} + +void recDADD_constv(int info, int creg, int vreg) +{ + assert( !(info&PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + int mmreg = vreg == _Rt_ ? EEREC_T : EEREC_S; + + if( g_cpuConstRegs[ creg ].UD[0] ) { + + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + PADDQMtoR(EEREC_D, (u32)_eeGetConstReg(creg)); + } + else { + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + } + } + else { + + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + int mmreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + + MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[ vreg ].UL[ 0 ]); + if( g_cpuConstRegs[ creg ].UD[0] ) PADDQMtoR(mmreg, (u32)_eeGetConstReg(creg)); + } + else { + + if( g_cpuConstRegs[ creg ].UL[0] == 0 && g_cpuConstRegs[ creg ].UL[1] == 0 && _hasFreeMMXreg() ) { + // copy + int mmreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + if( EEINST_ISLIVE1(_Rd_) ) MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[ vreg ].UL[ 0 ]); + else { + MOVDMtoMMX(mmreg, (int)&cpuRegs.GPR.r[ vreg ].UL[ 0 ]); + EEINST_RESETHASLIVE1(_Rd_); + } + } + else { + if( _Rd_ == vreg ) { + + if( EEINST_ISLIVE1(_Rd_) && (g_cpuConstRegs[ creg ].UL[ 0 ] || g_cpuConstRegs[ creg ].UL[ 1 ]) ) { + ADD32ItoM( (u32)&cpuRegs.GPR.r[_Rd_].UL[0], g_cpuConstRegs[ creg ].UL[ 0 ] ); + ADC32ItoM( (u32)&cpuRegs.GPR.r[_Rd_].UL[1], g_cpuConstRegs[ creg ].UL[ 1 ] ); + } + else if( g_cpuConstRegs[ creg ].UL[ 0 ] ) { + EEINST_RESETHASLIVE1(_Rd_); + ADD32ItoM( (u32)&cpuRegs.GPR.r[_Rd_].UL[0], g_cpuConstRegs[ creg ].UL[ 0 ] ); + } + + return; + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ vreg ].UL[ 0 ] ); + + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ vreg ].UL[ 1 ] ); + + if( g_cpuConstRegs[ creg ].UL[0] || g_cpuConstRegs[ creg ].UL[1] ) { + ADD32ItoR( EAX, g_cpuConstRegs[ creg ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + ADC32ItoR( EDX, g_cpuConstRegs[ creg ].UL[ 1 ] ); + } + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + + if( !EEINST_ISLIVE1(_Rd_) ) + EEINST_RESETHASLIVE1(_Rd_); + } + } + } +} + +void recDADD_consts(int info) +{ + recDADD_constv(info, _Rs_, _Rt_); +} + +void recDADD_constt(int info) +{ + recDADD_constv(info, _Rt_, _Rs_); +} + +void recDADD_(int info) +{ + assert( !(info&PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + + if( EEREC_D == EEREC_S ) PADDQRtoR(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) PADDQRtoR(EEREC_D, EEREC_S); + else { + MOVQRtoR(EEREC_D, EEREC_T); + PADDQRtoR(EEREC_D, EEREC_S); + } + } + else { + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + int mmreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + + MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + if( _Rs_ != _Rt_ ) PADDQMtoR(mmreg, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + else PSLLQItoR(mmreg, 1); // mult by 2 + } + else { + if( _Rd_ == _Rs_ || _Rd_ == _Rt_ ) { + int vreg = _Rd_ == _Rs_ ? _Rt_ : _Rs_; + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ vreg ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ vreg ].UL[ 1 ] ); + ADD32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX); + if( EEINST_ISLIVE1(_Rd_) ) + ADC32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX); + else + EEINST_RESETHASLIVE1(_Rd_); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + ADD32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + ADC32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + + if( !EEINST_ISLIVE1(_Rd_) ) + EEINST_RESETHASLIVE1(_Rd_); + } + } + } +} + +EERECOMPILE_CODE0(DADD, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + +//// DADDU +void recDADDU( void ) +{ + recDADD( ); +} + +//// SUB + +void recSUB_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].SL[0] - g_cpuConstRegs[_Rt_].SL[0]; +} + +void recSUB_consts(int info) +{ + assert( !(info&PROCESS_EE_XMM) ); + EEINST_SETSIGNEXT(_Rt_); + EEINST_SETSIGNEXT(_Rd_); + + if( info & PROCESS_EE_MMX ) { + if( g_cpuConstRegs[ _Rs_ ].UL[0] ) { + + if( EEREC_D != EEREC_T ) { + MOVQMtoR(EEREC_D, (u32)_eeGetConstReg(_Rs_)); + PSUBDRtoR(EEREC_D, EEREC_T); + + if( EEINST_ISLIVE1(_Rd_) ) _signExtendGPRtoMMX(EEREC_D, _Rd_, 0); + else EEINST_RESETHASLIVE1(_Rd_); + } + else { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVDMtoMMX(t0reg, (u32)_eeGetConstReg(_Rs_)); + PSUBDRtoR(t0reg, EEREC_T); + + // swap mmx regs + mmxregs[t0reg] = mmxregs[EEREC_D]; + mmxregs[EEREC_D].inuse = 0; + + if( EEINST_ISLIVE1(_Rd_) ) _signExtendGPRtoMMX(t0reg, _Rd_, 0); + else EEINST_RESETHASLIVE1(_Rd_); + } + } + else { + // just move and sign extend + if( EEREC_D != EEREC_T ) { + PXORRtoR(EEREC_D, EEREC_D); + PSUBDRtoR(EEREC_D, EEREC_T); + + if( EEINST_ISLIVE1(_Rd_) ) _signExtendGPRtoMMX(EEREC_D, _Rd_, 0); + else EEINST_RESETHASLIVE1(_Rd_); + } + else { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + PXORRtoR(t0reg, t0reg); + PSUBDRtoR(t0reg, EEREC_T); + + // swap mmx regs.. don't ask + mmxregs[t0reg] = mmxregs[EEREC_D]; + mmxregs[EEREC_D].inuse = 0; + + if( EEINST_ISLIVE1(_Rd_) ) _signExtendGPRtoMMX(t0reg, _Rd_, 0); + else EEINST_RESETHASLIVE1(_Rd_); + } + } + } + else { + if( _Rd_ == _Rt_ ) { + if( g_cpuConstRegs[ _Rs_ ].UL[ 0 ] ) SUB32ItoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], g_cpuConstRegs[ _Rs_ ].UL[ 0 ]); + NEG32M((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ]); + + if( EEINST_ISLIVE1(_Rd_) ) _signExtendSFtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ]); + else EEINST_RESETHASLIVE1(_Rd_); + } + else { + if( g_cpuConstRegs[ _Rs_ ].UL[ 0 ] ) { + MOV32ItoR( EAX, g_cpuConstRegs[ _Rs_ ].UL[ 0 ] ); + SUB32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + NEG32R(EAX); + } + + if( EEINST_ISLIVE1(_Rd_) ) { + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + } + } + } +} + +void recSUB_constt(int info) +{ + assert( !(info&PROCESS_EE_XMM) ); + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rd_); + + if( info & PROCESS_EE_MMX ) { + if( g_cpuConstRegs[ _Rt_ ].UL[0] ) { + + u32* ptempmem = _eeGetConstReg(_Rt_); + if( EEREC_D != EEREC_S ) MOVQRtoR(EEREC_D, EEREC_S); + PSUBDMtoR(EEREC_D, (u32)ptempmem); + + _signExtendGPRtoMMX(EEREC_D, _Rd_, 0); + } + else { + // just move and sign extend + if( EEINST_HASLIVE1(_Rs_) ) { + if( EEREC_D != EEREC_S ) MOVQRtoR(EEREC_D, EEREC_S); + } + else { + _signExtendGPRMMXtoMMX(EEREC_D, _Rd_, EEREC_S, _Rs_); + } + } + } + else { + + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) && (!EEINST_ISLIVE1(_Rd_) || !g_cpuConstRegs[_Rt_].UL[0]) ) { + int mmreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + + if( EEINST_HASLIVE1(_Rs_) && EEINST_ISLIVE1(_Rd_) ) { + MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + } + else { + if( g_cpuConstRegs[_Rt_].UL[0] ) { + MOVDMtoMMX(mmreg, (u32)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + PSUBDMtoR(mmreg, (u32)_eeGetConstReg(_Rt_)); + } + else { + if( EEINST_ISLIVE1(_Rd_) ) { + _signExtendMtoMMX(mmreg, (u32)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + } + else { + MOVDMtoMMX(mmreg, (u32)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + EEINST_RESETHASLIVE1(_Rd_); + } + } + } + } + else { + if( _Rd_ == _Rs_ ) { + if( g_cpuConstRegs[ _Rt_ ].UL[ 0 ] ) { + SUB32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], g_cpuConstRegs[ _Rt_ ].UL[ 0 ]); + + if( EEINST_ISLIVE1(_Rd_) ) _signExtendSFtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ]); + else EEINST_RESETHASLIVE1(_Rd_); + } + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if( g_cpuConstRegs[ _Rt_ ].UL[ 0 ] ) + SUB32ItoR( EAX, g_cpuConstRegs[ _Rt_ ].UL[ 0 ] ); + + if( EEINST_ISLIVE1(_Rd_) ) { + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + } + } + } + } +} + +void recSUB_(int info) +{ + assert( !(info&PROCESS_EE_XMM) ); + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + EEINST_SETSIGNEXT(_Rd_); + + if( info & PROCESS_EE_MMX ) { + + if( EEREC_D == EEREC_S ) PSUBDRtoR(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t0reg, EEREC_S); + PSUBDRtoR(t0reg, EEREC_T); + + // swap mmx regs.. don't ask + mmxregs[t0reg] = mmxregs[EEREC_D]; + mmxregs[EEREC_D].inuse = 0; + info = (info&~PROCESS_EE_SET_D(0xf))|PROCESS_EE_SET_D(t0reg); + } + else { + MOVQRtoR(EEREC_D, EEREC_S); + PSUBDRtoR(EEREC_D, EEREC_T); + } + + if( EEINST_ISLIVE1(_Rd_) ) { + // sign extend + _signExtendGPRtoMMX(EEREC_D, _Rd_, 0); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + } + } + else { + + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) && !EEINST_ISLIVE1(_Rd_)) { + int mmreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + + EEINST_RESETHASLIVE1(_Rd_); + MOVDMtoMMX(mmreg, (int)&cpuRegs.GPR.r[_Rs_].UL[0] ); + PSUBDMtoR(mmreg, (int)&cpuRegs.GPR.r[_Rt_].UL[0] ); + } + else { + if( !EEINST_ISLIVE1(_Rd_) ) { + if( _Rd_ == _Rs_) { + EEINST_RESETHASLIVE1(_Rd_); + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + SUB32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + return; + } + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + SUB32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + + if( EEINST_ISLIVE1(_Rd_) ) { + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + } + } + } +} + +EERECOMPILE_CODE0(SUB, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// SUBU +void recSUBU( void ) +{ + recSUB( ); +} + +//// DSUB +void recDSUB_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].SD[0] - g_cpuConstRegs[_Rt_].SD[0]; +} + +void recDSUB_consts(int info) +{ + assert( !(info&PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + + if( g_cpuConstRegs[ _Rs_ ].UD[0] ) { + + // flush + if( EEREC_D != EEREC_T ) { + MOVQMtoR(EEREC_D, (u32)_eeGetConstReg(_Rs_)); + PSUBQRtoR(EEREC_D, EEREC_T); + } + else { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(t0reg, (u32)_eeGetConstReg(_Rs_)); + PSUBQRtoR(t0reg, EEREC_T); + + // swap mmx regs.. don't ask + mmxregs[t0reg] = mmxregs[EEREC_D]; + mmxregs[EEREC_D].inuse = 0; + } + } + else { + // just move and sign extend + if( EEREC_D != EEREC_T ) { + PXORRtoR(EEREC_D, EEREC_D); + PSUBQRtoR(EEREC_D, EEREC_T); + } + else { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + PXORRtoR(t0reg, t0reg); + PSUBQRtoR(t0reg, EEREC_T); + + // swap mmx regs.. don't ask + mmxregs[t0reg] = mmxregs[EEREC_D]; + mmxregs[EEREC_D].inuse = 0; + } + } + } + else { + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + int mmreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + MOVQMtoR(mmreg, (u32)_eeGetConstReg(_Rs_)); + PSUBQMtoR(mmreg, (u32)&cpuRegs.GPR.r[_Rt_].UL[ 0 ]); + } + else { + if( g_cpuConstRegs[ _Rs_ ].UL[ 0 ] || g_cpuConstRegs[ _Rs_ ].UL[ 1 ] ) { + MOV32ItoR( EAX, g_cpuConstRegs[ _Rs_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32ItoR( EDX, g_cpuConstRegs[ _Rs_ ].UL[ 1 ] ); + SUB32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + SBB32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + + if( !EEINST_ISLIVE1(_Rd_) ) + EEINST_RESETHASLIVE1(_Rd_); + } + else { + + if( _Rd_ == _Rt_ ) { + // negate _Rt_ all in memory + if( EEINST_ISLIVE1(_Rd_) ) { + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + NEG32M((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + ADC32ItoR(EDX, 0); + NEG32R(EDX); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + NEG32M((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ]); + } + return; + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + // take negative of 64bit number + NEG32R(EAX); + + if( EEINST_ISLIVE1(_Rd_) ) { + ADC32ItoR(EDX, 0); + NEG32R(EDX); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + } + } + + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + else { + EEINST_RESETHASLIVE1(_Rd_); + } + } + } +} + +void recDSUB_constt(int info) +{ + assert( !(info&PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + + if( g_cpuConstRegs[ _Rt_ ].UD[0] ) { + + if( EEREC_D != EEREC_S ) MOVQRtoR(EEREC_D, EEREC_S); + PSUBQMtoR(EEREC_D, (u32)_eeGetConstReg(_Rt_)); + } + else { + if( EEREC_D != EEREC_S ) MOVQRtoR(EEREC_D, EEREC_S); + } + } + else { + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + int mmreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + if( g_cpuConstRegs[_Rt_].UD[0] ) PSUBQMtoR(mmreg, (u32)_eeGetConstReg(_Rt_)); + } + else { + + if( _Rd_ == _Rs_ ) { + if( EEINST_ISLIVE1(_Rd_) && (g_cpuConstRegs[ _Rt_ ].UL[ 0 ] || g_cpuConstRegs[ _Rt_ ].UL[ 1 ]) ) { + SUB32ItoM( (u32)&cpuRegs.GPR.r[_Rd_].UL[0], g_cpuConstRegs[ _Rt_ ].UL[ 0 ] ); + SBB32ItoM( (u32)&cpuRegs.GPR.r[_Rd_].UL[1], g_cpuConstRegs[ _Rt_ ].UL[ 1 ] ); + } + else if( g_cpuConstRegs[ _Rt_ ].UL[ 0 ] ) { + EEINST_RESETHASLIVE1(_Rd_); + SUB32ItoM( (u32)&cpuRegs.GPR.r[_Rd_].UL[0], g_cpuConstRegs[ _Rt_ ].UL[ 0 ] ); + } + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + + if( g_cpuConstRegs[ _Rt_ ].UL[ 0 ] || g_cpuConstRegs[ _Rt_ ].UL[ 1 ] ) { + SUB32ItoR( EAX, g_cpuConstRegs[ _Rt_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + SBB32ItoR( EDX, g_cpuConstRegs[ _Rt_ ].UL[ 1 ] ); + } + + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + else + EEINST_RESETHASLIVE1(_Rd_); + } + } + } +} + +void recDSUB_(int info) +{ + assert( !(info&PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + + if( EEREC_D == EEREC_S ) PSUBQRtoR(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t0reg, EEREC_S); + PSUBQRtoR(t0reg, EEREC_T); + + // swap mmx regs.. don't ask + mmxregs[t0reg] = mmxregs[EEREC_D]; + mmxregs[EEREC_D].inuse = 0; + } + else { + MOVQRtoR(EEREC_D, EEREC_S); + PSUBQRtoR(EEREC_D, EEREC_T); + } + } + else { + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + int mmreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[_Rs_].UL[ 0 ]); + PSUBQMtoR(mmreg, (int)&cpuRegs.GPR.r[_Rt_].UL[ 0 ]); + } + else { + if( _Rd_ == _Rs_ ) { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + SUB32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) + SBB32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + else + EEINST_RESETHASLIVE1(_Rd_); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + SUB32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rd_) ) + SBB32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + else + EEINST_RESETHASLIVE1(_Rd_); + } + } + } +} + +EERECOMPILE_CODE0(DSUB, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// DSUBU +void recDSUBU( void ) +{ + recDSUB( ); +} + +//// AND +void recAND_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] & g_cpuConstRegs[_Rt_].UD[0]; +} + +void recAND_constv(int info, int creg, int vreg) +{ + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + int mmreg = vreg == _Rt_ ? EEREC_T : EEREC_S; + + if( g_cpuConstRegs[ creg ].UL[0] || g_cpuConstRegs[ creg ].UL[1] ) { + _flushConstReg(creg); + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + PANDMtoR(EEREC_D, (u32)&cpuRegs.GPR.r[creg].UL[0] ); + } + else { + PXORRtoR(EEREC_D, EEREC_D); + } + } + else { + + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) || (_Rd_ != vreg && _hasFreeMMXreg()) ) { + int rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + + MOVQMtoR(rdreg, (u32)&cpuRegs.GPR.r[vreg].UL[0] ); + PANDMtoR(rdreg, (u32)_eeGetConstReg(creg) ); + } + else { + if( g_cpuConstRegs[ creg ].UL[0] || g_cpuConstRegs[ creg ].UL[1] ) { + + if( _Rd_ == vreg ) { + AND32ItoM((int)&cpuRegs.GPR.r[ vreg ].UL[0], g_cpuConstRegs[creg].UL[0]); + if( EEINST_ISLIVE1(_Rd_) ) { + if( g_cpuConstRegs[creg].UL[1] != 0xffffffff ) AND32ItoM((int)&cpuRegs.GPR.r[ vreg ].UL[1], g_cpuConstRegs[creg].UL[1]); + } + else + EEINST_RESETHASLIVE1(_Rd_); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ vreg ]); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ vreg ] + 4 ); + AND32ItoR(EAX, g_cpuConstRegs[ creg ].UL[0]); + if( EEINST_ISLIVE1(_Rd_) ) + AND32ItoR(ECX, g_cpuConstRegs[ creg ].UL[1]); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ], EAX); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ]+4, ECX); + else + EEINST_RESETHASLIVE1(_Rd_); + } + } + else { + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rd_ ], 0); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rd_ ]+4, 0); + else + EEINST_RESETHASLIVE1(_Rd_); + } + } + } +} + +void recAND_consts(int info) +{ + recAND_constv(info, _Rs_, _Rt_); +} + +void recAND_constt(int info) +{ + recAND_constv(info, _Rt_, _Rs_); +} + +void recLogicalOp(int info, int op) +{ + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + if( EEREC_D == EEREC_S ) LogicalOpRtoR(EEREC_D, EEREC_T, op); + else if( EEREC_D == EEREC_T ) LogicalOpRtoR(EEREC_D, EEREC_S, op); + else { + MOVQRtoR(EEREC_D, EEREC_S); + LogicalOpRtoR(EEREC_D, EEREC_T, op); + } + } + else if( (g_pCurInstInfo->regs[_Rs_]&EEINST_MMX) || (g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) || (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + int rsreg, rtreg, rdreg; + _addNeededMMXreg(MMX_GPR+_Rs_); + _addNeededMMXreg(MMX_GPR+_Rt_); + _addNeededMMXreg(MMX_GPR+_Rd_); + + rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + rsreg = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rs_, MODE_READ); + rtreg = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_READ); + SetMMXstate(); + + if( rdreg == rsreg ) { + if( rtreg >= 0 ) LogicalOpRtoR(rdreg, rtreg, op); + else LogicalOpMtoR(rdreg, (int)&cpuRegs.GPR.r[ _Rt_ ], op); + } + else { + if( rdreg != rtreg ) { + if( rtreg >= 0 ) MOVQRtoR(rdreg, rtreg); + else MOVQMtoR(rdreg, (int)&cpuRegs.GPR.r[ _Rt_ ]); + } + + if( rsreg >= 0 ) LogicalOpRtoR(rdreg, rsreg, op); + else LogicalOpMtoR(rdreg, (int)&cpuRegs.GPR.r[ _Rs_ ], op); + } + } + else { + if( _Rd_ == _Rs_ || _Rd_ == _Rt_ ) { + int vreg = _Rd_ == _Rs_ ? _Rt_ : _Rs_; + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ vreg ]); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ vreg ] + 4 ); + LogicalOp32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ], EAX, op ); + if( EEINST_ISLIVE1(_Rd_) ) + LogicalOp32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ] + 4, EDX, op ); + if( op == 3 ) { + NOT32M((int)&cpuRegs.GPR.r[ _Rd_ ]); + if( EEINST_ISLIVE1(_Rd_) ) + NOT32M((int)&cpuRegs.GPR.r[ _Rd_ ]+4); + } + + if( !EEINST_ISLIVE1(_Rd_) ) EEINST_RESETHASLIVE1(_Rd_); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ _Rs_ ]); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ _Rs_ ] + 4 ); + LogicalOp32MtoR(EAX, (int)&cpuRegs.GPR.r[ _Rt_ ], op ); + if( EEINST_ISLIVE1(_Rd_) ) + LogicalOp32MtoR(ECX, (int)&cpuRegs.GPR.r[ _Rt_ ] + 4, op ); + + if( op == 3 ) { + NOT32R(EAX); + if( EEINST_ISLIVE1(_Rd_) ) + NOT32R(ECX); + } + + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ], EAX); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ]+4, ECX); + else + EEINST_RESETHASLIVE1(_Rd_); + } + } +} + +void recAND_(int info) +{ + recLogicalOp(info, 0); +} + +EERECOMPILE_CODE0(AND, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// OR +void recOR_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] | g_cpuConstRegs[_Rt_].UD[0]; +} + +void recOR_constv(int info, int creg, int vreg) +{ + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + int mmreg = vreg == _Rt_ ? EEREC_T : EEREC_S; + + if( g_cpuConstRegs[ creg ].UL[0] || g_cpuConstRegs[ creg ].UL[1] ) { + _flushConstReg(creg); + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + PORMtoR(EEREC_D, (u32)&cpuRegs.GPR.r[creg].UL[0] ); + } + else { + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + } + } + else { + + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) || (_Rd_ != vreg && _hasFreeMMXreg()) ) { + int rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + + MOVQMtoR(rdreg, (u32)&cpuRegs.GPR.r[vreg].UL[0] ); + + if( g_cpuConstRegs[ creg ].UL[0] || g_cpuConstRegs[ creg ].UL[1] ) { + PORMtoR(rdreg, (u32)_eeGetConstReg(creg) ); + } + } + else { + if( _Rd_ == vreg ) { + OR32ItoM((int)&cpuRegs.GPR.r[ vreg ].UL[0], g_cpuConstRegs[creg].UL[0]); + if( EEINST_ISLIVE1(_Rd_) ) { + if( g_cpuConstRegs[creg].UL[1] ) OR32ItoM((int)&cpuRegs.GPR.r[ vreg ].UL[1], g_cpuConstRegs[creg].UL[1]); + } + else + EEINST_RESETHASLIVE1(_Rd_); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ vreg ]); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ vreg ] + 4 ); + + if( g_cpuConstRegs[ creg ].UL[0] ) OR32ItoR(EAX, g_cpuConstRegs[ creg ].UL[0]); + if( g_cpuConstRegs[ creg ].UL[1] && EEINST_ISLIVE1(_Rd_) ) OR32ItoR(ECX, g_cpuConstRegs[ creg ].UL[1]); + + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ], EAX); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ]+4, ECX); + else + EEINST_RESETHASLIVE1(_Rd_); + } + } + } +} + +void recOR_consts(int info) +{ + recOR_constv(info, _Rs_, _Rt_); +} + +void recOR_constt(int info) +{ + recOR_constv(info, _Rt_, _Rs_); +} + +void recOR_(int info) +{ + recLogicalOp(info, 1); +} + +EERECOMPILE_CODE0(OR, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// XOR +void recXOR_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] ^ g_cpuConstRegs[_Rt_].UD[0]; +} + +void recXOR_constv(int info, int creg, int vreg) +{ + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + int mmreg = vreg == _Rt_ ? EEREC_T : EEREC_S; + + if( g_cpuConstRegs[ creg ].UL[0] || g_cpuConstRegs[ creg ].UL[1] ) { + _flushConstReg(creg); + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + PXORMtoR(EEREC_D, (u32)&cpuRegs.GPR.r[creg].UL[0] ); + } + else { + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + } + } + else { + + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) || (_Rd_ != vreg && _hasFreeMMXreg()) ) { + int rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + MOVQMtoR(rdreg, (u32)&cpuRegs.GPR.r[vreg].UL[0] ); + + if( g_cpuConstRegs[ creg ].UL[0] || g_cpuConstRegs[ creg ].UL[1] ) { + PXORMtoR(rdreg, (u32)_eeGetConstReg(creg) ); + } + } + else { + if( _Rd_ == vreg ) { + XOR32ItoM((int)&cpuRegs.GPR.r[ vreg ].UL[0], g_cpuConstRegs[creg].UL[0]); + if( EEINST_ISLIVE1(_Rd_) ) { + if( g_cpuConstRegs[creg].UL[1] ) XOR32ItoM((int)&cpuRegs.GPR.r[ vreg ].UL[1], g_cpuConstRegs[creg].UL[1]); + } + else + EEINST_RESETHASLIVE1(_Rd_); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ vreg ]); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ vreg ] + 4 ); + + if( g_cpuConstRegs[ creg ].UL[0] ) XOR32ItoR(EAX, g_cpuConstRegs[ creg ].UL[0]); + if( g_cpuConstRegs[ creg ].UL[1] && EEINST_ISLIVE1(_Rd_) ) XOR32ItoR(ECX, g_cpuConstRegs[ creg ].UL[1]); + + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ], EAX); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ]+4, ECX); + else + EEINST_RESETHASLIVE1(_Rd_); + } + } + } +} + +void recXOR_consts(int info) +{ + recXOR_constv(info, _Rs_, _Rt_); +} + +void recXOR_constt(int info) +{ + recXOR_constv(info, _Rt_, _Rs_); +} + +void recXOR_(int info) +{ + recLogicalOp(info, 2); +} + +EERECOMPILE_CODE0(XOR, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// NOR +void recNOR_const() +{ + g_cpuConstRegs[_Rd_].UD[0] =~(g_cpuConstRegs[_Rs_].UD[0] | g_cpuConstRegs[_Rt_].UD[0]); +} + +void recNOR_constv(int info, int creg, int vreg) +{ + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + int mmreg = vreg == _Rt_ ? EEREC_T : EEREC_S; + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + if( g_cpuConstRegs[ creg ].UL[0] || g_cpuConstRegs[ creg ].UL[1] ) { + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + PORMtoR(EEREC_D, (u32)_eeGetConstReg(creg)); + } + else { + if( EEREC_D != mmreg ) MOVQRtoR(EEREC_D, mmreg); + } + + // take the NOT + PCMPEQDRtoR( t0reg,t0reg); + PXORRtoR( EEREC_D,t0reg); + _freeMMXreg(t0reg); + } + else { + + if( (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) || (_Rd_ != vreg && _hasFreeMMXreg()) ) { + int rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + SetMMXstate(); + + MOVQMtoR(rdreg, (u32)&cpuRegs.GPR.r[vreg].UL[0] ); + PCMPEQDRtoR( t0reg,t0reg); + if( g_cpuConstRegs[ creg ].UD[0] ) PORMtoR(rdreg, (u32)_eeGetConstReg(creg) ); + + // take the NOT + PXORRtoR( rdreg,t0reg); + + _freeMMXreg(t0reg); + } + else { + if( _Rd_ == vreg ) { + NOT32M((int)&cpuRegs.GPR.r[ vreg ].UL[0]); + if( EEINST_ISLIVE1(_Rd_) ) NOT32M((int)&cpuRegs.GPR.r[ vreg ].UL[1]); + + if( g_cpuConstRegs[creg].UL[0] ) AND32ItoM((int)&cpuRegs.GPR.r[ vreg ].UL[0], ~g_cpuConstRegs[creg].UL[0]); + if( EEINST_ISLIVE1(_Rd_) ) { + if( g_cpuConstRegs[creg].UL[1] ) AND32ItoM((int)&cpuRegs.GPR.r[ vreg ].UL[1], ~g_cpuConstRegs[creg].UL[1]); + } + else + EEINST_RESETHASLIVE1(_Rd_); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ vreg ]); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ vreg ] + 4 ); + if( g_cpuConstRegs[ creg ].UL[0] ) OR32ItoR(EAX, g_cpuConstRegs[ creg ].UL[0]); + if( g_cpuConstRegs[ creg ].UL[1] && EEINST_ISLIVE1(_Rd_) ) OR32ItoR(ECX, g_cpuConstRegs[ creg ].UL[1]); + NOT32R(EAX); + if( EEINST_ISLIVE1(_Rd_) ) + NOT32R(ECX); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ], EAX); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ]+4, ECX); + else + EEINST_RESETHASLIVE1(_Rd_); + } + } + } +} + +void recNOR_consts(int info) +{ + recNOR_constv(info, _Rs_, _Rt_); +} + +void recNOR_constt(int info) +{ + recNOR_constv(info, _Rt_, _Rs_); +} + +void recNOR_(int info) +{ + recLogicalOp(info, 3); +} + +EERECOMPILE_CODE0(NOR, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// SLT - test with silent hill, lemans +void recSLT_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].SD[0] < g_cpuConstRegs[_Rt_].SD[0]; +} + +static u32 s_sltconst = 0x80000000; +static u32 s_sltconst64[2] = {0, 0x80000000}; +u32 s_sltone = 1; + +void recSLTs_consts(int info, int sign) +{ + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + + if( _Rs_ == _Rt_ ) { + PXORRtoR(EEREC_D, EEREC_D); + return; + } + + if( g_cpuConstRegs[_Rs_].UL[1] == (g_cpuConstRegs[_Rs_].SL[0]<0?0xffffffff:0) && EEINST_ISSIGNEXT(_Rt_) ) { + // just compare the lower values + if( sign ) { + if( EEREC_D != EEREC_T ) MOVQRtoR(EEREC_D, EEREC_T); + PCMPGTDMtoR(EEREC_D, (u32)_eeGetConstReg(_Rs_)); + + PUNPCKLDQRtoR(EEREC_D, EEREC_D); + PSRLQItoR(EEREC_D, 63); + } + else { + u32* ptempmem = recAllocStackMem(8,4); + ptempmem[0] = g_cpuConstRegs[_Rs_].UL[0]^0x80000000; + ptempmem[1] = 0; + + if( EEREC_D != EEREC_T ) { + MOVDMtoMMX(EEREC_D, (u32)&s_sltconst); + PXORRtoR(EEREC_D, EEREC_T); + } + else { + PXORMtoR(EEREC_D, (u32)&s_sltconst); + } + + PCMPGTDMtoR(EEREC_D, (u32)ptempmem); + + PUNPCKLDQRtoR(EEREC_D, EEREC_D); + PSRLQItoR(EEREC_D, 63); + } + + return; + } + else { + // need to compare total 64 bit value + if( info & PROCESS_EE_MODEWRITET ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rt_], EEREC_T); + if( mmxregs[EEREC_T].reg == MMX_GPR+_Rt_ ) mmxregs[EEREC_T].mode &= ~MODE_WRITE; + } + + // fall through + mmxregs[EEREC_D].mode |= MODE_WRITE; // in case EEREC_D was just flushed + } + } + + if( info & PROCESS_EE_MMX ) PXORRtoR(EEREC_D, EEREC_D); + else XOR32RtoR(EAX, EAX); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], g_cpuConstRegs[_Rs_].UL[1]); + if( sign ) { + j8Ptr[0] = JL8( 0 ); + j8Ptr[2] = JG8( 0 ); + } + else { + j8Ptr[0] = JB8( 0 ); + j8Ptr[2] = JA8( 0 ); + } + + CMP32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], g_cpuConstRegs[_Rs_].UL[0]); + j8Ptr[1] = JBE8(0); + + x86SetJ8(j8Ptr[2]); + if( info & PROCESS_EE_MMX ) MOVDMtoMMX(EEREC_D, (u32)&s_sltone); + else MOV32ItoR(EAX, 1); + + x86SetJ8(j8Ptr[0]); + x86SetJ8(j8Ptr[1]); + + if( !(info & PROCESS_EE_MMX) ) { + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], 0 ); + else EEINST_RESETHASLIVE1(_Rd_); + } +} + +// SLT with one operand coming from mem (compares only low 32 bits) +void recSLTmemconstt(int regd, int regs, u32 mem, int sign) +{ + // just compare the lower values + int t0reg; + + if( sign ) { + if( regd == regs ) { + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(t0reg, mem); + PCMPGTDRtoR(t0reg, regs); + + PUNPCKLDQRtoR(t0reg, t0reg); + PSRLQItoR(t0reg, 63); + + // swap regs + mmxregs[t0reg] = mmxregs[regd]; + mmxregs[regd].inuse = 0; + } + else { + MOVQMtoR(regd, mem); + PCMPGTDRtoR(regd, regs); + + PUNPCKLDQRtoR(regd, regd); + PSRLQItoR(regd, 63); + } + } + else { + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + if( regd == regs ) { + MOVQMtoR(t0reg, mem); + PXORMtoR(regs, (u32)&s_sltconst); + PCMPGTDRtoR(t0reg, regs); + + PUNPCKLDQRtoR(t0reg, t0reg); + PSRLQItoR(t0reg, 63); + + // swap regs + mmxregs[t0reg] = mmxregs[regd]; + mmxregs[regd].inuse = 0; + } + else { + MOVQRtoR(t0reg, regs); + MOVQMtoR(regd, mem); + PXORMtoR(t0reg, (u32)&s_sltconst); + PCMPGTDRtoR(regd, t0reg); + + PUNPCKLDQRtoR(regd, regd); + PSRLQItoR(regd, 63); + _freeMMXreg(t0reg); + } + } +} + +void recSLTs_constt(int info, int sign) +{ + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + if( _Rs_ == _Rt_ ) { + PXORRtoR(EEREC_D, EEREC_D); + return; + } + + if( EEINST_ISSIGNEXT(_Rs_) && g_cpuConstRegs[_Rt_].UL[1] == (g_cpuConstRegs[_Rt_].SL[0]<0?0xffffffff:0) ) { + // just compare the lower values + if( sign ) { + recSLTmemconstt(EEREC_D, EEREC_S, (u32)_eeGetConstReg(_Rt_), 1); + } + else { + u32* ptempmem = recAllocStackMem(8,4); + ptempmem[0] = g_cpuConstRegs[_Rt_].UL[0]^0x80000000; + ptempmem[1] = 0; + + recSLTmemconstt(EEREC_D, EEREC_S, (u32)ptempmem, 0); + } + + return; + } + else { + // need to compare total 64 bit value + if( info & PROCESS_EE_MODEWRITES ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rs_], EEREC_S); + if( mmxregs[EEREC_S].reg == MMX_GPR+_Rs_ ) mmxregs[EEREC_S].mode &= ~MODE_WRITE; + } + mmxregs[EEREC_D].mode |= MODE_WRITE; // in case EEREC_D was just flushed + + // fall through + } + } + + if( info & PROCESS_EE_MMX ) MOVDMtoMMX(EEREC_D, (u32)&s_sltone); + else MOV32ItoR(EAX, 1); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], g_cpuConstRegs[_Rt_].UL[1]); + if( sign ) { + j8Ptr[0] = JL8( 0 ); + j8Ptr[2] = JG8( 0 ); + } + else { + j8Ptr[0] = JB8( 0 ); + j8Ptr[2] = JA8( 0 ); + } + + CMP32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], g_cpuConstRegs[_Rt_].UL[0]); + j8Ptr[1] = JB8(0); + + x86SetJ8(j8Ptr[2]); + if( info & PROCESS_EE_MMX ) PXORRtoR(EEREC_D, EEREC_D); + else XOR32RtoR(EAX, EAX); + + x86SetJ8(j8Ptr[0]); + x86SetJ8(j8Ptr[1]); + + if( !(info & PROCESS_EE_MMX) ) { + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], 0 ); + else EEINST_RESETHASLIVE1(_Rd_); + } +} + +void recSLTs_(int info, int sign) +{ + if( info & PROCESS_EE_MMX ) MOVDMtoMMX(EEREC_D, (u32)&s_sltone); + else MOV32ItoR(EAX, 1); + + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ]); + CMP32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ]); + + if( sign ) { + j8Ptr[0] = JL8( 0 ); + j8Ptr[2] = JG8( 0 ); + } + else { + j8Ptr[0] = JB8( 0 ); + j8Ptr[2] = JA8( 0 ); + } + + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + CMP32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + j8Ptr[1] = JB8(0); + + x86SetJ8(j8Ptr[2]); + if( info & PROCESS_EE_MMX ) PXORRtoR(EEREC_D, EEREC_D); + else XOR32RtoR(EAX, EAX); + + x86SetJ8(j8Ptr[0]); + x86SetJ8(j8Ptr[1]); + + if( !(info & PROCESS_EE_MMX) ) { + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], 0 ); + else EEINST_RESETHASLIVE1(_Rd_); + } +} + +void recSLT_consts(int info) +{ + recSLTs_consts(info, 1); +} + +void recSLT_constt(int info) +{ + recSLTs_constt(info, 1); +} + +void recSLT_(int info) +{ + int t0reg; + assert( !(info & PROCESS_EE_XMM) ); + + if( !(info & PROCESS_EE_MMX) ) { + recSLTs_(info, 1); + return; + } + + if( !EEINST_ISSIGNEXT(_Rs_) || !EEINST_ISSIGNEXT(_Rt_) ) { + // need to compare total 64 bit value + if( info & PROCESS_EE_MODEWRITES ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rs_], EEREC_S); + if( mmxregs[EEREC_S].reg == MMX_GPR+_Rs_ ) mmxregs[EEREC_S].mode &= ~MODE_WRITE; + } + if( info & PROCESS_EE_MODEWRITET ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rt_], EEREC_T); + if( mmxregs[EEREC_T].reg == MMX_GPR+_Rt_ ) mmxregs[EEREC_T].mode &= ~MODE_WRITE; + } + mmxregs[EEREC_D].mode |= MODE_WRITE; // in case EEREC_D was just flushed + recSLTs_(info, 1); + return; + } + + if( EEREC_S == EEREC_T ) { + PXORRtoR(EEREC_D, EEREC_D); + return; + } + + // just compare the lower values + if( EEREC_D == EEREC_S ) { + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t0reg, EEREC_T); + PCMPGTDRtoR(t0reg, EEREC_S); + + PUNPCKLDQRtoR(t0reg, t0reg); + PSRLQItoR(t0reg, 63); + + // swap regs + mmxregs[t0reg] = mmxregs[EEREC_D]; + mmxregs[EEREC_D].inuse = 0; + } + else { + if( EEREC_D != EEREC_T ) MOVQRtoR(EEREC_D, EEREC_T); + PCMPGTDRtoR(EEREC_D, EEREC_S); + + PUNPCKLDQRtoR(EEREC_D, EEREC_D); + PSRLQItoR(EEREC_D, 63); + } +} + +EERECOMPILE_CODE0(SLT, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +// SLTU - test with silent hill, lemans +void recSLTU_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] < g_cpuConstRegs[_Rt_].UD[0]; +} + +void recSLTU_consts(int info) +{ + recSLTs_consts(info, 0); + EEINST_SETSIGNEXT(_Rd_); +} + +void recSLTU_constt(int info) +{ + recSLTs_constt(info, 0); + EEINST_SETSIGNEXT(_Rd_); +} + +void recSLTU_(int info) +{ + int t1reg; + + assert( !(info & PROCESS_EE_XMM) ); + EEINST_SETSIGNEXT(_Rd_); + + if( !(info & PROCESS_EE_MMX) ) { + recSLTs_(info, 0); + return; + } + + if( EEREC_S == EEREC_T ) { + PXORRtoR(EEREC_D, EEREC_D); + return; + } + + if( !EEINST_ISSIGNEXT(_Rs_) || !EEINST_ISSIGNEXT(_Rt_) ) { + // need to compare total 64 bit value + if( info & PROCESS_EE_MODEWRITES ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rs_], EEREC_S); + if( mmxregs[EEREC_S].reg == MMX_GPR+_Rs_ ) mmxregs[EEREC_S].mode &= ~MODE_WRITE; + } + if( info & PROCESS_EE_MODEWRITET ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rt_], EEREC_T); + if( mmxregs[EEREC_T].reg == MMX_GPR+_Rt_ ) mmxregs[EEREC_T].mode &= ~MODE_WRITE; + } + mmxregs[EEREC_D].mode |= MODE_WRITE; // in case EEREC_D was just flushed + recSLTs_(info, 0); + return; + } + + t1reg = _allocMMXreg(-1, MMX_TEMP, 0); + + MOVDMtoMMX(t1reg, (u32)&s_sltconst); + + if( EEREC_D == EEREC_S ) { + PXORRtoR(EEREC_S, t1reg); + PXORRtoR(t1reg, EEREC_T); + PCMPGTDRtoR(t1reg, EEREC_S); + + PUNPCKLDQRtoR(t1reg, t1reg); + PSRLQItoR(t1reg, 63); + + // swap regs + mmxregs[t1reg] = mmxregs[EEREC_D]; + mmxregs[EEREC_D].inuse = 0; + } + else { + if( EEREC_D != EEREC_T ) { + MOVDMtoMMX(EEREC_D, (u32)&s_sltconst); + PXORRtoR(t1reg, EEREC_S); + PXORRtoR(EEREC_D, EEREC_T); + } + else { + PXORRtoR(EEREC_D, t1reg); + PXORRtoR(t1reg, EEREC_S); + } + + PCMPGTDRtoR(EEREC_D, t1reg); + + PUNPCKLDQRtoR(EEREC_D, EEREC_D); + PSRLQItoR(EEREC_D, 63); + + _freeMMXreg(t1reg); + } +} + +EERECOMPILE_CODE0(SLTU, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +#else + +//////////////////////////////////////////////////// +void recADD( void ) +{ + if ( ! _Rd_ ) + { + return; + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + ADD32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); +} + +//////////////////////////////////////////////////// +void recADDU( void ) +{ + recADD( ); +} + +//////////////////////////////////////////////////// +void recDADD( void ) +{ + if ( ! _Rd_ ) + { + return; + } + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + ADD32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + ADC32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); +} + +//////////////////////////////////////////////////// +void recDADDU( void ) +{ + recDADD( ); +} + +//////////////////////////////////////////////////// +void recSUB( void ) +{ + if ( ! _Rd_ ) return; + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + SUB32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + +} + +//////////////////////////////////////////////////// +void recSUBU( void ) +{ + recSUB( ); +} + +//////////////////////////////////////////////////// +void recDSUB( void ) +{ + if ( ! _Rd_ ) return; + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + SUB32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + SBB32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + +} + +//////////////////////////////////////////////////// +void recDSUBU( void ) +{ + recDSUB( ); +} + +//////////////////////////////////////////////////// +void recAND( void ) +{ + if ( ! _Rd_ ) + { + return; + } + if ( ( _Rt_ == 0 ) || ( _Rs_ == 0 ) ) + { + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], 0 ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], 0 ); + } + else + { + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + MOVQMtoR( MM1, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + PANDRtoR( MM0, MM1); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + } + +} + +//////////////////////////////////////////////////// +void recOR( void ) +{ + if ( ! _Rd_ ) + { + return; + } + + if ( ( _Rs_ == 0 ) && ( _Rt_ == 0 ) ) + { + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], 0x0 ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], 0x0 ); + } + else if ( _Rs_ == 0 ) + { + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + } + else if ( _Rt_ == 0 ) + { + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + } + else + { + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + MOVQMtoR( MM1, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + PORRtoR( MM0, MM1 ); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + } +} + +//////////////////////////////////////////////////// +void recXOR( void ) +{ + if ( ! _Rd_ ) + { + return; + } + + if ( ( _Rs_ == 0 ) && ( _Rt_ == 0 ) ) + { + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ],0x0); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ],0x0); + return; + } + + if ( _Rs_ == 0 ) + { + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + return; + } + + if ( _Rt_ == 0 ) + { + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + return; + } + + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + MOVQMtoR( MM1, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + PXORRtoR( MM0, MM1); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); +} + +//////////////////////////////////////////////////// +void recNOR( void ) +{ + if ( ! _Rd_ ) + { + return; + } + + if ( ( _Rs_ == 0 ) && ( _Rt_ == 0 ) ) + { + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ],0xffffffff); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ],0xffffffff); + return; + } + + if ( _Rs_ == 0 ) + { + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + PCMPEQDRtoR( MM1,MM1); + PXORRtoR( MM0,MM1); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ],MM0); + SetMMXstate(); + return; + } + + if ( _Rt_ == 0 ) + { + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + PCMPEQDRtoR( MM1,MM1); + PXORRtoR( MM0,MM1); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ],MM0); + SetMMXstate(); + return; + } + + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + PCMPEQDRtoR( MM1,MM1); + PORMtoR( MM0,(int)&cpuRegs.GPR.r[ _Rt_ ] ); + PXORRtoR( MM0,MM1); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ],MM0); + SetMMXstate(); +} + +//////////////////////////////////////////////////// +// test with silent hill, lemans +void recSLT( void ) +{ + if ( ! _Rd_ ) + return; + + MOV32ItoR(EAX, 1); + + if( _Rs_ == 0 ) { + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0); + j8Ptr[0] = JG8( 0 ); + j8Ptr[2] = JL8( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], 0 ); + j8Ptr[1] = JA8(0); + + x86SetJ8(j8Ptr[2]); + XOR32RtoR(EAX, EAX); + } + else if( _Rt_ == 0 ) { + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], 0); + j8Ptr[0] = JL8( 0 ); + j8Ptr[2] = JG8( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], 0); + j8Ptr[1] = JB8(0); + + x86SetJ8(j8Ptr[2]); + XOR32RtoR(EAX, EAX); + } + else { + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ]); + CMP32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ]); + j8Ptr[0] = JL8( 0 ); + j8Ptr[2] = JG8( 0 ); + + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + CMP32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + j8Ptr[1] = JB8(0); + + x86SetJ8(j8Ptr[2]); + XOR32RtoR(EAX, EAX); + } + + x86SetJ8(j8Ptr[0]); + x86SetJ8(j8Ptr[1]); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], 0 ); +} + +//////////////////////////////////////////////////// +void recSLTU( void ) +{ + MOV32ItoR(EAX, 1); + + if( _Rs_ == 0 ) { + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0); + j8Ptr[0] = JA8( 0 ); + j8Ptr[2] = JB8( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], 0 ); + j8Ptr[1] = JA8(0); + + x86SetJ8(j8Ptr[2]); + XOR32RtoR(EAX, EAX); + } + else if( _Rt_ == 0 ) { + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], 0); + j8Ptr[0] = JB8( 0 ); + j8Ptr[2] = JA8( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], 0); + j8Ptr[1] = JB8(0); + + x86SetJ8(j8Ptr[2]); + XOR32RtoR(EAX, EAX); + } + else { + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ]); + CMP32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ]); + j8Ptr[0] = JB8( 0 ); + j8Ptr[2] = JA8( 0 ); + + MOV32MtoR(ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + CMP32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + j8Ptr[1] = JB8(0); + + x86SetJ8(j8Ptr[2]); + XOR32RtoR(EAX, EAX); + } + + x86SetJ8(j8Ptr[0]); + x86SetJ8(j8Ptr[1]); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], 0 ); +} + +#endif + +} } } diff --git a/pcsx2/x86/ix86-32/iR5900AritImm.cpp b/pcsx2/x86/ix86-32/iR5900AritImm.cpp new file mode 100644 index 0000000000..ce9bbd98dd --- /dev/null +++ b/pcsx2/x86/ix86-32/iR5900AritImm.cpp @@ -0,0 +1,640 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl +{ + +/********************************************************* +* Arithmetic with immediate operand * +* Format: OP rt, rs, immediate * +*********************************************************/ + +#ifndef ARITHMETICIMM_RECOMPILE + +namespace Interp = R5900::Interpreter::OpcodeImpl; + +REC_FUNC_DEL(ADDI, _Rt_); +REC_FUNC_DEL(ADDIU, _Rt_); +REC_FUNC_DEL(DADDI, _Rt_); +REC_FUNC_DEL(DADDIU, _Rt_); +REC_FUNC_DEL(ANDI, _Rt_); +REC_FUNC_DEL(ORI, _Rt_); +REC_FUNC_DEL(XORI, _Rt_); + +REC_FUNC_DEL(SLTI, _Rt_); +REC_FUNC_DEL(SLTIU, _Rt_); + +#elif defined(EE_CONST_PROP) + +//// ADDI +void recADDI_const( void ) +{ + g_cpuConstRegs[_Rt_].SD[0] = (s64)(g_cpuConstRegs[_Rs_].SL[0] + (s32)_Imm_); +} + +void recADDI_(int info) +{ + assert( !(info&PROCESS_EE_XMM) ); + EEINST_SETSIGNEXT(_Rt_); + EEINST_SETSIGNEXT(_Rs_); + + if ( info & PROCESS_EE_MMX ) { + if ( _Imm_ != 0 ) { + + u32* ptempmem = recAllocStackMem(8, 8); + ptempmem[0] = (s32)_Imm_; + ptempmem[1] = 0; + + if ( EEREC_T != EEREC_S ) MOVQRtoR(EEREC_T, EEREC_S); + PADDDMtoR(EEREC_T, (u32)ptempmem); + if ( EEINST_ISLIVE1(_Rt_) ) _signExtendGPRtoMMX(EEREC_T, _Rt_, 0); + else EEINST_RESETHASLIVE1(_Rt_); + } + else { + // just move and sign extend + if ( !EEINST_HASLIVE1(_Rs_) ) { + if ( EEINST_ISLIVE1(_Rt_) ) _signExtendGPRMMXtoMMX(EEREC_T, _Rt_, EEREC_S, _Rs_); + else EEINST_RESETHASLIVE1(_Rt_); + } + else if ( EEREC_T != EEREC_S ) MOVQRtoR(EEREC_T, EEREC_S); + } + return; + } + + if ( (g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) && (_Rt_ != _Rs_) ) { + int rtreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_WRITE); + SetMMXstate(); + + if ( _Imm_ != 0 ) { + u32* ptempmem = recAllocStackMem(8, 8); + ptempmem[0] = (s32)_Imm_; + ptempmem[1] = 0; + + MOVDMtoMMX(rtreg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + PADDDMtoR(rtreg, (u32)ptempmem); + + if ( EEINST_ISLIVE1(_Rt_) ) _signExtendGPRtoMMX(rtreg, _Rt_, 0); + else EEINST_RESETHASLIVE1(_Rt_); + } + else { + // just move and sign extend + if ( !EEINST_HASLIVE1(_Rs_) ) { + MOVDMtoMMX(rtreg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + if ( EEINST_ISLIVE1(_Rt_) ) _signExtendGPRtoMMX(rtreg, _Rt_, 0); + else EEINST_RESETHASLIVE1(_Rt_); + } + else MOVQMtoR(rtreg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + } + } + else { + if ( _Rt_ == _Rs_ ) { + ADD32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], _Imm_); + if ( EEINST_ISLIVE1(_Rt_) ) _signExtendSFtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ]); + else EEINST_RESETHASLIVE1(_Rt_); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + + if ( _Imm_ != 0 ) ADD32ItoR( EAX, _Imm_ ); + + if ( EEINST_ISLIVE1(_Rt_) ) { + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + } + else { + EEINST_RESETHASLIVE1(_Rt_); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + } + } + } +} + +EERECOMPILE_CODEX(eeRecompileCode1, ADDI); + +//////////////////////////////////////////////////// +void recADDIU( void ) +{ + recADDI( ); +} + +//////////////////////////////////////////////////// +void recDADDI_const( void ) +{ + g_cpuConstRegs[_Rt_].SD[0] = g_cpuConstRegs[_Rs_].SD[0] + (s64)_Imm_; +} + +void recDADDI_(int info) +{ + assert( !(info&PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + + if( _Imm_ != 0 ) { + + // flush + u32* ptempmem = recAllocStackMem(8, 8); + ptempmem[0] = _Imm_; + ptempmem[1] = _Imm_ >= 0 ? 0 : 0xffffffff; + if( EEREC_T != EEREC_S ) MOVQRtoR(EEREC_T, EEREC_S); + PADDQMtoR(EEREC_T, (u32)ptempmem); + } + else { + if( EEREC_T != EEREC_S ) MOVQRtoR(EEREC_T, EEREC_S); + } + return; + } + + if( (g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) ) { + int rtreg; + u32* ptempmem = recAllocStackMem(8, 8); + ptempmem[0] = _Imm_; + ptempmem[1] = _Imm_ >= 0 ? 0 : 0xffffffff; + + rtreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_WRITE); + SetMMXstate(); + + MOVQMtoR(rtreg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + PADDQMtoR(rtreg, (u32)ptempmem); + } + else { + if( _Rt_ == _Rs_ ) { + ADD32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], _Imm_); + ADC32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], _Imm_<0?0xffffffff:0); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + + if( EEINST_ISLIVE1(_Rt_) ) + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + + if( EEINST_ISLIVE1(_Rt_) ) { + ADC32ItoR( EDX, _Imm_ < 0?0xffffffff:0); + } + } + + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + + if( EEINST_ISLIVE1(_Rt_) ) + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + else + EEINST_RESETHASLIVE1(_Rt_); + } + } +} + +EERECOMPILE_CODEX(eeRecompileCode1, DADDI); + +//// DADDIU +void recDADDIU( void ) +{ + recDADDI( ); +} + +//// SLTIU +void recSLTIU_const() +{ + g_cpuConstRegs[_Rt_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] < (u64)(_Imm_); +} + +extern void recSLTmemconstt(int regd, int regs, u32 mem, int sign); +extern u32 s_sltone; + +void recSLTIU_(int info) +{ + if( info & PROCESS_EE_MMX ) { + if( EEINST_ISSIGNEXT(_Rs_) ) { + u32* ptempmem = recAllocStackMem(8,4); + ptempmem[0] = ((s32)(_Imm_))^0x80000000; + ptempmem[1] = 0; + recSLTmemconstt(EEREC_T, EEREC_S, (u32)ptempmem, 0); + EEINST_SETSIGNEXT(_Rt_); + return; + } + + if( info & PROCESS_EE_MODEWRITES ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rs_], EEREC_S); + if( mmxregs[EEREC_S].reg == MMX_GPR+_Rs_ ) mmxregs[EEREC_S].mode &= ~MODE_WRITE; + } + mmxregs[EEREC_T].mode |= MODE_WRITE; // in case EEREC_T==EEREC_S + } + + if( info & PROCESS_EE_MMX ) MOVDMtoMMX(EEREC_T, (u32)&s_sltone); + else MOV32ItoR(EAX, 1); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], _Imm_ >= 0 ? 0 : 0xffffffff); + j8Ptr[0] = JB8( 0 ); + j8Ptr[2] = JA8( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], (s32)_Imm_ ); + j8Ptr[1] = JB8(0); + + x86SetJ8(j8Ptr[2]); + if( info & PROCESS_EE_MMX ) PXORRtoR(EEREC_T, EEREC_T); + else XOR32RtoR(EAX, EAX); + + x86SetJ8(j8Ptr[0]); + x86SetJ8(j8Ptr[1]); + + if( !(info & PROCESS_EE_MMX) ) { + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rt_) ) MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); + else EEINST_RESETHASLIVE1(_Rt_); + } + EEINST_SETSIGNEXT(_Rt_); +} + +EERECOMPILE_CODEX(eeRecompileCode1, SLTIU); + +//// SLTI +void recSLTI_const() +{ + g_cpuConstRegs[_Rt_].UD[0] = g_cpuConstRegs[_Rs_].SD[0] < (s64)(_Imm_); +} + +void recSLTI_(int info) +{ + if( info & PROCESS_EE_MMX) { + + if( EEINST_ISSIGNEXT(_Rs_) ) { + u32* ptempmem = recAllocStackMem(8,4); + ptempmem[0] = _Imm_; + ptempmem[1] = 0; + recSLTmemconstt(EEREC_T, EEREC_S, (u32)ptempmem, 1); + EEINST_SETSIGNEXT(_Rt_); + return; + } + + if( info & PROCESS_EE_MODEWRITES ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rs_], EEREC_S); + if( mmxregs[EEREC_S].reg == MMX_GPR+_Rs_ ) mmxregs[EEREC_S].mode &= ~MODE_WRITE; + } + mmxregs[EEREC_T].mode |= MODE_WRITE; // in case EEREC_T==EEREC_S + } + + // test silent hill if modding + if( info & PROCESS_EE_MMX ) MOVDMtoMMX(EEREC_T, (u32)&s_sltone); + else MOV32ItoR(EAX, 1); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], _Imm_ >= 0 ? 0 : 0xffffffff); + j8Ptr[0] = JL8( 0 ); + j8Ptr[2] = JG8( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], (s32)_Imm_ ); + j8Ptr[1] = JB8(0); + + x86SetJ8(j8Ptr[2]); + if( info & PROCESS_EE_MMX ) PXORRtoR(EEREC_T, EEREC_T); + else XOR32RtoR(EAX, EAX); + + x86SetJ8(j8Ptr[0]); + x86SetJ8(j8Ptr[1]); + + if( !(info & PROCESS_EE_MMX) ) { + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rt_) ) MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); + else EEINST_RESETHASLIVE1(_Rt_); + } + EEINST_SETSIGNEXT(_Rt_); +} + +EERECOMPILE_CODEX(eeRecompileCode1, SLTI); + +//// ANDI +void recANDI_const() +{ + g_cpuConstRegs[_Rt_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] & (u64)_ImmU_; // Zero-extended Immediate +} + +void recLogicalOpI(int info, int op) +{ + if( info & PROCESS_EE_MMX ) { + SetMMXstate(); + + if( _ImmU_ != 0 ) { + u32* ptempmem = recAllocStackMem(8, 8); + ptempmem[0] = _ImmU_; + ptempmem[1] = 0; + + if( EEREC_T != EEREC_S ) MOVQRtoR(EEREC_T, EEREC_S); + LogicalOpMtoR(EEREC_T, (u32)ptempmem, op); + } + else { + if( op == 0 ) PXORRtoR(EEREC_T, EEREC_T); + else if( EEREC_T != EEREC_S ) MOVQRtoR(EEREC_T, EEREC_S); + } + return; + } + + if( (g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) && ((_Rt_ != _Rs_) || (_ImmU_==0)) ) { + int rtreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_WRITE); + SetMMXstate(); + + if( op == 0 ) { + if ( _ImmU_ != 0 ) { + u32* ptempmem = recAllocStackMem(8, 8); + ptempmem[0] = _ImmU_; + ptempmem[1] = 0; + MOVDMtoMMX(rtreg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + PANDMtoR(rtreg, (u32)ptempmem); + } + else PXORRtoR(rtreg, rtreg); + } + else { + MOVQMtoR(rtreg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + if ( _ImmU_ != 0 ) { + u32* ptempmem = recAllocStackMem(8, 8); + ptempmem[0] = _ImmU_; + ptempmem[1] = 0; + LogicalOpMtoR(rtreg, (u32)ptempmem, op); + } + } + } + else { + if ( _ImmU_ != 0 ) + { + if( _Rt_ == _Rs_ ) { + LogicalOp32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], _ImmU_, op); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if( op != 0 && EEINST_ISLIVE1(_Rt_) ) + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + LogicalOp32ItoR( EAX, _ImmU_, op); + if( op != 0 && EEINST_ISLIVE1(_Rt_) ) + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + } + + if( op == 0 ) { + if( EEINST_ISLIVE1(_Rt_ ) ) MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); + else EEINST_RESETHASLIVE1(_Rt_); + } + } + else + { + if( op == 0 ) { + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], 0 ); + if( EEINST_ISLIVE1(_Rt_ ) ) + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); + } + else { + if( _Rt_ != _Rs_ ) { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if( EEINST_ISLIVE1(_Rt_ ) ) + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rt_ ) ) + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + } + } + + if( !EEINST_ISLIVE1(_Rt_) ) EEINST_RESETHASLIVE1(_Rt_); + } + } +} + +void recANDI_(int info) +{ + recLogicalOpI(info, 0); +} + +EERECOMPILE_CODEX(eeRecompileCode1, ANDI); + +//////////////////////////////////////////////////// +void recORI_const() +{ + g_cpuConstRegs[_Rt_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] | (u64)_ImmU_; // Zero-extended Immediate +} + +void recORI_(int info) +{ + recLogicalOpI(info, 1); +} + +EERECOMPILE_CODEX(eeRecompileCode1, ORI); + +//////////////////////////////////////////////////// +void recXORI_const() +{ + g_cpuConstRegs[_Rt_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] ^ (u64)_ImmU_; // Zero-extended Immediate +} + +void recXORI_(int info) +{ + recLogicalOpI(info, 2); +} + +EERECOMPILE_CODEX(eeRecompileCode1, XORI); + +#else + +//////////////////////////////////////////////////// +void recADDI( void ) +{ + if ( ! _Rt_ ) + { + return; + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + +} + +//////////////////////////////////////////////////// +void recADDIU( void ) +{ + recADDI( ); +} + +//////////////////////////////////////////////////// +void recDADDI( void ) +{ +#ifdef __x86_64_ + if ( ! _Rt_ ) + { + return; + } + + MOV64MtoR( RAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD64ItoR( EAX, _Imm_ ); + } + MOV64RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], RAX ); +#else + if ( ! _Rt_ ) + { + return; + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + if ( _Imm_ < 0 ) + { + ADC32ItoR( EDX, 0xffffffff ); + } + else + { + ADC32ItoR( EDX, 0 ); + } + } + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); +#endif +} + +//////////////////////////////////////////////////// +void recDADDIU( void ) +{ + recDADDI( ); +} + +//////////////////////////////////////////////////// +void recSLTIU( void ) +{ + if ( ! _Rt_ ) + return; + + MOV32ItoR(EAX, 1); + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], _Imm_ >= 0 ? 0 : 0xffffffff); + j8Ptr[0] = JB8( 0 ); + j8Ptr[2] = JA8( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], (s32)_Imm_ ); + j8Ptr[1] = JB8(0); + + x86SetJ8(j8Ptr[2]); + XOR32RtoR(EAX, EAX); + + x86SetJ8(j8Ptr[0]); + x86SetJ8(j8Ptr[1]); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); +} + +//////////////////////////////////////////////////// +void recSLTI( void ) +{ + if ( ! _Rt_ ) + return; + + // test silent hill if modding + MOV32ItoR(EAX, 1); + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], _Imm_ >= 0 ? 0 : 0xffffffff); + j8Ptr[0] = JL8( 0 ); + j8Ptr[2] = JG8( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], (s32)_Imm_ ); + j8Ptr[1] = JB8(0); + + x86SetJ8(j8Ptr[2]); + XOR32RtoR(EAX, EAX); + + x86SetJ8(j8Ptr[0]); + x86SetJ8(j8Ptr[1]); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); +} + +//////////////////////////////////////////////////// +void recANDI( void ) +{ + if ( ! _Rt_ ) + { + return; + } + if ( _ImmU_ != 0 ) + { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + AND32ItoR( EAX, _ImmU_ ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + } + else + { + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], 0 ); + } + +} + +//////////////////////////////////////////////////// +static u64 _imm = 0; // temp immediate + +void recORI( void ) +{ + if ( ! _Rt_ ) + return; + + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + if ( _ImmU_ != 0 ) + { + MOV32ItoM( (int)&_imm, _ImmU_ ); + MOVQMtoR( MM1, (int)&_imm ); + PORRtoR( MM0, MM1 ); + } + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ], MM0 ); + SetMMXstate(); +} + +//////////////////////////////////////////////////// +void recXORI( void ) +{ + if ( ! _Rt_ ) + return; + + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + if ( _ImmU_ != 0 ) + { + MOV32ItoM( (int)&_imm, _ImmU_ ); + MOVQMtoR( MM1, (int)&_imm ); + PXORRtoR( MM0, MM1 ); + } + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rt_ ], MM0 ); + SetMMXstate(); +} + +#endif + +} } } diff --git a/pcsx2/x86/ix86-32/iR5900Branch.cpp b/pcsx2/x86/ix86-32/iR5900Branch.cpp new file mode 100644 index 0000000000..7137531684 --- /dev/null +++ b/pcsx2/x86/ix86-32/iR5900Branch.cpp @@ -0,0 +1,1198 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// recompiler reworked to add dynamic linking zerofrog(@gmail.com) Jan06 + +#include "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" + +namespace Interp = R5900::Interpreter::OpcodeImpl; + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl +{ + +/********************************************************* +* Register branch logic * +* Format: OP rs, rt, offset * +*********************************************************/ +#ifndef BRANCH_RECOMPILE + +REC_SYS(BEQ); +REC_SYS(BEQL); +REC_SYS(BNE); +REC_SYS(BNEL); +REC_SYS(BLTZ); +REC_SYS(BGTZ); +REC_SYS(BLEZ); +REC_SYS(BGEZ); +REC_SYS(BGTZL); +REC_SYS(BLTZL); +REC_SYS(BLTZAL); +REC_SYS(BLTZALL); +REC_SYS(BLEZL); +REC_SYS(BGEZL); +REC_SYS(BGEZAL); +REC_SYS(BGEZALL); + +#else + +#if defined(EE_CONST_PROP) + +void recSetBranchEQ(int info, int bne, int process) +{ + if( info & PROCESS_EE_MMX ) { + int t0reg; + + SetMMXstate(); + + if( process & PROCESS_CONSTS ) { + if( (g_pCurInstInfo->regs[_Rt_] & EEINST_LASTUSE) || !EEINST_ISLIVE64(_Rt_) ) { + _deleteMMXreg(_Rt_, 1); + mmxregs[EEREC_T].inuse = 0; + t0reg = EEREC_T; + } + else { + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t0reg, EEREC_T); + } + + _flushConstReg(_Rs_); + PCMPEQDMtoR(t0reg, (u32)&cpuRegs.GPR.r[_Rs_].UL[0]); + + if( t0reg != EEREC_T ) _freeMMXreg(t0reg); + } + else if( process & PROCESS_CONSTT ) { + if( (g_pCurInstInfo->regs[_Rs_] & EEINST_LASTUSE) || !EEINST_ISLIVE64(_Rs_) ) { + _deleteMMXreg(_Rs_, 1); + mmxregs[EEREC_S].inuse = 0; + t0reg = EEREC_S; + } + else { + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t0reg, EEREC_S); + } + + _flushConstReg(_Rt_); + PCMPEQDMtoR(t0reg, (u32)&cpuRegs.GPR.r[_Rt_].UL[0]); + + if( t0reg != EEREC_S ) _freeMMXreg(t0reg); + } + else { + + if( (g_pCurInstInfo->regs[_Rs_] & EEINST_LASTUSE) || !EEINST_ISLIVE64(_Rs_) ) { + _deleteMMXreg(_Rs_, 1); + mmxregs[EEREC_S].inuse = 0; + t0reg = EEREC_S; + PCMPEQDRtoR(t0reg, EEREC_T); + } + else if( (g_pCurInstInfo->regs[_Rt_] & EEINST_LASTUSE) || !EEINST_ISLIVE64(_Rt_) ) { + _deleteMMXreg(_Rt_, 1); + mmxregs[EEREC_T].inuse = 0; + t0reg = EEREC_T; + PCMPEQDRtoR(t0reg, EEREC_S); + } + else { + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t0reg, EEREC_S); + PCMPEQDRtoR(t0reg, EEREC_T); + } + + if( t0reg != EEREC_S && t0reg != EEREC_T ) _freeMMXreg(t0reg); + } + + PMOVMSKBMMXtoR(EAX, t0reg); + + _eeFlushAllUnused(); + + CMP8ItoR( EAX, 0xff ); + + if( bne ) j32Ptr[ 1 ] = JE32( 0 ); + else j32Ptr[ 0 ] = j32Ptr[ 1 ] = JNE32( 0 ); + } + else if( info & PROCESS_EE_XMM ) { + int t0reg; + + if( process & PROCESS_CONSTS ) { + if( (g_pCurInstInfo->regs[_Rt_] & EEINST_LASTUSE) || !EEINST_ISLIVEXMM(_Rt_) ) { + _deleteGPRtoXMMreg(_Rt_, 1); + xmmregs[EEREC_T].inuse = 0; + t0reg = EEREC_T; + } + else { + t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_MOVQ_XMM_to_XMM(t0reg, EEREC_T); + } + + _flushConstReg(_Rs_); + SSE2_PCMPEQD_M128_to_XMM(t0reg, (u32)&cpuRegs.GPR.r[_Rs_].UL[0]); + + + if( t0reg != EEREC_T ) _freeXMMreg(t0reg); + } + else if( process & PROCESS_CONSTT ) { + if( (g_pCurInstInfo->regs[_Rs_] & EEINST_LASTUSE) || !EEINST_ISLIVEXMM(_Rs_) ) { + _deleteGPRtoXMMreg(_Rs_, 1); + xmmregs[EEREC_S].inuse = 0; + t0reg = EEREC_S; + } + else { + t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_MOVQ_XMM_to_XMM(t0reg, EEREC_S); + } + + _flushConstReg(_Rt_); + SSE2_PCMPEQD_M128_to_XMM(t0reg, (u32)&cpuRegs.GPR.r[_Rt_].UL[0]); + + if( t0reg != EEREC_S ) _freeXMMreg(t0reg); + } + else { + + if( (g_pCurInstInfo->regs[_Rs_] & EEINST_LASTUSE) || !EEINST_ISLIVEXMM(_Rs_) ) { + _deleteGPRtoXMMreg(_Rs_, 1); + xmmregs[EEREC_S].inuse = 0; + t0reg = EEREC_S; + SSE2_PCMPEQD_XMM_to_XMM(t0reg, EEREC_T); + } + else if( (g_pCurInstInfo->regs[_Rt_] & EEINST_LASTUSE) || !EEINST_ISLIVEXMM(_Rt_) ) { + _deleteGPRtoXMMreg(_Rt_, 1); + xmmregs[EEREC_T].inuse = 0; + t0reg = EEREC_T; + SSE2_PCMPEQD_XMM_to_XMM(t0reg, EEREC_S); + } + else { + t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE2_MOVQ_XMM_to_XMM(t0reg, EEREC_S); + SSE2_PCMPEQD_XMM_to_XMM(t0reg, EEREC_T); + } + + if( t0reg != EEREC_S && t0reg != EEREC_T ) _freeXMMreg(t0reg); + } + + SSE_MOVMSKPS_XMM_to_R32(EAX, t0reg); + + _eeFlushAllUnused(); + + AND8ItoR(EAX, 3); + CMP8ItoR( EAX, 0x3 ); + + if( bne ) j32Ptr[ 1 ] = JE32( 0 ); + else j32Ptr[ 0 ] = j32Ptr[ 1 ] = JNE32( 0 ); + } + else { + + _eeFlushAllUnused(); + + if( bne ) { + if( process & PROCESS_CONSTS ) { + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], g_cpuConstRegs[_Rs_].UL[0] ); + j8Ptr[ 0 ] = JNE8( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], g_cpuConstRegs[_Rs_].UL[1] ); + j32Ptr[ 1 ] = JE32( 0 ); + } + else if( process & PROCESS_CONSTT ) { + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], g_cpuConstRegs[_Rt_].UL[0] ); + j8Ptr[ 0 ] = JNE8( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], g_cpuConstRegs[_Rt_].UL[1] ); + j32Ptr[ 1 ] = JE32( 0 ); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + CMP32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + j8Ptr[ 0 ] = JNE8( 0 ); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + CMP32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + j32Ptr[ 1 ] = JE32( 0 ); + } + + x86SetJ8( j8Ptr[0] ); + } + else { + // beq + if( process & PROCESS_CONSTS ) { + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], g_cpuConstRegs[_Rs_].UL[0] ); + j32Ptr[ 0 ] = JNE32( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], g_cpuConstRegs[_Rs_].UL[1] ); + j32Ptr[ 1 ] = JNE32( 0 ); + } + else if( process & PROCESS_CONSTT ) { + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], g_cpuConstRegs[_Rt_].UL[0] ); + j32Ptr[ 0 ] = JNE32( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], g_cpuConstRegs[_Rt_].UL[1] ); + j32Ptr[ 1 ] = JNE32( 0 ); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + CMP32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + j32Ptr[ 0 ] = JNE32( 0 ); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + CMP32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + j32Ptr[ 1 ] = JNE32( 0 ); + } + } + } + + _clearNeededMMXregs(); + _clearNeededXMMregs(); +} + +void recSetBranchL(int ltz) +{ + int regs = _checkMMXreg(MMX_GPR+_Rs_, MODE_READ); + + if( regs >= 0 ) { + + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + SetMMXstate(); + + PXORRtoR(t0reg, t0reg); + PCMPGTDRtoR(t0reg, regs); + PMOVMSKBMMXtoR(EAX, t0reg); + + _freeMMXreg(t0reg); + _eeFlushAllUnused(); + + TEST8ItoR( EAX, 0x80 ); + + if( ltz ) j32Ptr[ 0 ] = JZ32( 0 ); + else j32Ptr[ 0 ] = JNZ32( 0 ); + + return; + } + + regs = _checkXMMreg(XMMTYPE_GPRREG, _Rs_, MODE_READ); + + if( regs >= 0 ) { + + int t0reg = _allocTempXMMreg(XMMT_INT, -1); + SSE_XORPS_XMM_to_XMM(t0reg, t0reg); + SSE2_PCMPGTD_XMM_to_XMM(t0reg, regs); + SSE_MOVMSKPS_XMM_to_R32(EAX, t0reg); + + _freeXMMreg(t0reg); + _eeFlushAllUnused(); + + TEST8ItoR( EAX, 2 ); + + if( ltz ) j32Ptr[ 0 ] = JZ32( 0 ); + else j32Ptr[ 0 ] = JNZ32( 0 ); + + return; + } + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], 0 ); + if( ltz ) j32Ptr[ 0 ] = JGE32( 0 ); + else j32Ptr[ 0 ] = JL32( 0 ); + + _clearNeededMMXregs(); + _clearNeededXMMregs(); +} + +//// BEQ +void recBEQ_const() +{ + u32 branchTo; + + if( g_cpuConstRegs[_Rs_].SD[0] == g_cpuConstRegs[_Rt_].SD[0] ) + branchTo = ((s32)_Imm_ * 4) + pc; + else + branchTo = pc+4; + + recompileNextInstruction(1); + SetBranchImm( branchTo ); +} + +void recBEQ_process(int info, int process) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + if ( _Rs_ == _Rt_ ) + { + recompileNextInstruction(1); + SetBranchImm( branchTo ); + } + else + { + recSetBranchEQ(info, 0, process); + + SaveBranchState(); + recompileNextInstruction(1); + + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + x86SetJ32( j32Ptr[ 1 ] ); + + // recopy the next inst + pc -= 4; + LoadBranchState(); + recompileNextInstruction(1); + + SetBranchImm(pc); + } +} + +void recBEQ_(int info) { recBEQ_process(info, 0); } +void recBEQ_consts(int info) { recBEQ_process(info, PROCESS_CONSTS); } +void recBEQ_constt(int info) { recBEQ_process(info, PROCESS_CONSTT); } + +EERECOMPILE_CODE0(BEQ, XMMINFO_READS|XMMINFO_READT); + +//// BNE +void recBNE_const() +{ + u32 branchTo; + + if( g_cpuConstRegs[_Rs_].SD[0] != g_cpuConstRegs[_Rt_].SD[0] ) + branchTo = ((s32)_Imm_ * 4) + pc; + else + branchTo = pc+4; + + recompileNextInstruction(1); + SetBranchImm( branchTo ); +} + +void recBNE_process(int info, int process) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + if ( _Rs_ == _Rt_ ) + { + recompileNextInstruction(1); + SetBranchImm(pc); + return; + } + + recSetBranchEQ(info, 1, process); + + SaveBranchState(); + recompileNextInstruction(1); + + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 1 ] ); + + // recopy the next inst + pc -= 4; + LoadBranchState(); + recompileNextInstruction(1); + + SetBranchImm(pc); +} + +void recBNE_(int info) { recBNE_process(info, 0); } +void recBNE_consts(int info) { recBNE_process(info, PROCESS_CONSTS); } +void recBNE_constt(int info) { recBNE_process(info, PROCESS_CONSTT); } + +EERECOMPILE_CODE0(BNE, XMMINFO_READS|XMMINFO_READT); + +//// BEQL +void recBEQL_const() +{ + if( g_cpuConstRegs[_Rs_].SD[0] == g_cpuConstRegs[_Rt_].SD[0] ) { + u32 branchTo = ((s32)_Imm_ * 4) + pc; + recompileNextInstruction(1); + SetBranchImm( branchTo ); + } + else { + SetBranchImm( pc+4 ); + } +} + +void recBEQL_process(int info, int process) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + recSetBranchEQ(info, 0, process); + + SaveBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + x86SetJ32( j32Ptr[ 1 ] ); + + LoadBranchState(); + SetBranchImm(pc); +} + +void recBEQL_(int info) { recBEQL_process(info, 0); } +void recBEQL_consts(int info) { recBEQL_process(info, PROCESS_CONSTS); } +void recBEQL_constt(int info) { recBEQL_process(info, PROCESS_CONSTT); } + +EERECOMPILE_CODE0(BEQL, XMMINFO_READS|XMMINFO_READT); + +//// BNEL +void recBNEL_const() +{ + if( g_cpuConstRegs[_Rs_].SD[0] != g_cpuConstRegs[_Rt_].SD[0] ) { + u32 branchTo = ((s32)_Imm_ * 4) + pc; + recompileNextInstruction(1); + SetBranchImm(branchTo); + } + else { + SetBranchImm( pc+4 ); + } +} + +void recBNEL_process(int info, int process) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + recSetBranchEQ(info, 0, process); + + SaveBranchState(); + SetBranchImm(pc+4); + + x86SetJ32( j32Ptr[ 0 ] ); + x86SetJ32( j32Ptr[ 1 ] ); + + // recopy the next inst + LoadBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); +} + +void recBNEL_(int info) { recBNEL_process(info, 0); } +void recBNEL_consts(int info) { recBNEL_process(info, PROCESS_CONSTS); } +void recBNEL_constt(int info) { recBNEL_process(info, PROCESS_CONSTT); } + +EERECOMPILE_CODE0(BNEL, XMMINFO_READS|XMMINFO_READT); + +/********************************************************* +* Register branch logic * +* Format: OP rs, offset * +*********************************************************/ + +//////////////////////////////////////////////////// +//void recBLTZAL( void ) +//{ +// SysPrintf("BLTZAL\n"); +// _eeFlushAllUnused(); +// MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); +// MOV32ItoM( (int)&cpuRegs.pc, pc ); +// iFlushCall(FLUSH_EVERYTHING); +// CALLFunc( (int)BLTZAL ); +// branch = 2; +//} + +//////////////////////////////////////////////////// +void recBLTZAL() +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeOnWriteReg(31, 0); + _eeFlushAllUnused(); + + _deleteEEreg(31, 0); + MOV32ItoM((uptr)&cpuRegs.GPR.r[31].UL[0], pc+4); + MOV32ItoM((uptr)&cpuRegs.GPR.r[31].UL[1], 0); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] < 0) ) + branchTo = pc+4; + + recompileNextInstruction(1); + SetBranchImm( branchTo ); + return; + } + + recSetBranchL(1); + + SaveBranchState(); + + recompileNextInstruction(1); + + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + + // recopy the next inst + pc -= 4; + LoadBranchState(); + recompileNextInstruction(1); + + SetBranchImm(pc); +} + +//////////////////////////////////////////////////// +void recBGEZAL( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeOnWriteReg(31, 0); + _eeFlushAllUnused(); + + _deleteEEreg(31, 0); + MOV32ItoM((uptr)&cpuRegs.GPR.r[31].UL[0], pc+4); + MOV32ItoM((uptr)&cpuRegs.GPR.r[31].UL[1], 0); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] >= 0) ) + branchTo = pc+4; + + recompileNextInstruction(1); + SetBranchImm( branchTo ); + return; + } + + recSetBranchL(0); + + SaveBranchState(); + + recompileNextInstruction(1); + + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + + // recopy the next inst + pc -= 4; + LoadBranchState(); + recompileNextInstruction(1); + + SetBranchImm(pc); +} + +//////////////////////////////////////////////////// +void recBLTZALL( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeOnWriteReg(31, 0); + _eeFlushAllUnused(); + + _deleteEEreg(31, 0); + MOV32ItoM((uptr)&cpuRegs.GPR.r[31].UL[0], pc+4); + MOV32ItoM((uptr)&cpuRegs.GPR.r[31].UL[1], 0); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] < 0) ) + SetBranchImm( pc + 4); + else { + recompileNextInstruction(1); + SetBranchImm( branchTo ); + } + return; + } + + recSetBranchL(1); + + SaveBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + + LoadBranchState(); + SetBranchImm(pc); +} + +//////////////////////////////////////////////////// +void recBGEZALL( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeOnWriteReg(31, 0); + _eeFlushAllUnused(); + + _deleteEEreg(31, 0); + MOV32ItoM((uptr)&cpuRegs.GPR.r[31].UL[0], pc+4); + MOV32ItoM((uptr)&cpuRegs.GPR.r[31].UL[1], 0); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] >= 0) ) + SetBranchImm( pc + 4); + else { + recompileNextInstruction(1); + SetBranchImm( branchTo ); + } + return; + } + + recSetBranchL(0); + + SaveBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + + LoadBranchState(); + SetBranchImm(pc); +} + +#else + + +//////////////////////////////////////////////////// +void recBEQ( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + if ( _Rs_ == _Rt_ ) + { + _clearNeededMMXregs(); + _clearNeededXMMregs(); + recompileNextInstruction(1); + SetBranchImm( branchTo ); + } + else + { + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + CMP32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + j32Ptr[ 0 ] = JNE32( 0 ); + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + CMP32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + j32Ptr[ 1 ] = JNE32( 0 ); + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + + SaveBranchState(); + recompileNextInstruction(1); + + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + x86SetJ32( j32Ptr[ 1 ] ); + + // recopy the next inst + pc -= 4; + LoadBranchState(); + recompileNextInstruction(1); + + SetBranchImm(pc); + } +} + +//////////////////////////////////////////////////// +void recBNE( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + if ( _Rs_ == _Rt_ ) + { + _clearNeededMMXregs(); + _clearNeededXMMregs(); + recompileNextInstruction(1); + SetBranchImm(pc); + return; + } + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + CMP32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + j32Ptr[ 0 ] = JNE32( 0 ); + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + CMP32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + j32Ptr[ 1 ] = JE32( 0 ); + + x86SetJ32( j32Ptr[ 0 ] ); + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + + SaveBranchState(); + recompileNextInstruction(1); + + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 1 ] ); + + // recopy the next inst + pc -= 4; + LoadBranchState(); + recompileNextInstruction(1); + + SetBranchImm(pc); +} + +//////////////////////////////////////////////////// +void recBEQL( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + CMP32RtoR( ECX, EDX ); + j32Ptr[ 0 ] = JNE32( 0 ); + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + CMP32RtoR( ECX, EDX ); + j32Ptr[ 1 ] = JNE32( 0 ); + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + + SaveBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + x86SetJ32( j32Ptr[ 1 ] ); + + LoadBranchState(); + SetBranchImm(pc); +} + +//////////////////////////////////////////////////// +void recBNEL( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + CMP32RtoR( ECX, EDX ); + j32Ptr[ 0 ] = JNE32( 0 ); + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + CMP32RtoR( ECX, EDX ); + j32Ptr[ 1 ] = JNE32( 0 ); + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + + SaveBranchState(); + SetBranchImm(pc+4); + + x86SetJ32( j32Ptr[ 0 ] ); + x86SetJ32( j32Ptr[ 1 ] ); + + // recopy the next inst + LoadBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); +} + +/********************************************************* +* Register branch logic * +* Format: OP rs, offset * +*********************************************************/ + +//////////////////////////////////////////////////// +void recBLTZAL( void ) +{ + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (int)BLTZAL ); + branch = 2; +} + +//////////////////////////////////////////////////// +void recBGEZAL( void ) +{ + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (int)BGEZAL ); + branch = 2; +} + +//////////////////////////////////////////////////// +void recBLTZALL( void ) +{ + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (int)BLTZALL ); + branch = 2; +} + +//////////////////////////////////////////////////// +void recBGEZALL( void ) +{ + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (int)BGEZALL ); + branch = 2; +} + +#endif + +//// BLEZ +void recBLEZ( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeFlushAllUnused(); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] <= 0) ) + branchTo = pc+4; + + recompileNextInstruction(1); + SetBranchImm( branchTo ); + return; + } + + _deleteEEreg(_Rs_, 1); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], 0 ); + j8Ptr[ 0 ] = JL8( 0 ); + j32Ptr[ 1 ] = JG32( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], 0 ); + j32Ptr[ 2 ] = JNZ32( 0 ); + + x86SetJ8( j8Ptr[ 0 ] ); + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + + SaveBranchState(); + recompileNextInstruction(1); + + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 1 ] ); + x86SetJ32( j32Ptr[ 2 ] ); + + // recopy the next inst + pc -= 4; + LoadBranchState(); + recompileNextInstruction(1); + + SetBranchImm(pc); +} + +//// BGTZ +void recBGTZ( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeFlushAllUnused(); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] > 0) ) + branchTo = pc+4; + + recompileNextInstruction(1); + SetBranchImm( branchTo ); + return; + } + + _deleteEEreg(_Rs_, 1); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], 0 ); + j8Ptr[ 0 ] = JG8( 0 ); + j32Ptr[ 1 ] = JL32( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], 0 ); + j32Ptr[ 2 ] = JZ32( 0 ); + + x86SetJ8( j8Ptr[ 0 ] ); + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + + SaveBranchState(); + recompileNextInstruction(1); + + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 1 ] ); + x86SetJ32( j32Ptr[ 2 ] ); + + // recopy the next inst + pc -= 4; + LoadBranchState(); + recompileNextInstruction(1); + + SetBranchImm(pc); +} + +//////////////////////////////////////////////////// +#ifdef EE_CONST_PROP +void recBLTZ() +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeFlushAllUnused(); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] < 0) ) + branchTo = pc+4; + + recompileNextInstruction(1); + SetBranchImm( branchTo ); + return; + } + + recSetBranchL(1); + + SaveBranchState(); + recompileNextInstruction(1); + + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + + // recopy the next inst + pc -= 4; + LoadBranchState(); + recompileNextInstruction(1); + + SetBranchImm(pc); +} + +//////////////////////////////////////////////////// +void recBGEZ( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeFlushAllUnused(); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] >= 0) ) + branchTo = pc+4; + + recompileNextInstruction(1); + SetBranchImm( branchTo ); + return; + } + + recSetBranchL(0); + + SaveBranchState(); + recompileNextInstruction(1); + + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + + // recopy the next inst + pc -= 4; + LoadBranchState(); + recompileNextInstruction(1); + + SetBranchImm(pc); +} + +//////////////////////////////////////////////////// +void recBLTZL( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeFlushAllUnused(); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] < 0) ) + SetBranchImm( pc + 4); + else { + recompileNextInstruction(1); + SetBranchImm( branchTo ); + } + return; + } + + recSetBranchL(1); + + SaveBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + + LoadBranchState(); + SetBranchImm(pc); +} + + +//////////////////////////////////////////////////// +void recBGEZL( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeFlushAllUnused(); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] >= 0) ) + SetBranchImm( pc + 4); + else { + recompileNextInstruction(1); + SetBranchImm( branchTo ); + } + return; + } + + recSetBranchL(0); + + SaveBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 0 ] ); + + LoadBranchState(); + SetBranchImm(pc); +} + +#else +void recBLTZ( void ) +{ + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (int)BLTZ ); + branch = 2; +} + +void recBGEZ( void ) +{ + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (int)BGEZ ); + branch = 2; +} + +void recBLTZL( void ) +{ + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (int)BLTZL ); + branch = 2; +} + +void recBGEZL( void ) +{ + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + iFlushCall(FLUSH_EVERYTHING); + CALLFunc( (int)BGEZL ); + branch = 2; +} + +#endif + + + +/********************************************************* +* Register branch logic Likely * +* Format: OP rs, offset * +*********************************************************/ + +//////////////////////////////////////////////////// +void recBLEZL( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeFlushAllUnused(); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] <= 0) ) + SetBranchImm( pc + 4); + else { + _clearNeededMMXregs(); + _clearNeededXMMregs(); + recompileNextInstruction(1); + SetBranchImm( branchTo ); + } + return; + } + + _deleteEEreg(_Rs_, 1); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], 0 ); + j32Ptr[ 0 ] = JL32( 0 ); + j32Ptr[ 1 ] = JG32( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], 0 ); + j32Ptr[ 2 ] = JNZ32( 0 ); + + x86SetJ32( j32Ptr[ 0 ] ); + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + + SaveBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 1 ] ); + x86SetJ32( j32Ptr[ 2 ] ); + + LoadBranchState(); + SetBranchImm(pc); +} + +//////////////////////////////////////////////////// +void recBGTZL( void ) +{ + u32 branchTo = ((s32)_Imm_ * 4) + pc; + + _eeFlushAllUnused(); + + if( GPR_IS_CONST1(_Rs_) ) { + if( !(g_cpuConstRegs[_Rs_].SD[0] > 0) ) + SetBranchImm( pc + 4); + else { + _clearNeededMMXregs(); + _clearNeededXMMregs(); + recompileNextInstruction(1); + SetBranchImm( branchTo ); + } + return; + } + + _deleteEEreg(_Rs_, 1); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], 0 ); + j32Ptr[ 0 ] = JG32( 0 ); + j32Ptr[ 1 ] = JL32( 0 ); + + CMP32ItoM( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], 0 ); + j32Ptr[ 2 ] = JZ32( 0 ); + + x86SetJ32( j32Ptr[ 0 ] ); + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + + SaveBranchState(); + recompileNextInstruction(1); + SetBranchImm(branchTo); + + x86SetJ32( j32Ptr[ 1 ] ); + x86SetJ32( j32Ptr[ 2 ] ); + + LoadBranchState(); + SetBranchImm(pc); +} + +#endif + +} } } diff --git a/pcsx2/x86/ix86-32/iR5900Jump.cpp b/pcsx2/x86/ix86-32/iR5900Jump.cpp new file mode 100644 index 0000000000..b38211cef5 --- /dev/null +++ b/pcsx2/x86/ix86-32/iR5900Jump.cpp @@ -0,0 +1,133 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ + +// recompiler reworked to add dynamic linking zerofrog(@gmail.com) Jan06 + +#include "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl +{ + +/********************************************************* +* Jump to target * +* Format: OP target * +*********************************************************/ +#ifndef JUMP_RECOMPILE + +namespace Interp = R5900::Interpreter::OpcodeImpl; + +REC_SYS(J); +REC_SYS(JAL); +REC_SYS(JR); +REC_SYS(JALR); + +#else + +//////////////////////////////////////////////////// +void recJ( void ) +{ + // SET_FPUSTATE; + u32 newpc = (_Target_ << 2) + ( pc & 0xf0000000 ); + recompileNextInstruction(1); + SetBranchImm(newpc); +} + +//////////////////////////////////////////////////// +void recJAL( void ) +{ + u32 newpc = (_Target_ << 2) + ( pc & 0xf0000000 ); + _deleteEEreg(31, 0); + GPR_SET_CONST(31); + g_cpuConstRegs[31].UL[0] = pc + 4; + g_cpuConstRegs[31].UL[1] = 0; + + recompileNextInstruction(1); + SetBranchImm(newpc); +} + +/********************************************************* +* Register jump * +* Format: OP rs, rd * +*********************************************************/ + +//////////////////////////////////////////////////// +void recJR( void ) +{ + SetBranchReg( _Rs_); +} + +//////////////////////////////////////////////////// +void recJALR( void ) +{ + _allocX86reg(ESI, X86TYPE_PCWRITEBACK, 0, MODE_WRITE); + _eeMoveGPRtoR(ESI, _Rs_); + // uncomment when there are NO instructions that need to call interpreter +// int mmreg; +// if( GPR_IS_CONST1(_Rs_) ) +// MOV32ItoM( (u32)&cpuRegs.pc, g_cpuConstRegs[_Rs_].UL[0] ); +// else { +// int mmreg; +// +// if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rs_, MODE_READ)) >= 0 ) { +// SSE_MOVSS_XMM_to_M32((u32)&cpuRegs.pc, mmreg); +// } +// else if( (mmreg = _checkMMXreg(MMX_GPR+_Rs_, MODE_READ)) >= 0 ) { +// MOVDMMXtoM((u32)&cpuRegs.pc, mmreg); +// SetMMXstate(); +// } +// else { +// MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); +// MOV32RtoM((u32)&cpuRegs.pc, EAX); +// } +// } + + if ( _Rd_ ) + { + _deleteEEreg(_Rd_, 0); + GPR_SET_CONST(_Rd_); + g_cpuConstRegs[_Rd_].UL[0] = pc + 4; + g_cpuConstRegs[_Rd_].UL[1] = 0; + } + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + recompileNextInstruction(1); + + if( x86regs[ESI].inuse ) { + assert( x86regs[ESI].type == X86TYPE_PCWRITEBACK ); + MOV32RtoM((int)&cpuRegs.pc, ESI); + x86regs[ESI].inuse = 0; + } + else { + MOV32MtoR(EAX, (u32)&g_recWriteback); + MOV32RtoM((int)&cpuRegs.pc, EAX); + } + + SetBranchReg(0xffffffff); +} + +#endif + +} } } diff --git a/pcsx2/x86/ix86-32/iR5900LoadStore.cpp b/pcsx2/x86/ix86-32/iR5900LoadStore.cpp new file mode 100644 index 0000000000..562947c5a4 --- /dev/null +++ b/pcsx2/x86/ix86-32/iR5900LoadStore.cpp @@ -0,0 +1,2765 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "iR5900LoadStore.h" +#include "ix86/ix86.h" +#include "iR5900.h" + +// Implemented at the bottom of the module: +void SetFastMemory(int bSetFast); + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { + +/********************************************************* +* Load and store for GPR * +* Format: OP rt, offset(base) * +*********************************************************/ +#ifndef LOADSTORE_RECOMPILE + +namespace Interp = R5900::Interpreter::OpcodeImpl; + +REC_FUNC(LB); +REC_FUNC(LBU); +REC_FUNC(LH); +REC_FUNC(LHU); +REC_FUNC(LW); +REC_FUNC(LWU); +REC_FUNC(LWL); +REC_FUNC(LWR); +REC_FUNC(LD); +REC_FUNC(LDR); +REC_FUNC(LDL); +REC_FUNC(LQ); +REC_FUNC(SB); +REC_FUNC(SH); +REC_FUNC(SW); +REC_FUNC(SWL); +REC_FUNC(SWR); +REC_FUNC(SD); +REC_FUNC(SDL); +REC_FUNC(SDR); +REC_FUNC(SQ); +REC_FUNC(LWC1); +REC_FUNC(SWC1); +REC_FUNC(LQC2); +REC_FUNC(SQC2); + +#else + +PCSX2_ALIGNED16(u64 retValues[2]); +static u32 s_bCachingMem = 0; +static u32 s_nAddMemOffset = 0; +static u32 s_tempaddr = 0; + +void _eeOnLoadWrite(int reg) +{ + int regt; + + if( !reg ) return; + + _eeOnWriteReg(reg, 1); + regt = _checkXMMreg(XMMTYPE_GPRREG, reg, MODE_READ); + + if( regt >= 0 ) { + if( xmmregs[regt].mode & MODE_WRITE ) { + if( reg != _Rs_ ) { + SSE2_PUNPCKHQDQ_XMM_to_XMM(regt, regt); + SSE2_MOVQ_XMM_to_M64((u32)&cpuRegs.GPR.r[reg].UL[2], regt); + } + else SSE_MOVHPS_XMM_to_M64((u32)&cpuRegs.GPR.r[reg].UL[2], regt); + } + xmmregs[regt].inuse = 0; + } +} + +#ifdef PCSX2_VIRTUAL_MEM + +//////////////////////////////////////////////////// +//#define REC_SLOWREAD +//#define REC_SLOWWRITE +#define REC_FORCEMMX 0 + +// if sp, always mem write +int _eeIsMemWrite() { return _Rs_==29||_Rs_== 31||_Rs_==26||_Rs_==27; } // sp, ra, k0, k1 +// gp can be 1000a020 (jak1) + +void recTransferX86ToReg(int x86reg, int gprreg, int sign) +{ + //if( !REC_FORCEMMX ) assert( _checkMMXreg(MMX_GPR+gprreg, MODE_WRITE) == -1 ); + if( sign ) { + if( x86reg == EAX && EEINST_ISLIVE1(gprreg) ) CDQ(); + + MOV32RtoM( (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ], x86reg ); + + if(EEINST_ISLIVE1(gprreg)) { + if( x86reg == EAX ) MOV32RtoM( (int)&cpuRegs.GPR.r[ gprreg ].UL[ 1 ], EDX ); + else { + SAR32ItoR(x86reg, 31); + MOV32RtoM( (int)&cpuRegs.GPR.r[ gprreg ].UL[ 1 ], x86reg ); + } + } + else { + EEINST_RESETHASLIVE1(gprreg); + } + } + else { + MOV32RtoM( (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ], x86reg ); + if(EEINST_ISLIVE1(gprreg)) MOV32ItoM( (int)&cpuRegs.GPR.r[ gprreg ].UL[ 1 ], 0 ); + else EEINST_RESETHASLIVE1(gprreg); + } +} + +#ifdef _DEBUG +void testaddrs() +{ + register int tempaddr; + + __asm mov tempaddr, ecx + //__asm mov incaddr, edx + + assert( (tempaddr < 0x40000000) || (tempaddr&0xd0000000)==0x50000000 || (tempaddr >= 0x80000000 && tempaddr < 0xc0000000) ); + //assert( (tempaddr>>28) == ((tempaddr+incaddr)>>28) ); + + __asm mov ecx, tempaddr +} +#endif + +static __forceinline void SET_HWLOC_R5900() { + if ( s_bCachingMem & 2 ) + { + x86SetJ32(j32Ptr[2]); + x86SetJ32(j32Ptr[3]); + } + else + { + x86SetJ8(j8Ptr[0]); + x86SetJ8(j8Ptr[3]); + } + + if (x86FpuState==MMX_STATE) { + if (cpucaps.has3DNOWInstructionExtensions) + FEMMS(); + else + EMMS(); + } + if( s_nAddMemOffset ) + ADD32ItoR(ECX, s_nAddMemOffset); + if( s_bCachingMem & 4 ) + AND32ItoR(ECX, 0x5fffffff); +} + +static u16 g_MemMasks0[16] = {0x00f0, 0x80f1, 0x00f2, 0x00f3, + 0x00f1, 0x00f5, 0x00f1, 0x00f5, + 0x00f5, 0x80f5, 0x00f5, 0x80f5, + 0x00f1, 0x00f1, 0x00f1, 0x00f5 }; +static u16 g_MemMasks8[16] = {0x0080, 0x8081, 0x0082, 0x0083, + 0x0081, 0x0085, 0x0081, 0x0085, + 0x0085, 0x8085, 0x0085, 0x8085, + 0x0081, 0x0081, 0x0081, 0x0085 }; +static u16 g_MemMasks16[16] ={0x0000, 0x8001, 0x0002, 0x0003, + 0x0001, 0x0005, 0x0001, 0x0005, + 0x0005, 0x8005, 0x0005, 0x8005, + 0x0001, 0x0001, 0x0001, 0x0005 }; + +void assertmem() +{ + __asm mov s_tempaddr, ecx + __asm mov s_bCachingMem, edx + Console::Error("%x(%x) not mem write!", params s_tempaddr, s_bCachingMem); + assert(0); +} + +int _eePrepareReg(int gprreg) +{ + int mmreg = _checkXMMreg(XMMTYPE_GPRREG, gprreg, MODE_READ); + + if( mmreg >= 0 && (xmmregs[mmreg].mode&MODE_WRITE) ) { + mmreg |= MEM_XMMTAG; + } + else if( (mmreg = _checkMMXreg(MMX_GPR+gprreg, MODE_READ)) >= 0 ) { + if( mmxregs[mmreg].mode&MODE_WRITE ) mmreg |= MEM_MMXTAG; + else { + mmxregs[mmreg].needed = 0; // coX can possibly use all regs + mmreg = 0; + } + } + else { + mmreg = 0; + } + + return mmreg; +} + +// returns true if should also include hardware writes +int recSetMemLocation(int regs, int imm, int mmreg, int msize, int j32) +{ + s_bCachingMem = j32 ? 2 : 0; + s_nAddMemOffset = 0; + + //int num; + if( mmreg >= 0 && (mmreg & MEM_XMMTAG) ) { + SSE2_MOVD_XMM_to_R(ECX, mmreg&0xf); + } + else if( mmreg >= 0 && (mmreg & MEM_MMXTAG) ) { + MOVD32MMXtoR(ECX, mmreg&0xf); + SetMMXstate(); + } + else { + MOV32MtoR( ECX, (uptr)&cpuRegs.GPR.r[ regs ].UL[ 0 ] ); + } + + if ( imm != 0 ) ADD32ItoR( ECX, imm ); + + LoadCW(); + +#ifdef _DEBUG + //CALLFunc((uptr)testaddrs); +#endif + + + // 32bit version (faster?) +// MOV32RtoR(EAX, ECX); +// ROR32ItoR(ECX, 28); +// SHR32ItoR(EAX, 28); +// MOV32RmSOffsettoR(EAX, EAX, msize == 2 ? (u32)&g_MemMasks16[0] : (msize == 1 ? (u32)&g_MemMasks8[0] : (u32)&g_MemMasks0[0]), 2); +// AND8RtoR(ECX, EAX); +// ROR32ItoR(ECX, 4); +// // do extra alignment masks here +// OR32RtoR(EAX, EAX); + + if( _eeIsMemWrite() ) { + u8* ptr; + CMP32ItoR(ECX, 0x40000000); + ptr = JB8(0); + if( msize == 1 ) AND32ItoR(ECX, 0x5ffffff8); + else if( msize == 2 ) AND32ItoR(ECX, 0x5ffffff0); + else AND32ItoR(ECX, 0x5fffffff); + x86SetJ8(ptr); + if( msize == 1 ) AND8ItoR(ECX, 0xf8); + else if( msize == 2 ) AND8ItoR(ECX, 0xf0); +#ifdef _DEBUG + MOV32RtoR(EAX, ECX); + SHR32ItoR(EAX, 28); + CMP32ItoR(EAX, 1); + ptr = JNE8(0); + MOV32ItoR(EDX, _Rs_); + CALLFunc((uptr)assertmem); + x86SetJ8(ptr); +#endif + return 0; + } + else { + // 16 bit version + MOV32RtoR(EAX, ECX); + ROR32ItoR(ECX, 28); + SHR32ItoR(EAX, 28); + MOV16RmSOffsettoR(EAX, EAX, msize == 2 ? (u32)&g_MemMasks16[0] : (msize == 1 ? (u32)&g_MemMasks8[0] : (u32)&g_MemMasks0[0]), 1); + AND8RtoR(ECX, EAX); + ROR32ItoR(ECX, 4); + + TEST16RtoR(EAX, EAX); // this is faster, because sets no dependend reg. + + if( s_bCachingMem & 2 ) j32Ptr[2] = j32Ptr[3] = JS32(0); + else j8Ptr[0] = j8Ptr[3] = JS8(0); + } + + return 1; +} + + +void recLoad32(u32 bit, u32 imm, u32 sign) +{ + int mmreg = -1; + +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + // do const processing + int ineax = 0; + + _eeOnLoadWrite(_Rt_); + if( bit == 32 ) { + mmreg = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); + if( mmreg >= 0 ) mmreg |= MEM_MMXTAG; + else mmreg = EAX; + } + else { + _deleteEEreg(_Rt_, 0); + mmreg = EAX; + } + + switch(bit) { + case 8: ineax = recMemConstRead8(mmreg, g_cpuConstRegs[_Rs_].UL[0]+imm, sign); break; + case 16: + assert( (g_cpuConstRegs[_Rs_].UL[0]+imm) % 2 == 0 ); + ineax = recMemConstRead16(mmreg, g_cpuConstRegs[_Rs_].UL[0]+imm, sign); + break; + case 32: + // used by LWL/LWR + //assert( (g_cpuConstRegs[_Rs_].UL[0]+imm) % 4 == 0 ); + ineax = recMemConstRead32(mmreg, g_cpuConstRegs[_Rs_].UL[0]+imm); + break; + } + + if( ineax || !(mmreg&MEM_MMXTAG) ) { + if( mmreg&MEM_MMXTAG ) mmxregs[mmreg&0xf].inuse = 0; + recTransferX86ToReg(EAX, _Rt_, sign); + } + else { + assert( mmxregs[mmreg&0xf].mode & MODE_WRITE ); + if( sign ) _signExtendGPRtoMMX(mmreg&0xf, _Rt_, 32-bit); + else if( bit < 32 ) PSRLDItoR(mmreg&0xf, 32-bit); + } + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + + _eeOnLoadWrite(_Rt_); + + if( REC_FORCEMMX ) mmreg = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); + else _deleteEEreg(_Rt_, 0); + + dohw = recSetMemLocation(_Rs_, imm, mmregs, 0, 0); + + if( mmreg >= 0 ) { + MOVD32RmOffsettoMMX(mmreg, ECX, PS2MEM_BASE_+s_nAddMemOffset-(32-bit)/8); + if( sign ) _signExtendGPRtoMMX(mmreg&0xf, _Rt_, 32-bit); + else if( bit < 32 ) PSRLDItoR(mmreg&0xf, 32-bit); + } + else { + switch(bit) { + case 8: + if( sign ) MOVSX32Rm8toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + else MOVZX32Rm8toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + break; + case 16: + if( sign ) MOVSX32Rm16toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + else MOVZX32Rm16toROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + break; + case 32: + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + break; + } + + if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + switch(bit) { + case 8: + CALLFunc( (int)recMemRead8 ); + if( sign ) MOVSX32R8toR(EAX, EAX); + else MOVZX32R8toR(EAX, EAX); + break; + case 16: + CALLFunc( (int)recMemRead16 ); + if( sign ) MOVSX32R16toR(EAX, EAX); + else MOVZX32R16toR(EAX, EAX); + break; + case 32: + iMemRead32Check(); + CALLFunc( (int)recMemRead32 ); + break; + } + + if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, sign); + + if( mmreg >= 0 ) { + if( EEINST_ISLIVE1(_Rt_) ) MOVQMtoR(mmreg, (u32)&cpuRegs.GPR.r[_Rt_]); + else MOVD32RtoMMX(mmreg, EAX); + } + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[2]); + } + } +} + +void recLB( void ) { recLoad32(8, _Imm_, 1); } +void recLBU( void ) { recLoad32(8, _Imm_, 0); } +void recLH( void ) { recLoad32(16, _Imm_, 1); } +void recLHU( void ) { recLoad32(16, _Imm_, 0); } +void recLW( void ) { recLoad32(32, _Imm_, 1); } +void recLWU( void ) { recLoad32(32, _Imm_, 0); } + +//////////////////////////////////////////////////// + +void recLWL( void ) +{ +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + recMemConstRead32(EAX, (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~3); + + if( _Rt_ ) { + u32 shift = (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&3; + + _eeMoveGPRtoR(ECX, _Rt_); + SHL32ItoR(EAX, 24-shift*8); + AND32ItoR(ECX, (0xffffff>>(shift*8))); + OR32RtoR(EAX, ECX); + + if ( _Rt_ ) recTransferX86ToReg(EAX, _Rt_, 1); + } + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); + + MOV32ItoR(EDX, 0x3); + AND32RtoR(EDX, ECX); + AND32ItoR(ECX, ~3); + SHL32ItoR(EDX, 3); // *8 + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + + if( dohw ) { + j8Ptr[1] = JMP8(0); + SET_HWLOC_R5900(); + + iMemRead32Check(); + + // repeat + MOV32ItoR(EDX, 0x3); + AND32RtoR(EDX, ECX); + AND32ItoR(ECX, ~3); + SHL32ItoR(EDX, 3); // *8 + + PUSH32R(EDX); + CALLFunc( (int)recMemRead32 ); + POP32R(EDX); + + x86SetJ8(j8Ptr[1]); + } + + if ( _Rt_ ) { + // mem << LWL_SHIFT[shift] + MOV32ItoR(ECX, 24); + SUB32RtoR(ECX, EDX); + SHL32CLtoR(EAX); + + // mov temp and compute _rt_ & LWL_MASK[shift] + MOV32RtoR(ECX, EDX); + MOV32ItoR(EDX, 0xffffff); + SAR32CLtoR(EDX); + + _eeMoveGPRtoR(ECX, _Rt_); + AND32RtoR(EDX, ECX); + + // combine + OR32RtoR(EAX, EDX); + recTransferX86ToReg(EAX, _Rt_, 1); + } + } +} + +//////////////////////////////////////////////////// + +void recLWR( void ) +{ +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + recMemConstRead32(EAX, (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~3); + + if( _Rt_ ) { + u32 shift = (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&3; + + _eeMoveGPRtoR(ECX, _Rt_); + SHL32ItoR(EAX, shift*8); + AND32ItoR(ECX, (0xffffff00<<(24-shift*8))); + OR32RtoR(EAX, ECX); + + recTransferX86ToReg(EAX, _Rt_, 1); + } + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); + + MOV32RtoR(EDX, ECX); + AND32ItoR(ECX, ~3); + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + + if( dohw ) { + j8Ptr[1] = JMP8(0); + SET_HWLOC_R5900(); + + iMemRead32Check(); + + PUSH32R(ECX); + AND32ItoR(ECX, ~3); + CALLFunc( (int)recMemRead32 ); + POP32R(EDX); + + x86SetJ8(j8Ptr[1]); + } + + if ( _Rt_ ) + { + // mem << LWL_SHIFT[shift] + MOV32RtoR(ECX, EDX); + AND32ItoR(ECX, 3); + SHL32ItoR(ECX, 3); // *8 + SHR32CLtoR(EAX); + + // mov temp and compute _rt_ & LWL_MASK[shift] + MOV32RtoR(EDX, ECX); + MOV32ItoR(ECX, 24); + SUB32RtoR(ECX, EDX); + MOV32ItoR(EDX, 0xffffff00); + SHL32CLtoR(EDX); + _eeMoveGPRtoR(ECX, _Rt_); + AND32RtoR(ECX, EDX); + + // combine + OR32RtoR(EAX, ECX); + + recTransferX86ToReg(EAX, _Rt_, 1); + } + } +} + +//////////////////////////////////////////////////// +void recLoad64(u32 imm, int align) +{ + int mmreg; + int mask = align ? ~7 : ~0; + +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + // also used by LDL/LDR + //assert( (g_cpuConstRegs[_Rs_].UL[0]+imm) % 8 == 0 ); + + mmreg = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); + if( _Rt_ && mmreg >= 0 ) { + recMemConstRead64((g_cpuConstRegs[_Rs_].UL[0]+imm)&mask, mmreg); + assert( mmxregs[mmreg&0xf].mode & MODE_WRITE ); + } + else if( _Rt_ && (mmreg = _allocCheckGPRtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE|MODE_READ)) >= 0 ) { + recMemConstRead64((g_cpuConstRegs[_Rs_].UL[0]+imm)&mask, mmreg|0x8000); + assert( xmmregs[mmreg&0xf].mode & MODE_WRITE ); + } + else { + if( !_hasFreeMMXreg() && _hasFreeXMMreg() ) { + mmreg = _allocGPRtoXMMreg(-1, _Rt_, MODE_READ|MODE_WRITE); + recMemConstRead64((g_cpuConstRegs[_Rs_].UL[0]+imm)&mask, mmreg|0x8000); + assert( xmmregs[mmreg&0xf].mode & MODE_WRITE ); + } + else { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + recMemConstRead64((g_cpuConstRegs[_Rs_].UL[0]+imm)&mask, t0reg); + + if( _Rt_ ) { + SetMMXstate(); + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rt_].UD[0], t0reg); + } + + _freeMMXreg(t0reg); + } + } + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + + if( _Rt_ && (mmreg = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE)) >= 0 ) { + dohw = recSetMemLocation(_Rs_, imm, mmregs, align, 0); + + MOVQRmtoROffset(mmreg, ECX, PS2MEM_BASE_+s_nAddMemOffset); + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + CALLFunc( (int)recMemRead64 ); + MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); + ADD32ItoR(ESP, 4); + } + + SetMMXstate(); + } + else if( _Rt_ && (mmreg = _allocCheckGPRtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE|MODE_READ)) >= 0 ) { + dohw = recSetMemLocation(_Rs_, imm, mmregs, align, 0); + + SSE_MOVLPSRmtoROffset(mmreg, ECX, PS2MEM_BASE_+s_nAddMemOffset); + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + CALLFunc( (int)recMemRead64 ); + SSE_MOVLPS_M64_to_XMM(mmreg, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); + ADD32ItoR(ESP, 4); + } + } + else { + int t0reg = _Rt_ ? _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_WRITE) : -1; + + dohw = recSetMemLocation(_Rs_, imm, mmregs, align, 0); + + if( t0reg >= 0 ) { + MOVQRmtoROffset(t0reg, ECX, PS2MEM_BASE_+s_nAddMemOffset); + SetMMXstate(); + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + if( _Rt_ ) { + //_deleteEEreg(_Rt_, 0); + PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + } + else PUSH32I( (int)&retValues[0] ); + + CALLFunc( (int)recMemRead64 ); + ADD32ItoR(ESP, 4); + + if( t0reg >= 0 ) MOVQMtoR(t0reg, (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ]); + } + } + + if( dohw ) { + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[2]); + } + } + + _clearNeededMMXregs(); + _clearNeededXMMregs(); + if( _Rt_ ) _eeOnWriteReg(_Rt_, 0); +} + +void recLD(void) { recLoad64(_Imm_, 1); } + +//////////////////////////////////////////////////// +void recLDL( void ) +{ + iFlushCall(FLUSH_NOCONST); + + if( GPR_IS_CONST1( _Rs_ ) ) { + // flush temporarily + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], g_cpuConstRegs[_Rs_].UL[0]); + //MOV32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], g_cpuConstRegs[_Rs_].UL[1]); + } + else { + _deleteGPRtoXMMreg(_Rs_, 1); + _deleteMMXreg(MMX_GPR+_Rs_, 1); + } + + if( _Rt_ ) + _deleteEEreg(_Rt_, _Rt_==_Rs_); + + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)R5900::Interpreter::OpcodeImpl::LDL ); +} + +//////////////////////////////////////////////////// +void recLDR( void ) +{ + iFlushCall(FLUSH_NOCONST); + + if( GPR_IS_CONST1( _Rs_ ) ) { + // flush temporarily + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], g_cpuConstRegs[_Rs_].UL[0]); + //MOV32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], g_cpuConstRegs[_Rs_].UL[1]); + } + else { + _deleteGPRtoXMMreg(_Rs_, 1); + _deleteMMXreg(MMX_GPR+_Rs_, 1); + } + + if( _Rt_ ) + _deleteEEreg(_Rt_, _Rt_==_Rs_); + + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)R5900::Interpreter::OpcodeImpl::LDR ); +} + +//////////////////////////////////////////////////// +void recLQ( void ) +{ + int mmreg = -1; +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + // malice hits this + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 16 == 0 ); + + if( _Rt_ ) { + if( (g_pCurInstInfo->regs[_Rt_]&EEINST_XMM) || !(g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) ) { + _deleteMMXreg(MMX_GPR+_Rt_, 2); + _eeOnWriteReg(_Rt_, 0); + mmreg = _allocGPRtoXMMreg(-1, _Rt_, MODE_WRITE); + } + else { + int t0reg; + _deleteGPRtoXMMreg(_Rt_, 2); + _eeOnWriteReg(_Rt_, 0); + mmreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_WRITE)|MEM_MMXTAG; + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + mmreg |= t0reg<<4; + } + } + else { + mmreg = _allocTempXMMreg(XMMT_INT, -1); + } + + recMemConstRead128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, mmreg); + + if( !_Rt_ ) _freeXMMreg(mmreg); + if( IS_MMXREG(mmreg) ) { + // flush temp + assert( mmxregs[mmreg&0xf].mode & MODE_WRITE ); + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rt_].UL[2], (mmreg>>4)&0xf); + _freeMMXreg((mmreg>>4)&0xf); + } + else assert( xmmregs[mmreg&0xf].mode & MODE_WRITE ); + } + else +#endif + { + int dohw; + int mmregs; + int t0reg = -1; + + if( GPR_IS_CONST1( _Rs_ ) ) + _flushConstReg(_Rs_); + + mmregs = _eePrepareReg(_Rs_); + + if( _Rt_ ) { + _eeOnWriteReg(_Rt_, 0); + + // check if can process mmx + if( _hasFreeMMXreg() ) { + mmreg = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_WRITE); + if( mmreg >= 0 ) { + mmreg |= MEM_MMXTAG; + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + } + } + else _deleteMMXreg(MMX_GPR+_Rt_, 2); + + if( mmreg < 0 ) { + mmreg = _allocGPRtoXMMreg(-1, _Rt_, MODE_WRITE); + if( mmreg >= 0 ) mmreg |= MEM_XMMTAG; + } + } + + if( mmreg < 0 ) { + _deleteEEreg(_Rt_, 1); + } + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 0); + + if( _Rt_ ) { + if( mmreg >= 0 && (mmreg & MEM_MMXTAG) ) { + MOVQRmtoROffset(t0reg, ECX, PS2MEM_BASE_+s_nAddMemOffset+8); + MOVQRmtoROffset(mmreg&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rt_].UL[2], t0reg); + } + else if( mmreg >= 0 && (mmreg & MEM_XMMTAG) ) { + SSEX_MOVDQARmtoROffset(mmreg&0xf, ECX, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + _recMove128RmOffsettoM((u32)&cpuRegs.GPR.r[_Rt_].UL[0], PS2MEM_BASE_+s_nAddMemOffset); + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + PUSH32I( (u32)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + CALLFunc( (uptr)recMemRead128 ); + + if( mmreg >= 0 && (mmreg & MEM_MMXTAG) ) MOVQMtoR(mmreg&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + else if( mmreg >= 0 && (mmreg & MEM_XMMTAG) ) SSEX_MOVDQA_M128_to_XMM(mmreg&0xf, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + + ADD32ItoR(ESP, 4); + } + } + else { + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + PUSH32I( (u32)&retValues[0] ); + CALLFunc( (uptr)recMemRead128 ); + ADD32ItoR(ESP, 4); + } + } + + if( dohw ) { + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[2]); + } + + if( t0reg >= 0 ) _freeMMXreg(t0reg); + } + + _clearNeededXMMregs(); // needed since allocing +} + +//////////////////////////////////////////////////// +static void recClear64(BASEBLOCK* p) +{ + int left = 4 - ((u32)p % 16)/sizeof(BASEBLOCK); + recClearMem(p); + + if( left > 1 && *(u32*)(p+1) ) recClearMem(p+1); +} + +static void recClear128(BASEBLOCK* p) +{ + int left = 4 - ((u32)p % 32)/sizeof(BASEBLOCK); + recClearMem(p); + + if( left > 1 && *(u32*)(p+1) ) recClearMem(p+1); + if( left > 2 && *(u32*)(p+2) ) recClearMem(p+2); + if( left > 3 && *(u32*)(p+3) ) recClearMem(p+3); +} + +// check if clearing executable code, size is in dwords +void recMemConstClear(u32 mem, u32 size) +{ + // NOTE! This assumes recLUT never changes its mapping + if( !recLUT[mem>>16] ) + return; + + //iFlushCall(0); // just in case + + // check if mem is executable, and clear it + //CMP32ItoM((u32)&maxrecmem, mem); + //j8Ptr[5] = JBE8(0); + + // can clear now + if( size == 1 ) { + CMP32ItoM((u32)PC_GETBLOCK(mem), 0); + j8Ptr[6] = JE8(0); + + PUSH32I((u32)PC_GETBLOCK(mem)); + CALLFunc((uptr)recClearMem); + ADD32ItoR(ESP, 4); + x86SetJ8(j8Ptr[6]); + } + else if( size == 2 ) { + // need to clear 8 bytes + + CMP32I8toM((u32)PC_GETBLOCK(mem), 0); + j8Ptr[6] = JNE8(0); + + CMP32I8toM((u32)PC_GETBLOCK(mem)+8, 0); + j8Ptr[7] = JNE8(0); + + j8Ptr[8] = JMP8(0); + + // call clear + x86SetJ8( j8Ptr[7] ); + x86SetJ8( j8Ptr[6] ); + + PUSH32I((u32)PC_GETBLOCK(mem)); + CALLFunc((uptr)recClear64); + ADD32ItoR(ESP, 4); + + x86SetJ8( j8Ptr[8] ); + } + else { + assert( size == 4 ); + // need to clear 16 bytes + + CMP32I8toM((u32)PC_GETBLOCK(mem), 0); + j8Ptr[6] = JNE8(0); + + CMP32I8toM((u32)PC_GETBLOCK(mem)+sizeof(BASEBLOCK), 0); + j8Ptr[7] = JNE8(0); + + CMP32I8toM((u32)PC_GETBLOCK(mem)+sizeof(BASEBLOCK)*2, 0); + j8Ptr[8] = JNE8(0); + + CMP32I8toM((u32)PC_GETBLOCK(mem)+sizeof(BASEBLOCK)*3, 0); + j8Ptr[9] = JNE8(0); + + j8Ptr[10] = JMP8(0); + + // call clear + x86SetJ8( j8Ptr[6] ); + x86SetJ8( j8Ptr[7] ); + x86SetJ8( j8Ptr[8] ); + x86SetJ8( j8Ptr[9] ); + + PUSH32I((u32)PC_GETBLOCK(mem)); + CALLFunc((uptr)recClear128); + ADD32ItoR(ESP, 4); + + x86SetJ8( j8Ptr[10] ); + } + + //x86SetJ8(j8Ptr[5]); +} + +// check if mem is executable, and clear it +__declspec(naked) void recWriteMemClear32() +{ + _asm { + mov edx, ecx + shr edx, 14 + and dl, 0xfc + add edx, recLUT + test dword ptr [edx], 0xffffffff + jnz Clear32 + ret +Clear32: + // recLUT[mem>>16] + (mem&0xfffc) + mov edx, dword ptr [edx] + mov eax, ecx + and eax, 0xfffc + // edx += 2*eax + shl eax, 1 + add edx, eax + cmp dword ptr [edx], 0 + je ClearRet + sub esp, 4 + mov dword ptr [esp], edx + call recClearMem + add esp, 4 +ClearRet: + ret + } +} + +// check if mem is executable, and clear it +__declspec(naked) void recWriteMemClear64() +{ + __asm { + // check if mem is executable, and clear it + mov edx, ecx + shr edx, 14 + and edx, 0xfffffffc + add edx, recLUT + cmp dword ptr [edx], 0 + jne Clear64 + ret +Clear64: + // recLUT[mem>>16] + (mem&0xffff) + mov edx, dword ptr [edx] + mov eax, ecx + and eax, 0xfffc + // edx += 2*eax + shl eax, 1 + add edx, eax + + // note: have to check both blocks + cmp dword ptr [edx], 0 + jne DoClear0 + cmp dword ptr [edx+8], 0 + jne DoClear1 + ret + +DoClear1: + add edx, 8 +DoClear0: + sub esp, 4 + mov dword ptr [esp], edx + call recClear64 + add esp, 4 + ret + } +} + +// check if mem is executable, and clear it +__declspec(naked) void recWriteMemClear128() +{ + __asm { + // check if mem is executable, and clear it + mov edx, ecx + shr edx, 14 + and edx, 0xfffffffc + add edx, recLUT + cmp dword ptr [edx], 0 + jne Clear128 + ret +Clear128: + // recLUT[mem>>16] + (mem&0xffff) + mov edx, dword ptr [edx] + mov eax, ecx + and eax, 0xfffc + // edx += 2*eax + shl eax, 1 + add edx, eax + + // note: have to check all 4 blocks + cmp dword ptr [edx], 0 + jne DoClear + add edx, 8 + cmp dword ptr [edx], 0 + jne DoClear + add edx, 8 + cmp dword ptr [edx], 0 + jne DoClear + add edx, 8 + cmp dword ptr [edx], 0 + jne DoClear + ret + +DoClear: + sub esp, 4 + mov dword ptr [esp], edx + call recClear128 + add esp, 4 + ret + } +} + +void recStore_raw(EEINST* pinst, int bit, int x86reg, int gprreg, u32 offset) +{ + if( bit == 128 ) { + int mmreg; + if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, gprreg, MODE_READ)) >= 0) { + SSEX_MOVDQARtoRmOffset(ECX, mmreg, PS2MEM_BASE_+offset); + } + else if( (mmreg = _checkMMXreg(MMX_GPR+gprreg, MODE_READ)) >= 0) { + + if( _hasFreeMMXreg() ) { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(t0reg, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 2 ]); + MOVQRtoRmOffset(ECX, mmreg, PS2MEM_BASE_+offset); + MOVQRtoRmOffset(ECX, t0reg, PS2MEM_BASE_+offset+8); + _freeMMXreg(t0reg); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 2 ]); + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 3 ]); + MOVQRtoRmOffset(ECX, mmreg, PS2MEM_BASE_+offset); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+offset+8); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+offset+12); + } + + SetMMXstate(); + } + else { + + if( GPR_IS_CONST1( gprreg ) ) { + assert( _checkXMMreg(XMMTYPE_GPRREG, gprreg, MODE_READ) == -1 ); + + if( _hasFreeMMXreg() ) { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + SetMMXstate(); + MOVQMtoR(t0reg, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 2 ]); + MOV32ItoRmOffset(ECX, g_cpuConstRegs[gprreg].UL[0], PS2MEM_BASE_+offset); + MOV32ItoRmOffset(ECX, g_cpuConstRegs[gprreg].UL[1], PS2MEM_BASE_+offset+4); + MOVQRtoRmOffset(ECX, t0reg, PS2MEM_BASE_+offset+8); + _freeMMXreg(t0reg); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 2 ]); + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 3 ]); + MOV32ItoRmOffset(ECX, g_cpuConstRegs[gprreg].UL[0], PS2MEM_BASE_+offset); + MOV32ItoRmOffset(ECX, g_cpuConstRegs[gprreg].UL[1], PS2MEM_BASE_+offset+4); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+offset+8); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+offset+12); + } + } + else { + if( _hasFreeXMMreg() ) { + mmreg = _allocGPRtoXMMreg(-1, gprreg, MODE_READ); + SSEX_MOVDQARtoRmOffset(ECX, mmreg, PS2MEM_BASE_+offset); + _freeXMMreg(mmreg); + } + else if( _hasFreeMMXreg() ) { + mmreg = _allocMMXreg(-1, MMX_TEMP, 0); + + if( _hasFreeMMXreg() ) { + int t0reg; + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); + MOVQMtoR(t0reg, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 2 ]); + MOVQRtoRmOffset(ECX, mmreg, PS2MEM_BASE_+offset); + MOVQRtoRmOffset(ECX, t0reg, PS2MEM_BASE_+offset+8); + _freeMMXreg(t0reg); + } + else { + MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 2 ]); + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 3 ]); + MOVQRtoRmOffset(ECX, mmreg, PS2MEM_BASE_+offset); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+offset+8); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+offset+12); +// MOVQMtoR(mmreg, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 2 ]); +// MOVQRtoRmOffset(ECX, mmreg, PS2MEM_BASE_+offset+8); + } + SetMMXstate(); + _freeMMXreg(mmreg); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 1 ]); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+offset); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+offset+4); + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 2 ]); + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 3 ]); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+offset+8); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+offset+12); + } + } + } + return; + } + + if( GPR_IS_CONST1( gprreg ) ) { + switch(bit) { + case 8: MOV8ItoRmOffset(ECX, g_cpuConstRegs[gprreg].UL[0], PS2MEM_BASE_+offset); break; + case 16: MOV16ItoRmOffset(ECX, g_cpuConstRegs[gprreg].UL[0], PS2MEM_BASE_+offset); break; + case 32: MOV32ItoRmOffset(ECX, g_cpuConstRegs[gprreg].UL[0], PS2MEM_BASE_+offset); break; + case 64: + MOV32ItoRmOffset(ECX, g_cpuConstRegs[gprreg].UL[0], PS2MEM_BASE_+offset); + MOV32ItoRmOffset(ECX, g_cpuConstRegs[gprreg].UL[1], PS2MEM_BASE_+offset+4); + break; + } + } + else { + int mmreg = _checkMMXreg(MMX_GPR+gprreg, MODE_READ); + if( mmreg < 0 ) { + mmreg = _checkXMMreg(XMMTYPE_GPRREG, gprreg, MODE_READ); + if( mmreg >= 0 ) mmreg |= 0x8000; + } + + if( bit == 64 ) { + //sd + if( mmreg >= 0 ) { + if( mmreg & 0x8000 ) { + SSE_MOVLPSRtoRmOffset(ECX, mmreg&0xf, PS2MEM_BASE_+offset); + } + else { + MOVQRtoRmOffset(ECX, mmreg&0xf, PS2MEM_BASE_+offset); + SetMMXstate(); + } + } + else { + if( (mmreg = _allocCheckGPRtoMMX(pinst, gprreg, MODE_READ)) >= 0 ) { + MOVQRtoRmOffset(ECX, mmreg, PS2MEM_BASE_+offset); + SetMMXstate(); + _freeMMXreg(mmreg); + } + else if( _hasFreeMMXreg() ) { + mmreg = _allocMMXreg(-1, MMX_GPR+gprreg, MODE_READ); + MOVQRtoRmOffset(ECX, mmreg, PS2MEM_BASE_+offset); + SetMMXstate(); + _freeMMXreg(mmreg); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 1 ]); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+offset); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+offset+4); + } + } + } + else if( bit == 32 ) { + // sw + if( mmreg >= 0 ) { + if( mmreg & 0x8000) { + SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg&0xf, PS2MEM_BASE_+offset); + } + else { + MOVD32MMXtoRmOffset(ECX, mmreg&0xf, PS2MEM_BASE_+offset); + SetMMXstate(); + } + } + else { + MOV32MtoR(x86reg, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); + MOV32RtoRmOffset(ECX, x86reg, PS2MEM_BASE_+offset); + } + } + else { + // sb, sh + if( mmreg >= 0) { + if( mmreg & 0x8000) { + if( !(xmmregs[mmreg&0xf].mode&MODE_WRITE) ) mmreg = -1; + } + else { + if( !(mmxregs[mmreg&0xf].mode&MODE_WRITE) ) mmreg = -1; + } + } + + if( mmreg >= 0) { + if( mmreg & 0x8000 ) SSE2_MOVD_XMM_to_R(x86reg, mmreg&0xf); + else { + MOVD32MMXtoR(x86reg, mmreg&0xf); + SetMMXstate(); + } + } + else { + switch(bit) { + case 8: MOV8MtoR(x86reg, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); break; + case 16: MOV16MtoR(x86reg, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); break; + case 32: MOV32MtoR(x86reg, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); break; + } + } + + switch(bit) { + case 8: MOV8RtoRmOffset(ECX, x86reg, PS2MEM_BASE_+offset); break; + case 16: MOV16RtoRmOffset(ECX, x86reg, PS2MEM_BASE_+offset); break; + case 32: MOV32RtoRmOffset(ECX, x86reg, PS2MEM_BASE_+offset); break; + } + } + } +} + +void recStore_call(int bit, int gprreg, u32 offset) +{ + // some type of hardware write + if( GPR_IS_CONST1( gprreg ) ) { + if( bit == 128 ) { + if( gprreg > 0 ) { + assert( _checkXMMreg(XMMTYPE_GPRREG, gprreg, MODE_READ) == -1 ); + MOV32ItoM((int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ], g_cpuConstRegs[gprreg].UL[0]); + MOV32ItoM((int)&cpuRegs.GPR.r[ gprreg ].UL[ 1 ], g_cpuConstRegs[gprreg].UL[1]); + } + + MOV32ItoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); + } + else if( bit == 64 ) { + if( !(g_cpuFlushedConstReg&(1<= 0) { + + if( xmmregs[mmreg].mode & MODE_WRITE ) { + SSEX_MOVDQA_XMM_to_M128((int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ], mmreg); + } + } + else if( (mmreg = _checkMMXreg(MMX_GPR+gprreg, MODE_READ)) >= 0) { + if( mmxregs[mmreg].mode & MODE_WRITE ) { + MOVQRtoM((int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ], mmreg); + } + } + + MOV32ItoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); + } + else if( bit == 64 ) { + // sd + if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, gprreg, MODE_READ)) >= 0) { + + if( xmmregs[mmreg].mode & MODE_WRITE ) { + SSE_MOVLPS_XMM_to_M64((int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ], mmreg); + } + } + else if( (mmreg = _checkMMXreg(MMX_GPR+gprreg, MODE_READ)) >= 0) { + if( mmxregs[mmreg].mode & MODE_WRITE ) { + MOVQRtoM((int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ], mmreg); + SetMMXstate(); + } + } + + MOV32ItoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); + } + else { + // sb, sh, sw + if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, gprreg, MODE_READ)) >= 0 && (xmmregs[mmreg].mode&MODE_WRITE) ) { + SSE2_MOVD_XMM_to_R(EAX, mmreg); + } + else if( (mmreg = _checkMMXreg(MMX_GPR+gprreg, MODE_READ)) >= 0 && (mmxregs[mmreg].mode&MODE_WRITE)) { + MOVD32MMXtoR(EAX, mmreg); + SetMMXstate(); + } + else { + switch(bit) { + case 8: MOV8MtoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); break; + case 16: MOV16MtoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); break; + case 32: MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ gprreg ].UL[ 0 ]); break; + } + } + } + } + + if( offset != 0 ) ADD32ItoR(ECX, offset); + + // some type of hardware write + switch(bit) { + case 8: CALLFunc( (int)recMemWrite8 ); break; + case 16: CALLFunc( (int)recMemWrite16 ); break; + case 32: CALLFunc( (int)recMemWrite32 ); break; + case 64: CALLFunc( (int)recMemWrite64 ); break; + case 128: CALLFunc( (int)recMemWrite128 ); break; + } +} + +int _eePrepConstWrite128(int gprreg) +{ + int mmreg = 0; + + if( GPR_IS_CONST1(gprreg) ) { + if( gprreg ) { + mmreg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(mmreg, (u32)&cpuRegs.GPR.r[gprreg].UL[2]); + _freeMMXreg(mmreg); + mmreg |= (gprreg<<16)|MEM_EECONSTTAG; + } + else { + mmreg = _allocTempXMMreg(XMMT_INT, -1); + SSEX_PXOR_XMM_to_XMM(mmreg, mmreg); + _freeXMMreg(mmreg); + mmreg |= MEM_XMMTAG; + } + } + else { + if( (mmreg = _checkMMXreg(MMX_GPR+gprreg, MODE_READ)) >= 0 ) { + int mmregtemp = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(mmregtemp, (u32)&cpuRegs.GPR.r[gprreg].UL[2]); + mmreg |= (mmregtemp<<4)|MEM_MMXTAG; + _freeMMXreg(mmregtemp); + } + else mmreg = _allocGPRtoXMMreg(-1, gprreg, MODE_READ)|MEM_XMMTAG; + } + + return mmreg; +} + +void recStore(int bit, u32 imm, int align) +{ + int mmreg; + +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + u32 addr = g_cpuConstRegs[_Rs_].UL[0]+imm; + int doclear = 0; + switch(bit) { + case 8: + if( GPR_IS_CONST1(_Rt_) ) doclear = recMemConstWrite8(addr, MEM_EECONSTTAG|(_Rt_<<16)); + else { + _eeMoveGPRtoR(EAX, _Rt_); + doclear = recMemConstWrite8(addr, EAX); + } + + if( doclear ) { + recMemConstClear((addr)&~3, 1); + } + + break; + case 16: + assert( (addr)%2 == 0 ); + if( GPR_IS_CONST1(_Rt_) ) doclear = recMemConstWrite16(addr, MEM_EECONSTTAG|(_Rt_<<16)); + else { + _eeMoveGPRtoR(EAX, _Rt_); + doclear = recMemConstWrite16(addr, EAX); + } + + if( doclear ) { + recMemConstClear((addr)&~3, 1); + } + + break; + case 32: + // used by SWL/SWR + //assert( (addr)%4 == 0 ); + if( GPR_IS_CONST1(_Rt_) ) doclear = recMemConstWrite32(addr, MEM_EECONSTTAG|(_Rt_<<16)); + else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ)) >= 0 ) { + doclear = recMemConstWrite32(addr, mmreg|MEM_XMMTAG|(_Rt_<<16)); + } + else if( (mmreg = _checkMMXreg(MMX_GPR+_Rt_, MODE_READ)) >= 0 ) { + doclear = recMemConstWrite32(addr, mmreg|MEM_MMXTAG|(_Rt_<<16)); + } + else { + _eeMoveGPRtoR(EAX, _Rt_); + doclear = recMemConstWrite32(addr, EAX); + } + + if( doclear ) { + recMemConstClear((addr)&~3, 1); + } + break; + case 64: + { + //assert( (addr)%8 == 0 ); + int mask = align ? ~7 : ~0; + + if( GPR_IS_CONST1(_Rt_) ) doclear = recMemConstWrite64(addr, MEM_EECONSTTAG|(_Rt_<<16)); + else if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_READ)) >= 0 ) { + doclear = recMemConstWrite64(addr&mask, mmreg|MEM_XMMTAG|(_Rt_<<16)); + } + else { + mmreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ); + doclear = recMemConstWrite64(addr&mask, mmreg|MEM_MMXTAG|(_Rt_<<16)); + } + + if( doclear ) { + recMemConstClear((addr)&~7, 2); + } + + break; + } + case 128: + //assert( (addr)%16 == 0 ); + + if( recMemConstWrite128((addr)&~15, _eePrepConstWrite128(_Rt_)) ) { + CMP32ItoM((u32)&maxrecmem, addr); + j8Ptr[0] = JB8(0); + recMemConstClear((addr)&~15, 4); + x86SetJ8(j8Ptr[0]); + } + + break; + } + } + else +#endif + { + int dohw; + int mmregs; + + if( GPR_IS_CONST1( _Rs_ ) ) { + _flushConstReg(_Rs_); + } + + mmregs = _eePrepareReg(_Rs_); + dohw = recSetMemLocation(_Rs_, imm, mmregs, align ? bit/64 : 0, 0); + + recStore_raw(g_pCurInstInfo, bit, EAX, _Rt_, s_nAddMemOffset); + + if( s_nAddMemOffset ) ADD32ItoR(ECX, s_nAddMemOffset); + CMP32MtoR(ECX, (u32)&maxrecmem); + + if( s_bCachingMem & 2 ) j32Ptr[4] = JAE32(0); + else j8Ptr[1] = JAE8(0); + + if( bit < 32 || !align ) AND8ItoR(ECX, 0xfc); + if( bit <= 32 ) CALLFunc((uptr)recWriteMemClear32); + else if( bit == 64 ) CALLFunc((uptr)recWriteMemClear64); + else CALLFunc((uptr)recWriteMemClear128); + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[5] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + recStore_call(bit, _Rt_, s_nAddMemOffset); + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[5]); + else x86SetJ8(j8Ptr[2]); + } + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[1]); + } + + _clearNeededMMXregs(); // needed since allocing + _clearNeededXMMregs(); // needed since allocing +} + + +void recSB( void ) { recStore(8, _Imm_, 1); } +void recSH( void ) { recStore(16, _Imm_, 1); } +void recSW( void ) { recStore(32, _Imm_, 1); } + +//////////////////////////////////////////////////// +void recSWL( void ) +{ +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + int shift = (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&3; + + recMemConstRead32(EAX, (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~3); + + _eeMoveGPRtoR(ECX, _Rt_); + AND32ItoR(EAX, 0xffffff00<<(shift*8)); + SHR32ItoR(ECX, 24-shift*8); + OR32RtoR(EAX, ECX); + + if( recMemConstWrite32((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~3, EAX) ) + recMemConstClear((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~3, 1); + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); + + MOV32ItoR(EDX, 0x3); + AND32RtoR(EDX, ECX); + AND32ItoR(ECX, ~3); + SHL32ItoR(EDX, 3); // *8 + PUSH32R(ECX); + XOR32RtoR(EDI, EDI); + + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + + if( dohw ) { + j8Ptr[1] = JMP8(0); + SET_HWLOC_R5900(); + + // repeat + MOV32ItoR(EDX, 0x3); + AND32RtoR(EDX, ECX); + AND32ItoR(ECX, ~3); + SHL32ItoR(EDX, 3); // *8 + + PUSH32R(ECX); + PUSH32R(EDX); + CALLFunc( (int)recMemRead32 ); + POP32R(EDX); + MOV8ItoR(EDI, 0xff); + + x86SetJ8(j8Ptr[1]); + } + + _eeMoveGPRtoR(EBX, _Rt_); + + // oldmem is in EAX + // mem >> SWL_SHIFT[shift] + MOV32ItoR(ECX, 24); + SUB32RtoR(ECX, EDX); + SHR32CLtoR(EBX); + + // mov temp and compute _rt_ & SWL_MASK[shift] + MOV32RtoR(ECX, EDX); + MOV32ItoR(EDX, 0xffffff00); + SHL32CLtoR(EDX); + AND32RtoR(EAX, EDX); + + // combine + OR32RtoR(EAX, EBX); + + POP32R(ECX); + + // read the old mem again + TEST32RtoR(EDI, EDI); + j8Ptr[0] = JNZ8(0); + + // manual write + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); + + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + + CALLFunc( (int)recMemWrite32 ); + + x86SetJ8(j8Ptr[1]); + } +} + +//////////////////////////////////////////////////// +void recSWR( void ) +{ +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + int shift = (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&3; + + recMemConstRead32(EAX, (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~3); + + _eeMoveGPRtoR(ECX, _Rt_); + AND32ItoR(EAX, 0x00ffffff>>(24-shift*8)); + SHL32ItoR(ECX, shift*8); + OR32RtoR(EAX, ECX); + + if( recMemConstWrite32((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~3, EAX) ) + recMemConstClear((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~3, 1); + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); + + MOV32ItoR(EDX, 0x3); + AND32RtoR(EDX, ECX); + AND32ItoR(ECX, ~3); + SHL32ItoR(EDX, 3); // *8 + + PUSH32R(ECX); + XOR32RtoR(EDI, EDI); + + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + + if( dohw ) { + j8Ptr[1] = JMP8(0); + SET_HWLOC_R5900(); + + // repeat + MOV32ItoR(EDX, 0x3); + AND32RtoR(EDX, ECX); + AND32ItoR(ECX, ~3); + SHL32ItoR(EDX, 3); // *8 + + PUSH32R(ECX); + PUSH32R(EDX); + CALLFunc( (int)recMemRead32 ); + POP32R(EDX); + MOV8ItoR(EDI, 0xff); + + x86SetJ8(j8Ptr[1]); + } + + _eeMoveGPRtoR(EBX, _Rt_); + + // oldmem is in EAX + // mem << SWR_SHIFT[shift] + MOV32RtoR(ECX, EDX); + SHL32CLtoR(EBX); + + // mov temp and compute _rt_ & SWR_MASK[shift] + MOV32ItoR(ECX, 24); + SUB32RtoR(ECX, EDX); + MOV32ItoR(EDX, 0x00ffffff); + SHR32CLtoR(EDX); + AND32RtoR(EAX, EDX); + + // combine + OR32RtoR(EAX, EBX); + + POP32R(ECX); + + // read the old mem again + TEST32RtoR(EDI, EDI); + j8Ptr[0] = JNZ8(0); + + // manual write + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); + + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + + CALLFunc( (int)recMemWrite32 ); + + x86SetJ8(j8Ptr[1]); + } +} + +void recSD( void ) { recStore(64, _Imm_, 1); } + +//////////////////////////////////////////////////// +void recSDR( void ) +{ + iFlushCall(FLUSH_NOCONST); + + if( GPR_IS_CONST1( _Rs_ ) ) { + // flush temporarily + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], g_cpuConstRegs[_Rs_].UL[0]); + //MOV32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], g_cpuConstRegs[_Rs_].UL[1]); + } + + if( GPR_IS_CONST1( _Rt_ ) && !(g_cpuFlushedConstReg&(1<<_Rt_)) ) { + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], g_cpuConstRegs[_Rt_].UL[0]); + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], g_cpuConstRegs[_Rt_].UL[1]); + g_cpuFlushedConstReg |= (1<<_Rt_); + } + + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)R5900::Interpreter::OpcodeImpl::SDR ); +} + +//////////////////////////////////////////////////// +void recSDL( void ) +{ + iFlushCall(FLUSH_NOCONST); + + if( GPR_IS_CONST1( _Rs_ ) ) { + // flush temporarily + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], g_cpuConstRegs[_Rs_].UL[0]); + //MOV32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], g_cpuConstRegs[_Rs_].UL[1]); + } + + if( GPR_IS_CONST1( _Rt_ ) && !(g_cpuFlushedConstReg&(1<<_Rt_)) ) { + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], g_cpuConstRegs[_Rt_].UL[0]); + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], g_cpuConstRegs[_Rt_].UL[1]); + g_cpuFlushedConstReg |= (1<<_Rt_); + } + + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)R5900::Interpreter::OpcodeImpl::SDL ); +} + +//////////////////////////////////////////////////// +void recSQ( void ) { recStore(128, _Imm_, 1); } + +/********************************************************* +* Load and store for COP1 * +* Format: OP rt, offset(base) * +*********************************************************/ + +//////////////////////////////////////////////////// +void recLWC1( void ) +{ +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + int mmreg; + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 4 == 0 ); + + mmreg = _allocCheckFPUtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE); + if( mmreg >= 0 ) mmreg |= MEM_XMMTAG; + else mmreg = EAX; + + if( recMemConstRead32(mmreg, g_cpuConstRegs[_Rs_].UL[0]+_Imm_) ) { + if( mmreg&MEM_XMMTAG ) xmmregs[mmreg&0xf].inuse = 0; + mmreg = EAX; + } + + if( !(mmreg&MEM_XMMTAG) ) + MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); + } + else +#endif + { + int dohw; + int regt = _allocCheckFPUtoXMM(g_pCurInstInfo, _Rt_, MODE_WRITE); + int mmregs = _eePrepareReg(_Rs_); + + _deleteMMXreg(MMX_FPU+_Rt_, 2); + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); + + if( regt >= 0 ) { + SSEX_MOVD_RmOffset_to_XMM(regt, ECX, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + MOV32RmtoROffset(EAX, ECX, PS2MEM_BASE_+s_nAddMemOffset); + MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + iMemRead32Check(); + CALLFunc( (int)recMemRead32 ); + + if( regt >= 0 ) SSE2_MOVD_R_to_XMM(regt, EAX); + else MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[2]); + } + } +} + +//////////////////////////////////////////////////// +void recSWC1( void ) +{ + int mmreg; + +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)%4 == 0 ); + + mmreg = _checkXMMreg(XMMTYPE_FPREG, _Rt_, MODE_READ); + + if( mmreg >= 0 ) mmreg |= MEM_XMMTAG|(_Rt_<<16); + else { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); + mmreg = EAX; + } + + recMemConstWrite32(g_cpuConstRegs[_Rs_].UL[0]+_Imm_, mmreg); + } + else +#endif + { + int dohw; + int mmregs = _eePrepareReg(_Rs_); + + assert( _checkMMXreg(MMX_FPU+_Rt_, MODE_READ) == -1 ); + + mmreg = _checkXMMreg(XMMTYPE_FPREG, _Rt_, MODE_READ); + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 0, 0); + + if( mmreg >= 0) { + SSEX_MOVD_XMM_to_RmOffset(ECX, mmreg, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); + } + + if( dohw ) { + if( s_bCachingMem & 2 ) j32Ptr[4] = JMP32(0); + else j8Ptr[2] = JMP8(0); + + SET_HWLOC_R5900(); + + // some type of hardware write + if( mmreg >= 0) SSE2_MOVD_XMM_to_R(EAX, mmreg); + else MOV32MtoR(EAX, (int)&fpuRegs.fpr[ _Rt_ ].UL); + + CALLFunc( (int)recMemWrite32 ); + + if( s_bCachingMem & 2 ) x86SetJ32(j32Ptr[4]); + else x86SetJ8(j8Ptr[2]); + } + } +} + +//////////////////////////////////////////////////// + +#define _Ft_ _Rt_ +#define _Fs_ _Rd_ +#define _Fd_ _Sa_ + +void recLQC2( void ) +{ + int mmreg; + +#ifdef REC_SLOWREAD + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_) % 16 == 0 ); + + if( _Ft_ ) mmreg = _allocVFtoXMMreg(&VU0, -1, _Ft_, MODE_WRITE); + else mmreg = _allocTempXMMreg(XMMT_FPS, -1); + recMemConstRead128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, mmreg); + + if( !_Ft_ ) _freeXMMreg(mmreg); + } + else +#endif + { + int dohw, mmregs; + + if( GPR_IS_CONST1( _Rs_ ) ) { + _flushConstReg(_Rs_); + } + + mmregs = _eePrepareReg(_Rs_); + + if( _Ft_ ) mmreg = _allocVFtoXMMreg(&VU0, -1, _Ft_, MODE_WRITE); + + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 0); + + if( _Ft_ ) { + u8* rawreadptr = x86Ptr; + + if( mmreg >= 0 ) { + SSEX_MOVDQARmtoROffset(mmreg, ECX, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + _recMove128RmOffsettoM((u32)&VU0.VF[_Ft_].UL[0], PS2MEM_BASE_+s_nAddMemOffset); + } + + if( dohw ) { + j8Ptr[1] = JMP8(0); + SET_HWLOC_R5900(); + + // check if writing to VUs + CMP32ItoR(ECX, 0x11000000); + JAE8(rawreadptr - (x86Ptr+2)); + + PUSH32I( (int)&VU0.VF[_Ft_].UD[0] ); + CALLFunc( (int)recMemRead128 ); + if( mmreg >= 0 ) SSEX_MOVDQA_M128_to_XMM(mmreg, (int)&VU0.VF[_Ft_].UD[0] ); + ADD32ItoR(ESP, 4); + + x86SetJ8(j8Ptr[1]); + } + } + else { + if( dohw ) { + j8Ptr[1] = JMP8(0); + SET_HWLOC_R5900(); + + PUSH32I( (int)&retValues[0] ); + CALLFunc( (int)recMemRead128 ); + ADD32ItoR(ESP, 4); + + x86SetJ8(j8Ptr[1]); + } + } + } + + _clearNeededXMMregs(); // needed since allocing +} + +//////////////////////////////////////////////////// +void recSQC2( void ) +{ + int mmreg; + +#ifdef REC_SLOWWRITE + _flushConstReg(_Rs_); +#else + if( GPR_IS_CONST1( _Rs_ ) ) { + assert( (g_cpuConstRegs[_Rs_].UL[0]+_Imm_)%16 == 0 ); + + mmreg = _allocVFtoXMMreg(&VU0, -1, _Ft_, MODE_READ)|MEM_XMMTAG; + recMemConstWrite128((g_cpuConstRegs[_Rs_].UL[0]+_Imm_)&~15, mmreg); + } + else +#endif + { + u8* rawreadptr; + int dohw, mmregs; + + if( GPR_IS_CONST1( _Rs_ ) ) { + _flushConstReg(_Rs_); + } + + mmregs = _eePrepareReg(_Rs_); + dohw = recSetMemLocation(_Rs_, _Imm_, mmregs, 2, 0); + + rawreadptr = x86Ptr; + + if( (mmreg = _checkXMMreg(XMMTYPE_VFREG, _Ft_, MODE_READ)) >= 0) { + SSEX_MOVDQARtoRmOffset(ECX, mmreg, PS2MEM_BASE_+s_nAddMemOffset); + } + else { + if( _hasFreeXMMreg() ) { + mmreg = _allocTempXMMreg(XMMT_FPS, -1); + SSEX_MOVDQA_M128_to_XMM(mmreg, (int)&VU0.VF[_Ft_].UD[0]); + SSEX_MOVDQARtoRmOffset(ECX, mmreg, PS2MEM_BASE_+s_nAddMemOffset); + _freeXMMreg(mmreg); + } + else if( _hasFreeMMXreg() ) { + mmreg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(mmreg, (int)&VU0.VF[_Ft_].UD[0]); + MOVQRtoRmOffset(ECX, mmreg, PS2MEM_BASE_+s_nAddMemOffset); + MOVQMtoR(mmreg, (int)&VU0.VF[_Ft_].UL[2]); + MOVQRtoRmOffset(ECX, mmreg, PS2MEM_BASE_+s_nAddMemOffset+8); + SetMMXstate(); + _freeMMXreg(mmreg); + } + else { + MOV32MtoR(EAX, (int)&VU0.VF[_Ft_].UL[0]); + MOV32MtoR(EDX, (int)&VU0.VF[_Ft_].UL[1]); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+4); + MOV32MtoR(EAX, (int)&VU0.VF[_Ft_].UL[2]); + MOV32MtoR(EDX, (int)&VU0.VF[_Ft_].UL[3]); + MOV32RtoRmOffset(ECX, EAX, PS2MEM_BASE_+s_nAddMemOffset+8); + MOV32RtoRmOffset(ECX, EDX, PS2MEM_BASE_+s_nAddMemOffset+12); + } + } + + if( dohw ) { + j8Ptr[1] = JMP8(0); + + SET_HWLOC_R5900(); + + // check if writing to VUs + CMP32ItoR(ECX, 0x11000000); + JAE8(rawreadptr - (x86Ptr+2)); + + // some type of hardware write + if( (mmreg = _checkXMMreg(XMMTYPE_VFREG, _Ft_, MODE_READ)) >= 0) { + + if( xmmregs[mmreg].mode & MODE_WRITE ) { + SSEX_MOVDQA_XMM_to_M128((int)&VU0.VF[_Ft_].UD[0], mmreg); + } + } + + MOV32ItoR(EAX, (int)&VU0.VF[_Ft_].UD[0]); + CALLFunc( (int)recMemWrite128 ); + + x86SetJ8A(j8Ptr[1]); + } + } +} + +#else + +using namespace Interpreter::OpcodeImpl; + +PCSX2_ALIGNED16(u32 dummyValue[4]); + +void SetFastMemory(int bSetFast) +{ + // nothing +} + +void recLoad64( u32 bits, bool sign ) +{ + jASSUME( bits == 64 || bits == 128 ); + + //no int 3? i love to get my hands dirty ;p - Raz + //write8(0xCC); + + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + + EEINST_RESETSIGNEXT(_Rt_); // remove the sign extension -> what does this really do ? + + _deleteEEreg(_Rt_, 0); + + // Load ECX with the source memory address that we're reading from. + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + ADD32ItoR( ECX, _Imm_ ); + + if( bits == 128 ) // force 16 byte alignment on 128 bit reads + AND32I8toR(ECX,0xF0); + + // Load EDX with the destination. 64/128 bit modes load the result directly into + // the cpuRegs.GPR struct. + + if ( _Rt_ ) + MOV32ItoR(EDX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + else + MOV32ItoR(EDX, (int)&dummyValue[0] ); + + vtlb_DynGenRead64(bits); +} + +void recLoad32(u32 bits,bool sign) +{ + jASSUME( bits <= 32 ); + + //no int 3? i love to get my hands dirty ;p - Raz + //write8(0xCC); + + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + // Load ECX with the source memory address that we're reading from. + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + ADD32ItoR( ECX, _Imm_ ); + + // 8/16/32 bit modes return the loaded value in EAX. + //MOV32ItoR(EDX, (int)&dummyValue[0] ); + + vtlb_DynGenRead32(bits, sign); + + if ( _Rt_ ) + { + // Perform sign extension if needed + + //MOV32MtoR( EAX, (int)&dummyValue[0] ); //ewww, lame ! movsx /zx has r/m forms too ... + /*if (bits==8) + { + if (sign) + //MOVSX32M8toR( EAX, (int)&dummyValue[0] ); + MOVSX32R8toR( EAX, EAX ); + //else + //MOVZX32M8toR( EAX, (int)&dummyValue[0] ); + //MOVZX32R8toR( EAX, EAX ); + } + else if (bits==16) + { + if (sign) + //MOVSX32M16toR( EAX, (int)&dummyValue[0] ); + MOVSX32R16toR( EAX, EAX ); + //else + //MOVZX32M16toR( EAX, (int)&dummyValue[0] ); + //MOVZX32R16toR( EAX, EAX ); + }*/ + + if (sign) + CDQ(); + else + XOR32RtoR(EDX,EDX); + + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + } +} + +//////////////////////////////////////////////////// +void recLB( void ) +{ + recLoad32(8,true); + /* + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + PUSH32I( (int)&dummyValue[0] ); + PUSH32R( EAX ); + + CALLFunc( (int)memRead8 ); + ADD32ItoR( ESP, 8 ); + if ( _Rt_ ) + { + u8* linkEnd; + TEST32RtoR( EAX, EAX ); + linkEnd = JNZ8( 0 ); + MOV32MtoR( EAX, (int)&dummyValue[0] ); + MOVSX32R8toR( EAX, EAX ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + x86SetJ8( linkEnd ); + } + */ +} + +//////////////////////////////////////////////////// +void recLBU( void ) +{ + recLoad32(8,false); + /* + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + PUSH32I( (int)&dummyValue[0] ); + PUSH32R( EAX ); + + CALLFunc( (int)memRead8 ); + ADD32ItoR( ESP, 8 ); + if ( _Rt_ ) + { + u8* linkEnd; + TEST32RtoR( EAX, EAX ); + linkEnd = JNZ8( 0 ); + MOV32MtoR( EAX, (int)&dummyValue[0] ); + MOVZX32R8toR( EAX, EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); + x86SetJ8( linkEnd ); + } + */ +} + +//////////////////////////////////////////////////// +void recLH( void ) +{ + recLoad32(16,true); + /* + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + PUSH32I( (int)&dummyValue[0] ); + PUSH32R( EAX ); + + CALLFunc( (int)memRead16 ); + ADD32ItoR( ESP, 8 ); + if ( _Rt_ ) + { + u8* linkEnd; + TEST32RtoR( EAX, EAX ); + linkEnd = JNZ8( 0 ); + MOV32MtoR( EAX, (int)&dummyValue[0]); + MOVSX32R16toR( EAX, EAX ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + x86SetJ8( linkEnd ); + } + */ +} + +//////////////////////////////////////////////////// +void recLHU( void ) +{ + recLoad32(16,false); + /* + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + PUSH32I( (int)&dummyValue[0] ); + PUSH32R( EAX ); + CALLFunc( (int)memRead16 ); + ADD32ItoR( ESP, 8 ); + if ( _Rt_ ) + { + u8* linkEnd; + TEST32RtoR( EAX, EAX ); + linkEnd = JNZ8( 0 ); + MOV32MtoR( EAX, (int)&dummyValue[0] ); + MOVZX32R16toR( EAX, EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); + x86SetJ8( linkEnd ); + }*/ +} + +//////////////////////////////////////////////////// +void recLW( void ) +{ + recLoad32(32,true); + /* + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + + PUSH32I( (int)&dummyValue[0]); + PUSH32R( EAX ); + + CALLFunc( (int)memRead32 ); + ADD32ItoR( ESP, 8 ); + + if ( _Rt_ ) + { + u8* linkEnd; + TEST32RtoR( EAX, EAX ); + linkEnd = JNZ8( 0 ); + MOV32MtoR( EAX, (int)&dummyValue[0]); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], EDX ); + x86SetJ8( linkEnd ); + }*/ +} + +//////////////////////////////////////////////////// +void recLWU( void ) +{ + recLoad32(32,false); + /* + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + + PUSH32I( (int)&dummyValue[0]); + PUSH32R( EAX ); + CALLFunc( (int)memRead32 ); + ADD32ItoR( ESP, 8 ); + if ( _Rt_ ) + { + u8* linkEnd; + TEST32RtoR( EAX, EAX ); + linkEnd = JNZ8( 0 ); + MOV32MtoR( EAX, (int)&dummyValue[0]); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], EAX ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); + x86SetJ8( linkEnd ); + } + */ +} + +//////////////////////////////////////////////////// +void recLWL( void ) +{ + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + _deleteEEreg(_Rt_, 0); + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)LWL ); +} + +//////////////////////////////////////////////////// +void recLWR( void ) +{ + iFlushCall(FLUSH_EVERYTHING); + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)LWR ); +} + +//////////////////////////////////////////////////// +extern void MOV64RmtoR( x86IntRegType to, x86IntRegType from ); + +void recLD( void ) +{ + recLoad64(64,false); + /* + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + EEINST_RESETSIGNEXT(_Rt_); // remove the sign extension + _deleteEEreg(_Rt_, 0); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + + if ( _Rt_ ) + { + PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + } + else + { + PUSH32I( (int)&dummyValue[0] ); + } + PUSH32R( EAX ); + CALLFunc( (int)memRead64 ); + ADD32ItoR( ESP, 8 ); + */ +} + +//////////////////////////////////////////////////// +void recLDL( void ) +{ + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + EEINST_RESETSIGNEXT(_Rt_); // remove the sign extension + _deleteEEreg(_Rt_, 0); + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)LDL ); +} + +//////////////////////////////////////////////////// +void recLDR( void ) +{ + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + EEINST_RESETSIGNEXT(_Rt_); // remove the sign extension + _deleteEEreg(_Rt_, 0); + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)LDR ); +} + +//////////////////////////////////////////////////// +void recLQ( void ) +{ + recLoad64(128,false); +/* + _deleteEEreg(_Rs_, 1); + _eeOnLoadWrite(_Rt_); + EEINST_RESETSIGNEXT(_Rt_); // remove the sign extension + _deleteEEreg(_Rt_, 0); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_); + } + AND32ItoR( EAX, ~0xf ); + + if ( _Rt_ ) + { + PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + } + else + { + PUSH32I( (int)&dummyValue[0] ); + } + PUSH32R( EAX ); + CALLFunc( (int)memRead128 ); + ADD32ItoR( ESP, 8 ); + */ +} +void recStore(u32 sz) +{ + //no int 3? i love to get my hands dirty ;p - Raz + //write8(0xCC); + + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( ECX, _Imm_); + } + if (sz==128) + { + AND32I8toR(ECX,0xF0); + } + + if (sz<64) + { + if (_Rt_) + MOV32MtoR(EDX,(int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + else + XOR32RtoR(EDX,EDX); + } + else if (sz==128 || sz==64) + { + MOV32ItoR(EDX,(int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ]); + } + + + vtlb_DynGenWrite(sz); + + /* + if (sz==8) + CALLFunc( (int)memWrite8 ); + else if (sz==16) + CALLFunc( (int)memWrite16 ); + else if (sz==32) + CALLFunc( (int)memWrite32 ); + else if (sz==64) + CALLFunc( (int)memWrite64 ); + else if (sz==128) + CALLFunc( (int)memWrite128 ); + */ +} +//////////////////////////////////////////////////// +void recSB( void ) +{ + recStore(8); + /* + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_); + } + PUSH32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + PUSH32R( EAX ); + CALLFunc( (int)memWrite8 ); + ADD32ItoR( ESP, 8 ); + */ +} + +//////////////////////////////////////////////////// +void recSH( void ) +{ + recStore(16); + /* + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + PUSH32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + PUSH32R( EAX ); + CALLFunc( (int)memWrite16 ); + ADD32ItoR( ESP, 8 ); + */ +} + +//////////////////////////////////////////////////// +void recSW( void ) +{ + recStore(32); + /* + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + + PUSH32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + PUSH32R( EAX ); + CALLFunc( (int)memWrite32 ); + ADD32ItoR( ESP, 8 ); + */ +} + +//////////////////////////////////////////////////// +void recSWL( void ) +{ + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)SWL ); +} + +//////////////////////////////////////////////////// +void recSWR( void ) +{ + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)SWR ); +} + +//////////////////////////////////////////////////// +void recSD( void ) +{ + recStore(64); + /* + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + + PUSH32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + PUSH32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + PUSH32R( EAX ); + CALLFunc( (int)memWrite64 ); + ADD32ItoR( ESP, 12 ); + */ +} + +//////////////////////////////////////////////////// +void recSDL( void ) +{ + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)SDL ); +} + +//////////////////////////////////////////////////// +void recSDR( void ) +{ + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + MOV32ItoM( (int)&cpuRegs.code, cpuRegs.code ); + MOV32ItoM( (int)&cpuRegs.pc, pc ); + CALLFunc( (int)SDR ); +} + +//////////////////////////////////////////////////// +void recSQ( void ) +{ + recStore(128); + /* + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + { + ADD32ItoR( EAX, _Imm_ ); + } + AND32ItoR( EAX, ~0xf ); + + PUSH32I( (int)&cpuRegs.GPR.r[ _Rt_ ].UD[ 0 ] ); + PUSH32R( EAX ); + CALLFunc( (int)memWrite128 ); + ADD32ItoR( ESP, 8 );*/ +} + +/********************************************************* +* Load and store for COP1 * +* Format: OP rt, offset(base) * +*********************************************************/ + +//////////////////////////////////////////////////// +void recLWC1( void ) +{ + _deleteEEreg(_Rs_, 1); + _deleteFPtoXMMreg(_Rt_, 2); + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + ADD32ItoR( ECX, _Imm_ ); + + //MOV32ItoR(EDX, (int)&fpuRegs.fpr[ _Rt_ ].UL ); //no 0 for fpu ? + //CALLFunc( (int)memRead32 ); + vtlb_DynGenRead32(32, false); + MOV32RtoM( (int)&fpuRegs.fpr[ _Rt_ ].UL, EAX ); +} + +//////////////////////////////////////////////////// +void recSWC1( void ) +{ + _deleteEEreg(_Rs_, 1); + _deleteFPtoXMMreg(_Rt_, 0); + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + ADD32ItoR( ECX, _Imm_ ); + + MOV32MtoR(EDX, (int)&fpuRegs.fpr[ _Rt_ ].UL ); + vtlb_DynGenWrite(32); +} + +//////////////////////////////////////////////////// + +/********************************************************* +* Load and store for COP2 (VU0 unit) * +* Format: OP rt, offset(base) * +*********************************************************/ + +#define _Ft_ _Rt_ +#define _Fs_ _Rd_ +#define _Fd_ _Sa_ + +void recLQC2( void ) +{ + _deleteEEreg(_Rs_, 1); + _deleteVFtoXMMreg(_Ft_, 0, 2); + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + ADD32ItoR( ECX, _Imm_); + + if ( _Rt_ ) + MOV32ItoR(EDX, (int)&VU0.VF[_Ft_].UD[0] ); + else + MOV32ItoR(EDX, (int)&dummyValue[0] ); + + vtlb_DynGenRead64(128); +} + +//////////////////////////////////////////////////// +void recSQC2( void ) +{ + _deleteEEreg(_Rs_, 1); + _deleteVFtoXMMreg(_Ft_, 0, 0); + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + if ( _Imm_ != 0 ) + ADD32ItoR( ECX, _Imm_ ); + + MOV32ItoR(EDX, (int)&VU0.VF[_Ft_].UD[0] ); + vtlb_DynGenWrite(128); +} + +#endif + +#endif + +} } } // end namespace R5900::Dynarec::OpcodeImpl + +using namespace R5900::Dynarec; +using namespace R5900::Dynarec::OpcodeImpl; + +#ifdef PCSX2_VIRTUAL_MEM + +#ifndef LOADSTORE_RECOMPILE + void SetFastMemory(int bSetFast) {} +#else + static int s_bFastMemory = 0; + void SetFastMemory(int bSetFast) + { + s_bFastMemory = bSetFast; + if( bSetFast ) { + g_MemMasks0[0] = 0x00f0; g_MemMasks0[1] = 0x80f1; g_MemMasks0[2] = 0x00f0; g_MemMasks0[3] = 0x00f1; + g_MemMasks8[0] = 0x0080; g_MemMasks8[1] = 0x8081; g_MemMasks8[2] = 0x0080; g_MemMasks8[3] = 0x0081; + g_MemMasks16[0] = 0x0000; g_MemMasks16[1] = 0x8001; g_MemMasks16[2] = 0x0000; g_MemMasks16[3] = 0x0001; + } + else { + g_MemMasks0[0] = 0x00f0; g_MemMasks0[1] = 0x80f1; g_MemMasks0[2] = 0x00f2; g_MemMasks0[3] = 0x00f3; + g_MemMasks8[0] = 0x0080; g_MemMasks8[1] = 0x8081; g_MemMasks8[2] = 0x0082; g_MemMasks8[3] = 0x0083; + g_MemMasks16[0] = 0x0000; g_MemMasks16[1] = 0x8001; g_MemMasks16[2] = 0x0002; g_MemMasks16[3] = 0x0003; + } + } +#endif + +#else // VTLB version + + void SetFastMemory(int bSetFast) {} + +#endif diff --git a/pcsx2/x86/ix86-32/iR5900Move.cpp b/pcsx2/x86/ix86-32/iR5900Move.cpp new file mode 100644 index 0000000000..e2a82d71d3 --- /dev/null +++ b/pcsx2/x86/ix86-32/iR5900Move.cpp @@ -0,0 +1,825 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" + + +#ifdef _WIN32 +#pragma warning(disable:4244) +#pragma warning(disable:4761) +#endif + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl +{ + +/********************************************************* +* Shift arithmetic with constant shift * +* Format: OP rd, rt, sa * +*********************************************************/ +#ifndef MOVE_RECOMPILE + +namespace Interp = R5900::Interpreter::OpcodeImpl; + +REC_FUNC_DEL(LUI,_Rt_); +REC_FUNC(MFLO); +REC_FUNC(MFHI); +REC_FUNC(MTLO); +REC_FUNC(MTHI); + +REC_FUNC( MFHI1 ); +REC_FUNC( MFLO1 ); +REC_FUNC( MTHI1 ); +REC_FUNC( MTLO1 ); + +REC_FUNC(MOVZ); +REC_FUNC(MOVN); + +#elif defined(EE_CONST_PROP) + +/********************************************************* +* Load higher 16 bits of the first word in GPR with imm * +* Format: OP rt, immediate * +*********************************************************/ + +//// LUI +void recLUI() +{ + int mmreg; + if(!_Rt_) return; + + _eeOnWriteReg(_Rt_, 1); + + if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_WRITE)) >= 0 ) { + if( xmmregs[mmreg].mode & MODE_WRITE ) { + SSE_MOVHPS_XMM_to_M64((u32)&cpuRegs.GPR.r[_Rt_].UL[2], mmreg); + } + xmmregs[mmreg].inuse = 0; + } + + _deleteEEreg(_Rt_, 0); + GPR_SET_CONST(_Rt_); + g_cpuConstRegs[_Rt_].UD[0] = (s32)(cpuRegs.code << 16); +} + +//////////////////////////////////////////////////// +void recMFHILO(int hi) +{ + int reghi, regd, xmmhilo; + if ( ! _Rd_ ) + return; + + xmmhilo = hi ? XMMGPR_HI : XMMGPR_LO; + reghi = _checkXMMreg(XMMTYPE_GPRREG, xmmhilo, MODE_READ); + + _eeOnWriteReg(_Rd_, 0); + + regd = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, MODE_READ|MODE_WRITE); + + if( reghi >= 0 ) { + if( regd >= 0 ) { + assert( regd != reghi ); + + xmmregs[regd].inuse = 0; + + SSE2_MOVQ_XMM_to_M64((u32)&cpuRegs.GPR.r[_Rd_].UL[0], reghi); + + if( xmmregs[regd].mode & MODE_WRITE ) { + SSE_MOVHPS_XMM_to_M64((u32)&cpuRegs.GPR.r[_Rd_].UL[2], regd); + } + } + else { + regd = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rd_, MODE_WRITE); + + if( regd >= 0 ) { + SSE2_MOVDQ2Q_XMM_to_MM(regd, reghi); + } + else { + _deleteEEreg(_Rd_, 0); + SSE2_MOVQ_XMM_to_M64((int)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], reghi); + } + } + } + else { + reghi = _checkMMXreg(MMX_GPR+xmmhilo, MODE_READ); + + if( reghi >= 0 ) { + + if( regd >= 0 ) { + if( EEINST_ISLIVE2(_Rd_) ) { + if( xmmregs[regd].mode & MODE_WRITE ) { + SSE_MOVHPS_XMM_to_M64((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 2 ], regd); + } + xmmregs[regd].inuse = 0; + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], reghi); + } + else { + SetMMXstate(); + SSE2_MOVQ2DQ_MM_to_XMM(regd, reghi); + xmmregs[regd].mode |= MODE_WRITE; + } + } + else { + regd = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rd_, MODE_WRITE); + SetMMXstate(); + + if( regd >= 0 ) { + MOVQRtoR(regd, reghi); + } + else { + _deleteEEreg(_Rd_, 0); + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], reghi); + } + } + } + else { + if( regd >= 0 ) { + if( EEINST_ISLIVE2(_Rd_) ) SSE_MOVLPS_M64_to_XMM(regd, hi ? (int)&cpuRegs.HI.UD[ 0 ] : (int)&cpuRegs.LO.UD[ 0 ]); + else SSE2_MOVQ_M64_to_XMM(regd, hi ? (int)&cpuRegs.HI.UD[ 0 ] : (int)&cpuRegs.LO.UD[ 0 ]); + } + else { + regd = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rd_, MODE_WRITE); + + if( regd >= 0 ) { + SetMMXstate(); + MOVQMtoR(regd, hi ? (int)&cpuRegs.HI.UD[ 0 ] : (int)&cpuRegs.LO.UD[ 0 ]); + } + else { + _deleteEEreg(_Rd_, 0); + MOV32MtoR( EAX, hi ? (int)&cpuRegs.HI.UL[ 0 ] : (int)&cpuRegs.LO.UL[ 0 ]); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32MtoR( EDX, hi ? (int)&cpuRegs.HI.UL[ 1 ] : (int)&cpuRegs.LO.UL[ 1 ]); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + else + EEINST_RESETHASLIVE1(_Rd_); + } + } + } + } +} + +void recMTHILO(int hi) +{ + int reghi, regs, xmmhilo; + u32 addrhilo; + + xmmhilo = hi ? XMMGPR_HI : XMMGPR_LO; + addrhilo = hi ? (int)&cpuRegs.HI.UD[0] : (int)&cpuRegs.LO.UD[0]; + + regs = _checkXMMreg(XMMTYPE_GPRREG, _Rs_, MODE_READ); + reghi = _checkXMMreg(XMMTYPE_GPRREG, xmmhilo, MODE_READ|MODE_WRITE); + + if( reghi >= 0 ) { + if( regs >= 0 ) { + assert( reghi != regs ); + + _deleteGPRtoXMMreg(_Rs_, 0); + SSE2_PUNPCKHQDQ_XMM_to_XMM(reghi, reghi); + SSE2_PUNPCKLQDQ_XMM_to_XMM(regs, reghi); + + // swap regs + xmmregs[regs] = xmmregs[reghi]; + xmmregs[reghi].inuse = 0; + xmmregs[regs].mode |= MODE_WRITE; + + } + else { + regs = _checkMMXreg(MMX_GPR+_Rs_, MODE_READ); + + if( regs >= 0 ) { + + if( EEINST_ISLIVE2(xmmhilo) ) { + if( xmmregs[reghi].mode & MODE_WRITE ) { + SSE_MOVHPS_XMM_to_M64(addrhilo+8, reghi); + } + xmmregs[reghi].inuse = 0; + MOVQRtoM(addrhilo, regs); + } + else { + SetMMXstate(); + SSE2_MOVQ2DQ_MM_to_XMM(reghi, regs); + xmmregs[reghi].mode |= MODE_WRITE; + } + } + else { + _flushConstReg(_Rs_); + SSE_MOVLPS_M64_to_XMM(reghi, (int)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ]); + xmmregs[reghi].mode |= MODE_WRITE; + } + } + } + else { + reghi = _allocCheckGPRtoMMX(g_pCurInstInfo, xmmhilo, MODE_WRITE); + + if( reghi >= 0 ) { + if( regs >= 0 ) { + //SetMMXstate(); + SSE2_MOVDQ2Q_XMM_to_MM(reghi, regs); + } + else { + regs = _checkMMXreg(MMX_GPR+_Rs_, MODE_WRITE); + + if( regs >= 0 ) { + SetMMXstate(); + MOVQRtoR(reghi, regs); + } + else { + _flushConstReg(_Rs_); + MOVQMtoR(reghi, (int)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ]); + } + } + } + else { + if( regs >= 0 ) { + SSE2_MOVQ_XMM_to_M64(addrhilo, regs); + } + else { + regs = _checkMMXreg(MMX_GPR+_Rs_, MODE_WRITE); + + if( regs >= 0 ) { + SetMMXstate(); + MOVQRtoM(addrhilo, regs); + } + else { + if( GPR_IS_CONST1(_Rs_) ) { + MOV32ItoM(addrhilo, g_cpuConstRegs[_Rs_].UL[0] ); + MOV32ItoM(addrhilo+4, g_cpuConstRegs[_Rs_].UL[1] ); + } + else { + _deleteEEreg(_Rs_, 1); + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ]); + MOV32RtoM( addrhilo, EAX ); + MOV32RtoM( addrhilo+4, EDX ); + } + } + } + } + } +} + +void recMFHI( void ) +{ + recMFHILO(1); +} + +void recMFLO( void ) +{ + recMFHILO(0); +} + +void recMTHI( void ) +{ + recMTHILO(1); +} + +void recMTLO( void ) +{ + recMTHILO(0); +} + +//////////////////////////////////////////////////// +void recMFHILO1(int hi) +{ + int reghi, regd, xmmhilo; + if ( ! _Rd_ ) + return; + + xmmhilo = hi ? XMMGPR_HI : XMMGPR_LO; + reghi = _checkXMMreg(XMMTYPE_GPRREG, xmmhilo, MODE_READ); + + _eeOnWriteReg(_Rd_, 0); + + regd = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, MODE_READ|MODE_WRITE); + + if( reghi >= 0 ) { + if( regd >= 0 ) { + SSEX_MOVHLPS_XMM_to_XMM(regd, reghi); + xmmregs[regd].mode |= MODE_WRITE; + } + else { + _deleteEEreg(_Rd_, 0); + SSE_MOVHPS_XMM_to_M64((int)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], reghi); + } + } + else { + if( regd >= 0 ) { + if( EEINST_ISLIVE2(_Rd_) ) { + SSE2_PUNPCKHQDQ_M128_to_XMM(regd, hi ? (int)&cpuRegs.HI.UD[ 0 ] : (int)&cpuRegs.LO.UD[ 0 ]); + SSE2_PSHUFD_XMM_to_XMM(regd, regd, 0x4e); + } + else { + SSE2_MOVQ_M64_to_XMM(regd, hi ? (int)&cpuRegs.HI.UD[ 1 ] : (int)&cpuRegs.LO.UD[ 1 ]); + } + + xmmregs[regd].mode |= MODE_WRITE; + } + else { + regd = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rd_, MODE_WRITE); + + if( regd >= 0 ) { + SetMMXstate(); + MOVQMtoR(regd, hi ? (int)&cpuRegs.HI.UD[ 1 ] : (int)&cpuRegs.LO.UD[ 1 ]); + } + else { + _deleteEEreg(_Rd_, 0); + MOV32MtoR( EAX, hi ? (int)&cpuRegs.HI.UL[ 2 ] : (int)&cpuRegs.LO.UL[ 2 ]); + MOV32MtoR( EDX, hi ? (int)&cpuRegs.HI.UL[ 3 ] : (int)&cpuRegs.LO.UL[ 3 ]); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + } + } +} + +void recMTHILO1(int hi) +{ + int reghi, regs, xmmhilo; + u32 addrhilo; + + xmmhilo = hi ? XMMGPR_HI : XMMGPR_LO; + addrhilo = hi ? (int)&cpuRegs.HI.UD[0] : (int)&cpuRegs.LO.UD[0]; + + regs = _checkXMMreg(XMMTYPE_GPRREG, _Rs_, MODE_READ); + reghi = _allocCheckGPRtoXMM(g_pCurInstInfo, xmmhilo, MODE_WRITE|MODE_READ); + + if( reghi >= 0 ) { + if( regs >= 0 ) { + SSE2_PUNPCKLQDQ_XMM_to_XMM(reghi, regs); + } + else { + _deleteEEreg(_Rs_, 1); + SSE2_PUNPCKLQDQ_M128_to_XMM(reghi, (int)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ]); + } + } + else { + if( regs >= 0 ) { + SSE2_MOVQ_XMM_to_M64(addrhilo+8, regs); + } + else { + regs = _checkMMXreg(MMX_GPR+_Rs_, MODE_WRITE); + + if( regs >= 0 ) { + SetMMXstate(); + MOVQRtoM(addrhilo+8, regs); + } + else { + if( GPR_IS_CONST1(_Rs_) ) { + MOV32ItoM(addrhilo+8, g_cpuConstRegs[_Rs_].UL[0] ); + MOV32ItoM(addrhilo+12, g_cpuConstRegs[_Rs_].UL[1] ); + } + else { + _deleteEEreg(_Rs_, 1); + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ]); + MOV32RtoM( addrhilo+8, EAX ); + MOV32RtoM( addrhilo+12, EDX ); + } + } + } + } +} + +void recMFHI1( void ) +{ + recMFHILO1(1); +} + +void recMFLO1( void ) +{ + recMFHILO1(0); +} + +void recMTHI1( void ) +{ + recMTHILO1(1); +} + +void recMTLO1( void ) +{ + recMTHILO1(0); +} + +//// MOVZ +void recMOVZtemp_const() +{ + GPR_DEL_CONST(_Rd_); + _deleteEEreg(_Rd_, 1); + _eeOnWriteReg(_Rd_, 0); + if (g_cpuConstRegs[_Rt_].UD[0] == 0) { + MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[0], g_cpuConstRegs[_Rs_].UL[0]); + MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[1], g_cpuConstRegs[_Rs_].UL[1]); + } +} + +//static PCSX2_ALIGNED16(u32 s_zero[4]) = {0,0,0xffffffff, 0xffffffff}; + +void recMOVZtemp_consts(int info) +{ + if( info & PROCESS_EE_MMX ) { + + u32* mem; + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + PXORRtoR(t0reg, t0reg); + PCMPEQDRtoR(t0reg, EEREC_T); + PMOVMSKBMMXtoR(EAX, t0reg); + CMP8ItoR(EAX, 0xff); + j8Ptr[ 0 ] = JNE8( 0 ); + + if( g_cpuFlushedConstReg & (1<<_Rs_) ) mem = &cpuRegs.GPR.r[_Rs_].UL[0]; + else { + mem = recAllocStackMem(8,8); + + mem[0] = g_cpuConstRegs[_Rs_].UL[0]; + mem[1] = g_cpuConstRegs[_Rs_].UL[1]; + } + + MOVQMtoR(EEREC_D, (u32)mem); + x86SetJ8( j8Ptr[ 0 ] ); + + _freeMMXreg(t0reg); + return; + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + OR32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + j8Ptr[ 0 ] = JNZ8( 0 ); + + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], g_cpuConstRegs[_Rs_].UL[0] ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], g_cpuConstRegs[_Rs_].UL[1] ); + + x86SetJ8( j8Ptr[ 0 ] ); +} + +void recMOVZtemp_constt(int info) +{ + if (g_cpuConstRegs[_Rt_].UD[0] == 0) { + if( info & PROCESS_EE_MMX ) { + if( EEREC_D != EEREC_S ) MOVQRtoR(EEREC_D, EEREC_S); + return; + } + + if( _hasFreeXMMreg() ) { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(t0reg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], t0reg); + _freeMMXreg(t0reg); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ]); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX); + } + } +} + +void recMOVZtemp_(int info) +{ + int t0reg = -1; + + if( info & PROCESS_EE_MMX ) { + + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + PXORRtoR(t0reg, t0reg); + PCMPEQDRtoR(t0reg, EEREC_T); + PMOVMSKBMMXtoR(EAX, t0reg); + CMP8ItoR(EAX, 0xff); + j8Ptr[ 0 ] = JNE8( 0 ); + + MOVQRtoR(EEREC_D, EEREC_S); + x86SetJ8( j8Ptr[ 0 ] ); + + _freeMMXreg(t0reg); + return; + } + + if( _hasFreeXMMreg() ) + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + OR32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + j8Ptr[ 0 ] = JNZ8( 0 ); + + if( t0reg >= 0 ) { + MOVQMtoR(t0reg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], t0reg); + _freeMMXreg(t0reg); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ]); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX); + } + + x86SetJ8( j8Ptr[ 0 ] ); + SetMMXstate(); +} + +EERECOMPILE_CODE0(MOVZtemp, XMMINFO_READS|XMMINFO_READD|XMMINFO_READD|XMMINFO_WRITED); + +void recMOVZ() +{ + if( _Rs_ == _Rd_ ) + return; + + if( GPR_IS_CONST1(_Rd_) ) { + + if( !GPR_IS_CONST2(_Rs_, _Rt_) ) { + // remove the const, since move is conditional + _deleteEEreg(_Rd_, 0); + MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[0], g_cpuConstRegs[_Rd_].UL[0]); + MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[1], g_cpuConstRegs[_Rd_].UL[1]); + } + else { + if (g_cpuConstRegs[_Rt_].UD[0] == 0) { + g_cpuConstRegs[_Rd_].UL[0] = g_cpuConstRegs[_Rs_].UL[0]; + g_cpuConstRegs[_Rd_].UL[1] = g_cpuConstRegs[_Rs_].UL[1]; + } + return; + } + } + + recMOVZtemp(); +} + +//// MOVN +void recMOVNtemp_const() +{ + GPR_DEL_CONST(_Rd_); + _deleteEEreg(_Rd_, 1); + _eeOnWriteReg(_Rd_, 0); + if (g_cpuConstRegs[_Rt_].UD[0] != 0) { + MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[0], g_cpuConstRegs[_Rs_].UL[0]); + MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[1], g_cpuConstRegs[_Rs_].UL[1]); + } +} + +void recMOVNtemp_consts(int info) +{ + if( info & PROCESS_EE_MMX ) { + + u32* mem; + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + PXORRtoR(t0reg, t0reg); + PCMPEQDRtoR(t0reg, EEREC_T); + + PMOVMSKBMMXtoR(EAX, t0reg); + CMP8ItoR(EAX, 0xff); + j8Ptr[ 0 ] = JE8( 0 ); + + if( g_cpuFlushedConstReg & (1<<_Rs_) ) mem = &cpuRegs.GPR.r[_Rs_].UL[0]; + else { + mem = recAllocStackMem(8,8); + + mem[0] = g_cpuConstRegs[_Rs_].UL[0]; + mem[1] = g_cpuConstRegs[_Rs_].UL[1]; + } + + MOVQMtoR(EEREC_D, (u32)mem); + x86SetJ8( j8Ptr[ 0 ] ); + + _freeMMXreg(t0reg); + return; + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + OR32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + j8Ptr[ 0 ] = JZ8( 0 ); + + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], g_cpuConstRegs[_Rs_].UL[0] ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], g_cpuConstRegs[_Rs_].UL[1] ); + + x86SetJ8( j8Ptr[ 0 ] ); +} + +void recMOVNtemp_constt(int info) +{ + if (g_cpuConstRegs[_Rt_].UD[0] != 0) { + if( _hasFreeXMMreg() ) { + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQMtoR(t0reg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], t0reg); + _freeMMXreg(t0reg); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ]); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX); + } + } +} + +void recMOVNtemp_(int info) +{ + int t0reg=-1; + + if( info & PROCESS_EE_MMX ) { + + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + PXORRtoR(t0reg, t0reg); + PCMPEQDRtoR(t0reg, EEREC_T); + PMOVMSKBMMXtoR(EAX, t0reg); + CMP8ItoR(EAX, 0xff); + j8Ptr[ 0 ] = JE8( 0 ); + + MOVQRtoR(EEREC_D, EEREC_S); + x86SetJ8( j8Ptr[ 0 ] ); + + _freeMMXreg(t0reg); + return; + } + + if( _hasFreeXMMreg() ) + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + OR32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + j8Ptr[ 0 ] = JZ8( 0 ); + + if( t0reg >= 0 ) { + MOVQMtoR(t0reg, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], t0reg); + _freeMMXreg(t0reg); + } + else { + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ]); + MOV32MtoR(EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ]); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX); + } + + x86SetJ8( j8Ptr[ 0 ] ); + + SetMMXstate(); +} + +EERECOMPILE_CODE0(MOVNtemp, XMMINFO_READS|XMMINFO_READD|XMMINFO_READD|XMMINFO_WRITED); + +void recMOVN() +{ + if( _Rs_ == _Rd_ ) + return; + + if( GPR_IS_CONST1(_Rd_) ) { + + if( !GPR_IS_CONST2(_Rs_, _Rt_) ) { + // remove the const, since move is conditional + _deleteEEreg(_Rd_, 0); + MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[0], g_cpuConstRegs[_Rd_].UL[0]); + MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[1], g_cpuConstRegs[_Rd_].UL[1]); + } + else { + if (g_cpuConstRegs[_Rt_].UD[0] != 0) { + g_cpuConstRegs[_Rd_].UL[0] = g_cpuConstRegs[_Rs_].UL[0]; + g_cpuConstRegs[_Rd_].UL[1] = g_cpuConstRegs[_Rs_].UL[1]; + } + return; + } + } + + recMOVNtemp(); +} + +#else + +//////////////////////////////////////////////////// +void recLUI( void ) +{ + if(!_Rt_) return; + if ( _Imm_ < 0 ) + { + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], (u32)_Imm_ << 16 ); //U + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0xffffffff ); //V + } + else + { + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], (u32)_Imm_ << 16 ); //U + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], 0 ); //V + } +} + +//////////////////////////////////////////////////// +void recMFHI( void ) +{ + + if ( ! _Rd_ ) + { + return; + } + + MOVQMtoR( MM0, (int)&cpuRegs.HI.UD[ 0 ] ); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], MM0 ); + SetMMXstate(); + +} + +//////////////////////////////////////////////////// +void recMFLO( void ) +{ + + if ( ! _Rd_ ) + { + return; + } + + MOVQMtoR( MM0, (int)&cpuRegs.LO.UD[ 0 ] ); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UD[ 0 ], MM0 ); + SetMMXstate(); + +} + +//////////////////////////////////////////////////// +void recMTHI( void ) +{ + + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQRtoM( (int)&cpuRegs.HI.UD[ 0 ], MM0 ); + SetMMXstate(); + +} + +//////////////////////////////////////////////////// +void recMTLO( void ) +{ + + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rs_ ].UD[ 0 ] ); + MOVQRtoM( (int)&cpuRegs.LO.UD[ 0 ], MM0 ); + SetMMXstate(); + +} + +//////////////////////////////////////////////////// +void recMOVZ( void ) +{ + if ( ! _Rd_ ) + { + return; + } + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + OR32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + j8Ptr[ 0 ] = JNZ8( 0 ); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + MOV32MtoR( EDX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + + x86SetJ8( j8Ptr[ 0 ] ); + +} + +//////////////////////////////////////////////////// +void recMOVN( void ) +{ + if ( ! _Rd_ ) + { + return; + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + OR32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + j8Ptr[ 0 ] = JZ8( 0 ); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ] ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], ECX ); + + x86SetJ8( j8Ptr[ 0 ] ); + +} + +REC_FUNC( MFHI1 ); +REC_FUNC( MFLO1 ); +REC_FUNC( MTHI1 ); +REC_FUNC( MTLO1 ); + +#endif + +} } } diff --git a/pcsx2/x86/ix86-32/iR5900MultDiv.cpp b/pcsx2/x86/ix86-32/iR5900MultDiv.cpp new file mode 100644 index 0000000000..99857d6f0d --- /dev/null +++ b/pcsx2/x86/ix86-32/iR5900MultDiv.cpp @@ -0,0 +1,955 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" + +namespace Interp = R5900::Interpreter::OpcodeImpl; + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl +{ + +/********************************************************* +* Register mult/div & Register trap logic * +* Format: OP rs, rt * +*********************************************************/ +#ifndef MULTDIV_RECOMPILE + +REC_FUNC(MULT); +REC_FUNC(MULTU); +REC_FUNC( MULT1 ); +REC_FUNC( MULTU1 ); + +REC_FUNC(DIV); +REC_FUNC(DIVU); +REC_FUNC( DIV1 ); +REC_FUNC( DIVU1 ); + +REC_FUNC( MADD ); +REC_FUNC( MADDU ); +REC_FUNC( MADD1 ); +REC_FUNC( MADDU1 ); + +#elif defined(EE_CONST_PROP) + +// if upper is 1, write in upper 64 bits of LO/HI +void recWritebackHILO(int info, int writed, int upper) +{ + int regd, reglo = -1, reghi, savedlo = 0; + u32 loaddr = (int)&cpuRegs.LO.UL[ upper ? 2 : 0 ]; + u32 hiaddr = (int)&cpuRegs.HI.UL[ upper ? 2 : 0 ]; + u8 testlive = upper?EEINST_LIVE2:EEINST_LIVE0; + + if( g_pCurInstInfo->regs[XMMGPR_HI] & testlive ) + MOV32RtoR( ECX, EDX ); + + if( g_pCurInstInfo->regs[XMMGPR_LO] & testlive ) { + + _deleteMMXreg(XMMGPR_LO, 2); + + if( (reglo = _checkXMMreg(XMMTYPE_GPRREG, XMMGPR_LO, MODE_READ)) >= 0 ) { + if( xmmregs[reglo].mode & MODE_WRITE ) { + if( upper ) SSE2_MOVQ_XMM_to_M64(loaddr-8, reglo); + else SSE_MOVHPS_XMM_to_M64(loaddr+8, reglo); + } + + xmmregs[reglo].inuse = 0; + reglo = -1; + } + + CDQ(); + MOV32RtoM( loaddr, EAX ); + MOV32RtoM( loaddr+4, EDX ); + savedlo = 1; + } + + if ( writed && _Rd_ ) + { + _eeOnWriteReg(_Rd_, 1); + + regd = -1; + if( g_pCurInstInfo->regs[_Rd_] & EEINST_MMX ) { + + if( savedlo ) { + regd = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + MOVQMtoR(regd, loaddr); + } + } + else if( g_pCurInstInfo->regs[_Rd_] & EEINST_XMM ) { + if( savedlo ) { + regd = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, MODE_WRITE|MODE_READ); + if( regd >= 0 ) { + SSE_MOVLPS_M64_to_XMM(regd, loaddr); + regd |= 0x8000; + } + } + } + + if( regd < 0 ) { + _deleteEEreg(_Rd_, 0); + + if( EEINST_ISLIVE1(_Rd_) && !savedlo ) CDQ(); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + + if( EEINST_ISLIVE1(_Rd_) ) { + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + else EEINST_RESETHASLIVE1(_Rd_); + } + } + + if( g_pCurInstInfo->regs[XMMGPR_HI] & testlive ) { + _deleteMMXreg(XMMGPR_HI, 2); + + if( (reghi = _checkXMMreg(XMMTYPE_GPRREG, XMMGPR_HI, MODE_READ)) >= 0 ) { + if( xmmregs[reghi].mode & MODE_WRITE ) { + if( upper ) SSE2_MOVQ_XMM_to_M64(hiaddr-8, reghi); + else SSE_MOVHPS_XMM_to_M64(hiaddr+8, reghi); + } + + xmmregs[reghi].inuse = 0; + reghi = -1; + } + + MOV32RtoM(hiaddr, ECX ); + SAR32ItoR(ECX, 31); + MOV32RtoM(hiaddr+4, ECX ); + } +} + +void recWritebackHILOMMX(int info, int regsource, int writed, int upper) +{ + int regd, t0reg, t1reg = -1; + u32 loaddr = (int)&cpuRegs.LO.UL[ upper ? 2 : 0 ]; + u32 hiaddr = (int)&cpuRegs.HI.UL[ upper ? 2 : 0 ]; + u8 testlive = upper?EEINST_LIVE2:EEINST_LIVE0; + + SetMMXstate(); + + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t0reg, regsource); + PSRADItoR(t0reg, 31); // shift in 0s + + if( (g_pCurInstInfo->regs[XMMGPR_LO] & testlive) || (writed && _Rd_) ) { + if( (g_pCurInstInfo->regs[XMMGPR_HI] & testlive) ) + { + if( !_hasFreeMMXreg() ) { + if( g_pCurInstInfo->regs[XMMGPR_HI] & testlive ) + _deleteMMXreg(MMX_GPR+MMX_HI, 2); + } + + t1reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t1reg, regsource); + } + + PUNPCKLDQRtoR(regsource, t0reg); + } + + if( g_pCurInstInfo->regs[XMMGPR_LO] & testlive ) { + int reglo; + if( !upper && (reglo = _allocCheckGPRtoMMX(g_pCurInstInfo, XMMGPR_LO, MODE_WRITE)) >= 0 ) { + MOVQRtoR(reglo, regsource); + } + else { + reglo = _checkXMMreg(XMMTYPE_GPRREG, XMMGPR_LO, MODE_READ); + + MOVQRtoM(loaddr, regsource); + + if( reglo >= 0 ) { + if( xmmregs[reglo].mode & MODE_WRITE ) { + if( upper ) SSE2_MOVQ_XMM_to_M64(loaddr-8, reglo); + else SSE_MOVHPS_XMM_to_M64(loaddr+8, reglo); + } + xmmregs[reglo].inuse = 0; + reglo = -1; + } + } + } + + if ( writed && _Rd_ ) + { + regd = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rd_, MODE_WRITE); + + if( regd >= 0 ) { + if( regd != regsource ) MOVQRtoR(regd, regsource); + } + else { + regd = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, MODE_READ); + + if( regd >= 0 ) { + if( g_pCurInstInfo->regs[XMMGPR_LO] & testlive ) { + // lo written + SSE_MOVLPS_M64_to_XMM(regd, loaddr); + xmmregs[regd].mode |= MODE_WRITE; + } + else { + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], regsource); + + if( xmmregs[regd].mode & MODE_WRITE ) { + SSE_MOVHPS_XMM_to_M64((int)&cpuRegs.GPR.r[_Rd_].UL[2], regd); + } + + xmmregs[regd].inuse = 0; + } + } + else { + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], regsource); + } + } + } + + if( g_pCurInstInfo->regs[XMMGPR_HI] & testlive ) { + + int mmreg = -1, reghi; + + if( t1reg >= 0 ) { + PUNPCKHDQRtoR(t1reg, t0reg); + mmreg = t1reg; + } + else { + // can't modify regsource + PUNPCKHDQRtoR(t0reg, regsource); + mmreg = t0reg; + PSHUFWRtoR(t0reg, t0reg, 0x4e); + } + + if( upper ) { + reghi = _checkXMMreg(XMMTYPE_GPRREG, XMMGPR_HI, MODE_READ); + if( reghi >= 0 ) { + if( xmmregs[reghi].mode & MODE_WRITE ) SSE2_MOVQ_XMM_to_M64(hiaddr-8, reghi); + } + + xmmregs[reghi].inuse = 0; + MOVQRtoM(hiaddr, mmreg); + } + else { + _deleteGPRtoXMMreg(XMMGPR_HI, 2); + _deleteMMXreg(MMX_GPR+MMX_HI, 2); + mmxregs[mmreg].mode = MODE_WRITE; + mmxregs[mmreg].reg = MMX_GPR+MMX_HI; + + if( t1reg >= 0 ) t1reg = -1; + else t0reg = -1; + } + } + + if( t0reg >= 0 ) _freeMMXreg(t0reg&0xf); + if( t1reg >= 0 ) _freeMMXreg(t1reg&0xf); +} + +void recWritebackConstHILO(u64 res, int writed, int upper) +{ + int reglo, reghi; + u32 loaddr = (int)&cpuRegs.LO.UL[ upper ? 2 : 0 ]; + u32 hiaddr = (int)&cpuRegs.HI.UL[ upper ? 2 : 0 ]; + u8 testlive = upper?EEINST_LIVE2:EEINST_LIVE0; + + if( g_pCurInstInfo->regs[XMMGPR_LO] & testlive ) { + if( !upper && (reglo = _allocCheckGPRtoMMX(g_pCurInstInfo, XMMGPR_LO, MODE_WRITE)) >= 0 ) { + u32* ptr = recAllocStackMem(8, 8); + ptr[0] = res & 0xffffffff; + ptr[1] = (res&0x80000000)?0xffffffff:0; + MOVQMtoR(reglo, (u32)ptr); + } + else { + reglo = _allocCheckGPRtoXMM(g_pCurInstInfo, XMMGPR_LO, MODE_WRITE|MODE_READ); + + if( reglo >= 0 ) { + u32* ptr = recAllocStackMem(8, 8); + ptr[0] = res & 0xffffffff; + ptr[1] = (res&0x80000000)?0xffffffff:0; + if( upper ) SSE_MOVHPS_M64_to_XMM(reglo, (u32)ptr); + else SSE_MOVLPS_M64_to_XMM(reglo, (u32)ptr); + } + else { + MOV32ItoM(loaddr, res & 0xffffffff); + MOV32ItoM(loaddr+4, (res&0x80000000)?0xffffffff:0); + } + } + } + + if( g_pCurInstInfo->regs[XMMGPR_HI] & testlive ) { + + if( !upper && (reghi = _allocCheckGPRtoMMX(g_pCurInstInfo, XMMGPR_HI, MODE_WRITE)) >= 0 ) { + u32* ptr = recAllocStackMem(8, 8); + ptr[0] = res >> 32; + ptr[1] = (res>>63)?0xffffffff:0; + MOVQMtoR(reghi, (u32)ptr); + } + else { + reghi = _allocCheckGPRtoXMM(g_pCurInstInfo, XMMGPR_HI, MODE_WRITE|MODE_READ); + + if( reghi >= 0 ) { + u32* ptr = recAllocStackMem(8, 8); + ptr[0] = res >> 32; + ptr[1] = (res>>63)?0xffffffff:0; + if( upper ) SSE_MOVHPS_M64_to_XMM(reghi, (u32)ptr); + else SSE_MOVLPS_M64_to_XMM(reghi, (u32)ptr); + } + else { + _deleteEEreg(XMMGPR_HI, 0); + MOV32ItoM(hiaddr, res >> 32); + MOV32ItoM(hiaddr+4, (res>>63)?0xffffffff:0); + } + } + } + + if (!writed || !_Rd_) return; + g_cpuConstRegs[_Rd_].UD[0] = (s32)(res & 0xffffffff); //that is the difference +} + +//// MULT +void recMULT_const() +{ + s64 res = (s64)g_cpuConstRegs[_Rs_].SL[0] * (s64)g_cpuConstRegs[_Rt_].SL[0]; + + recWritebackConstHILO(res, 1, 0); +} + +void recMULTUsuper(int info, int upper, int process); +void recMULTsuper(int info, int upper, int process) +{ + assert( !(info&PROCESS_EE_MMX) ); + if( _Rd_ ) EEINST_SETSIGNEXT(_Rd_); + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + + if( process & PROCESS_CONSTS ) { + MOV32ItoR( EAX, g_cpuConstRegs[_Rs_].UL[0] ); + IMUL32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + } + else if( process & PROCESS_CONSTT) { + MOV32ItoR( EAX, g_cpuConstRegs[_Rt_].UL[0] ); + IMUL32M( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + IMUL32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + } + + recWritebackHILO(info, 1, upper); +} + +//void recMULT_process(int info, int process) +//{ +// if( EEINST_ISLIVE64(XMMGPR_HI) || !(info&PROCESS_EE_MMX) ) { +// recMULTsuper(info, 0, process); +// } +// else { +// // EEREC_D isn't set +// int mmregd; +// +// if( _Rd_ ) { +// assert(EEREC_D == 0); +// mmregd = _checkMMXreg(MMX_GPR+_Rd_, MODE_WRITE); +// +// if( mmregd < 0 ) { +// if( !(process&PROCESS_CONSTS) && ((g_pCurInstInfo->regs[_Rs_]&EEINST_LASTUSE)||!EEINST_ISLIVE64(_Rs_)) ) { +// _freeMMXreg(EEREC_S); +// _deleteGPRtoXMMreg(_Rd_, 2); +// mmxregs[EEREC_S].inuse = 1; +// mmxregs[EEREC_S].reg = _Rd_; +// mmxregs[EEREC_S].mode = MODE_WRITE; +// mmregd = EEREC_S; +// } +// else if( !(process&PROCESS_CONSTT) && ((g_pCurInstInfo->regs[_Rt_]&EEINST_LASTUSE)||!EEINST_ISLIVE64(_Rt_)) ) { +// _freeMMXreg(EEREC_T); +// _deleteGPRtoXMMreg(_Rd_, 2); +// mmxregs[EEREC_T].inuse = 1; +// mmxregs[EEREC_T].reg = _Rd_; +// mmxregs[EEREC_T].mode = MODE_WRITE; +// mmregd = EEREC_T; +// } +// else mmregd = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); +// } +// +// info |= PROCESS_EE_SET_D(mmregd); +// } +// recMULTUsuper(info, 0, process); +// } +// +// // sometimes _Rd_ can be const +// if( _Rd_ ) _eeOnWriteReg(_Rd_, 1); +//} + +void recMULT_(int info) +{ + //recMULT_process(info, 0); + if( (g_pCurInstInfo->regs[XMMGPR_HI]&EEINST_LIVE2) || !(info&PROCESS_EE_MMX) ) { + recMULTsuper(info, 0, 0); + } + else recMULTUsuper(info, 0, 0); +} + +void recMULT_consts(int info) +{ + //recMULT_process(info, PROCESS_CONSTS); + if( (g_pCurInstInfo->regs[XMMGPR_HI]&EEINST_LIVE2) || !(info&PROCESS_EE_MMX) ) { + recMULTsuper(info, 0, PROCESS_CONSTS); + } + else recMULTUsuper(info, 0, PROCESS_CONSTS); +} + +void recMULT_constt(int info) +{ + //recMULT_process(info, PROCESS_CONSTT); + if( (g_pCurInstInfo->regs[XMMGPR_HI]&EEINST_LIVE2) || !(info&PROCESS_EE_MMX) ) { + recMULTsuper(info, 0, PROCESS_CONSTT); + } + else recMULTUsuper(info, 0, PROCESS_CONSTT); +} + +// don't set XMMINFO_WRITED|XMMINFO_WRITELO|XMMINFO_WRITEHI +EERECOMPILE_CODE0(MULT, XMMINFO_READS|XMMINFO_READT|(_Rd_?XMMINFO_WRITED:0) ); + +//// MULTU +void recMULTU_const() +{ + u64 res = (u64)g_cpuConstRegs[_Rs_].UL[0] * (u64)g_cpuConstRegs[_Rt_].UL[0]; + + recWritebackConstHILO(res, 1, 0); +} + +void recMULTUsuper(int info, int upper, int process) +{ + if( _Rd_ ) EEINST_SETSIGNEXT(_Rd_); + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + + if( (info & PROCESS_EE_MMX) ) { + + if( !_Rd_ ) { + // need some temp reg + int t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + assert( EEREC_D == 0 ); + info |= PROCESS_EE_SET_D(t0reg); + } + + if( process & PROCESS_CONSTS ) { + u32* ptempmem = _eeGetConstReg(_Rs_); + if( EEREC_D != EEREC_T ) MOVQRtoR(EEREC_D, EEREC_T); + PMULUDQMtoR(EEREC_D, (u32)ptempmem); + } + else if( process & PROCESS_CONSTT ) { + u32* ptempmem = _eeGetConstReg(_Rt_); + if( EEREC_D != EEREC_S ) MOVQRtoR(EEREC_D, EEREC_S); + PMULUDQMtoR(EEREC_D, (u32)ptempmem); + } + else { + if( EEREC_D == EEREC_S ) PMULUDQRtoR(EEREC_D, EEREC_T); + else if( EEREC_D == EEREC_T ) PMULUDQRtoR(EEREC_D, EEREC_S); + else { + MOVQRtoR(EEREC_D, EEREC_S); + PMULUDQRtoR(EEREC_D, EEREC_T); + } + } + + recWritebackHILOMMX(info, EEREC_D, 1, upper); + + if( !_Rd_ ) _freeMMXreg(EEREC_D); + return; + } + + if( info & PROCESS_EE_MMX ) { + if( info & PROCESS_EE_MODEWRITES ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rs_].UL[0], EEREC_S); + if( mmxregs[EEREC_S].reg == MMX_GPR+_Rs_ ) mmxregs[EEREC_S].mode &= ~MODE_WRITE; + } + if( info & PROCESS_EE_MODEWRITET ) { + MOVQRtoM((u32)&cpuRegs.GPR.r[_Rt_].UL[0], EEREC_T); + if( mmxregs[EEREC_T].reg == MMX_GPR+_Rt_ ) mmxregs[EEREC_T].mode &= ~MODE_WRITE; + } + _deleteMMXreg(MMX_GPR+_Rd_, 0); + } + + if( process & PROCESS_CONSTS ) { + MOV32ItoR( EAX, g_cpuConstRegs[_Rs_].UL[0] ); + + if( info & PROCESS_EE_MMX ) { + MOVD32MMXtoR(EDX, EEREC_T); + MUL32R(EDX); + } + else + MUL32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + } + else if( process & PROCESS_CONSTT) { + MOV32ItoR( EAX, g_cpuConstRegs[_Rt_].UL[0] ); + + if( info & PROCESS_EE_MMX ) { + MOVD32MMXtoR(EDX, EEREC_S); + MUL32R(EDX); + } + else + MUL32M( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + } + else { + if( info & PROCESS_EE_MMX ) { + MOVD32MMXtoR(EAX, EEREC_S); + MOVD32MMXtoR(EDX, EEREC_T); + MUL32R(EDX); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + MUL32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + } + } + + recWritebackHILO(info, 1, upper); +} + +void recMULTU_(int info) +{ + recMULTUsuper(info, 0, 0); +} + +void recMULTU_consts(int info) +{ + recMULTUsuper(info, 0, PROCESS_CONSTS); +} + +void recMULTU_constt(int info) +{ + recMULTUsuper(info, 0, PROCESS_CONSTT); +} + +// don't specify XMMINFO_WRITELO or XMMINFO_WRITEHI, that is taken care of +EERECOMPILE_CODE0(MULTU, XMMINFO_READS|XMMINFO_READT|(_Rd_?XMMINFO_WRITED:0)); + +//////////////////////////////////////////////////// +void recMULT1_const() +{ + u64 res = (u64)g_cpuConstRegs[_Rs_].UL[0] * (u64)g_cpuConstRegs[_Rt_].UL[0]; + + recWritebackConstHILO(res, 1, 1); +} + +void recMULT1_(int info) +{ + if( (g_pCurInstInfo->regs[XMMGPR_HI]&EEINST_LIVE2) || !(info&PROCESS_EE_MMX) ) { + recMULTsuper(info, 1, 0); + } + else recMULTUsuper(info, 1, 0); +} + +void recMULT1_consts(int info) +{ + if( (g_pCurInstInfo->regs[XMMGPR_HI]&EEINST_LIVE2) || !(info&PROCESS_EE_MMX) ) { + recMULTsuper(info, 1, PROCESS_CONSTS); + } + else recMULTUsuper(info, 1, PROCESS_CONSTS); +} + +void recMULT1_constt(int info) +{ + if( (g_pCurInstInfo->regs[XMMGPR_HI]&EEINST_LIVE2) || !(info&PROCESS_EE_MMX) ) { + recMULTsuper(info, 1, PROCESS_CONSTT); + } + else recMULTUsuper(info, 1, PROCESS_CONSTT); +} + +EERECOMPILE_CODE0(MULT1, XMMINFO_READS|XMMINFO_READT|(_Rd_?XMMINFO_WRITED:0) ); + +//////////////////////////////////////////////////// +void recMULTU1_const() +{ + u64 res = (u64)g_cpuConstRegs[_Rs_].UL[0] * (u64)g_cpuConstRegs[_Rt_].UL[0]; + + recWritebackConstHILO(res, 1, 1); +} + +void recMULTU1_(int info) +{ + recMULTUsuper(info, 1, 0); +} + +void recMULTU1_consts(int info) +{ + recMULTUsuper(info, 1, PROCESS_CONSTS); +} + +void recMULTU1_constt(int info) +{ + recMULTUsuper(info, 1, PROCESS_CONSTT); +} + +EERECOMPILE_CODE0(MULTU1, XMMINFO_READS|XMMINFO_READT|(_Rd_?XMMINFO_WRITED:0)); + +//// DIV +void recDIV_const() +{ + if (g_cpuConstRegs[_Rt_].SL[0] != 0) { + s32 quot = g_cpuConstRegs[_Rs_].SL[0] / g_cpuConstRegs[_Rt_].SL[0]; + s32 rem = g_cpuConstRegs[_Rs_].SL[0] % g_cpuConstRegs[_Rt_].SL[0]; + + recWritebackConstHILO((u64)quot|((u64)rem<<32), 0, 0); + } +} + +void recDIVsuper(int info, int sign, int upper, int process) +{ + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + + if( process & PROCESS_CONSTT ) { + if( !g_cpuConstRegs[_Rt_].UL[0] ) + return; + MOV32ItoR( ECX, g_cpuConstRegs[_Rt_].UL[0] ); + } + else { + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + + OR32RtoR( ECX, ECX ); + j8Ptr[ 0 ] = JE8( 0 ); + } + + if( process & PROCESS_CONSTS ) + MOV32ItoR( EAX, g_cpuConstRegs[_Rs_].UL[0] ); + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + } + + if( sign ) { + CDQ(); + IDIV32R( ECX ); + } + else { + XOR32RtoR( EDX, EDX ); + DIV32R( ECX ); + } + if( !(process & PROCESS_CONSTT) ) x86SetJ8( j8Ptr[ 0 ] ); + + // need to execute regardless of bad divide + recWritebackHILO(info, 0, upper); +} + +void recDIV_(int info) +{ + recDIVsuper(info, 1, 0, 0); +} + +void recDIV_consts(int info) +{ + recDIVsuper(info, 1, 0, PROCESS_CONSTS); +} + +void recDIV_constt(int info) +{ + recDIVsuper(info, 1, 0, PROCESS_CONSTT); +} + +EERECOMPILE_CODE0(DIV, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITELO|XMMINFO_WRITEHI); + +//// DIVU +void recDIVU_const() +{ + if (g_cpuConstRegs[_Rt_].UL[0] != 0) { + u32 quot = g_cpuConstRegs[_Rs_].UL[0] / g_cpuConstRegs[_Rt_].UL[0]; + u32 rem = g_cpuConstRegs[_Rs_].UL[0] % g_cpuConstRegs[_Rt_].UL[0]; + + recWritebackConstHILO((u64)quot|((u64)rem<<32), 0, 0); + } +} + +void recDIVU_(int info) +{ + recDIVsuper(info, 0, 0, 0); +} + +void recDIVU_consts(int info) +{ + recDIVsuper(info, 0, 0, PROCESS_CONSTS); +} + +void recDIVU_constt(int info) +{ + recDIVsuper(info, 0, 0, PROCESS_CONSTT); +} + +EERECOMPILE_CODE0(DIVU, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITELO|XMMINFO_WRITEHI); + +void recDIV1_const() +{ + if (g_cpuConstRegs[_Rt_].SL[0] != 0) { + s32 quot = g_cpuConstRegs[_Rs_].SL[0] / g_cpuConstRegs[_Rt_].SL[0]; + s32 rem = g_cpuConstRegs[_Rs_].SL[0] % g_cpuConstRegs[_Rt_].SL[0]; + + recWritebackConstHILO((u64)quot|((u64)rem<<32), 0, 1); + } +} + +void recDIV1_(int info) +{ + recDIVsuper(info, 1, 1, 0); +} + +void recDIV1_consts(int info) +{ + recDIVsuper(info, 1, 1, PROCESS_CONSTS); +} + +void recDIV1_constt(int info) +{ + recDIVsuper(info, 1, 1, PROCESS_CONSTT); +} + +EERECOMPILE_CODE0(DIV1, XMMINFO_READS|XMMINFO_READT); + +void recDIVU1_const() +{ + if (g_cpuConstRegs[_Rt_].UL[0] != 0) { + u32 quot = g_cpuConstRegs[_Rs_].UL[0] / g_cpuConstRegs[_Rt_].UL[0]; + u32 rem = g_cpuConstRegs[_Rs_].UL[0] % g_cpuConstRegs[_Rt_].UL[0]; + + recWritebackConstHILO((u64)quot|((u64)rem<<32), 0, 1); + } +} + +void recDIVU1_(int info) +{ + recDIVsuper(info, 0, 1, 0); +} + +void recDIVU1_consts(int info) +{ + recDIVsuper(info, 0, 1, PROCESS_CONSTS); +} + +void recDIVU1_constt(int info) +{ + recDIVsuper(info, 0, 1, PROCESS_CONSTT); +} + +EERECOMPILE_CODE0(DIVU1, XMMINFO_READS|XMMINFO_READT); + +//do EEINST_SETSIGNEXT +REC_FUNC_DEL( MADD, _Rd_ ); + +static PCSX2_ALIGNED16(u32 s_MaddMask[]) = { 0x80000000, 0, 0x80000000, 0 }; + +void recMADDU() +{ + _eeOnWriteReg(_Rd_, 1); + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + + if( GPR_IS_CONST2(_Rs_, _Rt_) ) { + u64 result = ((u64)g_cpuConstRegs[_Rs_].UL[0] * (u64)g_cpuConstRegs[_Rt_].UL[0]); + _deleteEEreg(XMMGPR_LO, 1); + _deleteEEreg(XMMGPR_HI, 1); + + // dadd + MOV32MtoR( EAX, (int)&cpuRegs.LO.UL[ 0 ] ); + MOV32MtoR( ECX, (int)&cpuRegs.HI.UL[ 0 ] ); + ADD32ItoR( EAX, (u32)result&0xffffffff ); + ADC32ItoR( ECX, (u32)(result>>32) ); + CDQ(); + if( _Rd_) { + _deleteEEreg(_Rd_, 0); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + + MOV32RtoM( (int)&cpuRegs.LO.UL[0], EAX ); + MOV32RtoM( (int)&cpuRegs.LO.UL[1], EDX ); + + MOV32RtoM( (int)&cpuRegs.HI.UL[0], ECX ); + MOV32RtoR(EAX, ECX); + CDQ(); + MOV32RtoM( (int)&cpuRegs.HI.UL[1], EDX ); + return; + } + + _deleteEEreg(XMMGPR_LO, 1); + _deleteEEreg(XMMGPR_HI, 1); + _deleteGPRtoXMMreg(_Rs_, 1); + _deleteGPRtoXMMreg(_Rt_, 1); + _deleteMMXreg(MMX_GPR+_Rs_, 1); + _deleteMMXreg(MMX_GPR+_Rt_, 1); + + if( GPR_IS_CONST1(_Rs_) ) { + MOV32ItoR( EAX, g_cpuConstRegs[_Rs_].UL[0] ); + IMUL32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + } + else if ( GPR_IS_CONST1(_Rt_) ) { + MOV32ItoR( EAX, g_cpuConstRegs[_Rt_].UL[0] ); + IMUL32M( (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + IMUL32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + } + + MOV32RtoR( ECX, EDX ); + ADD32MtoR( EAX, (u32)&cpuRegs.LO.UL[0] ); + ADC32MtoR( ECX, (u32)&cpuRegs.HI.UL[0] ); + CDQ(); + if( _Rd_ ) { + _deleteEEreg(_Rd_, 0); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + + MOV32RtoM( (int)&cpuRegs.LO.UL[0], EAX ); + MOV32RtoM( (int)&cpuRegs.LO.UL[1], EDX ); + + MOV32RtoM( (int)&cpuRegs.HI.UL[0], ECX ); + MOV32RtoR(EAX, ECX); + CDQ(); + MOV32RtoM( (int)&cpuRegs.HI.UL[1], EDX ); +} + +void recMADD1() +{ + //SysPrintf("MADD1 email zero if abnormal behavior\n"); + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + if( _Rd_ ) EEINST_SETSIGNEXT(_Rd_); + _deleteEEreg(XMMGPR_LO, 0); + _deleteEEreg(XMMGPR_HI, 0); + recCall( Interp::MADD1, _Rd_ ); +} + +void recMADDU1() +{ + //SysPrintf("MADDU1 email zero if abnormal behavior\n"); + EEINST_SETSIGNEXT(_Rs_); + EEINST_SETSIGNEXT(_Rt_); + if( _Rd_ ) EEINST_SETSIGNEXT(_Rd_); + _deleteEEreg(XMMGPR_LO, 0); + _deleteEEreg(XMMGPR_HI, 0); + recCall( Interp::MADDU1, _Rd_ ); +} + +#else + +//////////////////////////////////////////////////// +void recMULT( void ) +{ + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + IMUL32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + + MOV32RtoR( ECX, EDX ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.LO.UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.LO.UL[ 1 ], EDX ); + if ( _Rd_ ) + { + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + MOV32RtoR( EAX, ECX ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.HI.UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.HI.UL[ 1 ], EDX ); + +} + +//////////////////////////////////////////////////// +void recMULTU( void ) +{ + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + MUL32M( (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + + MOV32RtoR( ECX, EDX ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.LO.UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.LO.UL[ 1 ], EDX ); + if ( _Rd_ != 0 ) + { + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + MOV32RtoR( EAX, ECX ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.HI.UL[ 0 ], ECX ); + MOV32RtoM( (int)&cpuRegs.HI.UL[ 1 ], EDX ); + +} + +//////////////////////////////////////////////////// +void recDIV( void ) +{ + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + OR32RtoR( ECX, ECX ); + j8Ptr[ 0 ] = JE8( 0 ); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); +// XOR32RtoR( EDX,EDX ); + CDQ(); + IDIV32R( ECX ); + + MOV32RtoR( ECX, EDX ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.LO.UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.LO.UL[ 1 ], EDX ); + + MOV32RtoR( EAX, ECX ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.HI.UL[ 0 ], ECX ); + MOV32RtoM( (int)&cpuRegs.HI.UL[ 1 ], EDX ); + + x86SetJ8( j8Ptr[ 0 ] ); + +} + +//////////////////////////////////////////////////// +void recDIVU( void ) +{ + + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + OR32RtoR( ECX, ECX ); + j8Ptr[ 0 ] = JE8( 0 ); + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + XOR32RtoR( EDX, EDX ); + // CDQ(); + DIV32R( ECX ); + + MOV32RtoR( ECX, EDX ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.LO.UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.LO.UL[ 1 ], EDX ); + + MOV32RtoR( EAX,ECX ); + CDQ( ); + MOV32RtoM( (int)&cpuRegs.HI.UL[ 0 ], ECX ); + MOV32RtoM( (int)&cpuRegs.HI.UL[ 1 ], EDX ); + x86SetJ8( j8Ptr[ 0 ] ); + +} + +REC_FUNC_DEL( MULT1, _Rd_ ); +REC_FUNC_DEL( MULTU1, _Rd_ ); +REC_FUNC_DEL( DIV1, _Rd_ ); +REC_FUNC_DEL( DIVU1, _Rd_ ); + +REC_FUNC_DEL( MADD, _Rd_ ); +REC_FUNC_DEL( MADDU, _Rd_ ); +REC_FUNC_DEL( MADD1, _Rd_ ); +REC_FUNC_DEL( MADDU1, _Rd_ ); + +#endif + +} } } diff --git a/pcsx2/x86/ix86-32/iR5900Shift.cpp b/pcsx2/x86/ix86-32/iR5900Shift.cpp new file mode 100644 index 0000000000..780bc0314c --- /dev/null +++ b/pcsx2/x86/ix86-32/iR5900Shift.cpp @@ -0,0 +1,1351 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" + + +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl +{ + +/********************************************************* +* Shift arithmetic with constant shift * +* Format: OP rd, rt, sa * +*********************************************************/ +#ifndef SHIFT_RECOMPILE + +namespace Interp = R5900::Interpreter::OpcodeImpl; + +REC_FUNC(SLL); +REC_FUNC(SRL); +REC_FUNC(SRA); +REC_FUNC(DSLL); +REC_FUNC(DSRL); +REC_FUNC(DSRA); +REC_FUNC(DSLL32); +REC_FUNC(DSRL32); +REC_FUNC(DSRA32); + +REC_FUNC(SLLV); +REC_FUNC(SRLV); +REC_FUNC(SRAV); +REC_FUNC(DSLLV); +REC_FUNC(DSRLV); +REC_FUNC(DSRAV); + +#elif defined(EE_CONST_PROP) + +//// SLL +void recSLL_const() +{ + g_cpuConstRegs[_Rd_].SD[0] = (s32)(g_cpuConstRegs[_Rt_].UL[0] << _Sa_); +} + +void recSLLs_(int info, int sa) +{ + int rtreg, rdreg, t0reg; + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + rtreg = EEREC_T; + rdreg = EEREC_D; + } + else if( g_pCurInstInfo->regs[_Rd_]&EEINST_MMX ) { + _addNeededMMXreg(MMX_GPR+_Rd_); + rtreg = rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + MOVDMtoMMX(rtreg, (u32)&cpuRegs.GPR.r[_Rt_].UL[0]); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( sa != 0 ) + { + SHL32ItoR( EAX, sa ); + } + + if( EEINST_ISLIVE1(_Rd_) ) { + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + } + return; + } + + if( rtreg != rdreg ) MOVQRtoR(rdreg, rtreg); + + if( !EEINST_ISLIVE1(_Rd_) ) { + EEINST_RESETHASLIVE1(_Rd_); + PSLLDItoR(rdreg, sa); + return; + } + + if ( sa != 0 ) { + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + // it is a signed shift + PSLLDItoR(rdreg, sa); + MOVQRtoR(t0reg, rdreg); + PSRADItoR(t0reg, 31); + + // take lower dword of rdreg and lower dword of t0reg + PUNPCKLDQRtoR(rdreg, t0reg); + _freeMMXreg(t0reg); + } + else { + if( EEINST_ISLIVE1(_Rd_) ) _signExtendGPRtoMMX(rdreg, _Rd_, 0); + else EEINST_RESETHASLIVE1(_Rd_); + } +} + +void recSLL_(int info) +{ + recSLLs_(info, _Sa_); + EEINST_SETSIGNEXT(_Rd_); +} + +EERECOMPILE_CODEX(eeRecompileCode2, SLL); + +//// SRL +void recSRL_const() +{ + g_cpuConstRegs[_Rd_].SD[0] = (s32)(g_cpuConstRegs[_Rt_].UL[0] >> _Sa_); +} + +void recSRLs_(int info, int sa) +{ + int rtreg, rdreg; + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + rtreg = EEREC_T; + rdreg = EEREC_D; + } + else if( (g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) || (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + _addNeededMMXreg(MMX_GPR+_Rd_); + rtreg = rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + MOVDMtoMMX(rtreg, (u32)&cpuRegs.GPR.r[_Rt_].UL[0]); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( sa != 0 ) SHR32ItoR( EAX, sa); + + if( EEINST_ISLIVE1(_Rd_) ) { + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + } + return; + } + + if( rtreg != rdreg ) MOVQRtoR(rdreg, rtreg); + + if( !EEINST_ISLIVE1(_Rd_) ) { + EEINST_RESETHASLIVE1(_Rd_); + PSRLDItoR(rdreg, sa); + return; + } + + if ( sa != 0 ) { + // rdreg already sign extended + PSLLQItoR(rdreg, 32); + PSRLQItoR(rdreg, 32+sa); +// t0reg = _allocMMXreg(-1, MMX_TEMP, 0); +// +// // it is a signed shift +// PSRLDItoR(rdreg, sa); +// MOVQRtoR(t0reg, rdreg); +// PSRADItoR(t0reg, 31); +// +// take lower dword of rdreg and lower dword of t0reg +// PUNPCKLDQRtoR(rdreg, t0reg); +// _freeMMXreg(t0reg); + } +} + +void recSRL_(int info) +{ + recSRLs_(info, _Sa_); + EEINST_SETSIGNEXT(_Rd_); +} + +EERECOMPILE_CODEX(eeRecompileCode2, SRL); + +//// SRA +void recSRA_const() +{ + g_cpuConstRegs[_Rd_].SD[0] = (s32)(g_cpuConstRegs[_Rt_].SL[0] >> _Sa_); +} + +void recSRAs_(int info, int sa) +{ + int rtreg, rdreg, t0reg; + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + rtreg = EEREC_T; + rdreg = EEREC_D; + } + else if( (g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) || (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + _addNeededMMXreg(MMX_GPR+_Rd_); + rtreg = rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + MOVDMtoMMX(rtreg, (u32)&cpuRegs.GPR.r[_Rt_].UL[0]); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( sa != 0 ) SAR32ItoR( EAX, sa); + + if( EEINST_ISLIVE1(_Rd_) ) { + CDQ(); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + } + return; + } + + if( rtreg != rdreg ) MOVQRtoR(rdreg, rtreg); + + if( EEINST_ISSIGNEXT(_Rt_) && EEINST_HASLIVE1(_Rt_) ) { + PSRADItoR(rdreg, sa); + return; + } + + if( !EEINST_ISLIVE1(_Rd_) ) { + EEINST_RESETHASLIVE1(_Rd_); + PSRADItoR(rdreg, sa); + return; + } + + if ( sa != 0 ) { + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + // it is a signed shift + PSRADItoR(rdreg, sa); + MOVQRtoR(t0reg, rdreg); + PSRADItoR(rdreg, 31); + + // take lower dword of rdreg and lower dword of t0reg + PUNPCKLDQRtoR(t0reg, rdreg); + + // swap regs + mmxregs[t0reg] = mmxregs[rdreg]; + mmxregs[rdreg].inuse = 0; + } +} + +void recSRA_(int info) +{ + recSRAs_(info, _Sa_); + EEINST_SETSIGNEXT(_Rd_); +} + +EERECOMPILE_CODEX(eeRecompileCode2, SRA); + +//////////////////////////////////////////////////// +void recDSLL_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = (u64)(g_cpuConstRegs[_Rt_].UD[0] << _Sa_); +} + +void recDSLLs_(int info, int sa) +{ + int rtreg, rdreg; + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + rtreg = EEREC_T; + rdreg = EEREC_D; + } + else { + _addNeededMMXreg(MMX_GPR+_Rt_); + _addNeededMMXreg(MMX_GPR+_Rd_); + rtreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ); + rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + } + + if( rtreg != rdreg ) MOVQRtoR(rdreg, rtreg); + PSLLQItoR(rdreg, sa); +} + +void recDSLL_(int info) +{ + recDSLLs_(info, _Sa_); +} + +EERECOMPILE_CODEX(eeRecompileCode2, DSLL); + +//////////////////////////////////////////////////// +void recDSRL_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = (u64)(g_cpuConstRegs[_Rt_].UD[0] >> _Sa_); +} + +void recDSRLs_(int info, int sa) +{ + int rtreg, rdreg; + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + rtreg = EEREC_T; + rdreg = EEREC_D; + } + else { + _addNeededMMXreg(MMX_GPR+_Rt_); + _addNeededMMXreg(MMX_GPR+_Rd_); + rtreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ); + rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + } + + if( rtreg != rdreg ) MOVQRtoR(rdreg, rtreg); + PSRLQItoR(rdreg, sa); +} + +void recDSRL_(int info) +{ + recDSRLs_(info, _Sa_); +} + +EERECOMPILE_CODEX(eeRecompileCode2, DSRL); + +//// DSRA +void recDSRA_const() +{ + g_cpuConstRegs[_Rd_].SD[0] = (u64)(g_cpuConstRegs[_Rt_].SD[0] >> _Sa_); +} + +void recDSRAs_(int info, int sa) +{ + int rtreg, rdreg, t0reg; + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + rtreg = EEREC_T; + rdreg = EEREC_D; + } + else { + _addNeededMMXreg(MMX_GPR+_Rt_); + _addNeededMMXreg(MMX_GPR+_Rd_); + rtreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ); + rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + } + + if( rtreg != rdreg ) MOVQRtoR(rdreg, rtreg); + + if( EEINST_ISSIGNEXT(_Rt_) && EEINST_HASLIVE1(_Rt_) ) { + PSRADItoR(rdreg, sa); + return; + } + + if( !EEINST_ISLIVE1(_Rd_) ) { + EEINST_RESETHASLIVE1(_Rd_); + PSRLQItoR(rdreg, sa); + return; + } + + if ( sa != 0 ) { + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t0reg, rtreg); + + // it is a signed shift + PSRADItoR(t0reg, sa); + PSRLQItoR(rdreg, sa); + + PUNPCKHDQRtoR(t0reg, t0reg); // shift to lower + // take lower dword of rdreg and lower dword of t0reg + PUNPCKLDQRtoR(rdreg, t0reg); + + _freeMMXreg(t0reg); + } +} + +void recDSRA_(int info) +{ + recDSRAs_(info, _Sa_); +} + +EERECOMPILE_CODEX(eeRecompileCode2, DSRA); + +///// DSLL32 +void recDSLL32_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = (u64)(g_cpuConstRegs[_Rt_].UD[0] << (_Sa_+32)); +} + +void recDSLL32s_(int info, int sa) +{ + int rtreg, rdreg; + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + rtreg = EEREC_T; + rdreg = EEREC_D; + } + else if( (g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) || (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + _addNeededMMXreg(MMX_GPR+_Rt_); + _addNeededMMXreg(MMX_GPR+_Rd_); + rtreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ); + rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( sa != 0 ) + { + SHL32ItoR( EAX, sa ); + } + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], 0 ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EAX ); + return; + } + + if( rtreg != rdreg ) MOVQRtoR(rdreg, rtreg); + PSLLQItoR(rdreg, sa+32); +} + +void recDSLL32_(int info) +{ + recDSLL32s_(info, _Sa_); +} + +EERECOMPILE_CODEX(eeRecompileCode2, DSLL32); + +//// DSRL32 +void recDSRL32_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = (u64)(g_cpuConstRegs[_Rt_].UD[0] >> (_Sa_+32)); +} + +void recDSRL32s_(int info, int sa) +{ + int rtreg, rdreg; + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + rtreg = EEREC_T; + rdreg = EEREC_D; + } + else if( (g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) || (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + _addNeededMMXreg(MMX_GPR+_Rt_); + _addNeededMMXreg(MMX_GPR+_Rd_); + rtreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ); + rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + if ( sa != 0 ) SHR32ItoR( EAX, sa ); + + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], 0 ); + else EEINST_RESETHASLIVE1(_Rd_); + return; + } + + if( rtreg != rdreg ) MOVQRtoR(rdreg, rtreg); + PSRLQItoR(rdreg, sa+32); +} + +void recDSRL32_(int info) +{ + recDSRL32s_(info, _Sa_); +} + +EERECOMPILE_CODEX(eeRecompileCode2, DSRL32); + +//// DSRA32 +void recDSRA32_const() +{ + g_cpuConstRegs[_Rd_].SD[0] = (u64)(g_cpuConstRegs[_Rt_].SD[0] >> (_Sa_+32)); +} + +void recDSRA32s_(int info, int sa) +{ + int rtreg, rdreg, t0reg; + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + rtreg = EEREC_T; + rdreg = EEREC_D; + } + else if( (g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) || (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + _addNeededMMXreg(MMX_GPR+_Rt_); + _addNeededMMXreg(MMX_GPR+_Rd_); + rtreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ); + rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + } + else { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + CDQ( ); + if ( sa != 0 ) SAR32ItoR( EAX, sa ); + + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + if( EEINST_ISLIVE1(_Rd_) ) MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + else EEINST_RESETHASLIVE1(_Rd_); + return; + } + + if( rtreg != rdreg ) MOVQRtoR(rdreg, rtreg); + + if( EEINST_ISSIGNEXT(_Rt_) && EEINST_HASLIVE1(_Rt_) ) { + PSRADItoR(rdreg, 31); + return; + } + + if( !EEINST_ISLIVE1(_Rd_) ) { + EEINST_RESETHASLIVE1(_Rd_); + if( sa ) PSRADItoR(rdreg, sa); + PUNPCKHDQRtoR(rdreg, rdreg); + return; + } + + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(t0reg, rtreg); + + // it is a signed shift + if( sa ) { + PSRADItoR(rdreg, sa); + PSRADItoR(t0reg, 31); + + // take higher dword of rdreg and lower dword of t0reg + PUNPCKHDQRtoR(rdreg, t0reg); + _freeMMXreg(t0reg); + } + else { + // better timing + PSRADItoR(rdreg, 31); + + // take higher dword of rdreg and lower dword of t0reg + PUNPCKHDQRtoR(t0reg, rdreg); + + // swap + mmxregs[t0reg] = mmxregs[rdreg]; + mmxregs[rdreg].inuse = 0; + } +} + +void recDSRA32_(int info) +{ + recDSRA32s_(info, _Sa_); +} + +EERECOMPILE_CODEX(eeRecompileCode2, DSRA32); + +/********************************************************* +* Shift arithmetic with variant register shift * +* Format: OP rd, rt, rs * +*********************************************************/ + +PCSX2_ALIGNED16(u32 s_sa[4]) = {0x1f, 0, 0x3f, 0}; + +int recSetShiftV(int info, int* rsreg, int* rtreg, int* rdreg, int* rstemp, int forcemmx, int shift64) +{ + assert( !(info & PROCESS_EE_XMM) ); + + if( info & PROCESS_EE_MMX ) { + *rtreg = EEREC_T; + *rdreg = EEREC_D; + *rsreg = EEREC_S; + + // make sure to take only low 5 bits of *rsreg + if( !(g_pCurInstInfo->regs[_Rs_]&EEINST_LASTUSE) && EEINST_ISLIVE64(_Rs_)) { + *rstemp = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(*rstemp, *rsreg); + *rsreg = *rstemp; + } + else { + if( *rsreg != *rdreg ) { + _freeMMXreg(*rsreg); + mmxregs[*rsreg].inuse = 0; + } + } + + PANDMtoR(*rsreg, (u32)&s_sa[shift64?2:0]); + + if( EEREC_D == EEREC_S ) { + // need to be separate + int mmreg = _allocMMXreg(-1, MMX_TEMP, 0); + *rdreg = mmreg; + mmxregs[mmreg] = mmxregs[EEREC_S]; + mmxregs[EEREC_S].inuse = 0; + } + } + else if( forcemmx || (g_pCurInstInfo->regs[_Rt_]&EEINST_MMX) || (g_pCurInstInfo->regs[_Rd_]&EEINST_MMX) ) { + _addNeededMMXreg(MMX_GPR+_Rt_); + _addNeededMMXreg(MMX_GPR+_Rd_); + *rtreg = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ); + *rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + + *rstemp = _allocMMXreg(-1, MMX_TEMP, 0); + MOV32MtoR(EAX, (u32)&cpuRegs.GPR.r[_Rs_].UL[0]); + AND32ItoR(EAX, shift64?0x3f:0x1f); + MOVD32RtoMMX(*rstemp, EAX); + *rsreg = *rstemp; + } + else { + return 0; + } + + if( *rtreg != *rdreg ) MOVQRtoR(*rdreg, *rtreg); + return 1; +} + +void recSetConstShiftV(int info, int* rsreg, int* rdreg, int* rstemp, int shift64) +{ + if( info & PROCESS_EE_MMX ) { + *rdreg = EEREC_D; + *rsreg = EEREC_S; + + // make sure to take only low 5 bits of *rsreg + if( !(g_pCurInstInfo->regs[_Rs_]&EEINST_LASTUSE) && EEINST_ISLIVE64(_Rs_) ) { + *rstemp = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRtoR(*rstemp, *rsreg); + *rsreg = *rstemp; + } + else { + if( *rsreg != *rdreg ) { + _freeMMXreg(*rsreg); + mmxregs[*rsreg].inuse = 0; + } + } + + PANDMtoR(*rsreg, (u32)&s_sa[shift64?2:0]); + + + if( EEREC_D == EEREC_S ) { + // need to be separate + int mmreg = _allocMMXreg(-1, MMX_TEMP, 0); + *rdreg = mmreg; + mmxregs[mmreg] = mmxregs[EEREC_S]; + mmxregs[EEREC_S].inuse = 0; + } + } + else { + _addNeededMMXreg(MMX_GPR+_Rd_); + *rdreg = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + SetMMXstate(); + + *rstemp = _allocMMXreg(-1, MMX_TEMP, 0); + MOV32MtoR(EAX, (u32)&cpuRegs.GPR.r[_Rs_].UL[0]); + AND32ItoR(EAX, shift64?0x3f:0x1f); + MOVD32RtoMMX(*rstemp, EAX); + *rsreg = *rstemp; + } + + _flushConstReg(_Rt_); +} + +void recMoveSignToRd(int info) +{ + if( EEINST_ISLIVE1(_Rd_) ) { + CDQ(); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + } + else { + EEINST_RESETHASLIVE1(_Rd_); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + } + + if( info & PROCESS_EE_MMX ) { + mmxregs[EEREC_D].inuse = 0; + } +} + +//// SLLV +void recSLLV_const() +{ + g_cpuConstRegs[_Rd_].SD[0] = (s32)(g_cpuConstRegs[_Rt_].UL[0] << (g_cpuConstRegs[_Rs_].UL[0] &0x1f)); +} + +void recSLLV_consts(int info) +{ + recSLLs_(info, g_cpuConstRegs[_Rs_].UL[0]&0x1f); + EEINST_SETSIGNEXT(_Rd_); +} + +void recSLLV_constt(int info) +{ + if( (info & PROCESS_EE_MMX) && (info & PROCESS_EE_MODEWRITES) ) MOVD32MMXtoR(ECX, EEREC_S); + else MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + + MOV32ItoR( EAX, g_cpuConstRegs[_Rt_].UL[0] ); + AND32ItoR( ECX, 0x1f ); + SHL32CLtoR( EAX ); + + recMoveSignToRd(info); + EEINST_SETSIGNEXT(_Rd_); +} + +void recSLLV_(int info) +{ + int rsreg, rtreg, rdreg, rstemp = -1, t0reg; + EEINST_SETSIGNEXT(_Rd_); + + if( recSetShiftV(info, &rsreg, &rtreg, &rdreg, &rstemp, 0, 0) == 0 ) { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( _Rs_ != 0 ) + { + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + AND32ItoR( ECX, 0x1f ); + SHL32CLtoR( EAX ); + } + CDQ(); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + return; + } + + if( !EEINST_ISLIVE1(_Rd_) ) { + EEINST_RESETHASLIVE1(_Rd_); + PSLLDRtoR(rdreg, rsreg); + if( rstemp != -1 ) _freeMMXreg(rstemp); + return; + } + + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + // it is a signed shift + PSLLDRtoR(rdreg, rsreg); + MOVQRtoR(t0reg, rdreg); + PSRADItoR(t0reg, 31); + + // take lower dword of rdreg and lower dword of t0reg + PUNPCKLDQRtoR(rdreg, t0reg); + _freeMMXreg(t0reg); + if( rstemp != -1 ) _freeMMXreg(rstemp); +} + +EERECOMPILE_CODE0(SLLV, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// SRLV +void recSRLV_const() +{ + g_cpuConstRegs[_Rd_].SD[0] = (s32)(g_cpuConstRegs[_Rt_].UL[0] >> (g_cpuConstRegs[_Rs_].UL[0] &0x1f)); +} + +void recSRLV_consts(int info) +{ + recSRLs_(info, g_cpuConstRegs[_Rs_].UL[0]&0x1f); + EEINST_SETSIGNEXT(_Rd_); +} + +void recSRLV_constt(int info) +{ + if( (info & PROCESS_EE_MMX) && (info&PROCESS_EE_MODEWRITES) ) MOVD32MMXtoR(ECX, EEREC_S); + else MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + + MOV32ItoR( EAX, g_cpuConstRegs[_Rt_].UL[0] ); + AND32ItoR( ECX, 0x1f ); + SHR32CLtoR( EAX ); + + recMoveSignToRd(info); + EEINST_SETSIGNEXT(_Rd_); +} + +void recSRLV_(int info) +{ + int rsreg, rtreg, rdreg, rstemp = -1, t0reg; + EEINST_SETSIGNEXT(_Rd_); + + if( recSetShiftV(info, &rsreg, &rtreg, &rdreg, &rstemp, 0, 0) == 0 ) { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( _Rs_ != 0 ) + { + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + AND32ItoR( ECX, 0x1f ); + SHR32CLtoR( EAX ); + } + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + return; + } + + if( !EEINST_ISLIVE1(_Rd_) ) { + EEINST_RESETHASLIVE1(_Rd_); + PSRLDRtoR(rdreg, rsreg); + if( rstemp != -1 ) _freeMMXreg(rstemp); + return; + } + + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + // it is a signed shift + PSRLDRtoR(rdreg, rsreg); + MOVQRtoR(t0reg, rdreg); + PSRADItoR(t0reg, 31); + + // take lower dword of rdreg and lower dword of t0reg + PUNPCKLDQRtoR(rdreg, t0reg); + _freeMMXreg(t0reg); + if( rstemp != -1 ) _freeMMXreg(rstemp); +} + +EERECOMPILE_CODE0(SRLV, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// SRAV +void recSRAV_const() +{ + g_cpuConstRegs[_Rd_].SD[0] = (s32)(g_cpuConstRegs[_Rt_].SL[0] >> (g_cpuConstRegs[_Rs_].UL[0] &0x1f)); +} + +void recSRAV_consts(int info) +{ + recSRAs_(info, g_cpuConstRegs[_Rs_].UL[0]&0x1f); + EEINST_SETSIGNEXT(_Rd_); +} + +void recSRAV_constt(int info) +{ + if( (info & PROCESS_EE_MMX) && (info&PROCESS_EE_MODEWRITES) ) MOVD32MMXtoR(ECX, EEREC_S); + else MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + + MOV32ItoR( EAX, g_cpuConstRegs[_Rt_].UL[0] ); + AND32ItoR( ECX, 0x1f ); + SAR32CLtoR( EAX ); + + recMoveSignToRd(info); + EEINST_SETSIGNEXT(_Rd_); +} + +void recSRAV_(int info) +{ + int rsreg, rtreg, rdreg, rstemp = -1, t0reg; + EEINST_SETSIGNEXT(_Rd_); + + if( recSetShiftV(info, &rsreg, &rtreg, &rdreg, &rstemp, 0, 0) == 0 ) { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( _Rs_ != 0 ) + { + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + AND32ItoR( ECX, 0x1f ); + SAR32CLtoR( EAX ); + } + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + return; + } + + if( !EEINST_ISLIVE1(_Rd_) ) { + EEINST_RESETHASLIVE1(_Rd_); + PSRADRtoR(rdreg, rsreg); + if( rstemp != -1 ) _freeMMXreg(rstemp); + return; + } + + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + + // it is a signed shift + PSRADRtoR(rdreg, rsreg); + MOVQRtoR(t0reg, rdreg); + PSRADItoR(t0reg, 31); + + // take lower dword of rdreg and lower dword of t0reg + PUNPCKLDQRtoR(rdreg, t0reg); + _freeMMXreg(t0reg); + if( rstemp != -1 ) _freeMMXreg(rstemp); +} + +EERECOMPILE_CODE0(SRAV, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// DSLLV +void recDSLLV_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = (u64)(g_cpuConstRegs[_Rt_].UD[0] << (g_cpuConstRegs[_Rs_].UL[0] &0x3f)); +} + +void recDSLLV_consts(int info) +{ + int sa = g_cpuConstRegs[_Rs_].UL[0]&0x3f; + if( sa < 32 ) recDSLLs_(info, sa); + else recDSLL32s_(info, sa-32); +} + +void recDSLLV_constt(int info) +{ + int rsreg, rdreg, rstemp = -1; + recSetConstShiftV(info, &rsreg, &rdreg, &rstemp, 1); + + MOVQMtoR(rdreg, (u32)&cpuRegs.GPR.r[_Rt_]); + PSLLQRtoR(rdreg, rsreg); + if( rstemp != -1 ) _freeMMXreg(rstemp); +} + +void recDSLLV_(int info) +{ + int rsreg, rtreg, rdreg, rstemp = -1; + recSetShiftV(info, &rsreg, &rtreg, &rdreg, &rstemp, 1, 1); + + PSLLQRtoR(rdreg, rsreg); + if( rstemp != -1 ) _freeMMXreg(rstemp); +} + +EERECOMPILE_CODE0(DSLLV, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// DSRLV +void recDSRLV_const() +{ + g_cpuConstRegs[_Rd_].UD[0] = (u64)(g_cpuConstRegs[_Rt_].UD[0] >> (g_cpuConstRegs[_Rs_].UL[0] &0x3f)); +} + +void recDSRLV_consts(int info) +{ + int sa = g_cpuConstRegs[_Rs_].UL[0]&0x3f; + if( sa < 32 ) recDSRLs_(info, sa); + else recDSRL32s_(info, sa-32); +} + +void recDSRLV_constt(int info) +{ + int rsreg, rdreg, rstemp = -1; + recSetConstShiftV(info, &rsreg, &rdreg, &rstemp, 1); + + MOVQMtoR(rdreg, (u32)&cpuRegs.GPR.r[_Rt_]); + PSRLQRtoR(rdreg, rsreg); + if( rstemp != -1 ) _freeMMXreg(rstemp); +} + +void recDSRLV_(int info) +{ + int rsreg, rtreg, rdreg, rstemp = -1; + recSetShiftV(info, &rsreg, &rtreg, &rdreg, &rstemp, 1, 1); + + PSRLQRtoR(rdreg, rsreg); + if( rstemp != -1 ) _freeMMXreg(rstemp); +} + +EERECOMPILE_CODE0(DSRLV, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +//// DSRAV +void recDSRAV_const() +{ + g_cpuConstRegs[_Rd_].SD[0] = (s64)(g_cpuConstRegs[_Rt_].SD[0] >> (g_cpuConstRegs[_Rs_].UL[0] &0x3f)); +} + +void recDSRAV_consts(int info) +{ + int sa = g_cpuConstRegs[_Rs_].UL[0]&0x3f; + if( sa < 32 ) recDSRAs_(info, sa); + else recDSRA32s_(info, sa-32); +} + +void recDSRAV_constt(int info) +{ + int rsreg, rdreg, rstemp = -1, t0reg, t1reg; + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + t1reg = _allocMMXreg(-1, MMX_TEMP, 0); + + recSetConstShiftV(info, &rsreg, &rdreg, &rstemp, 1); + + MOVQMtoR(rdreg, (u32)&cpuRegs.GPR.r[_Rt_]); + PXORRtoR(t0reg, t0reg); + + // calc high bit + MOVQRtoR(t1reg, rdreg); + PCMPGTDRtoR(t0reg, rdreg); + PUNPCKHDQRtoR(t0reg, t0reg); // shift to lower + + // shift highest bit, 64 - eax + MOV32ItoR(EAX, 64); + MOVD32RtoMMX(t1reg, EAX); + PSUBDRtoR(t1reg, rsreg); + + // right logical shift + PSRLQRtoR(rdreg, rsreg); + PSLLQRtoR(t0reg, t1reg); // highest bits + + PORRtoR(rdreg, t0reg); + + _freeMMXreg(t0reg); + _freeMMXreg(t1reg); + if( rstemp != -1 ) _freeMMXreg(rstemp); +} + +void recDSRAV_(int info) +{ + int rsreg, rtreg, rdreg, rstemp = -1, t0reg, t1reg; + t0reg = _allocMMXreg(-1, MMX_TEMP, 0); + t1reg = _allocMMXreg(-1, MMX_TEMP, 0); + recSetShiftV(info, &rsreg, &rtreg, &rdreg, &rstemp, 1, 1); + + PXORRtoR(t0reg, t0reg); + + // calc high bit + MOVQRtoR(t1reg, rdreg); + PCMPGTDRtoR(t0reg, rdreg); + PUNPCKHDQRtoR(t0reg, t0reg); // shift to lower + + // shift highest bit, 64 - eax + MOV32ItoR(EAX, 64); + MOVD32RtoMMX(t1reg, EAX); + PSUBDRtoR(t1reg, rsreg); + + // right logical shift + PSRLQRtoR(rdreg, rsreg); + PSLLQRtoR(t0reg, t1reg); // highest bits + + PORRtoR(rdreg, t0reg); + + _freeMMXreg(t0reg); + _freeMMXreg(t1reg); + if( rstemp != -1 ) _freeMMXreg(rstemp); +} + +EERECOMPILE_CODE0(DSRAV, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED); + +#else + +//////////////////////////////////////////////////// +void recDSRA( void ) +{ + if( !_Rd_ ) return; //? + + if ( _Sa_ != 0 ) { + // it is a signed shift + MOVQMtoR(MM0, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + MOVQRtoR(MM1, MM0); + PSRADItoR(MM0, _Sa_); + PSRLQItoR(MM1, _Sa_); + + PUNPCKHDQRtoR(MM0, MM0); // shift to lower + // take lower dword of MM1 and lower dword of MM0 + + PUNPCKLDQRtoR(MM1, MM0); + + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], MM1); + } + else { + MOVQMtoR(MM0, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], MM0); + } + + SetMMXstate(); +} + +//////////////////////////////////////////////////// +void recDSRA32(void) +{ + if( !_Rd_ ) return; //? + + MOV32MtoR(EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + CDQ(); + + if ( _Sa_ != 0 ) + { + SAR32ItoR( EAX, _Sa_ ); + } + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX); + MOV32RtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX); +} + +//////////////////////////////////////////////////// +void recSLL( void ) +{ + if ( ! _Rd_ ) + return; + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( _Sa_ != 0 ) + { + SHL32ItoR( EAX, _Sa_ ); + } + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); +} + +//////////////////////////////////////////////////// +void recSRL( void ) +{ + if ( ! _Rd_ ) + { + return; + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( _Sa_ != 0 ) + { + SHR32ItoR( EAX, _Sa_); + } + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + +} + +//////////////////////////////////////////////////// +void recSRA( void ) +{ + if ( ! _Rd_ ) + { + return; + } + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( _Sa_ != 0 ) + { + SAR32ItoR( EAX, _Sa_); + } + CDQ(); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + +} + +//////////////////////////////////////////////////// +void recDSLL( void ) +{ + if ( ! _Rd_ ) + { + return; + } + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + if ( _Sa_ != 0 ) + { + PSLLQItoR( MM0, _Sa_ ); + } + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + +} + +//////////////////////////////////////////////////// +void recDSRL( void ) +{ + if ( ! _Rd_ ) + { + return; + } + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + if ( _Sa_ != 0 ) + { + PSRLQItoR( MM0, _Sa_ ); + } + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); +} + +//////////////////////////////////////////////////// +void recDSLL32( void ) +{ + if ( ! _Rd_ ) + { + return; + } + + if ( _Sa_ == 0 ) + { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], 0 ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EAX ); + return; + } + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + PSLLQItoR( MM0, _Sa_ + 32 ); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + +} + +//////////////////////////////////////////////////// +void recDSRL32( void ) +{ + if ( ! _Rd_ ) + { + return; + } + + + if ( _Sa_ == 0 ) + { + MOV32MtoR( EAX,(int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ] ); + MOV32ItoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], 0 ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + return; + } + + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + PSRLQItoR( MM0, _Sa_ + 32 ); + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + +} + + +/********************************************************* +* Shift arithmetic with variant register shift * +* Format: OP rd, rt, rs * +*********************************************************/ + +//////////////////////////////////////////////////// + + +//////////////////////////////////////////////////// +void recSLLV( void ) +{ + + if ( ! _Rd_ ) return; + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( _Rs_ != 0 ) + { + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + AND32ItoR( ECX, 0x1f ); + SHL32CLtoR( EAX ); + } + CDQ(); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + +} + +//////////////////////////////////////////////////// +void recSRLV( void ) +{ + + if ( ! _Rd_ ) return; + + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( _Rs_ != 0 ) + { + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + AND32ItoR( ECX, 0x1f ); + SHR32CLtoR( EAX ); + } + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + +} + +//////////////////////////////////////////////////// +void recSRAV( void ) +{ + if ( ! _Rd_ ) return; + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + if ( _Rs_ != 0 ) + { + MOV32MtoR( ECX, (int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ] ); + AND32ItoR( ECX, 0x1f ); + SAR32CLtoR( EAX ); + } + CDQ( ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], EAX ); + MOV32RtoM( (int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 1 ], EDX ); + +} + +//////////////////////////////////////////////////// +static u64 _sa = 0; +void recDSLLV( void ) +{ + if ( ! _Rd_ ) return; + + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + if ( _Rs_ != 0 ) + { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + AND32ItoR( EAX, 0x3f); + MOV32RtoM( (int)&_sa, EAX ); + PSLLQMtoR( MM0, (int)&_sa ); + } + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + +} + +//////////////////////////////////////////////////// +void recDSRLV( void ) +{ + if ( ! _Rd_ ) return; + MOVQMtoR( MM0, (int)&cpuRegs.GPR.r[ _Rt_ ] ); + if ( _Rs_ != 0 ) + { + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + AND32ItoR( EAX, 0x3f); + MOV32RtoM( (int)&_sa, EAX ); + PSRLQMtoR( MM0, (int)&_sa ); + } + MOVQRtoM( (int)&cpuRegs.GPR.r[ _Rd_ ], MM0 ); + SetMMXstate(); + +} +//////////////////////////////////////////////////////////////// +void recDSRAV( void ) +{ + MOVQMtoR(MM0, (int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ] ); + + if ( _Rs_ != 0 ) { + PXORRtoR(MM1, MM1); + + // calc high bit + MOVQRtoR(MM2, MM0); + PUNPCKHDQRtoR(MM2, MM2); // shift to lower + PCMPGTDRtoR(MM1, MM2); + + // it is a signed shift + MOV32MtoR( EAX, (int)&cpuRegs.GPR.r[ _Rs_ ] ); + AND32ItoR( EAX, 0x3f); + MOVD32RtoMMX(MM2, EAX); // amount to shift + NOT32R(EAX); + ADD32ItoR(EAX, 65); + + // right logical shift + PSRLQRtoR(MM0, MM2); + + // shift highest bit, 64 - eax + MOVD32RtoMMX(MM2, EAX); + PSLLQRtoR(MM1, MM2); // highest bits + + PORRtoR(MM0, MM1); + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], MM0); + } + else { + MOVQRtoM((int)&cpuRegs.GPR.r[ _Rd_ ].UL[ 0 ], MM0); + } + + SetMMXstate(); +} +#endif + +} } } diff --git a/pcsx2/x86/ix86-32/iR5900Templates.cpp b/pcsx2/x86/ix86-32/iR5900Templates.cpp new file mode 100644 index 0000000000..09eb8291fc --- /dev/null +++ b/pcsx2/x86/ix86-32/iR5900Templates.cpp @@ -0,0 +1,1005 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "Memory.h" +#include "R5900OpcodeTables.h" +#include "ix86/ix86.h" +#include "iR5900.h" +#include "iMMI.h" +#include "iFPU.h" +#include "iCOP0.h" +#include "iVUmicro.h" +#include "VU.h" +#include "VUmicro.h" + +#include "iVUzerorec.h" + +#include "vtlb.h" + +//////////////////// +// Code Templates // +//////////////////// + +void CHECK_SAVE_REG(int reg) +{ + if( s_saveConstGPRreg == 0xffffffff ) { + if( GPR_IS_CONST1(reg) ) { + s_saveConstGPRreg = reg; + s_ConstGPRreg = g_cpuConstRegs[reg]; + } + } + else { + assert( s_saveConstGPRreg == 0 || s_saveConstGPRreg == reg ); + } +} + +void _eeProcessHasLive(int reg, int signext) +{ + g_cpuPrevRegHasLive1 = g_cpuRegHasLive1; + g_cpuRegHasLive1 |= 1<regs[reg]&EEINST_LASTUSE) ) { + if( usemmx ) return _allocMMXreg(-1, MMX_GPR+reg, mode); + return _allocGPRtoXMMreg(-1, reg, mode); + } + + return -1; +} + +#define PROCESS_EE_SETMODES(mmreg) ((mmxregs[mmreg].mode&MODE_WRITE)?PROCESS_EE_MODEWRITES:0) +#define PROCESS_EE_SETMODET(mmreg) ((mmxregs[mmreg].mode&MODE_WRITE)?PROCESS_EE_MODEWRITET:0) + +// ignores XMMINFO_READS, XMMINFO_READT, and XMMINFO_READD_LO from xmminfo +// core of reg caching +void eeRecompileCode0(R5900FNPTR constcode, R5900FNPTR_INFO constscode, R5900FNPTR_INFO consttcode, R5900FNPTR_INFO noconstcode, int xmminfo) +{ + int mmreg1, mmreg2, mmreg3, mmtemp, moded; + + if ( ! _Rd_ && (xmminfo&XMMINFO_WRITED) ) return; + + if( xmminfo&XMMINFO_WRITED) { + CHECK_SAVE_REG(_Rd_); + _eeProcessHasLive(_Rd_, 0); + EEINST_RESETSIGNEXT(_Rd_); + } + + if( GPR_IS_CONST2(_Rs_, _Rt_) ) { + if( xmminfo & XMMINFO_WRITED ) { + _deleteMMXreg(MMX_GPR+_Rd_, 2); + _deleteGPRtoXMMreg(_Rd_, 2); + } + if( xmminfo&XMMINFO_WRITED ) GPR_SET_CONST(_Rd_); + constcode(); + return; + } + + moded = MODE_WRITE|((xmminfo&XMMINFO_READD)?MODE_READ:0); + + // test if should write mmx + if( g_pCurInstInfo->info & EEINST_MMX ) { + + if( xmminfo & (XMMINFO_READLO|XMMINFO_WRITELO) ) _addNeededMMXreg(MMX_GPR+MMX_LO); + if( xmminfo & (XMMINFO_READHI|XMMINFO_WRITEHI) ) _addNeededMMXreg(MMX_GPR+MMX_HI); + _addNeededMMXreg(MMX_GPR+_Rs_); + _addNeededMMXreg(MMX_GPR+_Rt_); + + if( GPR_IS_CONST1(_Rs_) || GPR_IS_CONST1(_Rt_) ) { + int creg = GPR_IS_CONST1(_Rs_) ? _Rs_ : _Rt_; + int vreg = creg == _Rs_ ? _Rt_ : _Rs_; + +// if(g_pCurInstInfo->regs[vreg]&EEINST_MMX) { +// mmreg1 = _allocMMXreg(-1, MMX_GPR+vreg, MODE_READ); +// _addNeededMMXreg(MMX_GPR+vreg); +// } + mmreg1 = _allocCheckGPRtoMMX(g_pCurInstInfo, vreg, MODE_READ); + + if( mmreg1 >= 0 ) { + int info = PROCESS_EE_MMX; + + if( GPR_IS_CONST1(_Rs_) ) info |= PROCESS_EE_SETMODET(mmreg1); + else info |= PROCESS_EE_SETMODES(mmreg1); + + if( xmminfo & XMMINFO_WRITED ) { + _addNeededMMXreg(MMX_GPR+_Rd_); + mmreg3 = _checkMMXreg(MMX_GPR+_Rd_, moded); + + if( !(xmminfo&XMMINFO_READD) && mmreg3 < 0 && ((g_pCurInstInfo->regs[vreg] & EEINST_LASTUSE) || !EEINST_ISLIVE64(vreg)) ) { + if( EEINST_ISLIVE64(vreg) ) { + _freeMMXreg(mmreg1); + if( GPR_IS_CONST1(_Rs_) ) info &= ~PROCESS_EE_MODEWRITET; + else info &= ~PROCESS_EE_MODEWRITES; + } + _deleteGPRtoXMMreg(_Rd_, 2); + mmxregs[mmreg1].inuse = 1; + mmxregs[mmreg1].reg = _Rd_; + mmxregs[mmreg1].mode = moded; + mmreg3 = mmreg1; + } + else if( mmreg3 < 0 ) mmreg3 = _allocMMXreg(-1, MMX_GPR+_Rd_, moded); + + info |= PROCESS_EE_SET_D(mmreg3); + } + + if( xmminfo & (XMMINFO_READLO|XMMINFO_WRITELO) ) { + mmtemp = eeProcessHILO(MMX_LO, ((xmminfo&XMMINFO_READLO)?MODE_READ:0)|((xmminfo&XMMINFO_WRITELO)?MODE_WRITE:0), 1); + if( mmtemp >= 0 ) info |= PROCESS_EE_SET_LO(mmtemp); + } + if( xmminfo & (XMMINFO_READHI|XMMINFO_WRITEHI) ) { + mmtemp = eeProcessHILO(MMX_HI, ((xmminfo&XMMINFO_READLO)?MODE_READ:0)|((xmminfo&XMMINFO_WRITELO)?MODE_WRITE:0), 1); + if( mmtemp >= 0 ) info |= PROCESS_EE_SET_HI(mmtemp); + } + + SetMMXstate(); + if( creg == _Rs_ ) constscode(info|PROCESS_EE_SET_T(mmreg1)); + else consttcode(info|PROCESS_EE_SET_S(mmreg1)); + _clearNeededMMXregs(); + if( xmminfo & XMMINFO_WRITED ) GPR_DEL_CONST(_Rd_); + return; + } + } + else { + // no const regs + mmreg1 = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rs_, MODE_READ); + mmreg2 = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_READ); + + if( mmreg1 >= 0 || mmreg2 >= 0 ) { + int info = PROCESS_EE_MMX; + + // do it all in mmx + if( mmreg1 < 0 ) mmreg1 = _allocMMXreg(-1, MMX_GPR+_Rs_, MODE_READ); + if( mmreg2 < 0 ) mmreg2 = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_READ); + + info |= PROCESS_EE_SETMODES(mmreg1)|PROCESS_EE_SETMODET(mmreg2); + + // check for last used, if so don't alloc a new MMX reg + if( xmminfo & XMMINFO_WRITED ) { + _addNeededMMXreg(MMX_GPR+_Rd_); + mmreg3 = _checkMMXreg(MMX_GPR+_Rd_, moded); + + if( mmreg3 < 0 ) { + if( !(xmminfo&XMMINFO_READD) && ((g_pCurInstInfo->regs[_Rt_] & EEINST_LASTUSE) || !EEINST_ISLIVE64(_Rt_)) ) { + if( EEINST_ISLIVE64(_Rt_) ) { + _freeMMXreg(mmreg2); + info &= ~PROCESS_EE_MODEWRITET; + } + _deleteGPRtoXMMreg(_Rd_, 2); + mmxregs[mmreg2].inuse = 1; + mmxregs[mmreg2].reg = _Rd_; + mmxregs[mmreg2].mode = moded; + mmreg3 = mmreg2; + } + else if( !(xmminfo&XMMINFO_READD) && ((g_pCurInstInfo->regs[_Rs_] & EEINST_LASTUSE) || !EEINST_ISLIVE64(_Rs_)) ) { + if( EEINST_ISLIVE64(_Rs_) ) { + _freeMMXreg(mmreg1); + info &= ~PROCESS_EE_MODEWRITES; + } + _deleteGPRtoXMMreg(_Rd_, 2); + mmxregs[mmreg1].inuse = 1; + mmxregs[mmreg1].reg = _Rd_; + mmxregs[mmreg1].mode = moded; + mmreg3 = mmreg1; + } + else mmreg3 = _allocMMXreg(-1, MMX_GPR+_Rd_, moded); + } + + info |= PROCESS_EE_SET_D(mmreg3); + } + + if( xmminfo & (XMMINFO_READLO|XMMINFO_WRITELO) ) { + mmtemp = eeProcessHILO(MMX_LO, ((xmminfo&XMMINFO_READLO)?MODE_READ:0)|((xmminfo&XMMINFO_WRITELO)?MODE_WRITE:0), 1); + if( mmtemp >= 0 ) info |= PROCESS_EE_SET_LO(mmtemp); + } + if( xmminfo & (XMMINFO_READHI|XMMINFO_WRITEHI) ) { + mmtemp = eeProcessHILO(MMX_HI, ((xmminfo&XMMINFO_READLO)?MODE_READ:0)|((xmminfo&XMMINFO_WRITELO)?MODE_WRITE:0), 1); + if( mmtemp >= 0 ) info |= PROCESS_EE_SET_HI(mmtemp); + } + + SetMMXstate(); + noconstcode(info|PROCESS_EE_SET_S(mmreg1)|PROCESS_EE_SET_T(mmreg2)); + _clearNeededMMXregs(); + if( xmminfo & XMMINFO_WRITED ) GPR_DEL_CONST(_Rd_); + return; + } + } + + _clearNeededMMXregs(); + } + + // test if should write xmm, mirror to mmx code + if( g_pCurInstInfo->info & EEINST_XMM ) { + + if( xmminfo & (XMMINFO_READLO|XMMINFO_WRITELO) ) _addNeededGPRtoXMMreg(XMMGPR_LO); + if( xmminfo & (XMMINFO_READHI|XMMINFO_WRITEHI) ) _addNeededGPRtoXMMreg(XMMGPR_HI); + _addNeededGPRtoXMMreg(_Rs_); + _addNeededGPRtoXMMreg(_Rt_); + + if( GPR_IS_CONST1(_Rs_) || GPR_IS_CONST1(_Rt_) ) { + int creg = GPR_IS_CONST1(_Rs_) ? _Rs_ : _Rt_; + int vreg = creg == _Rs_ ? _Rt_ : _Rs_; + +// if(g_pCurInstInfo->regs[vreg]&EEINST_XMM) { +// mmreg1 = _allocGPRtoXMMreg(-1, vreg, MODE_READ); +// _addNeededGPRtoXMMreg(vreg); +// } + mmreg1 = _allocCheckGPRtoXMM(g_pCurInstInfo, vreg, MODE_READ); + + if( mmreg1 >= 0 ) { + int info = PROCESS_EE_XMM; + + if( GPR_IS_CONST1(_Rs_) ) info |= PROCESS_EE_SETMODET(mmreg1); + else info |= PROCESS_EE_SETMODES(mmreg1); + + if( xmminfo & XMMINFO_WRITED ) { + + _addNeededGPRtoXMMreg(_Rd_); + mmreg3 = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, MODE_WRITE); + + if( !(xmminfo&XMMINFO_READD) && mmreg3 < 0 && ((g_pCurInstInfo->regs[vreg] & EEINST_LASTUSE) || !EEINST_ISLIVEXMM(vreg)) ) { + _freeXMMreg(mmreg1); + if( GPR_IS_CONST1(_Rs_) ) info &= ~PROCESS_EE_MODEWRITET; + else info &= ~PROCESS_EE_MODEWRITES; + _deleteMMXreg(MMX_GPR+_Rd_, 2); + xmmregs[mmreg1].inuse = 1; + xmmregs[mmreg1].reg = _Rd_; + xmmregs[mmreg1].mode = moded; + mmreg3 = mmreg1; + } + else if( mmreg3 < 0 ) mmreg3 = _allocGPRtoXMMreg(-1, _Rd_, moded); + + info |= PROCESS_EE_SET_D(mmreg3); + } + + if( xmminfo & (XMMINFO_READLO|XMMINFO_WRITELO) ) { + mmtemp = eeProcessHILO(XMMGPR_LO, ((xmminfo&XMMINFO_READLO)?MODE_READ:0)|((xmminfo&XMMINFO_WRITELO)?MODE_WRITE:0), 0); + if( mmtemp >= 0 ) info |= PROCESS_EE_SET_LO(mmtemp); + } + if( xmminfo & (XMMINFO_READHI|XMMINFO_WRITEHI) ) { + mmtemp = eeProcessHILO(XMMGPR_HI, ((xmminfo&XMMINFO_READLO)?MODE_READ:0)|((xmminfo&XMMINFO_WRITELO)?MODE_WRITE:0), 0); + if( mmtemp >= 0 ) info |= PROCESS_EE_SET_HI(mmtemp); + } + + if( creg == _Rs_ ) constscode(info|PROCESS_EE_SET_T(mmreg1)); + else consttcode(info|PROCESS_EE_SET_S(mmreg1)); + _clearNeededXMMregs(); + if( xmminfo & XMMINFO_WRITED ) GPR_DEL_CONST(_Rd_); + return; + } + } + else { + // no const regs + mmreg1 = _allocCheckGPRtoXMM(g_pCurInstInfo, _Rs_, MODE_READ); + mmreg2 = _allocCheckGPRtoXMM(g_pCurInstInfo, _Rt_, MODE_READ); + + if( mmreg1 >= 0 || mmreg2 >= 0 ) { + int info = PROCESS_EE_XMM; + + // do it all in xmm + if( mmreg1 < 0 ) mmreg1 = _allocGPRtoXMMreg(-1, _Rs_, MODE_READ); + if( mmreg2 < 0 ) mmreg2 = _allocGPRtoXMMreg(-1, _Rt_, MODE_READ); + + info |= PROCESS_EE_SETMODES(mmreg1)|PROCESS_EE_SETMODET(mmreg2); + + if( xmminfo & XMMINFO_WRITED ) { + // check for last used, if so don't alloc a new XMM reg + _addNeededGPRtoXMMreg(_Rd_); + mmreg3 = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, moded); + + if( mmreg3 < 0 ) { + if( !(xmminfo&XMMINFO_READD) && ((g_pCurInstInfo->regs[_Rt_] & EEINST_LASTUSE) || !EEINST_ISLIVEXMM(_Rt_)) ) { + _freeXMMreg(mmreg2); + info &= ~PROCESS_EE_MODEWRITET; + _deleteMMXreg(MMX_GPR+_Rd_, 2); + xmmregs[mmreg2].inuse = 1; + xmmregs[mmreg2].reg = _Rd_; + xmmregs[mmreg2].mode = moded; + mmreg3 = mmreg2; + } + else if( !(xmminfo&XMMINFO_READD) && ((g_pCurInstInfo->regs[_Rs_] & EEINST_LASTUSE) || !EEINST_ISLIVEXMM(_Rs_)) ) { + _freeXMMreg(mmreg1); + info &= ~PROCESS_EE_MODEWRITES; + _deleteMMXreg(MMX_GPR+_Rd_, 2); + xmmregs[mmreg1].inuse = 1; + xmmregs[mmreg1].reg = _Rd_; + xmmregs[mmreg1].mode = moded; + mmreg3 = mmreg1; + } + else mmreg3 = _allocGPRtoXMMreg(-1, _Rd_, moded); + } + + info |= PROCESS_EE_SET_D(mmreg3); + } + + if( xmminfo & (XMMINFO_READLO|XMMINFO_WRITELO) ) { + mmtemp = eeProcessHILO(XMMGPR_LO, ((xmminfo&XMMINFO_READLO)?MODE_READ:0)|((xmminfo&XMMINFO_WRITELO)?MODE_WRITE:0), 0); + if( mmtemp >= 0 ) info |= PROCESS_EE_SET_LO(mmtemp); + } + if( xmminfo & (XMMINFO_READHI|XMMINFO_WRITEHI) ) { + mmtemp = eeProcessHILO(XMMGPR_HI, ((xmminfo&XMMINFO_READLO)?MODE_READ:0)|((xmminfo&XMMINFO_WRITELO)?MODE_WRITE:0), 0); + if( mmtemp >= 0 ) info |= PROCESS_EE_SET_HI(mmtemp); + } + + noconstcode(info|PROCESS_EE_SET_S(mmreg1)|PROCESS_EE_SET_T(mmreg2)); + _clearNeededXMMregs(); + if( xmminfo & XMMINFO_WRITED ) GPR_DEL_CONST(_Rd_); + return; + } + } + + _clearNeededXMMregs(); + } + + // regular x86 + _deleteGPRtoXMMreg(_Rs_, 1); + _deleteGPRtoXMMreg(_Rt_, 1); + if( xmminfo&XMMINFO_WRITED ) + _deleteGPRtoXMMreg(_Rd_, (xmminfo&XMMINFO_READD)?0:2); + _deleteMMXreg(MMX_GPR+_Rs_, 1); + _deleteMMXreg(MMX_GPR+_Rt_, 1); + if( xmminfo&XMMINFO_WRITED ) + _deleteMMXreg(MMX_GPR+_Rd_, (xmminfo&XMMINFO_READD)?0:2); + + // don't delete, fn will take care of them +// if( xmminfo & (XMMINFO_READLO|XMMINFO_WRITELO) ) { +// _deleteGPRtoXMMreg(XMMGPR_LO, (xmminfo&XMMINFO_READLO)?1:0); +// _deleteMMXreg(MMX_GPR+MMX_LO, (xmminfo&XMMINFO_READLO)?1:0); +// } +// if( xmminfo & (XMMINFO_READHI|XMMINFO_WRITEHI) ) { +// _deleteGPRtoXMMreg(XMMGPR_HI, (xmminfo&XMMINFO_READHI)?1:0); +// _deleteMMXreg(MMX_GPR+MMX_HI, (xmminfo&XMMINFO_READHI)?1:0); +// } + + if( GPR_IS_CONST1(_Rs_) ) { + constscode(0); + if( xmminfo&XMMINFO_WRITED ) GPR_DEL_CONST(_Rd_); + return; + } + + if( GPR_IS_CONST1(_Rt_) ) { + consttcode(0); + if( xmminfo&XMMINFO_WRITED ) GPR_DEL_CONST(_Rd_); + return; + } + + noconstcode(0); + if( xmminfo&XMMINFO_WRITED ) GPR_DEL_CONST(_Rd_); +} + +// rt = rs op imm16 +void eeRecompileCode1(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode) +{ + int mmreg1, mmreg2; + if ( ! _Rt_ ) return; + + CHECK_SAVE_REG(_Rt_); + _eeProcessHasLive(_Rt_, 0); + EEINST_RESETSIGNEXT(_Rt_); + + if( GPR_IS_CONST1(_Rs_) ) { + _deleteMMXreg(MMX_GPR+_Rt_, 2); + _deleteGPRtoXMMreg(_Rt_, 2); + GPR_SET_CONST(_Rt_); + constcode(); + return; + } + + // test if should write mmx + if( g_pCurInstInfo->info & EEINST_MMX ) { + + // no const regs + mmreg1 = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rs_, MODE_READ); + + if( mmreg1 >= 0 ) { + int info = PROCESS_EE_MMX|PROCESS_EE_SETMODES(mmreg1); + + // check for last used, if so don't alloc a new MMX reg + _addNeededMMXreg(MMX_GPR+_Rt_); + mmreg2 = _checkMMXreg(MMX_GPR+_Rt_, MODE_WRITE); + + if( mmreg2 < 0 ) { + if( (g_pCurInstInfo->regs[_Rs_] & EEINST_LASTUSE) || !EEINST_ISLIVE64(_Rs_) ) { + if( EEINST_ISLIVE64(_Rs_) ) { + _freeMMXreg(mmreg1); + info &= ~PROCESS_EE_MODEWRITES; + } + _deleteGPRtoXMMreg(_Rt_, 2); + mmxregs[mmreg1].inuse = 1; + mmxregs[mmreg1].reg = _Rt_; + mmxregs[mmreg1].mode = MODE_WRITE|MODE_READ; + mmreg2 = mmreg1; + } + else mmreg2 = _allocMMXreg(-1, MMX_GPR+_Rt_, MODE_WRITE); + } + + SetMMXstate(); + noconstcode(info|PROCESS_EE_SET_S(mmreg1)|PROCESS_EE_SET_T(mmreg2)); + _clearNeededMMXregs(); + GPR_DEL_CONST(_Rt_); + return; + } + + _clearNeededMMXregs(); + } + + // test if should write xmm, mirror to mmx code + if( g_pCurInstInfo->info & EEINST_XMM ) { + + // no const regs + mmreg1 = _allocCheckGPRtoXMM(g_pCurInstInfo, _Rs_, MODE_READ); + + if( mmreg1 >= 0 ) { + int info = PROCESS_EE_XMM|PROCESS_EE_SETMODES(mmreg1); + + // check for last used, if so don't alloc a new XMM reg + _addNeededGPRtoXMMreg(_Rt_); + mmreg2 = _checkXMMreg(XMMTYPE_GPRREG, _Rt_, MODE_WRITE); + + if( mmreg2 < 0 ) { + if( (g_pCurInstInfo->regs[_Rs_] & EEINST_LASTUSE) || !EEINST_ISLIVEXMM(_Rs_) ) { + _freeXMMreg(mmreg1); + info &= ~PROCESS_EE_MODEWRITES; + _deleteMMXreg(MMX_GPR+_Rt_, 2); + xmmregs[mmreg1].inuse = 1; + xmmregs[mmreg1].reg = _Rt_; + xmmregs[mmreg1].mode = MODE_WRITE|MODE_READ; + mmreg2 = mmreg1; + } + else mmreg2 = _allocGPRtoXMMreg(-1, _Rt_, MODE_WRITE); + } + + noconstcode(info|PROCESS_EE_SET_S(mmreg1)|PROCESS_EE_SET_T(mmreg2)); + _clearNeededXMMregs(); + GPR_DEL_CONST(_Rt_); + return; + } + + _clearNeededXMMregs(); + } + + // regular x86 + _deleteGPRtoXMMreg(_Rs_, 1); + _deleteGPRtoXMMreg(_Rt_, 2); + _deleteMMXreg(MMX_GPR+_Rs_, 1); + _deleteMMXreg(MMX_GPR+_Rt_, 2); + + noconstcode(0); + GPR_DEL_CONST(_Rt_); +} + +// rd = rt op sa +void eeRecompileCode2(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode) +{ + int mmreg1, mmreg2; + if ( ! _Rd_ ) return; + + CHECK_SAVE_REG(_Rd_); + _eeProcessHasLive(_Rd_, 0); + EEINST_RESETSIGNEXT(_Rd_); + + if( GPR_IS_CONST1(_Rt_) ) { + _deleteMMXreg(MMX_GPR+_Rd_, 2); + _deleteGPRtoXMMreg(_Rd_, 2); + GPR_SET_CONST(_Rd_); + constcode(); + return; + } + + // test if should write mmx + if( g_pCurInstInfo->info & EEINST_MMX ) { + + // no const regs + mmreg1 = _allocCheckGPRtoMMX(g_pCurInstInfo, _Rt_, MODE_READ); + + if( mmreg1 >= 0 ) { + int info = PROCESS_EE_MMX|PROCESS_EE_SETMODET(mmreg1); + + // check for last used, if so don't alloc a new MMX reg + _addNeededMMXreg(MMX_GPR+_Rd_); + mmreg2 = _checkMMXreg(MMX_GPR+_Rd_, MODE_WRITE); + + if( mmreg2 < 0 ) { + if( (g_pCurInstInfo->regs[_Rt_] & EEINST_LASTUSE) || !EEINST_ISLIVE64(_Rt_) ) { + if( EEINST_ISLIVE64(_Rt_) ) { + _freeMMXreg(mmreg1); + info &= ~PROCESS_EE_MODEWRITET; + } + _deleteGPRtoXMMreg(_Rd_, 2); + mmxregs[mmreg1].inuse = 1; + mmxregs[mmreg1].reg = _Rd_; + mmxregs[mmreg1].mode = MODE_WRITE|MODE_READ; + mmreg2 = mmreg1; + } + else mmreg2 = _allocMMXreg(-1, MMX_GPR+_Rd_, MODE_WRITE); + } + + SetMMXstate(); + noconstcode(info|PROCESS_EE_SET_T(mmreg1)|PROCESS_EE_SET_D(mmreg2)); + _clearNeededMMXregs(); + GPR_DEL_CONST(_Rd_); + return; + } + + _clearNeededMMXregs(); + } + + // test if should write xmm, mirror to mmx code + if( g_pCurInstInfo->info & EEINST_XMM ) { + + // no const regs + mmreg1 = _allocCheckGPRtoXMM(g_pCurInstInfo, _Rt_, MODE_READ); + + if( mmreg1 >= 0 ) { + int info = PROCESS_EE_XMM|PROCESS_EE_SETMODET(mmreg1); + + // check for last used, if so don't alloc a new XMM reg + _addNeededGPRtoXMMreg(_Rd_); + mmreg2 = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, MODE_WRITE); + + if( mmreg2 < 0 ) { + if( (g_pCurInstInfo->regs[_Rt_] & EEINST_LASTUSE) || !EEINST_ISLIVE64(_Rt_) ) { + _freeXMMreg(mmreg1); + info &= ~PROCESS_EE_MODEWRITET; + _deleteMMXreg(MMX_GPR+_Rd_, 2); + xmmregs[mmreg1].inuse = 1; + xmmregs[mmreg1].reg = _Rd_; + xmmregs[mmreg1].mode = MODE_WRITE|MODE_READ; + mmreg2 = mmreg1; + } + else mmreg2 = _allocGPRtoXMMreg(-1, _Rd_, MODE_WRITE); + } + + noconstcode(info|PROCESS_EE_SET_T(mmreg1)|PROCESS_EE_SET_D(mmreg2)); + _clearNeededXMMregs(); + GPR_DEL_CONST(_Rd_); + return; + } + + _clearNeededXMMregs(); + } + + // regular x86 + _deleteGPRtoXMMreg(_Rt_, 1); + _deleteGPRtoXMMreg(_Rd_, 2); + _deleteMMXreg(MMX_GPR+_Rt_, 1); + _deleteMMXreg(MMX_GPR+_Rd_, 2); + + noconstcode(0); + GPR_DEL_CONST(_Rd_); +} + +// rt op rs +void eeRecompileCode3(R5900FNPTR constcode, R5900FNPTR_INFO multicode) +{ + assert(0); + // for now, don't support xmm + _deleteEEreg(_Rs_, 1); + _deleteEEreg(_Rt_, 1); + + if( GPR_IS_CONST2(_Rs_, _Rt_) ) { + constcode(); + return; + } + + if( GPR_IS_CONST1(_Rs_) ) { + //multicode(PROCESS_EE_CONSTT); + return; + } + + if( GPR_IS_CONST1(_Rt_) ) { + //multicode(PROCESS_EE_CONSTT); + return; + } + + multicode(0); +} + +// Simple Code Templates // + +// rd = rs op rt +void eeRecompileCodeConst0(R5900FNPTR constcode, R5900FNPTR_INFO constscode, R5900FNPTR_INFO consttcode, R5900FNPTR_INFO noconstcode) +{ + if ( ! _Rd_ ) return; + + // for now, don't support xmm + CHECK_SAVE_REG(_Rd_); + + _deleteGPRtoXMMreg(_Rs_, 1); + _deleteGPRtoXMMreg(_Rt_, 1); + _deleteGPRtoXMMreg(_Rd_, 0); + _deleteMMXreg(MMX_GPR+_Rs_, 1); + _deleteMMXreg(MMX_GPR+_Rt_, 1); + _deleteMMXreg(MMX_GPR+_Rd_, 0); + + if( GPR_IS_CONST2(_Rs_, _Rt_) ) { + GPR_SET_CONST(_Rd_); + constcode(); + return; + } + + if( GPR_IS_CONST1(_Rs_) ) { + constscode(0); + GPR_DEL_CONST(_Rd_); + return; + } + + if( GPR_IS_CONST1(_Rt_) ) { + consttcode(0); + GPR_DEL_CONST(_Rd_); + return; + } + + noconstcode(0); + GPR_DEL_CONST(_Rd_); +} + +// rt = rs op imm16 +void eeRecompileCodeConst1(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode) +{ + if ( ! _Rt_ ) + return; + + // for now, don't support xmm + CHECK_SAVE_REG(_Rt_); + + _deleteGPRtoXMMreg(_Rs_, 1); + _deleteGPRtoXMMreg(_Rt_, 0); + + if( GPR_IS_CONST1(_Rs_) ) { + GPR_SET_CONST(_Rt_); + constcode(); + return; + } + + noconstcode(0); + GPR_DEL_CONST(_Rt_); +} + +// rd = rt op sa +void eeRecompileCodeConst2(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode) +{ + if ( ! _Rd_ ) return; + + // for now, don't support xmm + CHECK_SAVE_REG(_Rd_); + + _deleteGPRtoXMMreg(_Rt_, 1); + _deleteGPRtoXMMreg(_Rd_, 0); + + if( GPR_IS_CONST1(_Rt_) ) { + GPR_SET_CONST(_Rd_); + constcode(); + return; + } + + noconstcode(0); + GPR_DEL_CONST(_Rd_); +} + +// rd = rt MULT rs (SPECIAL) +void eeRecompileCodeConstSPECIAL(R5900FNPTR constcode, R5900FNPTR_INFO multicode, int MULT) +{ + assert(0); + // for now, don't support xmm + if( MULT ) { + CHECK_SAVE_REG(_Rd_); + _deleteGPRtoXMMreg(_Rd_, 0); + } + + _deleteGPRtoXMMreg(_Rs_, 1); + _deleteGPRtoXMMreg(_Rt_, 1); + + if( GPR_IS_CONST2(_Rs_, _Rt_) ) { + if( MULT && _Rd_ ) GPR_SET_CONST(_Rd_); + constcode(); + return; + } + + if( GPR_IS_CONST1(_Rs_) ) { + //multicode(PROCESS_EE_CONSTS); + if( MULT && _Rd_ ) GPR_DEL_CONST(_Rd_); + return; + } + + if( GPR_IS_CONST1(_Rt_) ) { + //multicode(PROCESS_EE_CONSTT); + if( MULT && _Rd_ ) GPR_DEL_CONST(_Rd_); + return; + } + + multicode(0); + if( MULT && _Rd_ ) GPR_DEL_CONST(_Rd_); +} + +// EE XMM allocation code +int eeRecompileCodeXMM(int xmminfo) +{ + int info = PROCESS_EE_XMM; + + // save state + if( xmminfo & XMMINFO_WRITED ) { + CHECK_SAVE_REG(_Rd_); + _eeProcessHasLive(_Rd_, 0); + EEINST_RESETSIGNEXT(_Rd_); + } + + // flush consts + if( xmminfo & XMMINFO_READT ) { + if( GPR_IS_CONST1( _Rt_ ) && !(g_cpuFlushedConstReg&(1<<_Rt_)) ) { + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 0 ], g_cpuConstRegs[_Rt_].UL[0]); + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rt_ ].UL[ 1 ], g_cpuConstRegs[_Rt_].UL[1]); + g_cpuFlushedConstReg |= (1<<_Rt_); + } + } + if( xmminfo & XMMINFO_READS) { + if( GPR_IS_CONST1( _Rs_ ) && !(g_cpuFlushedConstReg&(1<<_Rs_)) ) { + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 0 ], g_cpuConstRegs[_Rs_].UL[0]); + MOV32ItoM((int)&cpuRegs.GPR.r[ _Rs_ ].UL[ 1 ], g_cpuConstRegs[_Rs_].UL[1]); + g_cpuFlushedConstReg |= (1<<_Rs_); + } + } + + if( xmminfo & XMMINFO_WRITED ) { + GPR_DEL_CONST(_Rd_); + } + + // add needed + if( xmminfo & (XMMINFO_READLO|XMMINFO_WRITELO) ) { + _addNeededGPRtoXMMreg(XMMGPR_LO); + } + if( xmminfo & (XMMINFO_READHI|XMMINFO_WRITEHI) ) { + _addNeededGPRtoXMMreg(XMMGPR_HI); + } + if( xmminfo & XMMINFO_READS) _addNeededGPRtoXMMreg(_Rs_); + if( xmminfo & XMMINFO_READT) _addNeededGPRtoXMMreg(_Rt_); + if( xmminfo & XMMINFO_WRITED ) _addNeededGPRtoXMMreg(_Rd_); + + // allocate + if( xmminfo & XMMINFO_READS) { + int reg = _allocGPRtoXMMreg(-1, _Rs_, MODE_READ); + info |= PROCESS_EE_SET_S(reg)|PROCESS_EE_SETMODES(reg); + } + if( xmminfo & XMMINFO_READT) { + int reg = _allocGPRtoXMMreg(-1, _Rt_, MODE_READ); + info |= PROCESS_EE_SET_T(reg)|PROCESS_EE_SETMODET(reg); + } + + if( xmminfo & XMMINFO_WRITED ) { + int readd = MODE_WRITE|((xmminfo&XMMINFO_READD)?((xmminfo&XMMINFO_READD_LO)?(MODE_READ|MODE_READHALF):MODE_READ):0); + + int regd = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, readd); + + if( regd < 0 ) { + if( !(xmminfo&XMMINFO_READD) && (xmminfo & XMMINFO_READT) && (_Rt_ == 0 || (g_pCurInstInfo->regs[_Rt_] & EEINST_LASTUSE) || !EEINST_ISLIVEXMM(_Rt_)) ) { + _freeXMMreg(EEREC_T); + _deleteMMXreg(MMX_GPR+_Rd_, 2); + xmmregs[EEREC_T].inuse = 1; + xmmregs[EEREC_T].reg = _Rd_; + xmmregs[EEREC_T].mode = readd; + regd = EEREC_T; + } + else if( !(xmminfo&XMMINFO_READD) && (xmminfo & XMMINFO_READS) && (_Rs_ == 0 || (g_pCurInstInfo->regs[_Rs_] & EEINST_LASTUSE) || !EEINST_ISLIVEXMM(_Rs_)) ) { + _freeXMMreg(EEREC_S); + _deleteMMXreg(MMX_GPR+_Rd_, 2); + xmmregs[EEREC_S].inuse = 1; + xmmregs[EEREC_S].reg = _Rd_; + xmmregs[EEREC_S].mode = readd; + regd = EEREC_S; + } + else regd = _allocGPRtoXMMreg(-1, _Rd_, readd); + } + + info |= PROCESS_EE_SET_D(regd); + } + if( xmminfo & (XMMINFO_READLO|XMMINFO_WRITELO) ) { + info |= PROCESS_EE_SET_LO(_allocGPRtoXMMreg(-1, XMMGPR_LO, ((xmminfo&XMMINFO_READLO)?MODE_READ:0)|((xmminfo&XMMINFO_WRITELO)?MODE_WRITE:0))); + info |= PROCESS_EE_LO; + } + if( xmminfo & (XMMINFO_READHI|XMMINFO_WRITEHI) ) { + info |= PROCESS_EE_SET_HI(_allocGPRtoXMMreg(-1, XMMGPR_HI, ((xmminfo&XMMINFO_READHI)?MODE_READ:0)|((xmminfo&XMMINFO_WRITEHI)?MODE_WRITE:0))); + info |= PROCESS_EE_HI; + } + return info; +} + +// EE COP1(FPU) XMM allocation code +#define _Ft_ _Rt_ +#define _Fs_ _Rd_ +#define _Fd_ _Sa_ + +#define PROCESS_EE_SETMODES_XMM(mmreg) ((xmmregs[mmreg].mode&MODE_WRITE)?PROCESS_EE_MODEWRITES:0) +#define PROCESS_EE_SETMODET_XMM(mmreg) ((xmmregs[mmreg].mode&MODE_WRITE)?PROCESS_EE_MODEWRITET:0) + +// rd = rs op rt +void eeFPURecompileCode(R5900FNPTR_INFO xmmcode, R5900FNPTR fpucode, int xmminfo) +{ + int mmregs=-1, mmregt=-1, mmregd=-1, mmregacc=-1; + int info = PROCESS_EE_XMM; + + if( xmminfo & XMMINFO_READS ) _addNeededFPtoXMMreg(_Fs_); + if( xmminfo & XMMINFO_READT ) _addNeededFPtoXMMreg(_Ft_); + if( xmminfo & (XMMINFO_WRITED|XMMINFO_READD) ) _addNeededFPtoXMMreg(_Fd_); + if( xmminfo & (XMMINFO_WRITEACC|XMMINFO_READACC) ) _addNeededFPACCtoXMMreg(); + + if( xmminfo & XMMINFO_READT ) { + if( g_pCurInstInfo->fpuregs[_Ft_] & EEINST_LASTUSE ) mmregt = _checkXMMreg(XMMTYPE_FPREG, _Ft_, MODE_READ); + else mmregt = _allocFPtoXMMreg(-1, _Ft_, MODE_READ); + } + + if( xmminfo & XMMINFO_READS ) { + if( ( !(xmminfo & XMMINFO_READT) || (mmregt >= 0) ) && (g_pCurInstInfo->fpuregs[_Fs_] & EEINST_LASTUSE) ) { + mmregs = _checkXMMreg(XMMTYPE_FPREG, _Fs_, MODE_READ); + } + else mmregs = _allocFPtoXMMreg(-1, _Fs_, MODE_READ); + } + + if( mmregs >= 0 ) info |= PROCESS_EE_SETMODES_XMM(mmregs); + if( mmregt >= 0 ) info |= PROCESS_EE_SETMODET_XMM(mmregt); + + if( xmminfo & XMMINFO_READD ) { + assert( xmminfo & XMMINFO_WRITED ); + mmregd = _allocFPtoXMMreg(-1, _Fd_, MODE_READ); + } + + if( xmminfo & XMMINFO_READACC ) { + if( !(xmminfo&XMMINFO_WRITEACC) && (g_pCurInstInfo->fpuregs[_Ft_] & EEINST_LASTUSE) ) + mmregacc = _checkXMMreg(XMMTYPE_FPACC, 0, MODE_READ); + else mmregacc = _allocFPACCtoXMMreg(-1, MODE_READ); + } + + if( xmminfo & XMMINFO_WRITEACC ) { + + // check for last used, if so don't alloc a new XMM reg + int readacc = MODE_WRITE|((xmminfo&XMMINFO_READACC)?MODE_READ:0); + + mmregacc = _checkXMMreg(XMMTYPE_FPACC, 0, readacc); + + if( mmregacc < 0 ) { + if( (xmminfo&XMMINFO_READT) && mmregt >= 0 && (FPUINST_LASTUSE(_Ft_) || !FPUINST_ISLIVE(_Ft_)) ) { + if( FPUINST_ISLIVE(_Ft_) ) { + _freeXMMreg(mmregt); + info &= ~PROCESS_EE_MODEWRITET; + } + _deleteMMXreg(MMX_FPU+XMMFPU_ACC, 2); + xmmregs[mmregt].inuse = 1; + xmmregs[mmregt].reg = 0; + xmmregs[mmregt].mode = readacc; + xmmregs[mmregt].type = XMMTYPE_FPACC; + mmregacc = mmregt; + } + else if( (xmminfo&XMMINFO_READS) && mmregs >= 0 && (FPUINST_LASTUSE(_Fs_) || !FPUINST_ISLIVE(_Fs_)) ) { + if( FPUINST_ISLIVE(_Fs_) ) { + _freeXMMreg(mmregs); + info &= ~PROCESS_EE_MODEWRITES; + } + _deleteMMXreg(MMX_FPU+XMMFPU_ACC, 2); + xmmregs[mmregs].inuse = 1; + xmmregs[mmregs].reg = 0; + xmmregs[mmregs].mode = readacc; + xmmregs[mmregs].type = XMMTYPE_FPACC; + mmregacc = mmregs; + } + else mmregacc = _allocFPACCtoXMMreg(-1, readacc); + } + + xmmregs[mmregacc].mode |= MODE_WRITE; + } + else if( xmminfo & XMMINFO_WRITED ) { + // check for last used, if so don't alloc a new XMM reg + int readd = MODE_WRITE|((xmminfo&XMMINFO_READD)?MODE_READ:0); + if( xmminfo&XMMINFO_READD ) mmregd = _allocFPtoXMMreg(-1, _Fd_, readd); + else mmregd = _checkXMMreg(XMMTYPE_FPREG, _Fd_, readd); + + if( mmregd < 0 ) { + if( (xmminfo&XMMINFO_READT) && mmregt >= 0 && (FPUINST_LASTUSE(_Ft_) || !FPUINST_ISLIVE(_Ft_)) ) { + if( FPUINST_ISLIVE(_Ft_) ) { + _freeXMMreg(mmregt); + info &= ~PROCESS_EE_MODEWRITET; + } + _deleteMMXreg(MMX_FPU+_Fd_, 2); + xmmregs[mmregt].inuse = 1; + xmmregs[mmregt].reg = _Fd_; + xmmregs[mmregt].mode = readd; + mmregd = mmregt; + } + else if( (xmminfo&XMMINFO_READS) && mmregs >= 0 && (FPUINST_LASTUSE(_Fs_) || !FPUINST_ISLIVE(_Fs_)) ) { + if( FPUINST_ISLIVE(_Fs_) ) { + _freeXMMreg(mmregs); + info &= ~PROCESS_EE_MODEWRITES; + } + _deleteMMXreg(MMX_FPU+_Fd_, 2); + xmmregs[mmregs].inuse = 1; + xmmregs[mmregs].reg = _Fd_; + xmmregs[mmregs].mode = readd; + mmregd = mmregs; + } + else if( (xmminfo&XMMINFO_READACC) && mmregacc >= 0 && (FPUINST_LASTUSE(XMMFPU_ACC) || !FPUINST_ISLIVE(XMMFPU_ACC)) ) { + if( FPUINST_ISLIVE(XMMFPU_ACC) ) + _freeXMMreg(mmregacc); + _deleteMMXreg(MMX_FPU+_Fd_, 2); + xmmregs[mmregacc].inuse = 1; + xmmregs[mmregacc].reg = _Fd_; + xmmregs[mmregacc].mode = readd; + xmmregs[mmregacc].type = XMMTYPE_FPREG; + mmregd = mmregacc; + } + else mmregd = _allocFPtoXMMreg(-1, _Fd_, readd); + } + } + + assert( mmregs >= 0 || mmregt >= 0 || mmregd >= 0 || mmregacc >= 0 ); + + if( xmminfo & XMMINFO_WRITED ) { + assert( mmregd >= 0 ); + info |= PROCESS_EE_SET_D(mmregd); + } + if( xmminfo & (XMMINFO_WRITEACC|XMMINFO_READACC) ) { + if( mmregacc >= 0 ) info |= PROCESS_EE_SET_ACC(mmregacc)|PROCESS_EE_ACC; + else assert( !(xmminfo&XMMINFO_WRITEACC)); + } + + if( xmminfo & XMMINFO_READS ) { + if( mmregs >= 0 ) info |= PROCESS_EE_SET_S(mmregs)|PROCESS_EE_S; + } + if( xmminfo & XMMINFO_READT ) { + if( mmregt >= 0 ) info |= PROCESS_EE_SET_T(mmregt)|PROCESS_EE_T; + } + + // at least one must be in xmm + if( (xmminfo & (XMMINFO_READS|XMMINFO_READT)) == (XMMINFO_READS|XMMINFO_READT) ) { + assert( mmregs >= 0 || mmregt >= 0 ); + } + + xmmcode(info); + _clearNeededXMMregs(); +} diff --git a/pcsx2/x86/ix86-32/recVTLB.cpp b/pcsx2/x86/ix86-32/recVTLB.cpp new file mode 100644 index 0000000000..80a05fb78c --- /dev/null +++ b/pcsx2/x86/ix86-32/recVTLB.cpp @@ -0,0 +1,319 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Common.h" +#include "vtlb.h" + +#include "x86/ix86/ix86.h" +#include "iCore.h" +#include "iR5900.h" + +using namespace vtlb_private; + +/* + // Pseudo-Code For the following Dynarec Implementations --> + + u32 vmv=vmap[addr>>VTLB_PAGE_BITS]; + s32 ppf=addr+vmv; + if (!(ppf<0)) + { + data[0]=*reinterpret_cast(ppf); + if (DataSize==128) + data[1]=*reinterpret_cast(ppf+8); + return 0; + } + else + { + //has to: translate, find function, call function + u32 hand=(u8)vmv; + u32 paddr=ppf-hand+0x80000000; + //SysPrintf("Translted 0x%08X to 0x%08X\n",addr,paddr); + return reinterpret_cast::HandlerType*>(RWFT[TemplateHelper::sidx][0][hand])(paddr,data); + } + + // And in ASM it looks something like this --> + + mov eax,ecx; + shr eax,VTLB_PAGE_BITS; + mov eax,[eax*4+vmap]; + add ecx,eax; + js _fullread; + + //these are wrong order, just an example ... + mov [eax],ecx; + mov ecx,[edx]; + mov [eax+4],ecx; + mov ecx,[edx+4]; + mov [eax+4+4],ecx; + mov ecx,[edx+4+4]; + mov [eax+4+4+4+4],ecx; + mov ecx,[edx+4+4+4+4]; + ///.... + + jmp cont; + _fullread: + movzx eax,al; + sub ecx,eax; + sub ecx,0x80000000; + call [eax+stuff]; + cont: + ........ + + */ + + +//ecx = addr +//edx = ptr +void vtlb_DynGenRead64(u32 bits) +{ + MOV32RtoR(EAX,ECX); + SHR32ItoR(EAX,VTLB_PAGE_BITS); + MOV32RmSOffsettoR(EAX,EAX,(int)vmap,2); + ADD32RtoR(ECX,EAX); + u8* _fullread=JS8(0); + switch(bits) + { + case 64: + if( _hasFreeMMXreg() ) + { + const int freereg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRmtoROffset(freereg,ECX,0); + MOVQRtoRmOffset(EDX,freereg,0); + _freeMMXreg(freereg); + } + else + { + MOV32RmtoR(EAX,ECX); + MOV32RtoRm(EDX,EAX); + + MOV32RmtoROffset(EAX,ECX,4); + MOV32RtoRmOffset(EDX,EAX,4); + } + break; + + case 128: + if( _hasFreeXMMreg() ) + { + const int freereg = _allocTempXMMreg( XMMT_INT, -1 ); + SSE2_MOVDQARmtoROffset(freereg,ECX,0); + SSE2_MOVDQARtoRmOffset(EDX,freereg,0); + _freeXMMreg(freereg); + } + else + { + // Could put in an MMX optimization here as well, but no point really. + // It's almost never used since there's almost always a free XMM reg. + + MOV32RmtoR(EAX,ECX); + MOV32RtoRm(EDX,EAX); + + MOV32RmtoROffset(EAX,ECX,4); + MOV32RtoRmOffset(EDX,EAX,4); + + MOV32RmtoROffset(EAX,ECX,8); + MOV32RtoRmOffset(EDX,EAX,8); + + MOV32RmtoROffset(EAX,ECX,12); + MOV32RtoRmOffset(EDX,EAX,12); + } + break; + + jNO_DEFAULT + } + + u8* cont=JMP8(0); + x86SetJ8(_fullread); + int szidx; + + switch(bits) + { + case 64: szidx=3; break; + case 128: szidx=4; break; + jNO_DEFAULT + } + + MOVZX32R8toR(EAX,EAX); + SUB32RtoR(ECX,EAX); + //eax=[funct+eax] + MOV32RmSOffsettoR(EAX,EAX,(int)RWFT[szidx][0],2); + SUB32ItoR(ECX,0x80000000); + CALL32R(EAX); + + x86SetJ8(cont); +} + +// ecx - source address to read from +// Returns read value in eax. +void vtlb_DynGenRead32(u32 bits, bool sign) +{ + jASSUME( bits <= 32 ); + + MOV32RtoR(EAX,ECX); + SHR32ItoR(EAX,VTLB_PAGE_BITS); + MOV32RmSOffsettoR(EAX,EAX,(int)vmap,2); + ADD32RtoR(ECX,EAX); + u8* _fullread=JS8(0); + + switch(bits) + { + case 8: + if( sign ) + MOVSX32Rm8toR(EAX,ECX); + else + MOVZX32Rm8toR(EAX,ECX); + break; + + case 16: + if( sign ) + MOVSX32Rm16toR(EAX,ECX); + else + MOVZX32Rm16toR(EAX,ECX); + break; + + case 32: + MOV32RmtoR(EAX,ECX); + break; + + jNO_DEFAULT + } + + u8* cont=JMP8(0); + x86SetJ8(_fullread); + int szidx; + + switch(bits) + { + case 8: szidx=0; break; + case 16: szidx=1; break; + case 32: szidx=2; break; + jNO_DEFAULT + } + + MOVZX32R8toR(EAX,EAX); + SUB32RtoR(ECX,EAX); + //eax=[funct+eax] + MOV32RmSOffsettoR(EAX,EAX,(int)RWFT[szidx][0],2); + SUB32ItoR(ECX,0x80000000); + CALL32R(EAX); + + // perform sign extension on the result: + + if( bits==8 ) + { + if( sign ) + MOVSX32R8toR(EAX,EAX); + else + MOVZX32R8toR(EAX,EAX); + } + else if( bits==16 ) + { + if( sign ) + MOVSX32R16toR(EAX,EAX); + else + MOVZX32R16toR(EAX,EAX); + } + + x86SetJ8(cont); +} + +void vtlb_DynGenWrite(u32 sz) +{ + MOV32RtoR(EAX,ECX); + SHR32ItoR(EAX,VTLB_PAGE_BITS); + MOV32RmSOffsettoR(EAX,EAX,(int)vmap,2); + ADD32RtoR(ECX,EAX); + u8* _full=JS8(0); + switch(sz) + { + //8 , 16, 32 : data on EDX + case 8: + MOV8RtoRm(ECX,EDX); + break; + case 16: + MOV16RtoRm(ECX,EDX); + break; + case 32: + MOV32RtoRm(ECX,EDX); + break; + + case 64: + if( _hasFreeMMXreg() ) + { + const int freereg = _allocMMXreg(-1, MMX_TEMP, 0); + MOVQRmtoROffset(freereg,EDX,0); + MOVQRtoRmOffset(ECX,freereg,0); + _freeMMXreg( freereg ); + } + else + { + MOV32RmtoR(EAX,EDX); + MOV32RtoRm(ECX,EAX); + + MOV32RmtoROffset(EAX,EDX,4); + MOV32RtoRmOffset(ECX,EAX,4); + } + break; + + case 128: + if( _hasFreeXMMreg() ) + { + const int freereg = _allocTempXMMreg( XMMT_INT, -1 ); + SSE2_MOVDQARmtoROffset(freereg,EDX,0); + SSE2_MOVDQARtoRmOffset(ECX,freereg,0); + _freeXMMreg( freereg ); + } + else + { + // Could put in an MMX optimization here as well, but no point really. + // It's almost never used since there's almost always a free XMM reg. + + MOV32RmtoR(EAX,EDX); + MOV32RtoRm(ECX,EAX); + MOV32RmtoROffset(EAX,EDX,4); + MOV32RtoRmOffset(ECX,EAX,4); + MOV32RmtoROffset(EAX,EDX,8); + MOV32RtoRmOffset(ECX,EAX,8); + MOV32RmtoROffset(EAX,EDX,12); + MOV32RtoRmOffset(ECX,EAX,12); + } + break; + } + u8* cont=JMP8(0); + + x86SetJ8(_full); + + int szidx=0; + switch(sz) + { + case 8: szidx=0; break; + case 16: szidx=1; break; + case 32: szidx=2; break; + case 64: szidx=3; break; + case 128: szidx=4; break; + } + MOVZX32R8toR(EAX,EAX); + SUB32RtoR(ECX,EAX); + //eax=[funct+eax] + MOV32RmSOffsettoR(EAX,EAX,(int)RWFT[szidx][1],2); + SUB32ItoR(ECX,0x80000000); + CALL32R(EAX); + + x86SetJ8(cont); +} diff --git a/pcsx2/x86/ix86/Makefile.am b/pcsx2/x86/ix86/Makefile.am new file mode 100644 index 0000000000..d6564bff29 --- /dev/null +++ b/pcsx2/x86/ix86/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = -I@srcdir@/.. -I@srcdir@/../../ -I@srcdir@/../../common/ +noinst_LIBRARIES = libix86.a + +libix86_a_SOURCES = ix86_tools.cpp ix86_3dnow.cpp ix86.cpp ix86_cpudetect.cpp ix86_fpu.cpp ix86.h ix86_sse.cpp ix86_mmx.cpp + diff --git a/pcsx2/x86/ix86/ix86.cpp b/pcsx2/x86/ix86/ix86.cpp new file mode 100644 index 0000000000..5ed9b637cd --- /dev/null +++ b/pcsx2/x86/ix86/ix86.cpp @@ -0,0 +1,3360 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +/* + * ix86 core v0.6.2 + * Authors: linuzappz + * alexey silinov + * goldfinger + * zerofrog(@gmail.com) + */ + +#include "PrecompiledHeader.h" +#include "System.h" + +#include "ix86.h" + +#define SWAP(x, y) { *(u32*)&y ^= *(u32*)&x; *(u32*)&x ^= *(u32*)&y; *(u32*)&y ^= *(u32*)&x; } + +u8 *x86Ptr; +u8 *j8Ptr[32]; +u32 *j32Ptr[32]; + +extern void SysPrintf(const char *fmt, ...); + +__forceinline void WriteRmOffset(x86IntRegType to, s32 offset) +{ + if( (to&7) == ESP ) { + if( offset == 0 ) { + ModRM( 0, 0, 4 ); + SibSB( 0, ESP, 4 ); + } + else if( offset < 128 && offset >= -128 ) { + ModRM( 1, 0, 4 ); + SibSB( 0, ESP, 4 ); + write8(offset); + } + else { + ModRM( 2, 0, 4 ); + SibSB( 0, ESP, 4 ); + write32(offset); + } + } + else { + if( offset == 0 ) { + ModRM( 0, 0, to ); + } + else if( offset < 128 && offset >= -128 ) { + ModRM( 1, 0, to ); + write8(offset); + } + else { + ModRM( 2, 0, to ); + write32(offset); + } + } +} + +__forceinline void WriteRmOffsetFrom(x86IntRegType to, x86IntRegType from, int offset) +{ + if ((from&7) == ESP) { + if( offset == 0 ) { + ModRM( 0, to, 0x4 ); + SibSB( 0, 0x4, 0x4 ); + } + else if( offset < 128 && offset >= -128 ) { + ModRM( 1, to, 0x4 ); + SibSB( 0, 0x4, 0x4 ); + write8(offset); + } + else { + ModRM( 2, to, 0x4 ); + SibSB( 0, 0x4, 0x4 ); + write32(offset); + } + } + else { + if( offset == 0 ) { + ModRM( 0, to, from ); + } + else if( offset < 128 && offset >= -128 ) { + ModRM( 1, to, from ); + write8(offset); + } + else { + ModRM( 2, to, from ); + write32(offset); + } + } +} + +// This function is just for rec debugging purposes +__forceinline void CheckX86Ptr( void ) +{ +} + +__forceinline void write64( u64 val ) +{ +#ifdef _DEBUG + CheckX86Ptr( ); +#endif + + *(u64*)x86Ptr = val; + x86Ptr += 8; +} + +__forceinline void ModRM( s32 mod, s32 reg, s32 rm ) +{ + write8( ( mod << 6 ) | ( (reg & 7) << 3 ) | ( rm & 7 ) ); +} + +__forceinline void SibSB( s32 ss, s32 index, s32 base ) +{ + write8( ( ss << 6 ) | ( (index & 7) << 3 ) | ( base & 7 ) ); +} + +__forceinline void SET8R( int cc, int to ) +{ + RexB(0, to); + write8( 0x0F ); + write8( cc ); + write8( 0xC0 | ( to ) ); +} + +__forceinline u8* J8Rel( int cc, int to ) +{ + write8( cc ); + write8( to ); + return (u8*)(x86Ptr - 1); +} + +__forceinline u16* J16Rel( int cc, u32 to ) +{ + write16( 0x0F66 ); + write8( cc ); + write16( to ); + return (u16*)( x86Ptr - 2 ); +} + +__forceinline u32* J32Rel( int cc, u32 to ) +{ + write8( 0x0F ); + write8( cc ); + write32( to ); + return (u32*)( x86Ptr - 4 ); +} + +__forceinline void CMOV32RtoR( int cc, int to, int from ) +{ + RexRB(0,to, from); + write8( 0x0F ); + write8( cc ); + ModRM( 3, to, from ); +} + +__forceinline void CMOV32MtoR( int cc, int to, uptr from ) +{ + RexR(0, to); + write8( 0x0F ); + write8( cc ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +//////////////////////////////////////////////////// +__forceinline void x86SetPtr( u8* ptr ) +{ + x86Ptr = ptr; +} + +//////////////////////////////////////////////////// +__forceinline void x86Shutdown( void ) +{ +} + +//////////////////////////////////////////////////// +__forceinline void x86SetJ8( u8* j8 ) +{ + u32 jump = ( x86Ptr - j8 ) - 1; + + if ( jump > 0x7f ) { + Console::Error( "j8 greater than 0x7f!!" ); + assert(0); + } + *j8 = (u8)jump; +} + +__forceinline void x86SetJ8A( u8* j8 ) +{ + u32 jump = ( x86Ptr - j8 ) - 1; + + if ( jump > 0x7f ) { + Console::Error( "j8 greater than 0x7f!!" ); + assert(0); + } + + if( ((uptr)x86Ptr&0xf) > 4 ) { + + uptr newjump = jump + 16-((uptr)x86Ptr&0xf); + + if( newjump <= 0x7f ) { + jump = newjump; + while((uptr)x86Ptr&0xf) *x86Ptr++ = 0x90; + } + } + *j8 = (u8)jump; +} + +__forceinline void x86SetJ16( u16 *j16 ) +{ + // doesn't work + u32 jump = ( x86Ptr - (u8*)j16 ) - 2; + + if ( jump > 0x7fff ) { + Console::Error( "j16 greater than 0x7fff!!" ); + assert(0); + } + *j16 = (u16)jump; +} + +__forceinline void x86SetJ16A( u16 *j16 ) +{ + if( ((uptr)x86Ptr&0xf) > 4 ) { + while((uptr)x86Ptr&0xf) *x86Ptr++ = 0x90; + } + x86SetJ16(j16); +} + +//////////////////////////////////////////////////// +__forceinline void x86SetJ32( u32* j32 ) +{ + *j32 = ( x86Ptr - (u8*)j32 ) - 4; +} + +__forceinline void x86SetJ32A( u32* j32 ) +{ + while((uptr)x86Ptr&0xf) *x86Ptr++ = 0x90; + x86SetJ32(j32); +} + +//////////////////////////////////////////////////// +__forceinline void x86Align( int bytes ) +{ + // fordward align + x86Ptr = (u8*)( ( (uptr)x86Ptr + bytes - 1) & ~( bytes - 1 ) ); +} + +//////////////////////////////////////////////////// +// Generates executable code to align to the given alignment (could be useful for the second leg +// of if/else conditionals, which usually fall through a jump target label). +void x86AlignExecutable( int align ) +{ + uptr newx86 = ( (uptr)x86Ptr + align - 1) & ~( align - 1 ); + uptr bytes = ( newx86 - (uptr)x86Ptr ); + + switch( bytes ) + { + case 0: break; + + case 1: NOP(); break; + case 2: MOV32RtoR( ESI, ESI ); break; + case 3: write8(0x08D); write8(0x024); write8(0x024); break; + case 5: NOP(); // falls through to 4... + case 4: write8(0x08D); write8(0x064); write8(0x024); write8(0); break; + case 6: write8(0x08D); write8(0x0B6); write32(0); break; + case 8: NOP(); // falls through to 7... + case 7: write8(0x08D); write8(0x034); write8(0x035); write32(0); break; + + default: + { + // for larger alignments, just use a JMP... + u8* aligned_target = JMP8(0); + x86Ptr = (u8*)newx86; + x86SetJ8( aligned_target ); + } + } + + jASSUME( x86Ptr == (u8*)newx86 ); +} + +/********************/ +/* IX86 intructions */ +/********************/ + +__forceinline void STC( void ) +{ + write8( 0xF9 ); +} + +__forceinline void CLC( void ) +{ + write8( 0xF8 ); +} + +// NOP 1-byte +__forceinline void NOP( void ) +{ + write8(0x90); +} + + +//////////////////////////////////// +// mov instructions / +//////////////////////////////////// + +/* mov r64 to r64 */ +__forceinline void MOV64RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(1, from, to); + write8( 0x89 ); + ModRM( 3, from, to ); +} + +/* mov r64 to m64 */ +__forceinline void MOV64RtoM( uptr to, x86IntRegType from ) +{ + RexR(1, from); + write8( 0x89 ); + ModRM( 0, from, DISP32 ); + write32( (u32)MEMADDR(to, 4) ); +} + +/* mov m64 to r64 */ +__forceinline void MOV64MtoR( x86IntRegType to, uptr from ) +{ + RexR(1, to); + write8( 0x8B ); + ModRM( 0, to, DISP32 ); + write32( (u32)MEMADDR(from, 4) ); +} + +/* mov imm32 to m64 */ +__forceinline void MOV64I32toM(uptr to, u32 from ) +{ + Rex(1, 0, 0, 0); + write8( 0xC7 ); + ModRM( 0, 0, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); +} + +// mov imm64 to r64 +__forceinline void MOV64ItoR( x86IntRegType to, u64 from) +{ + RexB(1, to); + write8( 0xB8 | (to & 0x7) ); + write64( from ); +} + +/* mov imm32 to r64 */ +__forceinline void MOV64I32toR( x86IntRegType to, s32 from ) +{ + RexB(1, to); + write8( 0xC7 ); + ModRM( 0, 0, to ); + write32( from ); +} + +// mov imm64 to [r64+off] +__forceinline void MOV64ItoRmOffset( x86IntRegType to, u32 from, int offset) +{ + RexB(1,to); + write8( 0xC7 ); + WriteRmOffset(to, offset); + write32(from); +} + +// mov [r64+offset] to r64 +__forceinline void MOV64RmOffsettoR( x86IntRegType to, x86IntRegType from, int offset ) +{ + RexRB(1, to, from); + write8( 0x8B ); + WriteRmOffsetFrom(to, from, offset); +} + +/* mov [r64][r64*scale] to r64 */ +__forceinline void MOV64RmStoR( x86IntRegType to, x86IntRegType from, x86IntRegType from2, int scale) { + RexRXB(1, to, from2, from); + write8( 0x8B ); + ModRM( 0, to, 0x4 ); + SibSB(scale, from2, from ); +} + +/* mov r64 to [r64+offset] */ +__forceinline void MOV64RtoRmOffset( x86IntRegType to, x86IntRegType from, int offset ) +{ + RexRB(1,from,to); + write8( 0x89 ); + WriteRmOffsetFrom(from, to, offset); +} + +/* mov r64 to [r64][r64*scale] */ +__forceinline void MOV64RtoRmS( x86IntRegType to, x86IntRegType from, x86IntRegType from2, int scale) { + RexRXB(1, to, from2, from); + write8( 0x89 ); + ModRM( 0, to, 0x4 ); + SibSB(scale, from2, from ); +} + + +/* mov r32 to r32 */ +__forceinline void MOV32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0, from, to); + write8( 0x89 ); + ModRM( 3, from, to ); +} + +/* mov r32 to m32 */ +void MOV32RtoM( uptr to, x86IntRegType from ) +{ + RexR(0, from); + write8( 0x89 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* mov m32 to r32 */ +__forceinline void MOV32MtoR( x86IntRegType to, uptr from ) +{ + RexR(0, to); + write8( 0x8B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* mov [r32] to r32 */ +__forceinline void MOV32RmtoR( x86IntRegType to, x86IntRegType from ) { + RexRB(0, to, from); + write8(0x8B); + WriteRmOffsetFrom(to, from, 0); +} + +__forceinline void MOV32RmtoROffset( x86IntRegType to, x86IntRegType from, int offset ) { + RexRB(0, to, from); + write8( 0x8B ); + WriteRmOffsetFrom(to, from, offset); +} + +/* mov [r32+r32*scale] to r32 */ +__forceinline void MOV32RmStoR( x86IntRegType to, x86IntRegType from, x86IntRegType from2, int scale) { + RexRXB(0,to,from2,from); + write8( 0x8B ); + ModRM( 0, to, 0x4 ); + SibSB(scale, from2, from ); +} + +// mov r32 to [r32<> 3); + if ( to == EAX) { + write8( 0x05 ); + } else { + write8( 0x81 ); + ModRM( 3, 0, to ); + } + write32( from ); +} + +/* add m64 to r64 */ +__forceinline void ADD64MtoR( x86IntRegType to, uptr from ) +{ + Rex(1, to >> 3, 0, 0); + write8( 0x03 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* add r64 to r64 */ +__forceinline void ADD64RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(1, from, to); + write8( 0x01 ); + ModRM( 3, from, to ); +} + +/* add imm32 to EAX */ +void ADD32ItoEAX( u32 from ) +{ + write8( 0x05 ); + write32( from ); +} + +/* add imm32 to r32 */ +__forceinline void ADD32ItoR( x86IntRegType to, u32 from ) +{ + RexB(0, to); + if(from < 0x80) + { + write8( 0x83 ); + ModRM( 3, 0, to ); + write8( from ); + } + else + { + if ( to == EAX ) { + ADD32ItoEAX(from); + } + else { + write8( 0x81 ); + ModRM( 3, 0, to ); + write32( from ); + } + } +} + +/* add imm32 to m32 */ +__forceinline void ADD32ItoM( uptr to, u32 from ) +{ + /*if(from < 0x80) // crashes games in 64bit build; TODO: figure out why. + { + write8( 0x83 ); + ModRM( 0, 0, DISP32 ); + write32( MEMADDR(to, 8) ); + write8( from ); + } + else*/ + { + write8( 0x81 ); + ModRM( 0, 0, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); + } +} + +// add imm32 to [r32+off] +__forceinline void ADD32ItoRmOffset( x86IntRegType to, u32 from, s32 offset) +{ + RexB(0,to); + if(from < 0x80) + { + write8( 0x83 ); + WriteRmOffset(to,offset); + write8(from); + } + else + { + write8( 0x81 ); + WriteRmOffset(to,offset); + write32(from); + } +} + +/* add r32 to r32 */ +__forceinline void ADD32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0, from, to); + write8( 0x01 ); + ModRM( 3, from, to ); +} + +/* add r32 to m32 */ +__forceinline void ADD32RtoM(uptr to, x86IntRegType from ) +{ + RexR(0,from); + write8( 0x01 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* add m32 to r32 */ +__forceinline void ADD32MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x03 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// add r16 to r16 +__forceinline void ADD16RtoR( x86IntRegType to , x86IntRegType from ) +{ + write8(0x66); + RexRB(0,to,from); + write8( 0x03 ); + ModRM( 3, to, from ); +} + +/* add imm16 to r16 */ +__forceinline void ADD16ItoR( x86IntRegType to, u16 from ) +{ + write8( 0x66 ); + RexB(0,to); + + if ( to == EAX) + { + write8( 0x05 ); + write16( from ); + } + else if(from < 0x80) + { + write8( 0x83 ); + ModRM( 3, 0, to ); + write8((u8)from ); + } + else + { + write8( 0x81 ); + ModRM( 3, 0, to ); + write16( from ); + } +} + +/* add imm16 to m16 */ +__forceinline void ADD16ItoM( uptr to, u16 from ) +{ + write8( 0x66 ); + if(from < 0x80) + { + write8( 0x83 ); + ModRM( 0, 0, DISP32 ); + write32( MEMADDR(to, 6) ); + write8((u8)from ); + } + else + { + write8( 0x81 ); + ModRM( 0, 0, DISP32 ); + write32( MEMADDR(to, 6) ); + write16( from ); + } +} + +/* add r16 to m16 */ +__forceinline void ADD16RtoM(uptr to, x86IntRegType from ) +{ + write8( 0x66 ); + RexR(0,from); + write8( 0x01 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* add m16 to r16 */ +__forceinline void ADD16MtoR( x86IntRegType to, uptr from ) +{ + write8( 0x66 ); + RexR(0,to); + write8( 0x03 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// add m8 to r8 +__forceinline void ADD8MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x02 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* adc imm32 to r32 */ +__forceinline void ADC32ItoR( x86IntRegType to, u32 from ) +{ + RexB(0,to); + if ( to == EAX ) { + write8( 0x15 ); + } + else { + write8( 0x81 ); + ModRM( 3, 2, to ); + } + write32( from ); +} + +/* adc imm32 to m32 */ +__forceinline void ADC32ItoM( uptr to, u32 from ) +{ + write8( 0x81 ); + ModRM( 0, 2, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); +} + +/* adc r32 to r32 */ +__forceinline void ADC32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0,from,to); + write8( 0x11 ); + ModRM( 3, from, to ); +} + +/* adc m32 to r32 */ +__forceinline void ADC32MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x13 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// adc r32 to m32 +__forceinline void ADC32RtoM( uptr to, x86IntRegType from ) +{ + RexR(0,from); + write8( 0x11 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* inc r32 */ +__forceinline void INC32R( x86IntRegType to ) +{ + write8( 0x40 + to ); +} + +/* inc m32 */ +__forceinline void INC32M( u32 to ) +{ + write8( 0xFF ); + ModRM( 0, 0, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* inc r16 */ +__forceinline void INC16R( x86IntRegType to ) +{ + write8( 0x66 ); + write8( 0x40 + to ); +} + +/* inc m16 */ +__forceinline void INC16M( u32 to ) +{ + write8( 0x66 ); + write8( 0xFF ); + ModRM( 0, 0, DISP32 ); + write32( MEMADDR(to, 4) ); +} + + +/* sub imm32 to r64 */ +__forceinline void SUB64ItoR( x86IntRegType to, u32 from ) +{ + RexB(1, to); + if ( to == EAX ) { + write8( 0x2D ); + } + else { + write8( 0x81 ); + ModRM( 3, 5, to ); + } + write32( from ); +} + +/* sub r64 to r64 */ +__forceinline void SUB64RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(1, from, to); + write8( 0x29 ); + ModRM( 3, from, to ); +} + +/* sub m64 to r64 */ +__forceinline void SUB64MtoR( x86IntRegType to, uptr from ) +{ + RexR(1, to); + write8( 0x2B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* sub imm32 to r32 */ +__forceinline void SUB32ItoR( x86IntRegType to, u32 from ) +{ + RexB(0,to); + if ( to == EAX ) { + write8( 0x2D ); + } + else { + write8( 0x81 ); + ModRM( 3, 5, to ); + } + write32( from ); +} + +/* sub imm32 to m32 */ +__forceinline void SUB32ItoM( uptr to, u32 from ) +{ + write8( 0x81 ); + ModRM( 0, 5, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); +} + +/* sub r32 to r32 */ +__forceinline void SUB32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0, from, to); + write8( 0x29 ); + ModRM( 3, from, to ); +} + +/* sub m32 to r32 */ +__forceinline void SUB32MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x2B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// sub r32 to m32 +__forceinline void SUB32RtoM( uptr to, x86IntRegType from ) +{ + RexR(0,from); + write8( 0x29 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +// sub r16 to r16 +__forceinline void SUB16RtoR( x86IntRegType to, u16 from ) +{ + write8(0x66); + RexRB(0,to,from); + write8( 0x2b ); + ModRM( 3, to, from ); +} + +/* sub imm16 to r16 */ +__forceinline void SUB16ItoR( x86IntRegType to, u16 from ) { + write8( 0x66 ); + RexB(0,to); + if ( to == EAX ) { + write8( 0x2D ); + } else { + write8( 0x81 ); + ModRM( 3, 5, to ); + } + write16( from ); +} + +/* sub imm16 to m16 */ +__forceinline void SUB16ItoM( uptr to, u16 from ) { + write8( 0x66 ); + write8( 0x81 ); + ModRM( 0, 5, DISP32 ); + write32( MEMADDR(to, 6) ); + write16( from ); +} + +/* sub m16 to r16 */ +__forceinline void SUB16MtoR( x86IntRegType to, uptr from ) { + write8( 0x66 ); + RexR(0,to); + write8( 0x2B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* sbb r64 to r64 */ +__forceinline void SBB64RtoR( x86IntRegType to, x86IntRegType from ) { + RexRB(1, from,to); + write8( 0x19 ); + ModRM( 3, from, to ); +} + +/* sbb imm32 to r32 */ +__forceinline void SBB32ItoR( x86IntRegType to, u32 from ) { + RexB(0,to); + if ( to == EAX ) { + write8( 0x1D ); + } + else { + write8( 0x81 ); + ModRM( 3, 3, to ); + } + write32( from ); +} + +/* sbb imm32 to m32 */ +__forceinline void SBB32ItoM( uptr to, u32 from ) { + write8( 0x81 ); + ModRM( 0, 3, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); +} + +/* sbb r32 to r32 */ +__forceinline void SBB32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0,from,to); + write8( 0x19 ); + ModRM( 3, from, to ); +} + +/* sbb m32 to r32 */ +__forceinline void SBB32MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x1B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* sbb r32 to m32 */ +__forceinline void SBB32RtoM( uptr to, x86IntRegType from ) +{ + RexR(0,from); + write8( 0x19 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* dec r32 */ +__forceinline void DEC32R( x86IntRegType to ) +{ + write8( 0x48 + to ); +} + +/* dec m32 */ +__forceinline void DEC32M( u32 to ) +{ + write8( 0xFF ); + ModRM( 0, 1, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* dec r16 */ +__forceinline void DEC16R( x86IntRegType to ) +{ + write8( 0x66 ); + write8( 0x48 + to ); +} + +/* dec m16 */ +__forceinline void DEC16M( u32 to ) +{ + write8( 0x66 ); + write8( 0xFF ); + ModRM( 0, 1, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* mul eax by r32 to edx:eax */ +__forceinline void MUL32R( x86IntRegType from ) +{ + RexB(0,from); + write8( 0xF7 ); + ModRM( 3, 4, from ); +} + +/* imul eax by r32 to edx:eax */ +__forceinline void IMUL32R( x86IntRegType from ) +{ + RexB(0,from); + write8( 0xF7 ); + ModRM( 3, 5, from ); +} + +/* mul eax by m32 to edx:eax */ +__forceinline void MUL32M( u32 from ) +{ + write8( 0xF7 ); + ModRM( 0, 4, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* imul eax by m32 to edx:eax */ +__forceinline void IMUL32M( u32 from ) +{ + write8( 0xF7 ); + ModRM( 0, 5, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* imul r32 by r32 to r32 */ +__forceinline void IMUL32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0,to,from); + write16( 0xAF0F ); + ModRM( 3, to, from ); +} + +/* div eax by r32 to edx:eax */ +__forceinline void DIV32R( x86IntRegType from ) +{ + RexB(0,from); + write8( 0xF7 ); + ModRM( 3, 6, from ); +} + +/* idiv eax by r32 to edx:eax */ +__forceinline void IDIV32R( x86IntRegType from ) +{ + RexB(0,from); + write8( 0xF7 ); + ModRM( 3, 7, from ); +} + +/* div eax by m32 to edx:eax */ +__forceinline void DIV32M( u32 from ) +{ + write8( 0xF7 ); + ModRM( 0, 6, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* idiv eax by m32 to edx:eax */ +__forceinline void IDIV32M( u32 from ) +{ + write8( 0xF7 ); + ModRM( 0, 7, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +//////////////////////////////////// +// shifting instructions / +//////////////////////////////////// + +/* shl imm8 to r64 */ +__forceinline void SHL64ItoR( x86IntRegType to, u8 from ) +{ + RexB(1, to); + if ( from == 1 ) + { + write8( 0xD1 ); + ModRM( 3, 4, to ); + return; + } + write8( 0xC1 ); + ModRM( 3, 4, to ); + write8( from ); +} + +/* shl cl to r64 */ +__forceinline void SHL64CLtoR( x86IntRegType to ) +{ + RexB(1, to); + write8( 0xD3 ); + ModRM( 3, 4, to ); +} + +/* shr imm8 to r64 */ +__forceinline void SHR64ItoR( x86IntRegType to, u8 from ) +{ + RexB(1,to); + if ( from == 1 ) { + write8( 0xD1 ); + ModRM( 3, 5, to ); + return; + } + write8( 0xC1 ); + ModRM( 3, 5, to ); + write8( from ); +} + +/* shr cl to r64 */ +__forceinline void SHR64CLtoR( x86IntRegType to ) +{ + RexB(1, to); + write8( 0xD3 ); + ModRM( 3, 5, to ); +} + +/* shl imm8 to r32 */ +__forceinline void SHL32ItoR( x86IntRegType to, u8 from ) +{ + RexB(0, to); + if ( from == 1 ) + { + write8( 0xD1 ); + write8( 0xE0 | (to & 0x7) ); + return; + } + write8( 0xC1 ); + ModRM( 3, 4, to ); + write8( from ); +} + +/* shl imm8 to m32 */ +__forceinline void SHL32ItoM( uptr to, u8 from ) +{ + if ( from == 1 ) + { + write8( 0xD1 ); + ModRM( 0, 4, DISP32 ); + write32( MEMADDR(to, 4) ); + } + else + { + write8( 0xC1 ); + ModRM( 0, 4, DISP32 ); + write32( MEMADDR(to, 5) ); + write8( from ); + } +} + +/* shl cl to r32 */ +__forceinline void SHL32CLtoR( x86IntRegType to ) +{ + RexB(0,to); + write8( 0xD3 ); + ModRM( 3, 4, to ); +} + +// shl imm8 to r16 +__forceinline void SHL16ItoR( x86IntRegType to, u8 from ) +{ + write8(0x66); + RexB(0,to); + if ( from == 1 ) + { + write8( 0xD1 ); + write8( 0xE0 | (to & 0x7) ); + return; + } + write8( 0xC1 ); + ModRM( 3, 4, to ); + write8( from ); +} + +// shl imm8 to r8 +__forceinline void SHL8ItoR( x86IntRegType to, u8 from ) +{ + RexB(0,to); + if ( from == 1 ) + { + write8( 0xD0 ); + write8( 0xE0 | (to & 0x7) ); + return; + } + write8( 0xC0 ); + ModRM( 3, 4, to ); + write8( from ); +} + +/* shr imm8 to r32 */ +__forceinline void SHR32ItoR( x86IntRegType to, u8 from ) { + RexB(0,to); + if ( from == 1 ) + { + write8( 0xD1 ); + write8( 0xE8 | (to & 0x7) ); + } + else + { + write8( 0xC1 ); + ModRM( 3, 5, to ); + write8( from ); + } +} + +/* shr imm8 to m32 */ +__forceinline void SHR32ItoM( uptr to, u8 from ) +{ + if ( from == 1 ) + { + write8( 0xD1 ); + ModRM( 0, 5, DISP32 ); + write32( MEMADDR(to, 4) ); + } + else + { + write8( 0xC1 ); + ModRM( 0, 5, DISP32 ); + write32( MEMADDR(to, 5) ); + write8( from ); + } +} + +/* shr cl to r32 */ +__forceinline void SHR32CLtoR( x86IntRegType to ) +{ + RexB(0,to); + write8( 0xD3 ); + ModRM( 3, 5, to ); +} + +// shr imm8 to r16 +__forceinline void SHR16ItoR( x86IntRegType to, u8 from ) +{ + RexB(0,to); + if ( from == 1 ) + { + write8( 0xD1 ); + ModRM( 3, 5, to ); + } + else + { + write8( 0xC1 ); + ModRM( 3, 5, to ); + write8( from ); + } +} + +// shr imm8 to r8 +__forceinline void SHR8ItoR( x86IntRegType to, u8 from ) +{ + RexB(0,to); + if ( from == 1 ) + { + write8( 0xD0 ); + write8( 0xE8 | (to & 0x7) ); + } + else + { + write8( 0xC0 ); + ModRM( 3, 5, to ); + write8( from ); + } +} + +/* sar imm8 to r64 */ +__forceinline void SAR64ItoR( x86IntRegType to, u8 from ) +{ + RexB(1,to); + if ( from == 1 ) + { + write8( 0xD1 ); + ModRM( 3, 7, to ); + return; + } + write8( 0xC1 ); + ModRM( 3, 7, to ); + write8( from ); +} + +/* sar cl to r64 */ +__forceinline void SAR64CLtoR( x86IntRegType to ) +{ + RexB(1, to); + write8( 0xD3 ); + ModRM( 3, 7, to ); +} + +/* sar imm8 to r32 */ +__forceinline void SAR32ItoR( x86IntRegType to, u8 from ) +{ + RexB(0,to); + if ( from == 1 ) + { + write8( 0xD1 ); + ModRM( 3, 7, to ); + return; + } + write8( 0xC1 ); + ModRM( 3, 7, to ); + write8( from ); +} + +/* sar imm8 to m32 */ +__forceinline void SAR32ItoM( uptr to, u8 from ) +{ + write8( 0xC1 ); + ModRM( 0, 7, DISP32 ); + write32( MEMADDR(to, 5) ); + write8( from ); +} + +/* sar cl to r32 */ +__forceinline void SAR32CLtoR( x86IntRegType to ) +{ + RexB(0,to); + write8( 0xD3 ); + ModRM( 3, 7, to ); +} + +// sar imm8 to r16 +__forceinline void SAR16ItoR( x86IntRegType to, u8 from ) +{ + write8(0x66); + RexB(0,to); + if ( from == 1 ) + { + write8( 0xD1 ); + ModRM( 3, 7, to ); + return; + } + write8( 0xC1 ); + ModRM( 3, 7, to ); + write8( from ); +} + +__forceinline void ROR32ItoR( x86IntRegType to,u8 from ) +{ + RexB(0,to); + if ( from == 1 ) { + write8( 0xd1 ); + write8( 0xc8 | to ); + } + else + { + write8( 0xc1 ); + write8( 0xc8 | to ); + write8( from ); + } +} + +__forceinline void RCR32ItoR( x86IntRegType to, u8 from ) +{ + RexB(0,to); + if ( from == 1 ) { + write8( 0xd1 ); + ModRM(3, 3, to); + } + else + { + write8( 0xc1 ); + ModRM(3, 3, to); + write8( from ); + } +} + +__forceinline void RCR32ItoM( uptr to, u8 from ) +{ + RexB(0,to); + if ( from == 1 ) { + write8( 0xd1 ); + ModRM( 0, 3, DISP32 ); + write32( MEMADDR(to, 8) ); + } + else + { + write8( 0xc1 ); + ModRM( 0, 3, DISP32 ); + write32( MEMADDR(to, 8) ); + write8( from ); + } +} + +// shld imm8 to r32 +__forceinline void SHLD32ItoR( x86IntRegType to, x86IntRegType from, u8 shift ) +{ + RexRB(0,from,to); + write8( 0x0F ); + write8( 0xA4 ); + ModRM( 3, from, to ); + write8( shift ); +} + +// shrd imm8 to r32 +__forceinline void SHRD32ItoR( x86IntRegType to, x86IntRegType from, u8 shift ) +{ + RexRB(0,from,to); + write8( 0x0F ); + write8( 0xAC ); + ModRM( 3, from, to ); + write8( shift ); +} + +//////////////////////////////////// +// logical instructions / +//////////////////////////////////// + +/* or imm32 to r32 */ +__forceinline void OR64ItoR( x86IntRegType to, u32 from ) +{ + RexB(1, to); + if ( to == EAX ) { + write8( 0x0D ); + } else { + write8( 0x81 ); + ModRM( 3, 1, to ); + } + write32( from ); +} + +/* or m64 to r64 */ +__forceinline void OR64MtoR( x86IntRegType to, uptr from ) +{ + RexR(1, to); + write8( 0x0B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* or r64 to r64 */ +__forceinline void OR64RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(1, from, to); + write8( 0x09 ); + ModRM( 3, from, to ); +} + +// or r32 to m64 +__forceinline void OR64RtoM(uptr to, x86IntRegType from ) +{ + RexR(1,from); + write8( 0x09 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* or imm32 to r32 */ +__forceinline void OR32ItoR( x86IntRegType to, u32 from ) +{ + RexB(0,to); + if ( to == EAX ) { + write8( 0x0D ); + } + else { + write8( 0x81 ); + ModRM( 3, 1, to ); + } + write32( from ); +} + +/* or imm32 to m32 */ +__forceinline void OR32ItoM(uptr to, u32 from ) +{ + write8( 0x81 ); + ModRM( 0, 1, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); +} + +/* or r32 to r32 */ +__forceinline void OR32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0,from,to); + write8( 0x09 ); + ModRM( 3, from, to ); +} + +/* or r32 to m32 */ +__forceinline void OR32RtoM(uptr to, x86IntRegType from ) +{ + RexR(0,from); + write8( 0x09 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* or m32 to r32 */ +__forceinline void OR32MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x0B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// or r16 to r16 +__forceinline void OR16RtoR( x86IntRegType to, x86IntRegType from ) +{ + write8(0x66); + RexRB(0,from,to); + write8( 0x09 ); + ModRM( 3, from, to ); +} + +// or imm16 to r16 +__forceinline void OR16ItoR( x86IntRegType to, u16 from ) +{ + write8(0x66); + RexB(0,to); + if ( to == EAX ) { + write8( 0x0D ); + } + else { + write8( 0x81 ); + ModRM( 3, 1, to ); + } + write16( from ); +} + +// or imm16 to m316 +__forceinline void OR16ItoM( uptr to, u16 from ) +{ + write8(0x66); + write8( 0x81 ); + ModRM( 0, 1, DISP32 ); + write32( MEMADDR(to, 6) ); + write16( from ); +} + +/* or m16 to r16 */ +__forceinline void OR16MtoR( x86IntRegType to, uptr from ) +{ + write8(0x66); + RexR(0,to); + write8( 0x0B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// or r16 to m16 +__forceinline void OR16RtoM( uptr to, x86IntRegType from ) +{ + write8(0x66); + RexR(0,from); + write8( 0x09 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +// or r8 to r8 +__forceinline void OR8RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0,from,to); + write8( 0x08 ); + ModRM( 3, from, to ); +} + +// or r8 to m8 +__forceinline void OR8RtoM( uptr to, x86IntRegType from ) +{ + RexR(0,from); + write8( 0x08 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +// or imm8 to m8 +__forceinline void OR8ItoM( uptr to, u8 from ) +{ + write8( 0x80 ); + ModRM( 0, 1, DISP32 ); + write32( MEMADDR(to, 5) ); + write8( from ); +} + +// or m8 to r8 +__forceinline void OR8MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x0A ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* xor imm32 to r64 */ +__forceinline void XOR64ItoR( x86IntRegType to, u32 from ) +{ + RexB(1,to); + if ( to == EAX ) { + write8( 0x35 ); + } else { + write8( 0x81 ); + ModRM( 3, 6, to ); + } + write32( from ); +} + +/* xor r64 to r64 */ +__forceinline void XOR64RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(1, from, to); + write8( 0x31 ); + ModRM( 3, from, to ); +} + +/* xor m64 to r64 */ +__forceinline void XOR64MtoR( x86IntRegType to, uptr from ) +{ + RexR(1, to); + write8( 0x33 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* xor r64 to m64 */ +__forceinline void XOR64RtoM( uptr to, x86IntRegType from ) +{ + RexR(1,from); + write8( 0x31 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* xor imm32 to r32 */ +__forceinline void XOR32ItoR( x86IntRegType to, u32 from ) +{ + RexB(0,to); + if ( to == EAX ) { + write8( 0x35 ); + } + else { + write8( 0x81 ); + ModRM( 3, 6, to ); + } + write32( from ); +} + +/* xor imm32 to m32 */ +__forceinline void XOR32ItoM( uptr to, u32 from ) +{ + write8( 0x81 ); + ModRM( 0, 6, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); +} + +/* xor r32 to r32 */ +__forceinline void XOR32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0,from,to); + write8( 0x31 ); + ModRM( 3, from, to ); +} + +/* xor r16 to r16 */ +__forceinline void XOR16RtoR( x86IntRegType to, x86IntRegType from ) +{ + write8( 0x66 ); + RexRB(0,from,to); + write8( 0x31 ); + ModRM( 3, from, to ); +} + +/* xor r32 to m32 */ +__forceinline void XOR32RtoM( uptr to, x86IntRegType from ) +{ + RexR(0,from); + write8( 0x31 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* xor m32 to r32 */ +__forceinline void XOR32MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x33 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// xor imm16 to r16 +__forceinline void XOR16ItoR( x86IntRegType to, u16 from ) +{ + write8(0x66); + RexB(0,to); + if ( to == EAX ) { + write8( 0x35 ); + } + else { + write8( 0x81 ); + ModRM( 3, 6, to ); + } + write16( from ); +} + +// xor r16 to m16 +__forceinline void XOR16RtoM( uptr to, x86IntRegType from ) +{ + write8(0x66); + RexR(0,from); + write8( 0x31 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* and imm32 to r64 */ +__forceinline void AND64I32toR( x86IntRegType to, u32 from ) +{ + RexB(1, to); + if ( to == EAX ) { + write8( 0x25 ); + } else { + write8( 0x81 ); + ModRM( 3, 0x4, to ); + } + write32( from ); +} + +/* and m64 to r64 */ +__forceinline void AND64MtoR( x86IntRegType to, uptr from ) +{ + RexR(1, to); + write8( 0x23 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* and r64 to m64 */ +__forceinline void AND64RtoM( uptr to, x86IntRegType from ) +{ + RexR(1, from); + write8( 0x21 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* and r64 to r64 */ +__forceinline void AND64RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(1, from, to); + write8( 0x21 ); + ModRM( 3, from, to ); +} + +/* and imm32 to m64 */ +__forceinline void AND64I32toM( uptr to, u32 from ) +{ + Rex(1,0,0,0); + write8( 0x81 ); + ModRM( 0, 0x4, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); +} + +/* and imm32 to r32 */ +__forceinline void AND32ItoR( x86IntRegType to, u32 from ) +{ + RexB(0,to); + if(from < 0x80) + { + AND32I8toR(to, (u8)from); + } + else + { + if ( to == EAX ) { + write8( 0x25 ); + } else { + write8( 0x81 ); + ModRM( 3, 0x4, to ); + } + write32( from ); + } +} + +/* and sign ext imm8 to r32 */ +__forceinline void AND32I8toR( x86IntRegType to, u8 from ) +{ + RexB(0,to); + write8( 0x83 ); + ModRM( 3, 0x4, to ); + write8( from ); +} + +/* and imm32 to m32 */ +__forceinline void AND32ItoM( uptr to, u32 from ) +{ + if(from < 0x80) + { + AND32I8toM(to, (u8)from); + } + else + { + write8( 0x81 ); + ModRM( 0, 0x4, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); + } +} + + +/* and sign ext imm8 to m32 */ +__forceinline void AND32I8toM( uptr to, u8 from ) +{ + write8( 0x83 ); + ModRM( 0, 0x4, DISP32 ); + write32( MEMADDR(to, 5) ); + write8( from ); +} + +/* and r32 to r32 */ +__forceinline void AND32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0,from,to); + write8( 0x21 ); + ModRM( 3, from, to ); +} + +/* and r32 to m32 */ +__forceinline void AND32RtoM( uptr to, x86IntRegType from ) +{ + RexR(0,from); + write8( 0x21 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* and m32 to r32 */ +__forceinline void AND32MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x23 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// and r16 to r16 +__forceinline void AND16RtoR( x86IntRegType to, x86IntRegType from ) +{ + write8(0x66); + RexRB(0,to,from); + write8( 0x23 ); + ModRM( 3, to, from ); +} + +/* and imm16 to r16 */ +__forceinline void AND16ItoR( x86IntRegType to, u16 from ) +{ + write8(0x66); + RexB(0,to); + + if ( to == EAX ) { + write8( 0x25 ); + write16( from ); + } + else if ( from < 0x80 ) { + write8( 0x83 ); + ModRM( 3, 0x4, to ); + write8((u8)from ); + } + else { + write8( 0x81 ); + ModRM( 3, 0x4, to ); + write16( from ); + } +} + +/* and imm16 to m16 */ +__forceinline void AND16ItoM( uptr to, u16 from ) +{ + write8(0x66); + if ( from < 0x80 ) { + write8( 0x83 ); + ModRM( 0, 0x4, DISP32 ); + write32( MEMADDR(to, 6) ); + write8((u8)from ); + } + else + { + write8( 0x81 ); + ModRM( 0, 0x4, DISP32 ); + write32( MEMADDR(to, 6) ); + write16( from ); + + } +} + +/* and r16 to m16 */ +__forceinline void AND16RtoM( uptr to, x86IntRegType from ) +{ + write8( 0x66 ); + RexR(0,from); + write8( 0x21 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* and m16 to r16 */ +__forceinline void AND16MtoR( x86IntRegType to, uptr from ) +{ + write8( 0x66 ); + RexR(0,to); + write8( 0x23 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4)); +} + +/* and imm8 to r8 */ +__forceinline void AND8ItoR( x86IntRegType to, u8 from ) +{ + RexB(0,to); + if ( to == EAX ) { + write8( 0x24 ); + } else { + write8( 0x80 ); + ModRM( 3, 0x4, to ); + } + write8( from ); +} + +/* and imm8 to m8 */ +__forceinline void AND8ItoM( uptr to, u8 from ) +{ + write8( 0x80 ); + ModRM( 0, 0x4, DISP32 ); + write32( MEMADDR(to, 5) ); + write8( from ); +} + +// and r8 to r8 +__forceinline void AND8RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0,to,from); + write8( 0x22 ); + ModRM( 3, to, from ); +} + +/* and r8 to m8 */ +__forceinline void AND8RtoM( uptr to, x86IntRegType from ) +{ + RexR(0,from); + write8( 0x20 ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* and m8 to r8 */ +__forceinline void AND8MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x22 ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4)); +} + +/* not r64 */ +__forceinline void NOT64R( x86IntRegType from ) +{ + RexB(1, from); + write8( 0xF7 ); + ModRM( 3, 2, from ); +} + +/* not r32 */ +__forceinline void NOT32R( x86IntRegType from ) +{ + RexB(0,from); + write8( 0xF7 ); + ModRM( 3, 2, from ); +} + +// not m32 +__forceinline void NOT32M( u32 from ) +{ + write8( 0xF7 ); + ModRM( 0, 2, DISP32 ); + write32( MEMADDR(from, 4)); +} + +/* neg r64 */ +__forceinline void NEG64R( x86IntRegType from ) +{ + RexB(1, from); + write8( 0xF7 ); + ModRM( 3, 3, from ); +} + +/* neg r32 */ +__forceinline void NEG32R( x86IntRegType from ) +{ + RexB(0,from); + write8( 0xF7 ); + ModRM( 3, 3, from ); +} + +__forceinline void NEG32M( u32 from ) +{ + write8( 0xF7 ); + ModRM( 0, 3, DISP32 ); + write32( MEMADDR(from, 4)); +} + +/* neg r16 */ +__forceinline void NEG16R( x86IntRegType from ) +{ + write8( 0x66 ); + RexB(0,from); + write8( 0xF7 ); + ModRM( 3, 3, from ); +} + +//////////////////////////////////// +// jump instructions / +//////////////////////////////////// + +__forceinline u8* JMP( uptr to ) { + uptr jump = ( x86Ptr - (u8*)to ) - 1; + + if ( jump > 0x7f ) { + assert( to <= 0xffffffff ); + return (u8*)JMP32( to ); + } + else { + return (u8*)JMP8( to ); + } +} + +/* jmp rel8 */ +__forceinline u8* JMP8( u8 to ) +{ + write8( 0xEB ); + write8( to ); + return x86Ptr - 1; +} + +/* jmp rel32 */ +__forceinline u32* JMP32( uptr to ) +{ + assert( (sptr)to <= 0x7fffffff && (sptr)to >= -0x7fffffff ); + write8( 0xE9 ); + write32( to ); + return (u32*)(x86Ptr - 4 ); +} + +/* jmp r32/r64 */ +__forceinline void JMPR( x86IntRegType to ) +{ + RexB(0, to); + write8( 0xFF ); + ModRM( 3, 4, to ); +} + +// jmp m32 +__forceinline void JMP32M( uptr to ) +{ + write8( 0xFF ); + ModRM( 0, 4, DISP32 ); + write32( MEMADDR(to, 4)); +} + +/* jp rel8 */ +__forceinline u8* JP8( u8 to ) { + return J8Rel( 0x7A, to ); +} + +/* jnp rel8 */ +__forceinline u8* JNP8( u8 to ) { + return J8Rel( 0x7B, to ); +} + +/* je rel8 */ +__forceinline u8* JE8( u8 to ) { + return J8Rel( 0x74, to ); +} + +/* jz rel8 */ +__forceinline u8* JZ8( u8 to ) +{ + return J8Rel( 0x74, to ); +} + +/* js rel8 */ +__forceinline u8* JS8( u8 to ) +{ + return J8Rel( 0x78, to ); +} + +/* jns rel8 */ +__forceinline u8* JNS8( u8 to ) +{ + return J8Rel( 0x79, to ); +} + +/* jg rel8 */ +__forceinline u8* JG8( u8 to ) +{ + return J8Rel( 0x7F, to ); +} + +/* jge rel8 */ +__forceinline u8* JGE8( u8 to ) +{ + return J8Rel( 0x7D, to ); +} + +/* jl rel8 */ +__forceinline u8* JL8( u8 to ) +{ + return J8Rel( 0x7C, to ); +} + +/* ja rel8 */ +__forceinline u8* JA8( u8 to ) +{ + return J8Rel( 0x77, to ); +} + +__forceinline u8* JAE8( u8 to ) +{ + return J8Rel( 0x73, to ); +} + +/* jb rel8 */ +__forceinline u8* JB8( u8 to ) +{ + return J8Rel( 0x72, to ); +} + +/* jbe rel8 */ +__forceinline u8* JBE8( u8 to ) +{ + return J8Rel( 0x76, to ); +} + +/* jle rel8 */ +__forceinline u8* JLE8( u8 to ) +{ + return J8Rel( 0x7E, to ); +} + +/* jne rel8 */ +__forceinline u8* JNE8( u8 to ) +{ + return J8Rel( 0x75, to ); +} + +/* jnz rel8 */ +__forceinline u8* JNZ8( u8 to ) +{ + return J8Rel( 0x75, to ); +} + +/* jng rel8 */ +__forceinline u8* JNG8( u8 to ) +{ + return J8Rel( 0x7E, to ); +} + +/* jnge rel8 */ +__forceinline u8* JNGE8( u8 to ) +{ + return J8Rel( 0x7C, to ); +} + +/* jnl rel8 */ +__forceinline u8* JNL8( u8 to ) +{ + return J8Rel( 0x7D, to ); +} + +/* jnle rel8 */ +__forceinline u8* JNLE8( u8 to ) +{ + return J8Rel( 0x7F, to ); +} + +/* jo rel8 */ +__forceinline u8* JO8( u8 to ) +{ + return J8Rel( 0x70, to ); +} + +/* jno rel8 */ +__forceinline u8* JNO8( u8 to ) +{ + return J8Rel( 0x71, to ); +} +/* Untested and slower, use 32bit versions instead +// ja rel16 +__forceinline u16* JA16( u16 to ) +{ + return J16Rel( 0x87, to ); +} + +// jb rel16 +__forceinline u16* JB16( u16 to ) +{ + return J16Rel( 0x82, to ); +} + +// je rel16 +__forceinline u16* JE16( u16 to ) +{ + return J16Rel( 0x84, to ); +} + +// jz rel16 +__forceinline u16* JZ16( u16 to ) +{ + return J16Rel( 0x84, to ); +} +*/ +// jb rel32 +__forceinline u32* JB32( u32 to ) +{ + return J32Rel( 0x82, to ); +} + +/* je rel32 */ +__forceinline u32* JE32( u32 to ) +{ + return J32Rel( 0x84, to ); +} + +/* jz rel32 */ +__forceinline u32* JZ32( u32 to ) +{ + return J32Rel( 0x84, to ); +} + +/* js rel32 */ +__forceinline u32* JS32( u32 to ) +{ + return J32Rel( 0x88, to ); +} + +/* jns rel32 */ +__forceinline u32* JNS32( u32 to ) +{ + return J32Rel( 0x89, to ); +} + +/* jg rel32 */ +__forceinline u32* JG32( u32 to ) +{ + return J32Rel( 0x8F, to ); +} + +/* jge rel32 */ +__forceinline u32* JGE32( u32 to ) +{ + return J32Rel( 0x8D, to ); +} + +/* jl rel32 */ +__forceinline u32* JL32( u32 to ) +{ + return J32Rel( 0x8C, to ); +} + +/* jle rel32 */ +__forceinline u32* JLE32( u32 to ) +{ + return J32Rel( 0x8E, to ); +} + +/* ja rel32 */ +__forceinline u32* JA32( u32 to ) +{ + return J32Rel( 0x87, to ); +} + +/* jae rel32 */ +__forceinline u32* JAE32( u32 to ) +{ + return J32Rel( 0x83, to ); +} + +/* jne rel32 */ +__forceinline u32* JNE32( u32 to ) +{ + return J32Rel( 0x85, to ); +} + +/* jnz rel32 */ +__forceinline u32* JNZ32( u32 to ) +{ + return J32Rel( 0x85, to ); +} + +/* jng rel32 */ +__forceinline u32* JNG32( u32 to ) +{ + return J32Rel( 0x8E, to ); +} + +/* jnge rel32 */ +__forceinline u32* JNGE32( u32 to ) +{ + return J32Rel( 0x8C, to ); +} + +/* jnl rel32 */ +__forceinline u32* JNL32( u32 to ) +{ + return J32Rel( 0x8D, to ); +} + +/* jnle rel32 */ +__forceinline u32* JNLE32( u32 to ) +{ + return J32Rel( 0x8F, to ); +} + +/* jo rel32 */ +__forceinline u32* JO32( u32 to ) +{ + return J32Rel( 0x80, to ); +} + +/* jno rel32 */ +__forceinline u32* JNO32( u32 to ) +{ + return J32Rel( 0x81, to ); +} + + + +/* call func */ +__forceinline void CALLFunc( uptr func ) +{ + func -= ( (uptr)x86Ptr + 5 ); + assert( (sptr)func <= 0x7fffffff && (sptr)func >= -0x7fffffff ); + CALL32(func); +} + +/* call rel32 */ +__forceinline void CALL32( u32 to ) +{ + write8( 0xE8 ); + write32( to ); +} + +/* call r32 */ +__forceinline void CALL32R( x86IntRegType to ) +{ + write8( 0xFF ); + ModRM( 3, 2, to ); +} + +/* call r64 */ +__forceinline void CALL64R( x86IntRegType to ) +{ + RexB(0, to); + write8( 0xFF ); + ModRM( 3, 2, to ); +} + +/* call m32 */ +__forceinline void CALL32M( u32 to ) +{ + write8( 0xFF ); + ModRM( 0, 2, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +//////////////////////////////////// +// misc instructions / +//////////////////////////////////// + +/* cmp imm32 to r64 */ +__forceinline void CMP64I32toR( x86IntRegType to, u32 from ) +{ + RexB(1, to); + if ( to == EAX ) { + write8( 0x3D ); + } + else { + write8( 0x81 ); + ModRM( 3, 7, to ); + } + write32( from ); +} + +/* cmp m64 to r64 */ +__forceinline void CMP64MtoR( x86IntRegType to, uptr from ) +{ + RexR(1, to); + write8( 0x3B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// cmp r64 to r64 +__forceinline void CMP64RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(1,from,to); + write8( 0x39 ); + ModRM( 3, from, to ); +} + +/* cmp imm32 to r32 */ +__forceinline void CMP32ItoR( x86IntRegType to, u32 from ) +{ + RexB(0,to); + if ( to == EAX ) { + write8( 0x3D ); + } + else { + write8( 0x81 ); + ModRM( 3, 7, to ); + } + write32( from ); +} + +/* cmp imm32 to m32 */ +__forceinline void CMP32ItoM( uptr to, u32 from ) +{ + write8( 0x81 ); + ModRM( 0, 7, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); +} + +/* cmp r32 to r32 */ +__forceinline void CMP32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0,from,to); + write8( 0x39 ); + ModRM( 3, from, to ); +} + +/* cmp m32 to r32 */ +__forceinline void CMP32MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x3B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// cmp imm8 to [r32] +__forceinline void CMP32I8toRm( x86IntRegType to, u8 from) +{ + RexB(0,to); + write8( 0x83 ); + ModRM( 0, 7, to ); + write8(from); +} + +// cmp imm32 to [r32+off] +__forceinline void CMP32I8toRmOffset8( x86IntRegType to, u8 from, u8 off) +{ + RexB(0,to); + write8( 0x83 ); + ModRM( 1, 7, to ); + write8(off); + write8(from); +} + +// cmp imm8 to [r32] +__forceinline void CMP32I8toM( uptr to, u8 from) +{ + write8( 0x83 ); + ModRM( 0, 7, DISP32 ); + write32( MEMADDR(to, 5) ); + write8( from ); +} + +/* cmp imm16 to r16 */ +__forceinline void CMP16ItoR( x86IntRegType to, u16 from ) +{ + write8( 0x66 ); + RexB(0,to); + if ( to == EAX ) + { + write8( 0x3D ); + } + else + { + write8( 0x81 ); + ModRM( 3, 7, to ); + } + write16( from ); +} + +/* cmp imm16 to m16 */ +__forceinline void CMP16ItoM( uptr to, u16 from ) +{ + write8( 0x66 ); + write8( 0x81 ); + ModRM( 0, 7, DISP32 ); + write32( MEMADDR(to, 6) ); + write16( from ); +} + +/* cmp r16 to r16 */ +__forceinline void CMP16RtoR( x86IntRegType to, x86IntRegType from ) +{ + write8( 0x66 ); + RexRB(0,from,to); + write8( 0x39 ); + ModRM( 3, from, to ); +} + +/* cmp m16 to r16 */ +__forceinline void CMP16MtoR( x86IntRegType to, uptr from ) +{ + write8( 0x66 ); + RexR(0,to); + write8( 0x3B ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// cmp imm8 to r8 +__forceinline void CMP8ItoR( x86IntRegType to, u8 from ) +{ + RexB(0,to); + if ( to == EAX ) + { + write8( 0x3C ); + } + else + { + write8( 0x80 ); + ModRM( 3, 7, to ); + } + write8( from ); +} + +// cmp m8 to r8 +__forceinline void CMP8MtoR( x86IntRegType to, uptr from ) +{ + RexR(0,to); + write8( 0x3A ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* test imm32 to r32 */ +__forceinline void TEST32ItoR( x86IntRegType to, u32 from ) +{ + RexB(0,to); + if ( to == EAX ) + { + write8( 0xA9 ); + } + else + { + write8( 0xF7 ); + ModRM( 3, 0, to ); + } + write32( from ); +} + +__forceinline void TEST32ItoM( uptr to, u32 from ) +{ + write8( 0xF7 ); + ModRM( 0, 0, DISP32 ); + write32( MEMADDR(to, 8) ); + write32( from ); +} + +/* test r32 to r32 */ +__forceinline void TEST32RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0,from,to); + write8( 0x85 ); + ModRM( 3, from, to ); +} + +// test imm32 to [r32] +__forceinline void TEST32ItoRm( x86IntRegType to, u32 from ) +{ + RexB(0,to); + write8( 0xF7 ); + ModRM( 0, 0, to ); + write32(from); +} + +// test imm16 to r16 +__forceinline void TEST16ItoR( x86IntRegType to, u16 from ) +{ + write8(0x66); + RexB(0,to); + if ( to == EAX ) + { + write8( 0xA9 ); + } + else + { + write8( 0xF7 ); + ModRM( 3, 0, to ); + } + write16( from ); +} + +// test r16 to r16 +__forceinline void TEST16RtoR( x86IntRegType to, x86IntRegType from ) +{ + write8(0x66); + RexRB(0,from,to); + write8( 0x85 ); + ModRM( 3, from, to ); +} + +// test r8 to r8 +__forceinline void TEST8RtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0, from, to); + write8( 0x84 ); + ModRM( 3, from, to ); +} + + +// test imm8 to r8 +__forceinline void TEST8ItoR( x86IntRegType to, u8 from ) +{ + RexB(0,to); + if ( to == EAX ) + { + write8( 0xA8 ); + } + else + { + write8( 0xF6 ); + ModRM( 3, 0, to ); + } + write8( from ); +} + +// test imm8 to r8 +__forceinline void TEST8ItoM( uptr to, u8 from ) +{ + write8( 0xF6 ); + ModRM( 0, 0, DISP32 ); + write32( MEMADDR(to, 5) ); + write8( from ); +} + +/* sets r8 */ +__forceinline void SETS8R( x86IntRegType to ) +{ + SET8R( 0x98, to ); +} + +/* setl r8 */ +__forceinline void SETL8R( x86IntRegType to ) +{ + SET8R( 0x9C, to ); +} + +// setge r8 +__forceinline void SETGE8R( x86IntRegType to ) { SET8R(0x9d, to); } +// setg r8 +__forceinline void SETG8R( x86IntRegType to ) { SET8R(0x9f, to); } +// seta r8 +__forceinline void SETA8R( x86IntRegType to ) { SET8R(0x97, to); } +// setae r8 +__forceinline void SETAE8R( x86IntRegType to ) { SET8R(0x99, to); } +/* setb r8 */ +__forceinline void SETB8R( x86IntRegType to ) { SET8R( 0x92, to ); } +/* setb r8 */ +__forceinline void SETNZ8R( x86IntRegType to ) { SET8R( 0x95, to ); } +// setz r8 +__forceinline void SETZ8R( x86IntRegType to ) { SET8R(0x94, to); } +// sete r8 +__forceinline void SETE8R( x86IntRegType to ) { SET8R(0x94, to); } + +/* push imm32 */ +__forceinline void PUSH32I( u32 from ) +{; + write8( 0x68 ); + write32( from ); +} + +/* push r32 */ +__forceinline void PUSH32R( x86IntRegType from ) { write8( 0x50 | from ); } + +/* push m32 */ +__forceinline void PUSH32M( u32 from ) +{ + write8( 0xFF ); + ModRM( 0, 6, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* pop r32 */ +__forceinline void POP32R( x86IntRegType from ) { write8( 0x58 | from ); } + +/* pushad */ +__forceinline void PUSHA32( void ) { write8( 0x60 ); } + +/* popad */ +__forceinline void POPA32( void ) { write8( 0x61 ); } + +__forceinline void PUSHR(x86IntRegType from) { PUSH32R(from); } +__forceinline void POPR(x86IntRegType from) { POP32R(from); } + + +/* pushfd */ +__forceinline void PUSHFD( void ) { write8( 0x9C ); } +/* popfd */ +__forceinline void POPFD( void ) { write8( 0x9D ); } + +__forceinline void RET( void ) { write8( 0xC3 ); } +__forceinline void RET2( void ) { write16( 0xc3f3 ); } + +__forceinline void CBW( void ) { write16( 0x9866 ); } +__forceinline void CWD( void ) { write8( 0x98 ); } +__forceinline void CDQ( void ) { write8( 0x99 ); } +__forceinline void CWDE() { write8(0x98); } + +__forceinline void LAHF() { write8(0x9f); } +__forceinline void SAHF() { write8(0x9e); } + +__forceinline void BT32ItoR( x86IntRegType to, u8 from ) +{ + write16( 0xBA0F ); + ModRM(3, 4, to); + write8( from ); +} + +__forceinline void BTR32ItoR( x86IntRegType to, u8 from ) +{ + write16( 0xBA0F ); + ModRM(3, 6, to); + write8( from ); +} + +__forceinline void BSRRtoR(x86IntRegType to, x86IntRegType from) +{ + write16( 0xBD0F ); + ModRM( 3, from, to ); +} + +__forceinline void BSWAP32R( x86IntRegType to ) +{ + write8( 0x0F ); + write8( 0xC8 + to ); +} + +// to = from + offset +__forceinline void LEA16RtoR(x86IntRegType to, x86IntRegType from, u16 offset) +{ + write8(0x66); + LEA32RtoR(to, from, offset); +} + +__forceinline void LEA32RtoR(x86IntRegType to, x86IntRegType from, u32 offset) +{ + RexRB(0,to,from); + write8(0x8d); + + if( (from&7) == ESP ) { + if( offset == 0 ) { + ModRM(1, to, from); + write8(0x24); + } + else if( offset < 128 ) { + ModRM(1, to, from); + write8(0x24); + write8(offset); + } + else { + ModRM(2, to, from); + write8(0x24); + write32(offset); + } + } + else { + if( offset == 0 && from != EBP && from!=ESP ) { + ModRM(0, to, from); + } + else if( offset < 128 ) { + ModRM(1, to, from); + write8(offset); + } + else { + ModRM(2, to, from); + write32(offset); + } + } +} + +// to = from0 + from1 +__forceinline void LEA16RRtoR(x86IntRegType to, x86IntRegType from0, x86IntRegType from1) +{ + write8(0x66); + LEA32RRtoR(to, from0, from1); +} + +__forceinline void LEA32RRtoR(x86IntRegType to, x86IntRegType from0, x86IntRegType from1) +{ + RexRXB(0, to, from0, from1); + write8(0x8d); + + if( (from1&7) == EBP ) { + ModRM(1, to, 4); + ModRM(0, from0, from1); + write8(0); + } + else { + ModRM(0, to, 4); + ModRM(0, from0, from1); + } +} + +// to = from << scale (max is 3) +__forceinline void LEA16RStoR(x86IntRegType to, x86IntRegType from, u32 scale) +{ + write8(0x66); + LEA32RStoR(to, from, scale); +} + +// Don't inline recursive functions +void LEA32RStoR(x86IntRegType to, x86IntRegType from, u32 scale) +{ + if( to == from ) { + SHL32ItoR(to, scale); + return; + } + + if( from != ESP ) { + RexRXB(0,to,from,0); + write8(0x8d); + ModRM(0, to, 4); + ModRM(scale, from, 5); + write32(0); + } + else { + assert( to != ESP ); + MOV32RtoR(to, from); + LEA32RStoR(to, to, scale); + } +} \ No newline at end of file diff --git a/pcsx2/x86/ix86/ix86.h b/pcsx2/x86/ix86/ix86.h new file mode 100644 index 0000000000..37a41e2371 --- /dev/null +++ b/pcsx2/x86/ix86/ix86.h @@ -0,0 +1,1750 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 + */ +/* + * ix86 definitions v0.6.2 + * Authors: linuzappz + * alexey silinov + * goldfinger + * shadow < shadow@pcsx2.net > + */ + +#ifndef __IX86_H__ +#define __IX86_H__ + +#include "PS2Etypes.h" // Basic types header + +#define XMMREGS 8 +#define X86REGS 8 + +#define MMXREGS 8 + +#define SIB 4 +#define SIBDISP 5 +#define DISP32 5 + +// general types +typedef int x86IntRegType; +#define EAX 0 +#define EBX 3 +#define ECX 1 +#define EDX 2 +#define ESI 6 +#define EDI 7 +#define EBP 5 +#define ESP 4 + +#define X86ARG1 EAX +#define X86ARG2 ECX +#define X86ARG3 EDX +#define X86ARG4 EBX + +#define MM0 0 +#define MM1 1 +#define MM2 2 +#define MM3 3 +#define MM4 4 +#define MM5 5 +#define MM6 6 +#define MM7 7 + +typedef int x86MMXRegType; + +#define XMM0 0 +#define XMM1 1 +#define XMM2 2 +#define XMM3 3 +#define XMM4 4 +#define XMM5 5 +#define XMM6 6 +#define XMM7 7 +#define XMM8 8 +#define XMM9 9 +#define XMM10 10 +#define XMM11 11 +#define XMM12 12 +#define XMM13 13 +#define XMM14 14 +#define XMM15 15 + +typedef int x86SSERegType; + +enum XMMSSEType +{ + XMMT_INT = 0, // integer (sse2 only) + XMMT_FPS = 1, // floating point + //XMMT_FPD = 3, // double +}; + +extern XMMSSEType g_xmmtypes[XMMREGS]; + +extern void cpudetectInit( void );//this is all that needs to be called and will fill up the below structs + +typedef struct CAPABILITIES CAPABILITIES; +//cpu capabilities structure +struct CAPABILITIES { + u32 hasFloatingPointUnit; + u32 hasVirtual8086ModeEnhancements; + u32 hasDebuggingExtensions; + u32 hasPageSizeExtensions; + u32 hasTimeStampCounter; + u32 hasModelSpecificRegisters; + u32 hasPhysicalAddressExtension; + u32 hasCOMPXCHG8BInstruction; + u32 hasAdvancedProgrammableInterruptController; + u32 hasSEPFastSystemCall; + u32 hasMemoryTypeRangeRegisters; + u32 hasPTEGlobalFlag; + u32 hasMachineCheckArchitecture; + u32 hasConditionalMoveAndCompareInstructions; + u32 hasFGPageAttributeTable; + u32 has36bitPageSizeExtension; + u32 hasProcessorSerialNumber; + u32 hasCFLUSHInstruction; + u32 hasDebugStore; + u32 hasACPIThermalMonitorAndClockControl; + u32 hasMultimediaExtensions; + u32 hasFastStreamingSIMDExtensionsSaveRestore; + u32 hasStreamingSIMDExtensions; + u32 hasStreamingSIMD2Extensions; + u32 hasSelfSnoop; + u32 hasMultiThreading; // is TRUE for both mutli-core and Hyperthreaded CPUs. + u32 hasThermalMonitor; + u32 hasIntel64BitArchitecture; + u32 hasStreamingSIMD3Extensions; + u32 hasStreamingSIMD4Extensions; + + // AMD-specific CPU Features + u32 hasMultimediaExtensionsExt; + u32 hasAMD64BitArchitecture; + u32 has3DNOWInstructionExtensionsExt; + u32 has3DNOWInstructionExtensions; +}; + +extern CAPABILITIES cpucaps; + +struct CPUINFO{ + + u32 x86Family; // Processor Family + u32 x86Model; // Processor Model + u32 x86PType; // Processor Type + u32 x86StepID; // Stepping ID + u32 x86Flags; // Feature Flags + u32 x86Flags2; // More Feature Flags + u32 x86EFlags; // Extended Feature Flags + + u32 PhysicalCores; + u32 LogicalCores; + + char x86ID[16]; // Vendor ID //the vendor creator (in %s) + char x86Type[20]; //cpu type in char format //the cpu type (in %s) + char x86Fam[50]; // family in char format //the original cpu name string (in %s) + u32 cpuspeed; // speed of cpu //this will give cpu speed (in %d) +}; + +extern CPUINFO cpuinfo; + +extern u8 *x86Ptr; +extern u8 *j8Ptr[32]; +extern u32 *j32Ptr[32]; + +#define MEMADDR(addr, oplen) (addr) + +#define Rex(w,r,x,b) assert(0); +#define RexR(w, reg) if( w||(reg)>=8 ) assert(0); +#define RexB(w, base) if( w||(base)>=8 ) assert(0); +#define RexRB(w, reg, base) if( w||(reg) >= 8 || (base)>=8 ) assert(0); +#define RexRXB(w, reg, index, base) if( w||(reg) >= 8 || (index) >= 8 || (base) >= 8 ) assert(0); + +// perf counters +#ifdef PCSX2_DEVBUILD +extern void StartPerfCounter(); +extern void StopPerfCounter(); +#else +#define StartPerfCounter() +#define StopPerfCounter() +#endif + + +extern __forceinline void write8( u8 val ); +extern __forceinline void write16( u16 val ); +extern __forceinline void write32( u32 val ); +extern void write64( u64 val ); + + +extern void x86SetPtr( u8 *ptr ); +extern void x86Shutdown( void ); + +extern void x86SetJ8( u8 *j8 ); +extern void x86SetJ8A( u8 *j8 ); +extern void x86SetJ16( u16 *j16 ); +extern void x86SetJ16A( u16 *j16 ); +extern void x86SetJ32( u32 *j32 ); +extern void x86SetJ32A( u32 *j32 ); + +extern void x86Align( int bytes ); +extern void x86AlignExecutable( int align ); + +u64 GetCPUTick( void ); + +// General Helper functions +extern void ModRM( int mod, int reg, int rm ); +extern void SibSB( int ss, int index, int base ); +extern void SET8R( int cc, int to ); +extern u8* J8Rel( int cc, int to ); +extern u32* J32Rel( int cc, u32 to ); +extern void CMOV32RtoR( int cc, int to, int from ); +extern void CMOV32MtoR( int cc, int to, uptr from ); + +//****************** +// IX86 intructions +//****************** + +// +// * scale values: +// * 0 - *1 +// * 1 - *2 +// * 2 - *4 +// * 3 - *8 +// + +extern void STC( void ); +extern void CLC( void ); +extern void NOP( void ); + +//////////////////////////////////// +// mov instructions // +//////////////////////////////////// + +// mov r64 to r64 +extern void MOV64RtoR( x86IntRegType to, x86IntRegType from ); +// mov r64 to m64 +extern void MOV64RtoM( uptr to, x86IntRegType from ); +// mov m64 to r64 +extern void MOV64MtoR( x86IntRegType to, uptr from ); +// mov sign ext imm32 to m64 +extern void MOV64I32toM( uptr to, u32 from ); +// mov sign ext imm32 to r64 +extern void MOV64I32toR( x86IntRegType to, s32 from); +// mov imm64 to r64 +extern void MOV64ItoR( x86IntRegType to, u64 from); +// mov imm64 to [r64+off] +extern void MOV64ItoRmOffset( x86IntRegType to, u32 from, int offset); +// mov [r64+offset] to r64 +extern void MOV64RmOffsettoR( x86IntRegType to, x86IntRegType from, int offset ); +// mov [r64][r64*scale] to r64 +extern void MOV64RmStoR( x86IntRegType to, x86IntRegType from, x86IntRegType from2, int scale); +// mov r64 to [r64+offset] +extern void MOV64RtoRmOffset( x86IntRegType to, x86IntRegType from, int offset ); +// mov r64 to [r64][r64*scale] +extern void MOV64RtoRmS( x86IntRegType to, x86IntRegType from, x86IntRegType from2, int scale); + +// mov r32 to r32 +extern void MOV32RtoR( x86IntRegType to, x86IntRegType from ); +// mov r32 to m32 +extern void MOV32RtoM( uptr to, x86IntRegType from ); +// mov m32 to r32 +extern void MOV32MtoR( x86IntRegType to, uptr from ); +// mov [r32] to r32 +extern void MOV32RmtoR( x86IntRegType to, x86IntRegType from ); +extern void MOV32RmtoROffset( x86IntRegType to, x86IntRegType from, int offset ); +// mov [r32][r32< subtract ST(0) from ST(1), store in ST(1) and POP stack +extern void FSUBP( void ); +// fmul ST(src) to fpu reg stack ST(0) +extern void FMUL32Rto0( x86IntRegType src ); +// fmul ST(0) to fpu reg stack ST(src) +extern void FMUL320toR( x86IntRegType src ); +// fdiv ST(src) to fpu reg stack ST(0) +extern void FDIV32Rto0( x86IntRegType src ); +// fdiv ST(0) to fpu reg stack ST(src) +extern void FDIV320toR( x86IntRegType src ); +// fdiv ST(0) to fpu reg stack ST(src), pop stack, store in ST(src) +extern void FDIV320toRP( x86IntRegType src ); + +// fadd m32 to fpu reg stack +extern void FADD32( u32 from ); +// fsub m32 to fpu reg stack +extern void FSUB32( u32 from ); +// fmul m32 to fpu reg stack +extern void FMUL32( u32 from ); +// fdiv m32 to fpu reg stack +extern void FDIV32( u32 from ); +// fcomi st, st( i) +extern void FCOMI( x86IntRegType src ); +// fcomip st, st( i) +extern void FCOMIP( x86IntRegType src ); +// fucomi st, st( i) +extern void FUCOMI( x86IntRegType src ); +// fucomip st, st( i) +extern void FUCOMIP( x86IntRegType src ); +// fcom m32 to fpu reg stack +extern void FCOM32( u32 from ); +// fabs fpu reg stack +extern void FABS( void ); +// fsqrt fpu reg stack +extern void FSQRT( void ); +// ftan fpu reg stack +extern void FPATAN( void ); +// fsin fpu reg stack +extern void FSIN( void ); +// fchs fpu reg stack +extern void FCHS( void ); + +// fcmovb fpu reg to fpu reg stack +extern void FCMOVB32( x86IntRegType from ); +// fcmove fpu reg to fpu reg stack +extern void FCMOVE32( x86IntRegType from ); +// fcmovbe fpu reg to fpu reg stack +extern void FCMOVBE32( x86IntRegType from ); +// fcmovu fpu reg to fpu reg stack +extern void FCMOVU32( x86IntRegType from ); +// fcmovnb fpu reg to fpu reg stack +extern void FCMOVNB32( x86IntRegType from ); +// fcmovne fpu reg to fpu reg stack +extern void FCMOVNE32( x86IntRegType from ); +// fcmovnbe fpu reg to fpu reg stack +extern void FCMOVNBE32( x86IntRegType from ); +// fcmovnu fpu reg to fpu reg stack +extern void FCMOVNU32( x86IntRegType from ); +extern void FCOMP32( u32 from ); +extern void FNSTSWtoAX( void ); + +#define MMXONLY(code) code + +//****************** +// MMX instructions +//****************** + +// r64 = mm + +// movq m64 to r64 +extern void MOVQMtoR( x86MMXRegType to, uptr from ); +// movq r64 to m64 +extern void MOVQRtoM( uptr to, x86MMXRegType from ); + +// pand r64 to r64 +extern void PANDRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PANDNRtoR( x86MMXRegType to, x86MMXRegType from ); +// pand m64 to r64 ; +extern void PANDMtoR( x86MMXRegType to, uptr from ); +// pandn r64 to r64 +extern void PANDNRtoR( x86MMXRegType to, x86MMXRegType from ); +// pandn r64 to r64 +extern void PANDNMtoR( x86MMXRegType to, uptr from ); +// por r64 to r64 +extern void PORRtoR( x86MMXRegType to, x86MMXRegType from ); +// por m64 to r64 +extern void PORMtoR( x86MMXRegType to, uptr from ); +// pxor r64 to r64 +extern void PXORRtoR( x86MMXRegType to, x86MMXRegType from ); +// pxor m64 to r64 +extern void PXORMtoR( x86MMXRegType to, uptr from ); + +// psllq r64 to r64 +extern void PSLLQRtoR( x86MMXRegType to, x86MMXRegType from ); +// psllq m64 to r64 +extern void PSLLQMtoR( x86MMXRegType to, uptr from ); +// psllq imm8 to r64 +extern void PSLLQItoR( x86MMXRegType to, u8 from ); +// psrlq r64 to r64 +extern void PSRLQRtoR( x86MMXRegType to, x86MMXRegType from ); +// psrlq m64 to r64 +extern void PSRLQMtoR( x86MMXRegType to, uptr from ); +// psrlq imm8 to r64 +extern void PSRLQItoR( x86MMXRegType to, u8 from ); + +// paddusb r64 to r64 +extern void PADDUSBRtoR( x86MMXRegType to, x86MMXRegType from ); +// paddusb m64 to r64 +extern void PADDUSBMtoR( x86MMXRegType to, uptr from ); +// paddusw r64 to r64 +extern void PADDUSWRtoR( x86MMXRegType to, x86MMXRegType from ); +// paddusw m64 to r64 +extern void PADDUSWMtoR( x86MMXRegType to, uptr from ); + +// paddb r64 to r64 +extern void PADDBRtoR( x86MMXRegType to, x86MMXRegType from ); +// paddb m64 to r64 +extern void PADDBMtoR( x86MMXRegType to, uptr from ); +// paddw r64 to r64 +extern void PADDWRtoR( x86MMXRegType to, x86MMXRegType from ); +// paddw m64 to r64 +extern void PADDWMtoR( x86MMXRegType to, uptr from ); +// paddd r64 to r64 +extern void PADDDRtoR( x86MMXRegType to, x86MMXRegType from ); +// paddd m64 to r64 +extern void PADDDMtoR( x86MMXRegType to, uptr from ); +extern void PADDSBRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PADDSWRtoR( x86MMXRegType to, x86MMXRegType from ); + +// paddq m64 to r64 (sse2 only?) +extern void PADDQMtoR( x86MMXRegType to, uptr from ); +// paddq r64 to r64 (sse2 only?) +extern void PADDQRtoR( x86MMXRegType to, x86MMXRegType from ); + +extern void PSUBSBRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PSUBSWRtoR( x86MMXRegType to, x86MMXRegType from ); + +extern void PSUBBRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PSUBWRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PSUBDRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PSUBDMtoR( x86MMXRegType to, uptr from ); + +// psubq m64 to r64 (sse2 only?) +extern void PSUBQMtoR( x86MMXRegType to, uptr from ); +// psubq r64 to r64 (sse2 only?) +extern void PSUBQRtoR( x86MMXRegType to, x86MMXRegType from ); + +// pmuludq m64 to r64 (sse2 only?) +extern void PMULUDQMtoR( x86MMXRegType to, uptr from ); +// pmuludq r64 to r64 (sse2 only?) +extern void PMULUDQRtoR( x86MMXRegType to, x86MMXRegType from ); + +extern void PCMPEQBRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PCMPEQWRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PCMPEQDRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PCMPEQDMtoR( x86MMXRegType to, uptr from ); +extern void PCMPGTBRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PCMPGTWRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PCMPGTDRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PCMPGTDMtoR( x86MMXRegType to, uptr from ); +extern void PSRLWItoR( x86MMXRegType to, u8 from ); +extern void PSRLDItoR( x86MMXRegType to, u8 from ); +extern void PSRLDRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PSLLWItoR( x86MMXRegType to, u8 from ); +extern void PSLLDItoR( x86MMXRegType to, u8 from ); +extern void PSLLDRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PSRAWItoR( x86MMXRegType to, u8 from ); +extern void PSRADItoR( x86MMXRegType to, u8 from ); +extern void PSRADRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PUNPCKLDQRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PUNPCKLDQMtoR( x86MMXRegType to, uptr from ); +extern void PUNPCKHDQRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void PUNPCKHDQMtoR( x86MMXRegType to, uptr from ); +extern void MOVQ64ItoR( x86MMXRegType reg, u64 i ); //Prototype.Todo add all consts to end of block.not after jr $+8 +extern void MOVQRtoR( x86MMXRegType to, x86MMXRegType from ); +extern void MOVQRmtoROffset( x86MMXRegType to, x86IntRegType from, u32 offset ); +extern void MOVQRtoRmOffset( x86IntRegType to, x86MMXRegType from, u32 offset ); +extern void MOVDMtoMMX( x86MMXRegType to, uptr from ); +extern void MOVDMMXtoM( uptr to, x86MMXRegType from ); +extern void MOVD32RtoMMX( x86MMXRegType to, x86IntRegType from ); +extern void MOVD32RmtoMMX( x86MMXRegType to, x86IntRegType from ); +extern void MOVD32RmOffsettoMMX( x86MMXRegType to, x86IntRegType from, u32 offset ); +extern void MOVD32MMXtoR( x86IntRegType to, x86MMXRegType from ); +extern void MOVD32MMXtoRm( x86IntRegType to, x86MMXRegType from ); +extern void MOVD32MMXtoRmOffset( x86IntRegType to, x86MMXRegType from, u32 offset ); +extern void PINSRWRtoMMX( x86MMXRegType to, x86SSERegType from, u8 imm8 ); +extern void PSHUFWRtoR(x86MMXRegType to, x86MMXRegType from, u8 imm8); +extern void PSHUFWMtoR(x86MMXRegType to, uptr from, u8 imm8); +extern void MASKMOVQRtoR(x86MMXRegType to, x86MMXRegType from); + +// emms +extern void EMMS( void ); + +//**********************************************************************************/ +//PACKSSWB,PACKSSDW: Pack Saturate Signed Word 64bits +//********************************************************************************** +extern void PACKSSWBMMXtoMMX(x86MMXRegType to, x86MMXRegType from); +extern void PACKSSDWMMXtoMMX(x86MMXRegType to, x86MMXRegType from); + +extern void PMOVMSKBMMXtoR(x86IntRegType to, x86MMXRegType from); + +extern void SSE2_MOVDQ2Q_XMM_to_MM( x86MMXRegType to, x86SSERegType from); +extern void SSE2_MOVQ2DQ_MM_to_XMM( x86SSERegType to, x86MMXRegType from); + +//********************* +// SSE instructions * +//********************* +extern void SSE_MOVAPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MOVAPS_XMM_to_M128( uptr to, x86SSERegType from ); +extern void SSE_MOVAPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); + +extern void SSE_MOVUPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MOVUPS_XMM_to_M128( uptr to, x86SSERegType from ); + +extern void SSE_MOVSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MOVSS_XMM_to_M32( u32 to, x86SSERegType from ); +extern void SSE_MOVSS_XMM_to_Rm( x86IntRegType to, x86SSERegType from ); +extern void SSE_MOVSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_MOVSS_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ); +extern void SSE_MOVSS_XMM_to_RmOffset( x86IntRegType to, x86SSERegType from, int offset ); + +extern void SSE2_MOVSD_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); + +extern void SSE2_MOVQ_M64_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_MOVQ_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_MOVQ_XMM_to_M64( u32 to, x86SSERegType from ); + +extern void SSE_MASKMOVDQU_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); + +extern void SSE_MOVLPS_M64_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MOVLPS_XMM_to_M64( u32 to, x86SSERegType from ); +extern void SSE_MOVLPS_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ); +extern void SSE_MOVLPS_XMM_to_RmOffset( x86IntRegType to, x86SSERegType from, int offset ); + +extern void SSE_MOVHPS_M64_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MOVHPS_XMM_to_M64( u32 to, x86SSERegType from ); +extern void SSE_MOVHPS_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ); +extern void SSE_MOVHPS_XMM_to_RmOffset( x86IntRegType to, x86SSERegType from, int offset ); + +extern void SSE_MOVLHPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_MOVHLPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_MOVLPSRmtoR( x86SSERegType to, x86IntRegType from ); +extern void SSE_MOVLPSRmtoROffset( x86SSERegType to, x86IntRegType from, int offset ); +extern void SSE_MOVLPSRtoRm( x86SSERegType to, x86IntRegType from ); +extern void SSE_MOVLPSRtoRmOffset( x86SSERegType to, x86IntRegType from, int offset ); + +extern void SSE_MOVAPSRmStoR( x86SSERegType to, x86IntRegType from, x86IntRegType from2, int scale ); +extern void SSE_MOVAPSRtoRmS( x86SSERegType to, x86IntRegType from, x86IntRegType from2, int scale ); +extern void SSE_MOVAPSRtoRmOffset( x86IntRegType to, x86SSERegType from, int offset ); +extern void SSE_MOVAPSRmtoROffset( x86SSERegType to, x86IntRegType from, int offset ); +extern void SSE_MOVUPSRmStoR( x86SSERegType to, x86IntRegType from, x86IntRegType from2, int scale ); +extern void SSE_MOVUPSRtoRmS( x86SSERegType to, x86IntRegType from, x86IntRegType from2, int scale ); +extern void SSE_MOVUPSRtoRm( x86IntRegType to, x86IntRegType from ); +extern void SSE_MOVUPSRmtoR( x86IntRegType to, x86IntRegType from ); + +extern void SSE_MOVUPSRmtoROffset( x86SSERegType to, x86IntRegType from, int offset ); +extern void SSE_MOVUPSRtoRmOffset( x86SSERegType to, x86IntRegType from, int offset ); + +extern void SSE2_MOVDQARtoRmOffset( x86IntRegType to, x86SSERegType from, int offset ); +extern void SSE2_MOVDQARmtoROffset( x86SSERegType to, x86IntRegType from, int offset ); + +extern void SSE_RCPPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_RCPPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_RCPSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_RCPSS_M32_to_XMM( x86SSERegType to, uptr from ); + +extern void SSE_ORPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_ORPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_XORPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_XORPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_ANDPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_ANDPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_ANDNPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_ANDNPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_ADDPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_ADDPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_ADDSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_ADDSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_SUBPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_SUBPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_SUBSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_SUBSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_MULPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MULPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_MULSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MULSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPEQSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPEQSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPLTSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPLTSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPLESS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPLESS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPUNORDSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPUNORDSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPNESS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPNESS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPNLTSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPNLTSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPNLESS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPNLESS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPORDSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPORDSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); + +extern void SSE_UCOMISS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_UCOMISS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); + +extern void SSE_PMAXSW_MM_to_MM( x86MMXRegType to, x86MMXRegType from ); +extern void SSE_PMINSW_MM_to_MM( x86MMXRegType to, x86MMXRegType from ); +extern void SSE_CVTPI2PS_MM_to_XMM( x86SSERegType to, x86MMXRegType from ); +extern void SSE_CVTPS2PI_M64_to_MM( x86MMXRegType to, uptr from ); +extern void SSE_CVTPS2PI_XMM_to_MM( x86MMXRegType to, x86SSERegType from ); + +extern void SSE_CVTPI2PS_M64_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CVTTSS2SI_M32_to_R32(x86IntRegType to, uptr from); +extern void SSE_CVTTSS2SI_XMM_to_R32(x86IntRegType to, x86SSERegType from); +extern void SSE_CVTSI2SS_M32_to_XMM(x86SSERegType to, uptr from); +extern void SSE_CVTSI2SS_R_to_XMM(x86SSERegType to, x86IntRegType from); + +extern void SSE2_CVTDQ2PS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_CVTDQ2PS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_CVTPS2DQ_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_CVTPS2DQ_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_CVTTPS2DQ_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); + +extern void SSE_MAXPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MAXPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_MAXSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MAXSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_MINPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MINPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_MINSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_MINSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_RSQRTPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_RSQRTPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_RSQRTSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_RSQRTSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_SQRTPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_SQRTPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_SQRTSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_SQRTSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_UNPCKLPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_UNPCKLPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_UNPCKHPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_UNPCKHPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_SHUFPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from, u8 imm8 ); +extern void SSE_SHUFPS_M128_to_XMM( x86SSERegType to, uptr from, u8 imm8 ); +extern void SSE_SHUFPS_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset, u8 imm8 ); +extern void SSE_CMPEQPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPEQPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPLTPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPLTPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPLEPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPLEPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPUNORDPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPUNORDPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPNEPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPNEPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPNLTPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPNLTPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPNLEPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPNLEPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_CMPORDPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_CMPORDPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_DIVPS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_DIVPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE_DIVSS_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE_DIVSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +// VectorPath +extern void SSE2_PSHUFD_XMM_to_XMM( x86SSERegType to, x86SSERegType from, u8 imm8 ); +extern void SSE2_PSHUFD_M128_to_XMM( x86SSERegType to, uptr from, u8 imm8 ); + +extern void SSE2_PSHUFLW_XMM_to_XMM( x86SSERegType to, x86SSERegType from, u8 imm8 ); +extern void SSE2_PSHUFLW_M128_to_XMM( x86SSERegType to, uptr from, u8 imm8 ); +extern void SSE2_PSHUFHW_XMM_to_XMM( x86SSERegType to, x86SSERegType from, u8 imm8 ); +extern void SSE2_PSHUFHW_M128_to_XMM( x86SSERegType to, uptr from, u8 imm8 ); + +extern void SSE_STMXCSR( uptr from ); +extern void SSE_LDMXCSR( uptr from ); + + +//********************* +// SSE 2 Instructions* +//********************* +extern void SSE2_MOVDQA_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_MOVDQA_XMM_to_M128( uptr to, x86SSERegType from); +extern void SSE2_MOVDQA_XMM_to_XMM( x86SSERegType to, x86SSERegType from); + +extern void SSE2_MOVDQU_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_MOVDQU_XMM_to_M128( uptr to, x86SSERegType from); +extern void SSE2_MOVDQU_XMM_to_XMM( x86SSERegType to, x86SSERegType from); + +extern void SSE2_PSRLW_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PSRLW_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PSRLW_I8_to_XMM(x86SSERegType to, u8 imm8); +extern void SSE2_PSRLD_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PSRLD_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PSRLD_I8_to_XMM(x86SSERegType to, u8 imm8); +extern void SSE2_PSRLQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PSRLQ_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PSRLQ_I8_to_XMM(x86SSERegType to, u8 imm8); +extern void SSE2_PSRLDQ_I8_to_XMM(x86SSERegType to, u8 imm8); +extern void SSE2_PSRAW_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PSRAW_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PSRAW_I8_to_XMM(x86SSERegType to, u8 imm8); +extern void SSE2_PSRAD_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PSRAD_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PSRAD_I8_to_XMM(x86SSERegType to, u8 imm8); +extern void SSE2_PSLLW_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PSLLW_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PSLLW_I8_to_XMM(x86SSERegType to, u8 imm8); +extern void SSE2_PSLLD_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PSLLD_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PSLLD_I8_to_XMM(x86SSERegType to, u8 imm8); +extern void SSE2_PSLLQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PSLLQ_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PSLLQ_I8_to_XMM(x86SSERegType to, u8 imm8); +extern void SSE2_PSLLDQ_I8_to_XMM(x86SSERegType to, u8 imm8); +extern void SSE2_PMAXSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PMAXSW_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PMAXUB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PMAXUB_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PMINSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PMINSW_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PMINUB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PMINUB_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PADDSB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PADDSB_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PADDSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PADDSW_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PSUBSB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PSUBSB_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PSUBSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PSUBSW_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PSUBUSB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PSUBUSB_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PSUBUSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PSUBUSW_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PAND_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PAND_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PANDN_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PANDN_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PXOR_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PXOR_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PADDW_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PADDW_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PADDUSB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PADDUSB_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PADDUSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_PADDUSW_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_PADDB_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PADDB_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PADDD_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PADDD_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PADDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PADDQ_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PMADDWD_XMM_to_XMM(x86SSERegType to, x86SSERegType from); + + +//**********************************************************************************/ +//PACKSSWB,PACKSSDW: Pack Saturate Signed Word +//********************************************************************************** +extern void SSE2_PACKSSWB_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PACKSSWB_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PACKSSDW_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PACKSSDW_M128_to_XMM(x86SSERegType to, uptr from); + +extern void SSE2_PACKUSWB_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PACKUSWB_M128_to_XMM(x86SSERegType to, uptr from); + +//**********************************************************************************/ +//PUNPCKHWD: Unpack 16bit high +//********************************************************************************** +extern void SSE2_PUNPCKLBW_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PUNPCKLBW_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PUNPCKHBW_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PUNPCKHBW_M128_to_XMM(x86SSERegType to, uptr from); + +extern void SSE2_PUNPCKLWD_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PUNPCKLWD_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PUNPCKHWD_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PUNPCKHWD_M128_to_XMM(x86SSERegType to, uptr from); + +extern void SSE2_PUNPCKLDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PUNPCKLDQ_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PUNPCKHDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PUNPCKHDQ_M128_to_XMM(x86SSERegType to, uptr from); + +extern void SSE2_PUNPCKLQDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PUNPCKLQDQ_M128_to_XMM(x86SSERegType to, uptr from); + +extern void SSE2_PUNPCKHQDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PUNPCKHQDQ_M128_to_XMM(x86SSERegType to, uptr from); + +// mult by half words +extern void SSE2_PMULLW_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PMULLW_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE2_PMULHW_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PMULHW_M128_to_XMM(x86SSERegType to, uptr from); + +extern void SSE2_PMULUDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE2_PMULUDQ_M128_to_XMM(x86SSERegType to, uptr from); + + +//**********************************************************************************/ +//PMOVMSKB: Create 16bit mask from signs of 8bit integers +//********************************************************************************** +extern void SSE2_PMOVMSKB_XMM_to_R32(x86IntRegType to, x86SSERegType from); + +extern void SSE_MOVMSKPS_XMM_to_R32(x86IntRegType to, x86SSERegType from); +extern void SSE2_MOVMSKPD_XMM_to_R32(x86IntRegType to, x86SSERegType from); + +//**********************************************************************************/ +//PEXTRW,PINSRW: Packed Extract/Insert Word * +//********************************************************************************** +extern void SSE_PEXTRW_XMM_to_R32(x86IntRegType to, x86SSERegType from, u8 imm8 ); +extern void SSE_PINSRW_R32_to_XMM(x86SSERegType from, x86IntRegType to, u8 imm8 ); + + +//**********************************************************************************/ +//PSUBx: Subtract Packed Integers * +//********************************************************************************** +extern void SSE2_PSUBB_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PSUBB_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PSUBW_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PSUBW_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PSUBD_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PSUBD_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PSUBQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PSUBQ_M128_to_XMM(x86SSERegType to, uptr from ); +/////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//PCMPxx: Compare Packed Integers * +//********************************************************************************** +extern void SSE2_PCMPGTB_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PCMPGTB_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PCMPGTW_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PCMPGTW_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PCMPGTD_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PCMPGTD_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PCMPEQB_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PCMPEQB_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PCMPEQW_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PCMPEQW_M128_to_XMM(x86SSERegType to, uptr from ); +extern void SSE2_PCMPEQD_XMM_to_XMM(x86SSERegType to, x86SSERegType from ); +extern void SSE2_PCMPEQD_M128_to_XMM(x86SSERegType to, uptr from ); +//**********************************************************************************/ +//MOVD: Move Dword(32bit) to /from XMM reg * +//********************************************************************************** +extern void SSE2_MOVD_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2_MOVD_R_to_XMM( x86SSERegType to, x86IntRegType from ); +extern void SSE2_MOVD_Rm_to_XMM( x86SSERegType to, x86IntRegType from ); +extern void SSE2_MOVD_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ); +extern void SSE2_MOVD_XMM_to_M32( u32 to, x86SSERegType from ); +extern void SSE2_MOVD_XMM_to_R( x86IntRegType to, x86SSERegType from ); +extern void SSE2_MOVD_XMM_to_Rm( x86IntRegType to, x86SSERegType from ); +extern void SSE2_MOVD_XMM_to_RmOffset( x86IntRegType to, x86SSERegType from, int offset ); + +extern void SSE2_MOVQ_XMM_to_R( x86IntRegType to, x86SSERegType from ); +extern void SSE2_MOVQ_R_to_XMM( x86SSERegType to, x86IntRegType from ); + +//**********************************************************************************/ +//POR : SSE Bitwise OR * +//********************************************************************************** +extern void SSE2_POR_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2_POR_M128_to_XMM( x86SSERegType to, uptr from ); + +extern void SSE3_HADDPS_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE3_HADDPS_M128_to_XMM(x86SSERegType to, uptr from); + +extern void SSE3_MOVSLDUP_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE3_MOVSLDUP_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE3_MOVSHDUP_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE3_MOVSHDUP_M128_to_XMM(x86SSERegType to, uptr from); + +// SSE4.1 + +#ifndef _MM_MK_INSERTPS_NDX +#define _MM_MK_INSERTPS_NDX(srcField, dstField, zeroMask) (((srcField)<<6) | ((dstField)<<4) | (zeroMask)) +#endif + +extern void SSE4_DPPS_XMM_to_XMM(x86SSERegType to, x86SSERegType from, u8 imm8); +extern void SSE4_DPPS_M128_to_XMM(x86SSERegType to, uptr from, u8 imm8); +extern void SSE4_INSERTPS_XMM_to_XMM(x86SSERegType to, x86SSERegType from, u8 imm8); +extern void SSE4_EXTRACTPS_XMM_to_R32(x86IntRegType to, x86SSERegType from, u8 imm8); +extern void SSE4_BLENDPS_XMM_to_XMM(x86SSERegType to, x86SSERegType from, u8 imm8); +extern void SSE4_BLENDVPS_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE4_BLENDVPS_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE4_PMOVSXDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE4_PINSRD_R32_to_XMM(x86SSERegType to, x86IntRegType from, u8 imm8); +extern void SSE4_PMAXSD_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE4_PMINSD_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE4_PMAXUD_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE4_PMINUD_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSE4_PMAXSD_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE4_PMINSD_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE4_PMAXUD_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSE4_PMINUD_M128_to_XMM(x86SSERegType to, uptr from); + +//********************* +// SSE-X - uses both SSE,SSE2 code and tries to keep consistensies between the data +// Uses g_xmmtypes to infer the correct type. +//********************* +extern void SSEX_MOVDQA_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSEX_MOVDQA_XMM_to_M128( uptr to, x86SSERegType from ); +extern void SSEX_MOVDQA_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); + +extern void SSEX_MOVDQARmtoROffset( x86SSERegType to, x86IntRegType from, int offset ); +extern void SSEX_MOVDQARtoRmOffset( x86IntRegType to, x86SSERegType from, int offset ); + +extern void SSEX_MOVDQU_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSEX_MOVDQU_XMM_to_M128( uptr to, x86SSERegType from ); +extern void SSEX_MOVDQU_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); + +extern void SSEX_MOVD_M32_to_XMM( x86SSERegType to, uptr from ); +extern void SSEX_MOVD_XMM_to_M32( u32 to, x86SSERegType from ); +extern void SSEX_MOVD_XMM_to_Rm( x86IntRegType to, x86SSERegType from ); +extern void SSEX_MOVD_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ); +extern void SSEX_MOVD_XMM_to_RmOffset( x86IntRegType to, x86SSERegType from, int offset ); + +extern void SSEX_POR_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSEX_POR_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSEX_PXOR_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSEX_PXOR_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSEX_PAND_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSEX_PAND_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSEX_PANDN_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSEX_PANDN_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); + +extern void SSEX_PUNPCKLDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSEX_PUNPCKLDQ_M128_to_XMM(x86SSERegType to, uptr from); +extern void SSEX_PUNPCKHDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from); +extern void SSEX_PUNPCKHDQ_M128_to_XMM(x86SSERegType to, uptr from); + +extern void SSEX_MOVHLPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); + +//********************* +// 3DNOW instructions * +//********************* +extern void FEMMS( void ); +extern void PFCMPEQMtoR( x86IntRegType to, uptr from ); +extern void PFCMPGTMtoR( x86IntRegType to, uptr from ); +extern void PFCMPGEMtoR( x86IntRegType to, uptr from ); +extern void PFADDMtoR( x86IntRegType to, uptr from ); +extern void PFADDRtoR( x86IntRegType to, x86IntRegType from ); +extern void PFSUBMtoR( x86IntRegType to, uptr from ); +extern void PFSUBRtoR( x86IntRegType to, x86IntRegType from ); +extern void PFMULMtoR( x86IntRegType to, uptr from ); +extern void PFMULRtoR( x86IntRegType to, x86IntRegType from ); +extern void PFRCPMtoR( x86IntRegType to, uptr from ); +extern void PFRCPRtoR( x86IntRegType to, x86IntRegType from ); +extern void PFRCPIT1RtoR( x86IntRegType to, x86IntRegType from ); +extern void PFRCPIT2RtoR( x86IntRegType to, x86IntRegType from ); +extern void PFRSQRTRtoR( x86IntRegType to, x86IntRegType from ); +extern void PFRSQIT1RtoR( x86IntRegType to, x86IntRegType from ); +extern void PF2IDMtoR( x86IntRegType to, uptr from ); +extern void PI2FDMtoR( x86IntRegType to, uptr from ); +extern void PI2FDRtoR( x86IntRegType to, x86IntRegType from ); +extern void PFMAXMtoR( x86IntRegType to, uptr from ); +extern void PFMAXRtoR( x86IntRegType to, x86IntRegType from ); +extern void PFMINMtoR( x86IntRegType to, uptr from ); +extern void PFMINRtoR( x86IntRegType to, x86IntRegType from ); + +extern void SSE2EMU_MOVSD_XMM_to_XMM( x86SSERegType to, x86SSERegType from); +extern void SSE2EMU_MOVQ_M64_to_XMM( x86SSERegType to, uptr from); +extern void SSE2EMU_MOVQ_XMM_to_XMM( x86SSERegType to, x86SSERegType from); +extern void SSE2EMU_MOVD_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ); +extern void SSE2EMU_MOVD_XMM_to_RmOffset(x86IntRegType to, x86SSERegType from, int offset ); + +extern void SSE2EMU_MOVDQ2Q_XMM_to_MM( x86MMXRegType to, x86SSERegType from); +extern void SSE2EMU_MOVQ2DQ_MM_to_XMM( x86SSERegType to, x86MMXRegType from); + +/* SSE2 emulated functions for SSE CPU's by kekko*/ + +extern void SSE2EMU_PSHUFD_XMM_to_XMM( x86SSERegType to, x86SSERegType from, u8 imm8 ); +extern void SSE2EMU_MOVD_XMM_to_R( x86IntRegType to, x86SSERegType from ); +extern void SSE2EMU_CVTPS2DQ_XMM_to_XMM( x86SSERegType to, x86SSERegType from ); +extern void SSE2EMU_CVTDQ2PS_M128_to_XMM( x86SSERegType to, uptr from ); +extern void SSE2EMU_MOVD_XMM_to_M32( u32 to, x86SSERegType from ); +extern void SSE2EMU_MOVD_R_to_XMM( x86SSERegType to, x86IntRegType from ); + +//////////////////////////////////////////////////// +#ifdef _DEBUG +#define WRITECHECK() CheckX86Ptr() +#else +#define WRITECHECK() +#endif + +__forceinline void write8(u8 val ) { + *x86Ptr = (u8)val; + x86Ptr++; +} + +__forceinline void write16(u16 val ) +{ + *(u16*)x86Ptr = (u16)val; + x86Ptr += 2; +} + +__forceinline void write24(u32 val ) +{ + *(u8*)x86Ptr = (u8)(val & 0xff); + x86Ptr++; + *(u8*)x86Ptr = (u8)((val >> 8) & 0xff); + x86Ptr++; + *(u8*)x86Ptr = (u8)((val >> 16) & 0xff); + x86Ptr++; +} + +__forceinline void write32(u32 val ) +{ + *(u32*)x86Ptr = val; + x86Ptr += 4; +} + +#endif // __IX86_H__ diff --git a/pcsx2/x86/ix86/ix86_3dnow.cpp b/pcsx2/x86/ix86/ix86_3dnow.cpp new file mode 100644 index 0000000000..acc9b96f45 --- /dev/null +++ b/pcsx2/x86/ix86/ix86_3dnow.cpp @@ -0,0 +1,202 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "ix86.h" + +/**********************/ +/* 3DNOW instructions */ +/**********************/ + +/* femms */ +void FEMMS( void ) +{ + write16( 0x0E0F ); +} + +void PFCMPEQMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0xB0 ); +} + +void PFCMPGTMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0xA0 ); +} + +void PFCMPGEMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0x90 ); +} + +void PFADDMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0x9E ); +} + +void PFADDRtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0x9E ); +} + +void PFSUBMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0x9A ); +} + +void PFSUBRtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0x9A ); +} + +void PFMULMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0xB4 ); +} + +void PFMULRtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0xB4 ); +} + +void PFRCPMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0x96 ); +} + +void PFRCPRtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0x96 ); +} + +void PFRCPIT1RtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0xA6 ); +} + +void PFRCPIT2RtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0xB6 ); +} + +void PFRSQRTRtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0x97 ); +} + +void PFRSQIT1RtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0xA7 ); +} + +void PF2IDMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0x1D ); +} + +void PF2IDRtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0x1D ); +} + +void PI2FDMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0x0D ); +} + +void PI2FDRtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0x0D ); +} + +void PFMAXMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0xA4 ); +} + +void PFMAXRtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0xA4 ); +} + +void PFMINMtoR( x86IntRegType to, uptr from ) +{ + write16( 0x0F0F ); + ModRM( 0, to, DISP32 ); + write32( from ); + write8( 0x94 ); +} + +void PFMINRtoR( x86IntRegType to, x86IntRegType from ) +{ + write16( 0x0F0F ); + ModRM( 3, to, from ); + write8( 0x94 ); +} diff --git a/pcsx2/x86/ix86/ix86_cpudetect.cpp b/pcsx2/x86/ix86/ix86_cpudetect.cpp new file mode 100644 index 0000000000..d0da987e99 --- /dev/null +++ b/pcsx2/x86/ix86/ix86_cpudetect.cpp @@ -0,0 +1,403 @@ +/* Cpudetection lib + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "ix86.h" +#include "Misc.h" +#include "Threading.h" + +#if defined (_MSC_VER) && _MSC_VER >= 1400 + + extern "C" + { + void __cpuid(int* CPUInfo, int InfoType); + unsigned __int64 __rdtsc(); +# pragma intrinsic(__cpuid) +# pragma intrinsic(__rdtsc) + } + +#endif + +CAPABILITIES cpucaps; +CPUINFO cpuinfo; + +#define cpuid(cmd,a,b,c,d) \ + __asm__ __volatile__("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) : "0" (cmd), "c" (0)) + +extern s32 iCpuId( u32 cmd, u32 *regs ) +{ + int flag=1; + +#if defined (_MSC_VER) && _MSC_VER >= 1400 + + __asm + { + xor ecx, ecx; /* ecx should be zero for CPUID(4) */ + } + __cpuid( (int*)regs, cmd ); + + return 0; + +#elif defined (_MSC_VER) + __asm + { + push ebx; + push edi; + + pushfd; + pop eax; + mov edx, eax; + xor eax, 1 << 21; + push eax; + popfd; + pushfd; + pop eax; + xor eax, edx; + mov flag, eax; + } + if ( ! flag ) + { + return -1; + } + + __asm + { + mov eax, cmd; + xor ecx, ecx; /* ecx should be zero for CPUID(4) */ + cpuid; + mov edi, [regs] + mov [edi], eax; + mov [edi+4], ebx; + mov [edi+8], ecx; + mov [edi+12], edx; + + pop edi; + pop ebx; + } + return 0; + +#else + + // GCC Assembly Code --> + + // see if we can use cpuid + __asm__ __volatile__ ( + "sub $0x18, %%esp\n" + "pushf\n" + "pop %%eax\n" + "mov %%eax, %%edx\n" + "xor $0x200000, %%eax\n" + "push %%eax\n" + "popf\n" + "pushf\n" + "pop %%eax\n" + "xor %%edx, %%eax\n" + "mov %%eax, %0\n" + "add $0x18, %%esp\n" + "cmpl $0x0,%%eax\n" + "jne 1f\n" + "mov $0xffffffff, %%eax\n" + "leave\n" + "ret\n" + "1:\n" + : "=r"(flag) : + ); + + cpuid(cmd, regs[0], regs[1], regs[2], regs[3]); + return 0; + +#endif // _MSC_VER +} + +u64 GetCPUTick( void ) +{ +#if defined (_MSC_VER) && _MSC_VER >= 1400 + + return __rdtsc(); + +#elif defined(_WIN32) + + __asm rdtsc; + +#else + + u32 _a, _d; + __asm__ __volatile__ ("rdtsc" : "=a"(_a), "=d"(_d)); + return (u64)_a | ((u64)_d << 32); + +#endif +} + +// Note: This function doesn't support GCC/Linux. Looking online it seems the only +// way to simulate the Micrsoft SEH model is to use unix signals, and the 'sigaction' +// function specifically. Maybe a project for a linux developer at a later date. :) +void cpudetectSSE3(void* pfnCallSSE3) +{ + cpucaps.hasStreamingSIMD3Extensions = 1; + +#ifdef _MSC_VER + __try { + ((void (*)())pfnCallSSE3)(); + } + __except(EXCEPTION_EXECUTE_HANDLER) { + cpucaps.hasStreamingSIMD3Extensions = 0; + } +#else // linux + +#ifdef PCSX2_FORCESSE3 + cpucaps.hasStreamingSIMD3Extensions = 1; +#else + // exception handling doesn't work, so disable for x86 builds of linux + cpucaps.hasStreamingSIMD3Extensions = 0; +#endif +#endif +} + +#if defined __LINUX__ + +#include +#include + +#endif + +s64 CPUSpeedHz( unsigned int time ) +{ + s64 timeStart, + timeStop; + s64 startTick, + endTick; + s64 overhead; + + if( ! cpucaps.hasTimeStampCounter ) + { + return 0; //check if function is supported + } + + overhead = GetCPUTick() - GetCPUTick(); + + timeStart = timeGetTime( ); + while( timeGetTime( ) == timeStart ) + { + timeStart = timeGetTime( ); + } + for(;;) + { + timeStop = timeGetTime( ); + if ( ( timeStop - timeStart ) > 1 ) + { + startTick = GetCPUTick( ); + break; + } + } + + timeStart = timeStop; + for(;;) + { + timeStop = timeGetTime( ); + if ( ( timeStop - timeStart ) > time ) + { + endTick = GetCPUTick( ); + break; + } + } + + return (s64)( ( endTick - startTick ) + ( overhead ) ); +} + +//////////////////////////////////////////////////// +int arr[] = { + 0x65746e49, 0x2952286c, 0x726f4320, 0x4d542865, + 0x51203229,0x20646175,0x20555043,0x20202020 , + 0x20202020,0x20402020,0x36362e32,0x7a4847 +}; + +void cpudetectInit() +{ + u32 regs[ 4 ]; + u32 cmds; + int cputype=0; // Cpu type + //AMD 64 STUFF + u32 x86_64_8BITBRANDID; + u32 x86_64_12BITBRANDID; + int num; + char str[50]; + + memzero_obj( cpuinfo.x86ID ); + cpuinfo.x86Family = 0; + cpuinfo.x86Model = 0; + cpuinfo.x86PType = 0; + cpuinfo.x86StepID = 0; + cpuinfo.x86Flags = 0; + cpuinfo.x86EFlags = 0; + + if ( iCpuId( 0, regs ) == -1 ) return; + + cmds = regs[ 0 ]; + ((u32*)cpuinfo.x86ID)[ 0 ] = regs[ 1 ]; + ((u32*)cpuinfo.x86ID)[ 1 ] = regs[ 3 ]; + ((u32*)cpuinfo.x86ID)[ 2 ] = regs[ 2 ]; + + // Hack - prevents reg[2] & reg[3] from being optimized out of existance! + num = sprintf(str, "\tx86Flags = %8.8x %8.8x\n", regs[3], regs[2]); + + u32 LogicalCoresPerPhysicalCPU = 0; + u32 PhysicalCoresPerPhysicalCPU = 1; + + if ( cmds >= 0x00000001 ) + { + if ( iCpuId( 0x00000001, regs ) != -1 ) + { + cpuinfo.x86StepID = regs[ 0 ] & 0xf; + cpuinfo.x86Model = (regs[ 0 ] >> 4) & 0xf; + cpuinfo.x86Family = (regs[ 0 ] >> 8) & 0xf; + cpuinfo.x86PType = (regs[ 0 ] >> 12) & 0x3; + LogicalCoresPerPhysicalCPU = ( regs[1] >> 16 ) & 0xff; + x86_64_8BITBRANDID = regs[1] & 0xff; + cpuinfo.x86Flags = regs[ 3 ]; + cpuinfo.x86Flags2 = regs[ 2 ]; + } + } + /* detect multicore for intel cpu */ + if ((cmds >= 0x00000004) && !strcmp("GenuineIntel",cpuinfo.x86ID)) + { + if ( iCpuId( 0x00000004, regs ) != -1 ) + { + PhysicalCoresPerPhysicalCPU += ( regs[0] >> 26) & 0x3f; + } + } + + if ( iCpuId( 0x80000000, regs ) != -1 ) + { + cmds = regs[ 0 ]; + if ( cmds >= 0x80000001 ) + { + if ( iCpuId( 0x80000001, regs ) != -1 ) + { + x86_64_12BITBRANDID = regs[1] & 0xfff; + cpuinfo.x86EFlags = regs[ 3 ]; + + } + } + /* detect multicore for amd cpu */ + if ((cmds >= 0x80000008) && !strcmp("AuthenticAMD",cpuinfo.x86ID)) + { + if ( iCpuId( 0x80000008, regs ) != -1 ) + { + PhysicalCoresPerPhysicalCPU += ( regs[2] ) & 0xff; + } + } + } + + switch(cpuinfo.x86PType) + { + case 0: + strcpy( cpuinfo.x86Type, "Standard OEM"); + break; + case 1: + strcpy( cpuinfo.x86Type, "Overdrive"); + break; + case 2: + strcpy( cpuinfo.x86Type, "Dual"); + break; + case 3: + strcpy( cpuinfo.x86Type, "Reserved"); + break; + default: + strcpy( cpuinfo.x86Type, "Unknown"); + break; + } + if ( cpuinfo.x86ID[ 0 ] == 'G' ){ cputype=0;}//trick lines but if you know a way better ;p + if ( cpuinfo.x86ID[ 0 ] == 'A' ){ cputype=1;} + + memzero_obj( cpuinfo.x86Fam ); + iCpuId( 0x80000002, (u32*)cpuinfo.x86Fam); + iCpuId( 0x80000003, (u32*)(cpuinfo.x86Fam+16)); + iCpuId( 0x80000004, (u32*)(cpuinfo.x86Fam+32)); + + //capabilities + cpucaps.hasFloatingPointUnit = ( cpuinfo.x86Flags >> 0 ) & 1; + cpucaps.hasVirtual8086ModeEnhancements = ( cpuinfo.x86Flags >> 1 ) & 1; + cpucaps.hasDebuggingExtensions = ( cpuinfo.x86Flags >> 2 ) & 1; + cpucaps.hasPageSizeExtensions = ( cpuinfo.x86Flags >> 3 ) & 1; + cpucaps.hasTimeStampCounter = ( cpuinfo.x86Flags >> 4 ) & 1; + cpucaps.hasModelSpecificRegisters = ( cpuinfo.x86Flags >> 5 ) & 1; + cpucaps.hasPhysicalAddressExtension = ( cpuinfo.x86Flags >> 6 ) & 1; + cpucaps.hasMachineCheckArchitecture = ( cpuinfo.x86Flags >> 7 ) & 1; + cpucaps.hasCOMPXCHG8BInstruction = ( cpuinfo.x86Flags >> 8 ) & 1; + cpucaps.hasAdvancedProgrammableInterruptController = ( cpuinfo.x86Flags >> 9 ) & 1; + cpucaps.hasSEPFastSystemCall = ( cpuinfo.x86Flags >> 11 ) & 1; + cpucaps.hasMemoryTypeRangeRegisters = ( cpuinfo.x86Flags >> 12 ) & 1; + cpucaps.hasPTEGlobalFlag = ( cpuinfo.x86Flags >> 13 ) & 1; + cpucaps.hasMachineCheckArchitecture = ( cpuinfo.x86Flags >> 14 ) & 1; + cpucaps.hasConditionalMoveAndCompareInstructions = ( cpuinfo.x86Flags >> 15 ) & 1; + cpucaps.hasFGPageAttributeTable = ( cpuinfo.x86Flags >> 16 ) & 1; + cpucaps.has36bitPageSizeExtension = ( cpuinfo.x86Flags >> 17 ) & 1; + cpucaps.hasProcessorSerialNumber = ( cpuinfo.x86Flags >> 18 ) & 1; + cpucaps.hasCFLUSHInstruction = ( cpuinfo.x86Flags >> 19 ) & 1; + cpucaps.hasDebugStore = ( cpuinfo.x86Flags >> 21 ) & 1; + cpucaps.hasACPIThermalMonitorAndClockControl = ( cpuinfo.x86Flags >> 22 ) & 1; + cpucaps.hasMultimediaExtensions = ( cpuinfo.x86Flags >> 23 ) & 1; //mmx + cpucaps.hasFastStreamingSIMDExtensionsSaveRestore = ( cpuinfo.x86Flags >> 24 ) & 1; + cpucaps.hasStreamingSIMDExtensions = ( cpuinfo.x86Flags >> 25 ) & 1; //sse + cpucaps.hasStreamingSIMD2Extensions = ( cpuinfo.x86Flags >> 26 ) & 1; //sse2 + cpucaps.hasSelfSnoop = ( cpuinfo.x86Flags >> 27 ) & 1; + cpucaps.hasMultiThreading = ( cpuinfo.x86Flags >> 28 ) & 1; + cpucaps.hasThermalMonitor = ( cpuinfo.x86Flags >> 29 ) & 1; + cpucaps.hasIntel64BitArchitecture = ( cpuinfo.x86Flags >> 30 ) & 1; + //that is only for AMDs + cpucaps.hasMultimediaExtensionsExt = ( cpuinfo.x86EFlags >> 22 ) & 1; //mmx2 + cpucaps.hasAMD64BitArchitecture = ( cpuinfo.x86EFlags >> 29 ) & 1; //64bit cpu + cpucaps.has3DNOWInstructionExtensionsExt = ( cpuinfo.x86EFlags >> 30 ) & 1; //3dnow+ + cpucaps.has3DNOWInstructionExtensions = ( cpuinfo.x86EFlags >> 31 ) & 1; //3dnow + + cpuinfo.cpuspeed = (u32)(CPUSpeedHz( 1000 ) / 1000000); + + // --> SSE 4.1 detection <-- + // We don't care about the small subset of CPUs using SSE4 (which is also hard to + // detect, in addition to being of limited use due to the abbreviated instruction set). + // So we'll just leave it at SSE 4.1. SSE4 cpu detection is ignored. + + cpucaps.hasStreamingSIMD4Extensions = ( cpuinfo.x86Flags2 >> 19 ) & 1; //sse4.1 + + // --> SSE3 detection <-- + // These instructions may not be recognized by some compilers, or may not have + // intrinsic equivalents available. So we use our own ix86 emitter to generate + // some code and run it that way. :) + + u8* recSSE = (u8*)SysMmap( NULL, 0x1000 ); + if( recSSE != NULL ) + { + x86SetPtr(recSSE); + SSE3_MOVSLDUP_XMM_to_XMM(XMM0, XMM0); + RET(); + cpudetectSSE3(recSSE); + SysMunmap( recSSE, 0x1000 ); + } + + ////////////////////////////////////// + // Core Counting! + + if( !cpucaps.hasMultiThreading || LogicalCoresPerPhysicalCPU == 0 ) + LogicalCoresPerPhysicalCPU = 1; + + // This will assign values into cpuinfo.LogicalCores and PhysicalCores + Threading::CountLogicalCores( LogicalCoresPerPhysicalCPU, PhysicalCoresPerPhysicalCPU ); +} + diff --git a/pcsx2/x86/ix86/ix86_fpu.cpp b/pcsx2/x86/ix86/ix86_fpu.cpp new file mode 100644 index 0000000000..41e40380ca --- /dev/null +++ b/pcsx2/x86/ix86/ix86_fpu.cpp @@ -0,0 +1,286 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "ix86.h" + +/********************/ +/* FPU instructions */ +/********************/ + +/* fild m32 to fpu reg stack */ +void FILD32( u32 from ) +{ + write8( 0xDB ); + ModRM( 0, 0x0, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* fistp m32 from fpu reg stack */ +void FISTP32( u32 from ) +{ + write8( 0xDB ); + ModRM( 0, 0x3, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* fld m32 to fpu reg stack */ +void FLD32( u32 from ) +{ + write8( 0xD9 ); + ModRM( 0, 0x0, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// fld st(i) +void FLD(int st) { write16(0xc0d9+(st<<8)); } + +void FLD1() { write16(0xe8d9); } +void FLDL2E() { write16(0xead9); } + +/* fst m32 from fpu reg stack */ +void FST32( u32 to ) +{ + write8( 0xD9 ); + ModRM( 0, 0x2, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +/* fstp m32 from fpu reg stack */ +void FSTP32( u32 to ) +{ + write8( 0xD9 ); + ModRM( 0, 0x3, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +// fstp st(i) +void FSTP(int st) { write16(0xd8dd+(st<<8)); } + +/* fldcw fpu control word from m16 */ +void FLDCW( u32 from ) +{ + write8( 0xD9 ); + ModRM( 0, 0x5, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* fnstcw fpu control word to m16 */ +void FNSTCW( u32 to ) +{ + write8( 0xD9 ); + ModRM( 0, 0x7, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +void FNSTSWtoAX( void ) +{ + write16( 0xE0DF ); +} + +void FXAM() +{ + write16(0xe5d9); +} + +void FDECSTP() { write16(0xf6d9); } +void FRNDINT() { write16(0xfcd9); } +void FXCH(int st) { write16(0xc8d9+(st<<8)); } +void F2XM1() { write16(0xf0d9); } +void FSCALE() { write16(0xfdd9); } + +/* fadd ST(src) to fpu reg stack ST(0) */ +void FADD32Rto0( x86IntRegType src ) +{ + write8( 0xD8 ); + write8( 0xC0 + src ); +} + +/* fadd ST(0) to fpu reg stack ST(src) */ +void FADD320toR( x86IntRegType src ) +{ + write8( 0xDC ); + write8( 0xC0 + src ); +} + +/* fsub ST(src) to fpu reg stack ST(0) */ +void FSUB32Rto0( x86IntRegType src ) +{ + write8( 0xD8 ); + write8( 0xE0 + src ); +} + +/* fsub ST(0) to fpu reg stack ST(src) */ +void FSUB320toR( x86IntRegType src ) +{ + write8( 0xDC ); + write8( 0xE8 + src ); +} + +/* fsubp -> substract ST(0) from ST(1), store in ST(1) and POP stack */ +void FSUBP( void ) +{ + write8( 0xDE ); + write8( 0xE9 ); +} + +/* fmul ST(src) to fpu reg stack ST(0) */ +void FMUL32Rto0( x86IntRegType src ) +{ + write8( 0xD8 ); + write8( 0xC8 + src ); +} + +/* fmul ST(0) to fpu reg stack ST(src) */ +void FMUL320toR( x86IntRegType src ) +{ + write8( 0xDC ); + write8( 0xC8 + src ); +} + +/* fdiv ST(src) to fpu reg stack ST(0) */ +void FDIV32Rto0( x86IntRegType src ) +{ + write8( 0xD8 ); + write8( 0xF0 + src ); +} + +/* fdiv ST(0) to fpu reg stack ST(src) */ +void FDIV320toR( x86IntRegType src ) +{ + write8( 0xDC ); + write8( 0xF8 + src ); +} + +void FDIV320toRP( x86IntRegType src ) +{ + write8( 0xDE ); + write8( 0xF8 + src ); +} + +/* fadd m32 to fpu reg stack */ +void FADD32( u32 from ) +{ + write8( 0xD8 ); + ModRM( 0, 0x0, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* fsub m32 to fpu reg stack */ +void FSUB32( u32 from ) +{ + write8( 0xD8 ); + ModRM( 0, 0x4, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* fmul m32 to fpu reg stack */ +void FMUL32( u32 from ) +{ + write8( 0xD8 ); + ModRM( 0, 0x1, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* fdiv m32 to fpu reg stack */ +void FDIV32( u32 from ) +{ + write8( 0xD8 ); + ModRM( 0, 0x6, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* fabs fpu reg stack */ +void FABS( void ) +{ + write16( 0xE1D9 ); +} + +/* fsqrt fpu reg stack */ +void FSQRT( void ) +{ + write16( 0xFAD9 ); +} + +void FPATAN(void) { write16(0xf3d9); } +void FSIN(void) { write16(0xfed9); } + +/* fchs fpu reg stack */ +void FCHS( void ) +{ + write16( 0xE0D9 ); +} + +/* fcomi st, st(i) */ +void FCOMI( x86IntRegType src ) +{ + write8( 0xDB ); + write8( 0xF0 + src ); +} + +/* fcomip st, st(i) */ +void FCOMIP( x86IntRegType src ) +{ + write8( 0xDF ); + write8( 0xF0 + src ); +} + +/* fucomi st, st(i) */ +void FUCOMI( x86IntRegType src ) +{ + write8( 0xDB ); + write8( 0xE8 + src ); +} + +/* fucomip st, st(i) */ +void FUCOMIP( x86IntRegType src ) +{ + write8( 0xDF ); + write8( 0xE8 + src ); +} + +/* fcom m32 to fpu reg stack */ +void FCOM32( u32 from ) +{ + write8( 0xD8 ); + ModRM( 0, 0x2, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* fcomp m32 to fpu reg stack */ +void FCOMP32( u32 from ) +{ + write8( 0xD8 ); + ModRM( 0, 0x3, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +#define FCMOV32( low, high ) \ + { \ + write8( low ); \ + write8( high + from ); \ + } + +void FCMOVB32( x86IntRegType from ) { FCMOV32( 0xDA, 0xC0 ); } +void FCMOVE32( x86IntRegType from ) { FCMOV32( 0xDA, 0xC8 ); } +void FCMOVBE32( x86IntRegType from ) { FCMOV32( 0xDA, 0xD0 ); } +void FCMOVU32( x86IntRegType from ) { FCMOV32( 0xDA, 0xD8 ); } +void FCMOVNB32( x86IntRegType from ) { FCMOV32( 0xDB, 0xC0 ); } +void FCMOVNE32( x86IntRegType from ) { FCMOV32( 0xDB, 0xC8 ); } +void FCMOVNBE32( x86IntRegType from ) { FCMOV32( 0xDB, 0xD0 ); } +void FCMOVNU32( x86IntRegType from ) { FCMOV32( 0xDB, 0xD8 ); } diff --git a/pcsx2/x86/ix86/ix86_mmx.cpp b/pcsx2/x86/ix86/ix86_mmx.cpp new file mode 100644 index 0000000000..82701894cf --- /dev/null +++ b/pcsx2/x86/ix86/ix86_mmx.cpp @@ -0,0 +1,648 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "ix86.h" + +/********************/ +/* MMX instructions */ +/********************/ + +// r64 = mm + +/* movq m64 to r64 */ +void MOVQMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0x6F0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* movq r64 to m64 */ +void MOVQRtoM( uptr to, x86MMXRegType from ) +{ + write16( 0x7F0F ); + ModRM( 0, from, DISP32 ); + write32(MEMADDR(to, 4)); +} + +/* pand r64 to r64 */ +void PANDRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xDB0F ); + ModRM( 3, to, from ); +} + +void PANDNRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xDF0F ); + ModRM( 3, to, from ); +} + +/* por r64 to r64 */ +void PORRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xEB0F ); + ModRM( 3, to, from ); +} + +/* pxor r64 to r64 */ +void PXORRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xEF0F ); + ModRM( 3, to, from ); +} + +/* psllq r64 to r64 */ +void PSLLQRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xF30F ); + ModRM( 3, to, from ); +} + +/* psllq m64 to r64 */ +void PSLLQMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xF30F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* psllq imm8 to r64 */ +void PSLLQItoR( x86MMXRegType to, u8 from ) +{ + write16( 0x730F ); + ModRM( 3, 6, to); + write8( from ); +} + +/* psrlq r64 to r64 */ +void PSRLQRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xD30F ); + ModRM( 3, to, from ); +} + +/* psrlq m64 to r64 */ +void PSRLQMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xD30F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* psrlq imm8 to r64 */ +void PSRLQItoR( x86MMXRegType to, u8 from ) +{ + write16( 0x730F ); + ModRM( 3, 2, to); + write8( from ); +} + +/* paddusb r64 to r64 */ +void PADDUSBRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xDC0F ); + ModRM( 3, to, from ); +} + +/* paddusb m64 to r64 */ +void PADDUSBMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xDC0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* paddusw r64 to r64 */ +void PADDUSWRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xDD0F ); + ModRM( 3, to, from ); +} + +/* paddusw m64 to r64 */ +void PADDUSWMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xDD0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* paddb r64 to r64 */ +void PADDBRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xFC0F ); + ModRM( 3, to, from ); +} + +/* paddb m64 to r64 */ +void PADDBMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xFC0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* paddw r64 to r64 */ +void PADDWRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xFD0F ); + ModRM( 3, to, from ); +} + +/* paddw m64 to r64 */ +void PADDWMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xFD0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* paddd r64 to r64 */ +void PADDDRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xFE0F ); + ModRM( 3, to, from ); +} + +/* paddd m64 to r64 */ +void PADDDMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xFE0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* emms */ +void EMMS( void ) +{ + write16( 0x770F ); +} + +void PADDSBRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xEC0F ); + ModRM( 3, to, from ); +} + +void PADDSWRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xED0F ); + ModRM( 3, to, from ); +} + +// paddq m64 to r64 (sse2 only?) +void PADDQMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xD40F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// paddq r64 to r64 (sse2 only?) +void PADDQRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xD40F ); + ModRM( 3, to, from ); +} + +void PSUBSBRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xE80F ); + ModRM( 3, to, from ); +} + +void PSUBSWRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xE90F ); + ModRM( 3, to, from ); +} + + +void PSUBBRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xF80F ); + ModRM( 3, to, from ); +} + +void PSUBWRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xF90F ); + ModRM( 3, to, from ); +} + +void PSUBDRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xFA0F ); + ModRM( 3, to, from ); +} + +void PSUBDMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xFA0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +void PSUBUSBRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xD80F ); + ModRM( 3, to, from ); +} + +void PSUBUSWRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xD90F ); + ModRM( 3, to, from ); +} + +// psubq m64 to r64 (sse2 only?) +void PSUBQMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xFB0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// psubq r64 to r64 (sse2 only?) +void PSUBQRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xFB0F ); + ModRM( 3, to, from ); +} + +// pmuludq m64 to r64 (sse2 only?) +void PMULUDQMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xF40F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +// pmuludq r64 to r64 (sse2 only?) +void PMULUDQRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xF40F ); + ModRM( 3, to, from ); +} + +void PCMPEQBRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0x740F ); + ModRM( 3, to, from ); +} + +void PCMPEQWRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0x750F ); + ModRM( 3, to, from ); +} + +void PCMPEQDRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0x760F ); + ModRM( 3, to, from ); +} + +void PCMPEQDMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0x760F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +void PCMPGTBRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0x640F ); + ModRM( 3, to, from ); +} + +void PCMPGTWRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0x650F ); + ModRM( 3, to, from ); +} + +void PCMPGTDRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0x660F ); + ModRM( 3, to, from ); +} + +void PCMPGTDMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0x660F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +void PSRLWItoR( x86MMXRegType to, u8 from ) +{ + write16( 0x710F ); + ModRM( 3, 2 , to ); + write8( from ); +} + +void PSRLDItoR( x86MMXRegType to, u8 from ) +{ + write16( 0x720F ); + ModRM( 3, 2 , to ); + write8( from ); +} + +void PSRLDRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xD20F ); + ModRM( 3, to, from ); +} + +void PSLLWItoR( x86MMXRegType to, u8 from ) +{ + write16( 0x710F ); + ModRM( 3, 6 , to ); + write8( from ); +} + +void PSLLDItoR( x86MMXRegType to, u8 from ) +{ + write16( 0x720F ); + ModRM( 3, 6 , to ); + write8( from ); +} + +void PSLLDRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xF20F ); + ModRM( 3, to, from ); +} + +void PSRAWItoR( x86MMXRegType to, u8 from ) +{ + write16( 0x710F ); + ModRM( 3, 4 , to ); + write8( from ); +} + +void PSRADItoR( x86MMXRegType to, u8 from ) +{ + write16( 0x720F ); + ModRM( 3, 4 , to ); + write8( from ); +} + +void PSRADRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0xE20F ); + ModRM( 3, to, from ); +} + +/* por m64 to r64 */ +void PORMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xEB0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* pxor m64 to r64 */ +void PXORMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xEF0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* pand m64 to r64 */ +void PANDMtoR( x86MMXRegType to, uptr from ) +{ + //u64 rip = (u64)x86Ptr + 7; + write16( 0xDB0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +void PANDNMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0xDF0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +void PUNPCKHDQRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0x6A0F ); + ModRM( 3, to, from ); +} + +void PUNPCKHDQMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0x6A0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +void PUNPCKLDQRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0x620F ); + ModRM( 3, to, from ); +} + +void PUNPCKLDQMtoR( x86MMXRegType to, uptr from ) +{ + write16( 0x620F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +void MOVQ64ItoR( x86MMXRegType reg, u64 i ) +{ + MOVQMtoR( reg, ( uptr )(x86Ptr) + 2 + 7 ); + JMP8( 8 ); + write64( i ); +} + +void MOVQRtoR( x86MMXRegType to, x86MMXRegType from ) +{ + write16( 0x6F0F ); + ModRM( 3, to, from ); +} + +void MOVQRmtoROffset( x86MMXRegType to, x86IntRegType from, u32 offset ) +{ + write16( 0x6F0F ); + + if( offset < 128 ) { + ModRM( 1, to, from ); + write8(offset); + } + else { + ModRM( 2, to, from ); + write32(offset); + } +} + +void MOVQRtoRmOffset( x86IntRegType to, x86MMXRegType from, u32 offset ) +{ + write16( 0x7F0F ); + + if( offset < 128 ) { + ModRM( 1, from , to ); + write8(offset); + } + else { + ModRM( 2, from, to ); + write32(offset); + } +} + +/* movd m32 to r64 */ +void MOVDMtoMMX( x86MMXRegType to, uptr from ) +{ + write16( 0x6E0F ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +/* movd r64 to m32 */ +void MOVDMMXtoM( uptr to, x86MMXRegType from ) +{ + write16( 0x7E0F ); + ModRM( 0, from, DISP32 ); + write32( MEMADDR(to, 4) ); +} + +void MOVD32RtoMMX( x86MMXRegType to, x86IntRegType from ) +{ + write16( 0x6E0F ); + ModRM( 3, to, from ); +} + +void MOVD32RmtoMMX( x86MMXRegType to, x86IntRegType from ) +{ + write16( 0x6E0F ); + ModRM( 0, to, from ); +} + +void MOVD32RmOffsettoMMX( x86MMXRegType to, x86IntRegType from, u32 offset ) +{ + write16( 0x6E0F ); + + if( offset < 128 ) { + ModRM( 1, to, from ); + write8(offset); + } + else { + ModRM( 2, to, from ); + write32(offset); + } +} + +void MOVD32MMXtoR( x86IntRegType to, x86MMXRegType from ) +{ + write16( 0x7E0F ); + ModRM( 3, from, to ); +} + +void MOVD32MMXtoRm( x86IntRegType to, x86MMXRegType from ) +{ + write16( 0x7E0F ); + ModRM( 0, from, to ); + if( to >= 4 ) { + // no idea why + assert( to == ESP ); + write8(0x24); + } + +} + +void MOVD32MMXtoRmOffset( x86IntRegType to, x86MMXRegType from, u32 offset ) +{ + write16( 0x7E0F ); + + if( offset < 128 ) { + ModRM( 1, from, to ); + write8(offset); + } + else { + ModRM( 2, from, to ); + write32(offset); + } +} + +///* movd r32 to r64 */ +//void MOVD32MMXtoMMX( x86MMXRegType to, x86MMXRegType from ) +//{ +// write16( 0x6E0F ); +// ModRM( 3, to, from ); +//} +// +///* movq r64 to r32 */ +//void MOVD64MMXtoMMX( x86MMXRegType to, x86MMXRegType from ) +//{ +// write16( 0x7E0F ); +// ModRM( 3, from, to ); +//} + +// untested +void PACKSSWBMMXtoMMX(x86MMXRegType to, x86MMXRegType from) +{ + write16( 0x630F ); + ModRM( 3, to, from ); +} + +void PACKSSDWMMXtoMMX(x86MMXRegType to, x86MMXRegType from) +{ + write16( 0x6B0F ); + ModRM( 3, to, from ); +} + +void PMOVMSKBMMXtoR(x86IntRegType to, x86MMXRegType from) +{ + write16( 0xD70F ); + ModRM( 3, to, from ); +} + +void PINSRWRtoMMX( x86MMXRegType to, x86SSERegType from, u8 imm8 ) +{ + if (to > 7 || from > 7) Rex(1, to >> 3, 0, from >> 3); + write16( 0xc40f ); + ModRM( 3, to, from ); + write8( imm8 ); +} + +void PSHUFWRtoR(x86MMXRegType to, x86MMXRegType from, u8 imm8) +{ + write16(0x700f); + ModRM( 3, to, from ); + write8(imm8); +} + +void PSHUFWMtoR(x86MMXRegType to, uptr from, u8 imm8) +{ + write16( 0x700f ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); + write8(imm8); +} + +void MASKMOVQRtoR(x86MMXRegType to, x86MMXRegType from) +{ + write16(0xf70f); + ModRM( 3, to, from ); +} diff --git a/pcsx2/x86/ix86/ix86_sse.cpp b/pcsx2/x86/ix86/ix86_sse.cpp new file mode 100644 index 0000000000..6c48026a83 --- /dev/null +++ b/pcsx2/x86/ix86/ix86_sse.cpp @@ -0,0 +1,1381 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" +#include "ix86.h" + +PCSX2_ALIGNED16(unsigned int p[4]); +PCSX2_ALIGNED16(unsigned int p2[4]); +PCSX2_ALIGNED16(float f[4]); + + +XMMSSEType g_xmmtypes[XMMREGS] = { XMMT_INT }; + +/********************/ +/* SSE instructions */ +/********************/ + +#define SSEMtoR( code, overb ) \ + assert( to < XMMREGS ) ; \ + RexR(0, to); \ + write16( code ); \ + ModRM( 0, to, DISP32 ); \ + write32( MEMADDR(from, 4 + overb) ); \ + +#define SSERtoM( code, overb ) \ + assert( from < XMMREGS) ; \ + RexR(0, from); \ + write16( code ); \ + ModRM( 0, from, DISP32 ); \ + write32( MEMADDR(to, 4 + overb) ); \ + +#define SSE_SS_MtoR( code, overb ) \ + assert( to < XMMREGS ) ; \ + write8( 0xf3 ); \ + RexR(0, to); \ + write16( code ); \ + ModRM( 0, to, DISP32 ); \ + write32( MEMADDR(from, 4 + overb) ); \ + +#define SSE_SS_RtoM( code, overb ) \ + assert( from < XMMREGS) ; \ + write8( 0xf3 ); \ + RexR(0, from); \ + write16( code ); \ + ModRM( 0, from, DISP32 ); \ + write32( MEMADDR(to, 4 + overb) ); \ + +#define SSERtoR( code ) \ + assert( to < XMMREGS && from < XMMREGS) ; \ + RexRB(0, to, from); \ + write16( code ); \ + ModRM( 3, to, from ); + +#define SSEMtoR66( code ) \ + write8( 0x66 ); \ + SSEMtoR( code, 0 ); + +#define SSERtoM66( code ) \ + write8( 0x66 ); \ + SSERtoM( code, 0 ); + +#define SSERtoR66( code ) \ + write8( 0x66 ); \ + SSERtoR( code ); + +#define _SSERtoR66( code ) \ + assert( to < XMMREGS && from < XMMREGS) ; \ + write8( 0x66 ); \ + RexRB(0, from, to); \ + write16( code ); \ + ModRM( 3, from, to ); + +#define SSE_SS_RtoR( code ) \ + assert( to < XMMREGS && from < XMMREGS) ; \ + write8( 0xf3 ); \ + RexRB(0, to, from); \ + write16( code ); \ + ModRM( 3, to, from ); + +#define CMPPSMtoR( op ) \ + SSEMtoR( 0xc20f, 1 ); \ + write8( op ); + +#define CMPPSRtoR( op ) \ + SSERtoR( 0xc20f ); \ + write8( op ); + +#define CMPSSMtoR( op ) \ + SSE_SS_MtoR( 0xc20f, 1 ); \ + write8( op ); + +#define CMPSSRtoR( op ) \ + SSE_SS_RtoR( 0xc20f ); \ + write8( op ); + + + +void WriteRmOffset(x86IntRegType to, int offset); +void WriteRmOffsetFrom(x86IntRegType to, x86IntRegType from, int offset); + +/* movups [r32][r32*scale] to xmm1 */ +__forceinline void SSE_MOVUPSRmStoR( x86SSERegType to, x86IntRegType from, x86IntRegType from2, int scale ) +{ + RexRXB(0, to, from2, from); + write16( 0x100f ); + ModRM( 0, to, 0x4 ); + SibSB( scale, from2, from ); +} + +/* movups xmm1 to [r32][r32*scale] */ +__forceinline void SSE_MOVUPSRtoRmS( x86SSERegType to, x86IntRegType from, x86IntRegType from2, int scale ) +{ + RexRXB(1, to, from2, from); + write16( 0x110f ); + ModRM( 0, to, 0x4 ); + SibSB( scale, from2, from ); +} + +/* movups [r32] to r32 */ +__forceinline void SSE_MOVUPSRmtoR( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0, to, from); + write16( 0x100f ); + ModRM( 0, to, from ); +} + +/* movups r32 to [r32] */ +__forceinline void SSE_MOVUPSRtoRm( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0, from, to); + write16( 0x110f ); + ModRM( 0, from, to ); +} + +/* movlps [r32] to r32 */ +__forceinline void SSE_MOVLPSRmtoR( x86SSERegType to, x86IntRegType from ) +{ + RexRB(1, to, from); + write16( 0x120f ); + ModRM( 0, to, from ); +} + +__forceinline void SSE_MOVLPSRmtoROffset( x86SSERegType to, x86IntRegType from, int offset ) +{ + RexRB(0, to, from); + write16( 0x120f ); + WriteRmOffsetFrom(to, from, offset); +} + +/* movaps r32 to [r32] */ +__forceinline void SSE_MOVLPSRtoRm( x86IntRegType to, x86IntRegType from ) +{ + RexRB(0, from, to); + write16( 0x130f ); + ModRM( 0, from, to ); +} + +__forceinline void SSE_MOVLPSRtoRmOffset( x86SSERegType to, x86IntRegType from, int offset ) +{ + RexRB(0, from, to); + write16( 0x130f ); + WriteRmOffsetFrom(from, to, offset); +} + +/* movaps [r32][r32*scale] to xmm1 */ +__forceinline void SSE_MOVAPSRmStoR( x86SSERegType to, x86IntRegType from, x86IntRegType from2, int scale ) +{ + assert( from != EBP ); + RexRXB(0, to, from2, from); + write16( 0x280f ); + ModRM( 0, to, 0x4 ); + SibSB( scale, from2, from ); +} + +/* movaps xmm1 to [r32][r32*scale] */ +__forceinline void SSE_MOVAPSRtoRmS( x86SSERegType to, x86IntRegType from, x86IntRegType from2, int scale ) +{ + assert( from != EBP ); + RexRXB(0, to, from2, from); + write16( 0x290f ); + ModRM( 0, to, 0x4 ); + SibSB( scale, from2, from ); +} + +// movaps [r32+offset] to r32 +__forceinline void SSE_MOVAPSRmtoROffset( x86SSERegType to, x86IntRegType from, int offset ) +{ + RexRB(0, to, from); + write16( 0x280f ); + WriteRmOffsetFrom(to, from, offset); +} + +// movaps r32 to [r32+offset] +__forceinline void SSE_MOVAPSRtoRmOffset( x86IntRegType to, x86SSERegType from, int offset ) +{ + RexRB(0, from, to); + write16( 0x290f ); + WriteRmOffsetFrom(from, to, offset); +} + +// movdqa [r32+offset] to r32 +__forceinline void SSE2_MOVDQARmtoROffset( x86SSERegType to, x86IntRegType from, int offset ) +{ + write8(0x66); + RexRB(0, to, from); + write16( 0x6f0f ); + WriteRmOffsetFrom(to, from, offset); +} + +// movdqa r32 to [r32+offset] +__forceinline void SSE2_MOVDQARtoRmOffset( x86IntRegType to, x86SSERegType from, int offset ) +{ + write8(0x66); + RexRB(0, from, to); + write16( 0x7f0f ); + WriteRmOffsetFrom(from, to, offset); +} + +// movups [r32+offset] to r32 +__forceinline void SSE_MOVUPSRmtoROffset( x86SSERegType to, x86IntRegType from, int offset ) +{ + RexRB(0, to, from); + write16( 0x100f ); + WriteRmOffsetFrom(to, from, offset); +} + +// movups r32 to [r32+offset] +__forceinline void SSE_MOVUPSRtoRmOffset( x86SSERegType to, x86IntRegType from, int offset ) +{ + RexRB(0, from, to); + write16( 0x110f ); + WriteRmOffsetFrom(from, to, offset); +} + +//**********************************************************************************/ +//MOVAPS: Move aligned Packed Single Precision FP values * +//********************************************************************************** +__forceinline void SSE_MOVAPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x280f, 0 ); } +__forceinline void SSE_MOVAPS_XMM_to_M128( uptr to, x86SSERegType from ) { SSERtoM( 0x290f, 0 ); } +__forceinline void SSE_MOVAPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { if (to != from) { SSERtoR( 0x280f ); } } + +__forceinline void SSE_MOVUPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x100f, 0 ); } +__forceinline void SSE_MOVUPS_XMM_to_M128( uptr to, x86SSERegType from ) { SSERtoM( 0x110f, 0 ); } + +__forceinline void SSE2_MOVSD_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) +{ + write8(0xf2); + SSERtoR( 0x100f); +} + +__forceinline void SSE2_MOVQ_M64_to_XMM( x86SSERegType to, uptr from ) +{ + write8(0xf3); SSEMtoR( 0x7e0f, 0); +} + +__forceinline void SSE2_MOVQ_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) +{ + write8(0xf3); SSERtoR( 0x7e0f); +} + +__forceinline void SSE2_MOVQ_XMM_to_M64( u32 to, x86SSERegType from ) +{ + SSERtoM66(0xd60f); +} + +__forceinline void SSE2_MOVDQ2Q_XMM_to_MM( x86MMXRegType to, x86SSERegType from) +{ + write8(0xf2); + SSERtoR( 0xd60f); +} +__forceinline void SSE2_MOVQ2DQ_MM_to_XMM( x86SSERegType to, x86MMXRegType from) +{ + write8(0xf3); + SSERtoR( 0xd60f); +} + +//**********************************************************************************/ +//MOVSS: Move Scalar Single-Precision FP value * +//********************************************************************************** +__forceinline void SSE_MOVSS_M32_to_XMM( x86SSERegType to, uptr from ) { SSE_SS_MtoR( 0x100f, 0 ); } +__forceinline void SSE_MOVSS_XMM_to_M32( u32 to, x86SSERegType from ) { SSE_SS_RtoM( 0x110f, 0 ); } +__forceinline void SSE_MOVSS_XMM_to_Rm( x86IntRegType to, x86SSERegType from ) +{ + write8(0xf3); + RexRB(0, from, to); + write16(0x110f); + ModRM(0, from, to); +} + +__forceinline void SSE_MOVSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { if (to != from) { SSE_SS_RtoR( 0x100f ); } } + +__forceinline void SSE_MOVSS_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ) +{ + write8(0xf3); + RexRB(0, to, from); + write16( 0x100f ); + WriteRmOffsetFrom(to, from, offset); +} + +__forceinline void SSE_MOVSS_XMM_to_RmOffset( x86IntRegType to, x86SSERegType from, int offset ) +{ + write8(0xf3); + RexRB(0, from, to); + write16(0x110f); + WriteRmOffsetFrom(from, to, offset); +} + +__forceinline void SSE_MASKMOVDQU_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR66( 0xf70f ); } +//**********************************************************************************/ +//MOVLPS: Move low Packed Single-Precision FP * +//********************************************************************************** +__forceinline void SSE_MOVLPS_M64_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x120f, 0 ); } +__forceinline void SSE_MOVLPS_XMM_to_M64( u32 to, x86SSERegType from ) { SSERtoM( 0x130f, 0 ); } + +__forceinline void SSE_MOVLPS_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ) +{ + RexRB(0, to, from); + write16( 0x120f ); + WriteRmOffsetFrom(to, from, offset); +} + +__forceinline void SSE_MOVLPS_XMM_to_RmOffset( x86IntRegType to, x86SSERegType from, int offset ) +{ + RexRB(0, from, to); + write16(0x130f); + WriteRmOffsetFrom(from, to, offset); +} + +///////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//MOVHPS: Move High Packed Single-Precision FP * +//********************************************************************************** +__forceinline void SSE_MOVHPS_M64_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x160f, 0 ); } +__forceinline void SSE_MOVHPS_XMM_to_M64( u32 to, x86SSERegType from ) { SSERtoM( 0x170f, 0 ); } + +__forceinline void SSE_MOVHPS_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ) +{ + RexRB(0, to, from); + write16( 0x160f ); + WriteRmOffsetFrom(to, from, offset); +} + +__forceinline void SSE_MOVHPS_XMM_to_RmOffset( x86IntRegType to, x86SSERegType from, int offset ) +{ + RexRB(0, from, to); + write16(0x170f); + WriteRmOffsetFrom(from, to, offset); +} + +///////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//MOVLHPS: Moved packed Single-Precision FP low to high * +//********************************************************************************** +__forceinline void SSE_MOVLHPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x160f ); } + +////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//MOVHLPS: Moved packed Single-Precision FP High to Low * +//********************************************************************************** +__forceinline void SSE_MOVHLPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x120f ); } + +/////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//ANDPS: Logical Bit-wise AND for Single FP * +//********************************************************************************** +__forceinline void SSE_ANDPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x540f, 0 ); } +__forceinline void SSE_ANDPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x540f ); } + +/////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//ANDNPS : Logical Bit-wise AND NOT of Single-precision FP values * +//********************************************************************************** +__forceinline void SSE_ANDNPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x550f, 0 ); } +__forceinline void SSE_ANDNPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR( 0x550f ); } + +///////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//RCPPS : Packed Single-Precision FP Reciprocal * +//********************************************************************************** +__forceinline void SSE_RCPPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x530f ); } +__forceinline void SSE_RCPPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x530f, 0 ); } + +__forceinline void SSE_RCPSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSE_SS_RtoR(0x530f); } +__forceinline void SSE_RCPSS_M32_to_XMM( x86SSERegType to, uptr from ) { SSE_SS_MtoR(0x530f, 0); } + +////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//ORPS : Bit-wise Logical OR of Single-Precision FP Data * +//********************************************************************************** +__forceinline void SSE_ORPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x560f, 0 ); } +__forceinline void SSE_ORPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x560f ); } + +///////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//XORPS : Bitwise Logical XOR of Single-Precision FP Values * +//********************************************************************************** + +__forceinline void SSE_XORPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x570f, 0 ); } +__forceinline void SSE_XORPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x570f ); } + +/////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//ADDPS : ADD Packed Single-Precision FP Values * +//********************************************************************************** +__forceinline void SSE_ADDPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x580f, 0 ); } +__forceinline void SSE_ADDPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x580f ); } + +//////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//ADDSS : ADD Scalar Single-Precision FP Values * +//********************************************************************************** +__forceinline void SSE_ADDSS_M32_to_XMM( x86SSERegType to, uptr from ) { SSE_SS_MtoR( 0x580f, 0 ); } +__forceinline void SSE_ADDSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSE_SS_RtoR( 0x580f ); } + +///////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//SUBPS: Packed Single-Precision FP Subtract * +//********************************************************************************** +__forceinline void SSE_SUBPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x5c0f, 0 ); } +__forceinline void SSE_SUBPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x5c0f ); } + +/////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//SUBSS : Scalar Single-Precision FP Subtract * +//********************************************************************************** +__forceinline void SSE_SUBSS_M32_to_XMM( x86SSERegType to, uptr from ) { SSE_SS_MtoR( 0x5c0f, 0 ); } +__forceinline void SSE_SUBSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSE_SS_RtoR( 0x5c0f ); } + +///////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//MULPS : Packed Single-Precision FP Multiply * +//********************************************************************************** +__forceinline void SSE_MULPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x590f, 0 ); } +__forceinline void SSE_MULPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x590f ); } + +//////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//MULSS : Scalar Single-Precision FP Multiply * +//********************************************************************************** +__forceinline void SSE_MULSS_M32_to_XMM( x86SSERegType to, uptr from ) { SSE_SS_MtoR( 0x590f, 0 ); } +__forceinline void SSE_MULSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSE_SS_RtoR( 0x590f ); } + +//////////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//Packed Single-Precission FP compare (CMPccPS) * +//********************************************************************************** +//missing SSE_CMPPS_I8_to_XMM +// SSE_CMPPS_M32_to_XMM +// SSE_CMPPS_XMM_to_XMM +__forceinline void SSE_CMPEQPS_M128_to_XMM( x86SSERegType to, uptr from ) { CMPPSMtoR( 0 ); } +__forceinline void SSE_CMPEQPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPPSRtoR( 0 ); } +__forceinline void SSE_CMPLTPS_M128_to_XMM( x86SSERegType to, uptr from ) { CMPPSMtoR( 1 ); } +__forceinline void SSE_CMPLTPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPPSRtoR( 1 ); } +__forceinline void SSE_CMPLEPS_M128_to_XMM( x86SSERegType to, uptr from ) { CMPPSMtoR( 2 ); } +__forceinline void SSE_CMPLEPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPPSRtoR( 2 ); } +__forceinline void SSE_CMPUNORDPS_M128_to_XMM( x86SSERegType to, uptr from ) { CMPPSMtoR( 3 ); } +__forceinline void SSE_CMPUNORDPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPPSRtoR( 3 ); } +__forceinline void SSE_CMPNEPS_M128_to_XMM( x86SSERegType to, uptr from ) { CMPPSMtoR( 4 ); } +__forceinline void SSE_CMPNEPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPPSRtoR( 4 ); } +__forceinline void SSE_CMPNLTPS_M128_to_XMM( x86SSERegType to, uptr from ) { CMPPSMtoR( 5 ); } +__forceinline void SSE_CMPNLTPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPPSRtoR( 5 ); } +__forceinline void SSE_CMPNLEPS_M128_to_XMM( x86SSERegType to, uptr from ) { CMPPSMtoR( 6 ); } +__forceinline void SSE_CMPNLEPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPPSRtoR( 6 ); } +__forceinline void SSE_CMPORDPS_M128_to_XMM( x86SSERegType to, uptr from ) { CMPPSMtoR( 7 ); } +__forceinline void SSE_CMPORDPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPPSRtoR( 7 ); } + +/////////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//Scalar Single-Precission FP compare (CMPccSS) * +//********************************************************************************** +//missing SSE_CMPSS_I8_to_XMM +// SSE_CMPSS_M32_to_XMM +// SSE_CMPSS_XMM_to_XMM +__forceinline void SSE_CMPEQSS_M32_to_XMM( x86SSERegType to, uptr from ) { CMPSSMtoR( 0 ); } +__forceinline void SSE_CMPEQSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPSSRtoR( 0 ); } +__forceinline void SSE_CMPLTSS_M32_to_XMM( x86SSERegType to, uptr from ) { CMPSSMtoR( 1 ); } +__forceinline void SSE_CMPLTSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPSSRtoR( 1 ); } +__forceinline void SSE_CMPLESS_M32_to_XMM( x86SSERegType to, uptr from ) { CMPSSMtoR( 2 ); } +__forceinline void SSE_CMPLESS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPSSRtoR( 2 ); } +__forceinline void SSE_CMPUNORDSS_M32_to_XMM( x86SSERegType to, uptr from ) { CMPSSMtoR( 3 ); } +__forceinline void SSE_CMPUNORDSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPSSRtoR( 3 ); } +__forceinline void SSE_CMPNESS_M32_to_XMM( x86SSERegType to, uptr from ) { CMPSSMtoR( 4 ); } +__forceinline void SSE_CMPNESS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPSSRtoR( 4 ); } +__forceinline void SSE_CMPNLTSS_M32_to_XMM( x86SSERegType to, uptr from ) { CMPSSMtoR( 5 ); } +__forceinline void SSE_CMPNLTSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPSSRtoR( 5 ); } +__forceinline void SSE_CMPNLESS_M32_to_XMM( x86SSERegType to, uptr from ) { CMPSSMtoR( 6 ); } +__forceinline void SSE_CMPNLESS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPSSRtoR( 6 ); } +__forceinline void SSE_CMPORDSS_M32_to_XMM( x86SSERegType to, uptr from ) { CMPSSMtoR( 7 ); } +__forceinline void SSE_CMPORDSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { CMPSSRtoR( 7 ); } + +__forceinline void SSE_UCOMISS_M32_to_XMM( x86SSERegType to, uptr from ) +{ + RexR(0, to); + write16( 0x2e0f ); + ModRM( 0, to, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +__forceinline void SSE_UCOMISS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) +{ + RexRB(0, to, from); + write16( 0x2e0f ); + ModRM( 3, to, from ); +} + +////////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//RSQRTPS : Packed Single-Precision FP Square Root Reciprocal * +//********************************************************************************** +__forceinline void SSE_RSQRTPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x520f, 0 ); } +__forceinline void SSE_RSQRTPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR( 0x520f ); } + +///////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//RSQRTSS : Scalar Single-Precision FP Square Root Reciprocal * +//********************************************************************************** +__forceinline void SSE_RSQRTSS_M32_to_XMM( x86SSERegType to, uptr from ) { SSE_SS_MtoR( 0x520f, 0 ); } +__forceinline void SSE_RSQRTSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSE_SS_RtoR( 0x520f ); } + +//////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//SQRTPS : Packed Single-Precision FP Square Root * +//********************************************************************************** +__forceinline void SSE_SQRTPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x510f, 0 ); } +__forceinline void SSE_SQRTPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR( 0x510f ); } + +////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//SQRTSS : Scalar Single-Precision FP Square Root * +//********************************************************************************** +__forceinline void SSE_SQRTSS_M32_to_XMM( x86SSERegType to, uptr from ) { SSE_SS_MtoR( 0x510f, 0 ); } +__forceinline void SSE_SQRTSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSE_SS_RtoR( 0x510f ); } + +//////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//MAXPS: Return Packed Single-Precision FP Maximum * +//********************************************************************************** +__forceinline void SSE_MAXPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x5f0f, 0 ); } +__forceinline void SSE_MAXPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x5f0f ); } + +///////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//MAXSS: Return Scalar Single-Precision FP Maximum * +//********************************************************************************** +__forceinline void SSE_MAXSS_M32_to_XMM( x86SSERegType to, uptr from ) { SSE_SS_MtoR( 0x5f0f, 0 ); } +__forceinline void SSE_MAXSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSE_SS_RtoR( 0x5f0f ); } + +///////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//CVTPI2PS: Packed Signed INT32 to Packed Single FP Conversion * +//********************************************************************************** +__forceinline void SSE_CVTPI2PS_M64_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x2a0f, 0 ); } +__forceinline void SSE_CVTPI2PS_MM_to_XMM( x86SSERegType to, x86MMXRegType from ) { SSERtoR( 0x2a0f ); } + +/////////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//CVTPS2PI: Packed Single FP to Packed Signed INT32 Conversion * +//********************************************************************************** +__forceinline void SSE_CVTPS2PI_M64_to_MM( x86MMXRegType to, uptr from ) { SSEMtoR( 0x2d0f, 0 ); } +__forceinline void SSE_CVTPS2PI_XMM_to_MM( x86MMXRegType to, x86SSERegType from ) { SSERtoR( 0x2d0f ); } + +__forceinline void SSE_CVTTSS2SI_M32_to_R32(x86IntRegType to, uptr from) { write8(0xf3); SSEMtoR(0x2c0f, 0); } +__forceinline void SSE_CVTTSS2SI_XMM_to_R32(x86IntRegType to, x86SSERegType from) +{ + write8(0xf3); + RexRB(0, to, from); + write16(0x2c0f); + ModRM(3, to, from); +} + +__forceinline void SSE_CVTSI2SS_M32_to_XMM(x86SSERegType to, uptr from) { write8(0xf3); SSEMtoR(0x2a0f, 0); } +__forceinline void SSE_CVTSI2SS_R_to_XMM(x86SSERegType to, x86IntRegType from) +{ + write8(0xf3); + RexRB(0, to, from); + write16(0x2a0f); + ModRM(3, to, from); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//CVTDQ2PS: Packed Signed INT32 to Packed Single Precision FP Conversion * +//********************************************************************************** +__forceinline void SSE2_CVTDQ2PS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x5b0f, 0 ); } +__forceinline void SSE2_CVTDQ2PS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x5b0f ); } + +//**********************************************************************************/ +//CVTPS2DQ: Packed Single Precision FP to Packed Signed INT32 Conversion * +//********************************************************************************** +__forceinline void SSE2_CVTPS2DQ_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR66( 0x5b0f ); } +__forceinline void SSE2_CVTPS2DQ_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR66( 0x5b0f ); } + +__forceinline void SSE2_CVTTPS2DQ_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { write8(0xf3); SSERtoR(0x5b0f); } +///////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//MINPS: Return Packed Single-Precision FP Minimum * +//********************************************************************************** +__forceinline void SSE_MINPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x5d0f, 0 ); } +__forceinline void SSE_MINPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x5d0f ); } + +////////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//MINSS: Return Scalar Single-Precision FP Minimum * +//********************************************************************************** +__forceinline void SSE_MINSS_M32_to_XMM( x86SSERegType to, uptr from ) { SSE_SS_MtoR( 0x5d0f, 0 ); } +__forceinline void SSE_MINSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSE_SS_RtoR( 0x5d0f ); } + +/////////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//PMAXSW: Packed Signed Integer Word Maximum * +//********************************************************************************** +//missing + // SSE_PMAXSW_M64_to_MM +// SSE2_PMAXSW_M128_to_XMM +// SSE2_PMAXSW_XMM_to_XMM +__forceinline void SSE_PMAXSW_MM_to_MM( x86MMXRegType to, x86MMXRegType from ){ SSERtoR( 0xEE0F ); } + +/////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//PMINSW: Packed Signed Integer Word Minimum * +//********************************************************************************** +//missing + // SSE_PMINSW_M64_to_MM +// SSE2_PMINSW_M128_to_XMM +// SSE2_PMINSW_XMM_to_XMM +__forceinline void SSE_PMINSW_MM_to_MM( x86MMXRegType to, x86MMXRegType from ){ SSERtoR( 0xEA0F ); } + +////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//SHUFPS: Shuffle Packed Single-Precision FP Values * +//********************************************************************************** +__forceinline void SSE_SHUFPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from, u8 imm8 ) { SSERtoR( 0xC60F ); write8( imm8 ); } +__forceinline void SSE_SHUFPS_M128_to_XMM( x86SSERegType to, uptr from, u8 imm8 ) { SSEMtoR( 0xC60F, 1 ); write8( imm8 ); } + +__forceinline void SSE_SHUFPS_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset, u8 imm8 ) +{ + RexRB(0, to, from); + write16(0xc60f); + WriteRmOffsetFrom(to, from, offset); + write8(imm8); +} + +//////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//PSHUFD: Shuffle Packed DoubleWords * +//********************************************************************************** +__forceinline void SSE2_PSHUFD_XMM_to_XMM( x86SSERegType to, x86SSERegType from, u8 imm8 ) +{ + SSERtoR66( 0x700F ); + write8( imm8 ); +} +__forceinline void SSE2_PSHUFD_M128_to_XMM( x86SSERegType to, uptr from, u8 imm8 ) { SSEMtoR66( 0x700F ); write8( imm8 ); } + +__forceinline void SSE2_PSHUFLW_XMM_to_XMM( x86SSERegType to, x86SSERegType from, u8 imm8 ) { write8(0xF2); SSERtoR(0x700F); write8(imm8); } +__forceinline void SSE2_PSHUFLW_M128_to_XMM( x86SSERegType to, uptr from, u8 imm8 ) { write8(0xF2); SSEMtoR(0x700F, 1); write8(imm8); } +__forceinline void SSE2_PSHUFHW_XMM_to_XMM( x86SSERegType to, x86SSERegType from, u8 imm8 ) { write8(0xF3); SSERtoR(0x700F); write8(imm8); } +__forceinline void SSE2_PSHUFHW_M128_to_XMM( x86SSERegType to, uptr from, u8 imm8 ) { write8(0xF3); SSEMtoR(0x700F, 1); write8(imm8); } + +/////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//UNPCKLPS: Unpack and Interleave low Packed Single-Precision FP Data * +//********************************************************************************** +__forceinline void SSE_UNPCKLPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR(0x140f, 0); } +__forceinline void SSE_UNPCKLPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x140F ); } + +//////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//UNPCKHPS: Unpack and Interleave High Packed Single-Precision FP Data * +//********************************************************************************** +__forceinline void SSE_UNPCKHPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR(0x150f, 0); } +__forceinline void SSE_UNPCKHPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x150F ); } + +//////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//DIVPS : Packed Single-Precision FP Divide * +//********************************************************************************** +__forceinline void SSE_DIVPS_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR( 0x5e0F, 0 ); } +__forceinline void SSE_DIVPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR( 0x5e0F ); } + +////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//DIVSS : Scalar Single-Precision FP Divide * +//********************************************************************************** +__forceinline void SSE_DIVSS_M32_to_XMM( x86SSERegType to, uptr from ) { SSE_SS_MtoR( 0x5e0F, 0 ); } +__forceinline void SSE_DIVSS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSE_SS_RtoR( 0x5e0F ); } + +///////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//STMXCSR : Store Streaming SIMD Extension Control/Status * +//********************************************************************************** +__forceinline void SSE_STMXCSR( uptr from ) { + write16( 0xAE0F ); + ModRM( 0, 0x3, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +///////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//LDMXCSR : Load Streaming SIMD Extension Control/Status * +//********************************************************************************** +__forceinline void SSE_LDMXCSR( uptr from ) { + write16( 0xAE0F ); + ModRM( 0, 0x2, DISP32 ); + write32( MEMADDR(from, 4) ); +} + +///////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//PADDB,PADDW,PADDD : Add Packed Integers * +//********************************************************************************** +__forceinline void SSE2_PADDB_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xFC0F ); } +__forceinline void SSE2_PADDB_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0xFC0F ); } +__forceinline void SSE2_PADDW_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xFD0F ); } +__forceinline void SSE2_PADDW_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0xFD0F ); } +__forceinline void SSE2_PADDD_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xFE0F ); } +__forceinline void SSE2_PADDD_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0xFE0F ); } + +__forceinline void SSE2_PADDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from ) { SSERtoR66( 0xD40F ); } +__forceinline void SSE2_PADDQ_M128_to_XMM(x86SSERegType to, uptr from ) { SSEMtoR66( 0xD40F ); } + +/////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//PCMPxx: Compare Packed Integers * +//********************************************************************************** +__forceinline void SSE2_PCMPGTB_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0x640F ); } +__forceinline void SSE2_PCMPGTB_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0x640F ); } +__forceinline void SSE2_PCMPGTW_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0x650F ); } +__forceinline void SSE2_PCMPGTW_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0x650F ); } +__forceinline void SSE2_PCMPGTD_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0x660F ); } +__forceinline void SSE2_PCMPGTD_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0x660F ); } +__forceinline void SSE2_PCMPEQB_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0x740F ); } +__forceinline void SSE2_PCMPEQB_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0x740F ); } +__forceinline void SSE2_PCMPEQW_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0x750F ); } +__forceinline void SSE2_PCMPEQW_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0x750F ); } +__forceinline void SSE2_PCMPEQD_XMM_to_XMM(x86SSERegType to, x86SSERegType from ) +{ + SSERtoR66( 0x760F ); +} + +__forceinline void SSE2_PCMPEQD_M128_to_XMM(x86SSERegType to, uptr from ) +{ + SSEMtoR66( 0x760F ); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//PEXTRW,PINSRW: Packed Extract/Insert Word * +//********************************************************************************** +__forceinline void SSE_PEXTRW_XMM_to_R32(x86IntRegType to, x86SSERegType from, u8 imm8 ){ SSERtoR66(0xC50F); write8( imm8 ); } +__forceinline void SSE_PINSRW_R32_to_XMM(x86SSERegType to, x86IntRegType from, u8 imm8 ){ SSERtoR66(0xC40F); write8( imm8 ); } + +//////////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//PSUBx: Subtract Packed Integers * +//********************************************************************************** +__forceinline void SSE2_PSUBB_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xF80F ); } +__forceinline void SSE2_PSUBB_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0xF80F ); } +__forceinline void SSE2_PSUBW_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xF90F ); } +__forceinline void SSE2_PSUBW_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0xF90F ); } +__forceinline void SSE2_PSUBD_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xFA0F ); } +__forceinline void SSE2_PSUBD_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0xFA0F ); } +__forceinline void SSE2_PSUBQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xFB0F ); } +__forceinline void SSE2_PSUBQ_M128_to_XMM(x86SSERegType to, uptr from ){ SSEMtoR66( 0xFB0F ); } + +/////////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//MOVD: Move Dword(32bit) to /from XMM reg * +//********************************************************************************** +__forceinline void SSE2_MOVD_M32_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR66(0x6E0F); } +__forceinline void SSE2_MOVD_R_to_XMM( x86SSERegType to, x86IntRegType from ) +{ + SSERtoR66(0x6E0F); +} + +__forceinline void SSE2_MOVD_Rm_to_XMM( x86SSERegType to, x86IntRegType from ) +{ + write8(0x66); + RexRB(0, to, from); + write16( 0x6e0f ); + ModRM( 0, to, from); +} + +__forceinline void SSE2_MOVD_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ) +{ + write8(0x66); + RexRB(0, to, from); + write16( 0x6e0f ); + WriteRmOffsetFrom(to, from, offset); +} + +__forceinline void SSE2_MOVD_XMM_to_M32( u32 to, x86SSERegType from ) { SSERtoM66(0x7E0F); } +__forceinline void SSE2_MOVD_XMM_to_R( x86IntRegType to, x86SSERegType from ) +{ + _SSERtoR66(0x7E0F); +} + +__forceinline void SSE2_MOVD_XMM_to_Rm( x86IntRegType to, x86SSERegType from ) +{ + write8(0x66); + RexRB(0, from, to); + write16( 0x7e0f ); + ModRM( 0, from, to ); +} + +__forceinline void SSE2_MOVD_XMM_to_RmOffset( x86IntRegType to, x86SSERegType from, int offset ) +{ + write8(0x66); + RexRB(0, from, to); + write16( 0x7e0f ); + WriteRmOffsetFrom(from, to, offset); +} + +//////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//POR : SSE Bitwise OR * +//********************************************************************************** +__forceinline void SSE2_POR_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR66( 0xEB0F ); } +__forceinline void SSE2_POR_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR66( 0xEB0F ); } + +// logical and to &= from +__forceinline void SSE2_PAND_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR66( 0xDB0F ); } +__forceinline void SSE2_PAND_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR66( 0xDB0F ); } + +// to = (~to) & from +__forceinline void SSE2_PANDN_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR66( 0xDF0F ); } +__forceinline void SSE2_PANDN_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR66( 0xDF0F ); } + +///////////////////////////////////////////////////////////////////////////////////// +//**********************************************************************************/ +//PXOR : SSE Bitwise XOR * +//********************************************************************************** +__forceinline void SSE2_PXOR_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xEF0F ); } +__forceinline void SSE2_PXOR_M128_to_XMM( x86SSERegType to, uptr from ){ SSEMtoR66( 0xEF0F ) }; +/////////////////////////////////////////////////////////////////////////////////////// + +__forceinline void SSE2_MOVDQA_M128_to_XMM(x86SSERegType to, uptr from) {SSEMtoR66(0x6F0F); } +__forceinline void SSE2_MOVDQA_XMM_to_M128( uptr to, x86SSERegType from ){SSERtoM66(0x7F0F);} +__forceinline void SSE2_MOVDQA_XMM_to_XMM( x86SSERegType to, x86SSERegType from) { if (to != from) { SSERtoR66(0x6F0F); } } + +__forceinline void SSE2_MOVDQU_M128_to_XMM(x86SSERegType to, uptr from) { write8(0xF3); SSEMtoR(0x6F0F, 0); } +__forceinline void SSE2_MOVDQU_XMM_to_M128( uptr to, x86SSERegType from) { write8(0xF3); SSERtoM(0x7F0F, 0); } +__forceinline void SSE2_MOVDQU_XMM_to_XMM( x86SSERegType to, x86SSERegType from) { write8(0xF3); SSERtoR(0x6F0F); } + +// shift right logical + +__forceinline void SSE2_PSRLW_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66(0xD10F); } +__forceinline void SSE2_PSRLW_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66(0xD10F); } +__forceinline void SSE2_PSRLW_I8_to_XMM(x86SSERegType to, u8 imm8) +{ + write8( 0x66 ); + RexB(0, to); + write16( 0x710F ); + ModRM( 3, 2 , to ); + write8( imm8 ); +} + +__forceinline void SSE2_PSRLD_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66(0xD20F); } +__forceinline void SSE2_PSRLD_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66(0xD20F); } +__forceinline void SSE2_PSRLD_I8_to_XMM(x86SSERegType to, u8 imm8) +{ + write8( 0x66 ); + RexB(0, to); + write16( 0x720F ); + ModRM( 3, 2 , to ); + write8( imm8 ); +} + +__forceinline void SSE2_PSRLQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66(0xD30F); } +__forceinline void SSE2_PSRLQ_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66(0xD30F); } +__forceinline void SSE2_PSRLQ_I8_to_XMM(x86SSERegType to, u8 imm8) +{ + write8( 0x66 ); + RexB(0, to); + write16( 0x730F ); + ModRM( 3, 2 , to ); + write8( imm8 ); +} + +__forceinline void SSE2_PSRLDQ_I8_to_XMM(x86SSERegType to, u8 imm8) +{ + write8( 0x66 ); + RexB(0, to); + write16( 0x730F ); + ModRM( 3, 3 , to ); + write8( imm8 ); +} + +// shift right arithmetic + +__forceinline void SSE2_PSRAW_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66(0xE10F); } +__forceinline void SSE2_PSRAW_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66(0xE10F); } +__forceinline void SSE2_PSRAW_I8_to_XMM(x86SSERegType to, u8 imm8) +{ + write8( 0x66 ); + RexB(0, to); + write16( 0x710F ); + ModRM( 3, 4 , to ); + write8( imm8 ); +} + +__forceinline void SSE2_PSRAD_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66(0xE20F); } +__forceinline void SSE2_PSRAD_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66(0xE20F); } +__forceinline void SSE2_PSRAD_I8_to_XMM(x86SSERegType to, u8 imm8) +{ + write8( 0x66 ); + RexB(0, to); + write16( 0x720F ); + ModRM( 3, 4 , to ); + write8( imm8 ); +} + +// shift left logical + +__forceinline void SSE2_PSLLW_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66(0xF10F); } +__forceinline void SSE2_PSLLW_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66(0xF10F); } +__forceinline void SSE2_PSLLW_I8_to_XMM(x86SSERegType to, u8 imm8) +{ + write8( 0x66 ); + RexB(0, to); + write16( 0x710F ); + ModRM( 3, 6 , to ); + write8( imm8 ); +} + +__forceinline void SSE2_PSLLD_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66(0xF20F); } +__forceinline void SSE2_PSLLD_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66(0xF20F); } +__forceinline void SSE2_PSLLD_I8_to_XMM(x86SSERegType to, u8 imm8) +{ + write8( 0x66 ); + RexB(0, to); + write16( 0x720F ); + ModRM( 3, 6 , to ); + write8( imm8 ); +} + +__forceinline void SSE2_PSLLQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66(0xF30F); } +__forceinline void SSE2_PSLLQ_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66(0xF30F); } +__forceinline void SSE2_PSLLQ_I8_to_XMM(x86SSERegType to, u8 imm8) +{ + write8( 0x66 ); + RexB(0, to); + write16( 0x730F ); + ModRM( 3, 6 , to ); + write8( imm8 ); +} + +__forceinline void SSE2_PSLLDQ_I8_to_XMM(x86SSERegType to, u8 imm8) +{ + write8( 0x66 ); + RexB(0, to); + write16( 0x730F ); + ModRM( 3, 7 , to ); + write8( imm8 ); +} + + +__forceinline void SSE2_PMAXSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xEE0F ); } +__forceinline void SSE2_PMAXSW_M128_to_XMM( x86SSERegType to, uptr from ){ SSEMtoR66( 0xEE0F ); } + +__forceinline void SSE2_PMAXUB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xDE0F ); } +__forceinline void SSE2_PMAXUB_M128_to_XMM( x86SSERegType to, uptr from ){ SSEMtoR66( 0xDE0F ); } + +__forceinline void SSE2_PMINSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xEA0F ); } +__forceinline void SSE2_PMINSW_M128_to_XMM( x86SSERegType to, uptr from ){ SSEMtoR66( 0xEA0F ); } + +__forceinline void SSE2_PMINUB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xDA0F ); } +__forceinline void SSE2_PMINUB_M128_to_XMM( x86SSERegType to, uptr from ){ SSEMtoR66( 0xDA0F ); } + +// + +__forceinline void SSE2_PADDSB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xEC0F ); } +__forceinline void SSE2_PADDSB_M128_to_XMM( x86SSERegType to, uptr from ){ SSEMtoR66( 0xEC0F ); } + +__forceinline void SSE2_PADDSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xED0F ); } +__forceinline void SSE2_PADDSW_M128_to_XMM( x86SSERegType to, uptr from ){ SSEMtoR66( 0xED0F ); } + +__forceinline void SSE2_PSUBSB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xE80F ); } +__forceinline void SSE2_PSUBSB_M128_to_XMM( x86SSERegType to, uptr from ){ SSEMtoR66( 0xE80F ); } + +__forceinline void SSE2_PSUBSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ){ SSERtoR66( 0xE90F ); } +__forceinline void SSE2_PSUBSW_M128_to_XMM( x86SSERegType to, uptr from ){ SSEMtoR66( 0xE90F ); } + +__forceinline void SSE2_PSUBUSB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR66( 0xD80F ); } +__forceinline void SSE2_PSUBUSB_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR66( 0xD80F ); } +__forceinline void SSE2_PSUBUSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR66( 0xD90F ); } +__forceinline void SSE2_PSUBUSW_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR66( 0xD90F ); } + +__forceinline void SSE2_PADDUSB_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR66( 0xDC0F ); } +__forceinline void SSE2_PADDUSB_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR66( 0xDC0F ); } +__forceinline void SSE2_PADDUSW_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) { SSERtoR66( 0xDD0F ); } +__forceinline void SSE2_PADDUSW_M128_to_XMM( x86SSERegType to, uptr from ) { SSEMtoR66( 0xDD0F ); } + +//**********************************************************************************/ +//PACKSSWB,PACKSSDW: Pack Saturate Signed Word +//********************************************************************************** +__forceinline void SSE2_PACKSSWB_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x630F ); } +__forceinline void SSE2_PACKSSWB_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x630F ); } +__forceinline void SSE2_PACKSSDW_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x6B0F ); } +__forceinline void SSE2_PACKSSDW_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x6B0F ); } + +__forceinline void SSE2_PACKUSWB_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x670F ); } +__forceinline void SSE2_PACKUSWB_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x670F ); } + +//**********************************************************************************/ +//PUNPCKHWD: Unpack 16bit high +//********************************************************************************** +__forceinline void SSE2_PUNPCKLBW_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x600F ); } +__forceinline void SSE2_PUNPCKLBW_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x600F ); } + +__forceinline void SSE2_PUNPCKHBW_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x680F ); } +__forceinline void SSE2_PUNPCKHBW_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x680F ); } + +__forceinline void SSE2_PUNPCKLWD_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x610F ); } +__forceinline void SSE2_PUNPCKLWD_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x610F ); } +__forceinline void SSE2_PUNPCKHWD_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x690F ); } +__forceinline void SSE2_PUNPCKHWD_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x690F ); } + +__forceinline void SSE2_PUNPCKLDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x620F ); } +__forceinline void SSE2_PUNPCKLDQ_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x620F ); } +__forceinline void SSE2_PUNPCKHDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x6A0F ); } +__forceinline void SSE2_PUNPCKHDQ_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x6A0F ); } + +__forceinline void SSE2_PUNPCKLQDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x6C0F ); } +__forceinline void SSE2_PUNPCKLQDQ_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x6C0F ); } + +__forceinline void SSE2_PUNPCKHQDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0x6D0F ); } +__forceinline void SSE2_PUNPCKHQDQ_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0x6D0F ); } + +__forceinline void SSE2_PMULLW_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0xD50F ); } +__forceinline void SSE2_PMULLW_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0xD50F ); } +__forceinline void SSE2_PMULHW_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0xE50F ); } +__forceinline void SSE2_PMULHW_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0xE50F ); } + +__forceinline void SSE2_PMULUDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66( 0xF40F ); } +__forceinline void SSE2_PMULUDQ_M128_to_XMM(x86SSERegType to, uptr from) { SSEMtoR66( 0xF40F ); } + +__forceinline void SSE2_PMOVMSKB_XMM_to_R32(x86IntRegType to, x86SSERegType from) { SSERtoR66(0xD70F); } + +__forceinline void SSE_MOVMSKPS_XMM_to_R32(x86IntRegType to, x86SSERegType from) { SSERtoR(0x500F); } +__forceinline void SSE2_MOVMSKPD_XMM_to_R32(x86IntRegType to, x86SSERegType from) { SSERtoR66(0x500F); } + +__forceinline void SSE2_PMADDWD_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { SSERtoR66(0xF50F); } + +__forceinline void SSE3_HADDPS_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { write8(0xf2); SSERtoR( 0x7c0f ); } +__forceinline void SSE3_HADDPS_M128_to_XMM(x86SSERegType to, uptr from){ write8(0xf2); SSEMtoR( 0x7c0f, 0 ); } + +__forceinline void SSE3_MOVSLDUP_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { + write8(0xf3); + RexRB(0, to, from); + write16( 0x120f); + ModRM( 3, to, from ); +} + +__forceinline void SSE3_MOVSLDUP_M128_to_XMM(x86SSERegType to, uptr from) { write8(0xf3); SSEMtoR(0x120f, 0); } +__forceinline void SSE3_MOVSHDUP_XMM_to_XMM(x86SSERegType to, x86SSERegType from) { write8(0xf3); SSERtoR(0x160f); } +__forceinline void SSE3_MOVSHDUP_M128_to_XMM(x86SSERegType to, uptr from) { write8(0xf3); SSEMtoR(0x160f, 0); } + +// SSE4.1 + +__forceinline void SSE4_DPPS_XMM_to_XMM(x86SSERegType to, x86SSERegType from, u8 imm8) +{ + write8(0x66); + write24(0x403A0F); + ModRM(3, to, from); + write8(imm8); +} + +__forceinline void SSE4_DPPS_M128_to_XMM(x86SSERegType to, uptr from, u8 imm8) +{ + write8(0x66); + write24(0x403A0F); + ModRM(0, to, DISP32); + write32(MEMADDR(from, 4)); + write8(imm8); +} + +__forceinline void SSE4_INSERTPS_XMM_to_XMM(x86SSERegType to, x86SSERegType from, u8 imm8) +{ + write8(0x66); + RexRB(0, to, from); + write24(0x213A0F); + ModRM(3, to, from); + write8(imm8); +} + +__forceinline void SSE4_EXTRACTPS_XMM_to_R32(x86IntRegType to, x86SSERegType from, u8 imm8) +{ + write8(0x66); + RexRB(0, to, from); + write24(0x173A0F); + ModRM(3, to, from); + write8(imm8); +} + +__forceinline void SSE4_BLENDPS_XMM_to_XMM(x86IntRegType to, x86SSERegType from, u8 imm8) +{ + write8(0x66); + RexRB(0, to, from); + write24(0x0C3A0F); + ModRM(3, to, from); + write8(imm8); +} + +__forceinline void SSE4_BLENDVPS_XMM_to_XMM(x86SSERegType to, x86SSERegType from) +{ + write8(0x66); + RexRB(0, to, from); + write24(0x14380F); + ModRM(3, to, from); +} + +__forceinline void SSE4_BLENDVPS_M128_to_XMM(x86SSERegType to, uptr from) +{ + write8(0x66); + RexR(0, to); + write24(0x14380F); + ModRM(0, to, DISP32); + write32(MEMADDR(from, 4)); +} + +__forceinline void SSE4_PMOVSXDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from) +{ + write8(0x66); + RexRB(0, to, from); + write24(0x25380F); + ModRM(3, to, from); +} + +__forceinline void SSE4_PINSRD_R32_to_XMM(x86SSERegType to, x86IntRegType from, u8 imm8) +{ + write8(0x66); + RexRB(0, to, from); + write24(0x223A0F); + ModRM(3, to, from); + write8(imm8); +} + +__forceinline void SSE4_PMAXSD_XMM_to_XMM(x86SSERegType to, x86SSERegType from) +{ + write8(0x66); + RexRB(0, to, from); + write24(0x3D380F); + ModRM(3, to, from); +} + +__forceinline void SSE4_PMINSD_XMM_to_XMM(x86SSERegType to, x86SSERegType from) +{ + write8(0x66); + RexRB(0, to, from); + write24(0x39380F); + ModRM(3, to, from); +} + +__forceinline void SSE4_PMAXUD_XMM_to_XMM(x86SSERegType to, x86SSERegType from) +{ + write8(0x66); + RexRB(0, to, from); + write24(0x3F380F); + ModRM(3, to, from); +} + +__forceinline void SSE4_PMINUD_XMM_to_XMM(x86SSERegType to, x86SSERegType from) +{ + write8(0x66); + RexRB(0, to, from); + write24(0x3B380F); + ModRM(3, to, from); +} + +__forceinline void SSE4_PMAXSD_M128_to_XMM(x86SSERegType to, uptr from) +{ + write8(0x66); + RexR(0, to); + write24(0x3D380F); + ModRM( 0, to, DISP32 ); + write32(MEMADDR(from, 4)); +} + +__forceinline void SSE4_PMINSD_M128_to_XMM(x86SSERegType to, uptr from) +{ + write8(0x66); + RexR(0, to); + write24(0x39380F); + ModRM( 0, to, DISP32 ); + write32(MEMADDR(from, 4)); +} + +__forceinline void SSE4_PMAXUD_M128_to_XMM(x86SSERegType to, uptr from) +{ + write8(0x66); + RexR(0, to); + write24(0x3F380F); + ModRM( 0, to, DISP32 ); + write32(MEMADDR(from, 4)); +} + +__forceinline void SSE4_PMINUD_M128_to_XMM(x86SSERegType to, uptr from) +{ + write8(0x66); + RexR(0, to); + write24(0x3B380F); + ModRM( 0, to, DISP32 ); + write32(MEMADDR(from, 4)); +} + +// SSE-X +__forceinline void SSEX_MOVDQA_M128_to_XMM( x86SSERegType to, uptr from ) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_MOVDQA_M128_to_XMM(to, from); + else SSE_MOVAPS_M128_to_XMM(to, from); +} + +__forceinline void SSEX_MOVDQA_XMM_to_M128( uptr to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_MOVDQA_XMM_to_M128(to, from); + else SSE_MOVAPS_XMM_to_M128(to, from); +} + +__forceinline void SSEX_MOVDQA_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_MOVDQA_XMM_to_XMM(to, from); + else SSE_MOVAPS_XMM_to_XMM(to, from); +} + +__forceinline void SSEX_MOVDQARmtoROffset( x86SSERegType to, x86IntRegType from, int offset ) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_MOVDQARmtoROffset(to, from, offset); + else SSE_MOVAPSRmtoROffset(to, from, offset); +} + +__forceinline void SSEX_MOVDQARtoRmOffset( x86IntRegType to, x86SSERegType from, int offset ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_MOVDQARtoRmOffset(to, from, offset); + else SSE_MOVAPSRtoRmOffset(to, from, offset); +} + +__forceinline void SSEX_MOVDQU_M128_to_XMM( x86SSERegType to, uptr from ) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_MOVDQU_M128_to_XMM(to, from); + else SSE_MOVAPS_M128_to_XMM(to, from); +} + +__forceinline void SSEX_MOVDQU_XMM_to_M128( uptr to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_MOVDQU_XMM_to_M128(to, from); + else SSE_MOVAPS_XMM_to_M128(to, from); +} + +__forceinline void SSEX_MOVDQU_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_MOVDQU_XMM_to_XMM(to, from); + else SSE_MOVAPS_XMM_to_XMM(to, from); +} + +__forceinline void SSEX_MOVD_M32_to_XMM( x86SSERegType to, uptr from ) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_MOVD_M32_to_XMM(to, from); + else SSE_MOVSS_M32_to_XMM(to, from); +} + +__forceinline void SSEX_MOVD_XMM_to_M32( u32 to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_MOVD_XMM_to_M32(to, from); + else SSE_MOVSS_XMM_to_M32(to, from); +} + +__forceinline void SSEX_MOVD_XMM_to_Rm( x86IntRegType to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_MOVD_XMM_to_Rm(to, from); + else SSE_MOVSS_XMM_to_Rm(to, from); +} + +__forceinline void SSEX_MOVD_RmOffset_to_XMM( x86SSERegType to, x86IntRegType from, int offset ) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_MOVD_RmOffset_to_XMM(to, from, offset); + else SSE_MOVSS_RmOffset_to_XMM(to, from, offset); +} + +__forceinline void SSEX_MOVD_XMM_to_RmOffset( x86IntRegType to, x86SSERegType from, int offset ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_MOVD_XMM_to_RmOffset(to, from, offset); + else SSE_MOVSS_XMM_to_RmOffset(to, from, offset); +} + +__forceinline void SSEX_POR_M128_to_XMM( x86SSERegType to, uptr from ) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_POR_M128_to_XMM(to, from); + else SSE_ORPS_M128_to_XMM(to, from); +} + +__forceinline void SSEX_POR_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_POR_XMM_to_XMM(to, from); + else SSE_ORPS_XMM_to_XMM(to, from); +} + +__forceinline void SSEX_PXOR_M128_to_XMM( x86SSERegType to, uptr from ) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_PXOR_M128_to_XMM(to, from); + else SSE_XORPS_M128_to_XMM(to, from); +} + +__forceinline void SSEX_PXOR_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_PXOR_XMM_to_XMM(to, from); + else SSE_XORPS_XMM_to_XMM(to, from); +} + +__forceinline void SSEX_PAND_M128_to_XMM( x86SSERegType to, uptr from ) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_PAND_M128_to_XMM(to, from); + else SSE_ANDPS_M128_to_XMM(to, from); +} + +__forceinline void SSEX_PAND_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_PAND_XMM_to_XMM(to, from); + else SSE_ANDPS_XMM_to_XMM(to, from); +} + +__forceinline void SSEX_PANDN_M128_to_XMM( x86SSERegType to, uptr from ) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_PANDN_M128_to_XMM(to, from); + else SSE_ANDNPS_M128_to_XMM(to, from); +} + +__forceinline void SSEX_PANDN_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_PANDN_XMM_to_XMM(to, from); + else SSE_ANDNPS_XMM_to_XMM(to, from); +} + +__forceinline void SSEX_PUNPCKLDQ_M128_to_XMM(x86SSERegType to, uptr from) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_PUNPCKLDQ_M128_to_XMM(to, from); + else SSE_UNPCKLPS_M128_to_XMM(to, from); +} + +__forceinline void SSEX_PUNPCKLDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_PUNPCKLDQ_XMM_to_XMM(to, from); + else SSE_UNPCKLPS_XMM_to_XMM(to, from); +} + +__forceinline void SSEX_PUNPCKHDQ_M128_to_XMM(x86SSERegType to, uptr from) +{ + if( g_xmmtypes[to] == XMMT_INT ) SSE2_PUNPCKHDQ_M128_to_XMM(to, from); + else SSE_UNPCKHPS_M128_to_XMM(to, from); +} + +__forceinline void SSEX_PUNPCKHDQ_XMM_to_XMM(x86SSERegType to, x86SSERegType from) +{ + if( g_xmmtypes[from] == XMMT_INT ) SSE2_PUNPCKHDQ_XMM_to_XMM(to, from); + else SSE_UNPCKHPS_XMM_to_XMM(to, from); +} + +__forceinline void SSEX_MOVHLPS_XMM_to_XMM( x86SSERegType to, x86SSERegType from ) +{ + if( g_xmmtypes[from] == XMMT_INT ) { + SSE2_PUNPCKHQDQ_XMM_to_XMM(to, from); + if( to != from ) SSE2_PSHUFD_XMM_to_XMM(to, to, 0x4e); + } + else { + SSE_MOVHLPS_XMM_to_XMM(to, from); + } +} diff --git a/pcsx2/x86/ix86/ix86_tools.cpp b/pcsx2/x86/ix86/ix86_tools.cpp new file mode 100644 index 0000000000..3c67a62797 --- /dev/null +++ b/pcsx2/x86/ix86/ix86_tools.cpp @@ -0,0 +1,275 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +#include "Misc.h" +#include "ix86/ix86.h" + +// used to make sure regs don't get changed while in recompiler +// use FreezeMMXRegs, FreezeXMMRegs + +u8 g_globalMMXSaved = 0; +u8 g_globalXMMSaved = 0; + +PCSX2_ALIGNED16( static u64 g_globalMMXData[8] ); +PCSX2_ALIGNED16( static u64 g_globalXMMData[2*XMMREGS] ); + +// performance counter vars. +LARGE_INTEGER lbase = {0}, lfinal = {0}; +u32 s_pCurBlock_ltime; + + +///////////////////////////////////////////////////////////////////// +// SetCPUState -- for assugnment of SSE roundmodes and clampmodes. + +u32 g_sseMXCSR = DEFAULT_sseMXCSR; +u32 g_sseVUMXCSR = DEFAULT_sseVUMXCSR; + +void SetCPUState(u32 sseMXCSR, u32 sseVUMXCSR) +{ + //Msgbox::Alert("SetCPUState: Config.sseMXCSR = %x; Config.sseVUMXCSR = %x \n", Config.sseMXCSR, Config.sseVUMXCSR); + // SSE STATE // + // WARNING: do not touch unless you know what you are doing + + sseMXCSR &= 0xffff; // clear the upper 16 bits since they shouldn't be set + sseVUMXCSR &= 0xffff; + + if( !cpucaps.hasStreamingSIMD2Extensions ) + { + // SSE1 cpus do not support Denormals Are Zero flag (throws an exception + // if we don't mask them off) + + sseMXCSR &= ~0x0040; + sseVUMXCSR &= ~0x0040; + } + + g_sseMXCSR = sseMXCSR; + g_sseVUMXCSR = sseVUMXCSR; + +#ifdef _MSC_VER + __asm ldmxcsr g_sseMXCSR; // set the new sse control +#else + __asm__("ldmxcsr %0" : : "m"(g_sseMXCSR) ); +#endif + //g_sseVUMXCSR = g_sseMXCSR|0x6000; +} + +///////////////////////////////////////////////////////////////////// +// +__forceinline void FreezeMMXRegs_(int save) +{ + assert( g_EEFreezeRegs ); + + if( save ) { + g_globalMMXSaved++; + if( g_globalMMXSaved>1 ) + { + //SysPrintf("MMX Already Saved!\n"); + return; + } + +#ifdef _MSC_VER + __asm { + movntq mmword ptr [g_globalMMXData + 0], mm0 + movntq mmword ptr [g_globalMMXData + 8], mm1 + movntq mmword ptr [g_globalMMXData + 16], mm2 + movntq mmword ptr [g_globalMMXData + 24], mm3 + movntq mmword ptr [g_globalMMXData + 32], mm4 + movntq mmword ptr [g_globalMMXData + 40], mm5 + movntq mmword ptr [g_globalMMXData + 48], mm6 + movntq mmword ptr [g_globalMMXData + 56], mm7 + emms + } +#else + __asm__(".intel_syntax\n" + "movq [%0+0x00], %%mm0\n" + "movq [%0+0x08], %%mm1\n" + "movq [%0+0x10], %%mm2\n" + "movq [%0+0x18], %%mm3\n" + "movq [%0+0x20], %%mm4\n" + "movq [%0+0x28], %%mm5\n" + "movq [%0+0x30], %%mm6\n" + "movq [%0+0x38], %%mm7\n" + "emms\n" + ".att_syntax\n" : : "r"(g_globalMMXData) ); +#endif + + } + else { + if( g_globalMMXSaved==0 ) + { + //SysPrintf("MMX Not Saved!\n"); + return; + } + g_globalMMXSaved--; + + if( g_globalMMXSaved > 0 ) return; + +#ifdef _MSC_VER + __asm { + movq mm0, mmword ptr [g_globalMMXData + 0] + movq mm1, mmword ptr [g_globalMMXData + 8] + movq mm2, mmword ptr [g_globalMMXData + 16] + movq mm3, mmword ptr [g_globalMMXData + 24] + movq mm4, mmword ptr [g_globalMMXData + 32] + movq mm5, mmword ptr [g_globalMMXData + 40] + movq mm6, mmword ptr [g_globalMMXData + 48] + movq mm7, mmword ptr [g_globalMMXData + 56] + emms + } +#else + __asm__(".intel_syntax\n" + "movq %%mm0, [%0+0x00]\n" + "movq %%mm1, [%0+0x08]\n" + "movq %%mm2, [%0+0x10]\n" + "movq %%mm3, [%0+0x18]\n" + "movq %%mm4, [%0+0x20]\n" + "movq %%mm5, [%0+0x28]\n" + "movq %%mm6, [%0+0x30]\n" + "movq %%mm7, [%0+0x38]\n" + "emms\n" + ".att_syntax\n" : : "r"(g_globalMMXData) ); +#endif + } +} + +////////////////////////////////////////////////////////////////////// + +__forceinline void FreezeXMMRegs_(int save) +{ + //SysPrintf("FreezeXMMRegs_(%d); [%d]\n", save, g_globalXMMSaved); + assert( g_EEFreezeRegs ); + + if( save ) { + g_globalXMMSaved++; + if( g_globalXMMSaved > 1 ){ + //SysPrintf("XMM Already saved\n"); + return; + } + + +#ifdef _MSC_VER + __asm { + movaps xmmword ptr [g_globalXMMData + 0x00], xmm0 + movaps xmmword ptr [g_globalXMMData + 0x10], xmm1 + movaps xmmword ptr [g_globalXMMData + 0x20], xmm2 + movaps xmmword ptr [g_globalXMMData + 0x30], xmm3 + movaps xmmword ptr [g_globalXMMData + 0x40], xmm4 + movaps xmmword ptr [g_globalXMMData + 0x50], xmm5 + movaps xmmword ptr [g_globalXMMData + 0x60], xmm6 + movaps xmmword ptr [g_globalXMMData + 0x70], xmm7 + } + +#else + __asm__(".intel_syntax\n" + "movaps [%0+0x00], %%xmm0\n" + "movaps [%0+0x10], %%xmm1\n" + "movaps [%0+0x20], %%xmm2\n" + "movaps [%0+0x30], %%xmm3\n" + "movaps [%0+0x40], %%xmm4\n" + "movaps [%0+0x50], %%xmm5\n" + "movaps [%0+0x60], %%xmm6\n" + "movaps [%0+0x70], %%xmm7\n" + ".att_syntax\n" : : "r"(g_globalXMMData) ); + +#endif // _MSC_VER + } + else { + if( g_globalXMMSaved==0 ) + { + //SysPrintf("XMM Regs not saved!\n"); + return; + } + + // TODO: really need to backup all regs? + g_globalXMMSaved--; + if( g_globalXMMSaved > 0 ) return; + +#ifdef _MSC_VER + __asm { + movaps xmm0, xmmword ptr [g_globalXMMData + 0x00] + movaps xmm1, xmmword ptr [g_globalXMMData + 0x10] + movaps xmm2, xmmword ptr [g_globalXMMData + 0x20] + movaps xmm3, xmmword ptr [g_globalXMMData + 0x30] + movaps xmm4, xmmword ptr [g_globalXMMData + 0x40] + movaps xmm5, xmmword ptr [g_globalXMMData + 0x50] + movaps xmm6, xmmword ptr [g_globalXMMData + 0x60] + movaps xmm7, xmmword ptr [g_globalXMMData + 0x70] + } + +#else + __asm__(".intel_syntax\n" + "movaps %%xmm0, [%0+0x00]\n" + "movaps %%xmm1, [%0+0x10]\n" + "movaps %%xmm2, [%0+0x20]\n" + "movaps %%xmm3, [%0+0x30]\n" + "movaps %%xmm4, [%0+0x40]\n" + "movaps %%xmm5, [%0+0x50]\n" + "movaps %%xmm6, [%0+0x60]\n" + "movaps %%xmm7, [%0+0x70]\n" + ".att_syntax\n" : : "r"(g_globalXMMData) ); + +#endif // _MSC_VER + } +} + +#ifdef PCSX2_DEVBUILD +#ifdef _WIN32 +__declspec(naked) void _StartPerfCounter() +{ + __asm { + push eax + push ebx + push ecx + + rdtsc + mov dword ptr [offset lbase], eax + mov dword ptr [offset lbase + 4], edx + + pop ecx + pop ebx + pop eax + ret + } +} + +__declspec(naked) void _StopPerfCounter() +{ + __asm { + push eax + push ebx + push ecx + + rdtsc + + sub eax, dword ptr [offset lbase] + sbb edx, dword ptr [offset lbase + 4] + mov ecx, s_pCurBlock_ltime + add eax, dword ptr [ecx] + adc edx, dword ptr [ecx + 4] + mov dword ptr [ecx], eax + mov dword ptr [ecx + 4], edx + pop ecx + pop ebx + pop eax + ret + } +} +#endif // WIN32 +#endif // PCSX2_DEVBUILD \ No newline at end of file diff --git a/pcsx2/x86/megaVU.cpp b/pcsx2/x86/megaVU.cpp new file mode 100644 index 0000000000..c166a76d0f --- /dev/null +++ b/pcsx2/x86/megaVU.cpp @@ -0,0 +1,282 @@ +/* Pcsx2-Playground - Pc Ps2 Emulator +* Copyright (C) 2009 Pcsx2-Playground 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 +*/ + +// Mega VU(micro) recompiler! - author: cottonvibes(@gmail.com) + +#include "PrecompiledHeader.h" +#include "megaVU.h" +#ifdef PCSX2_MEGAVU + +//------------------------------------------------------------------ +// Mega VU - Global Variables +//------------------------------------------------------------------ + +megaVU megaVU0; +megaVU megaVU1; + +//------------------------------------------------------------------ +// Mega VU - Main Functions +//------------------------------------------------------------------ + +// Only run this once! ;) +__forceinline void mVUinit(megaVU* mVU, VURegs* vuRegsPtr, const int vuIndex) { + + mVU->regs = vuRegsPtr; + mVU->index = vuIndex; + mVU->microSize = (vuIndex ? 0x4000 : 0x1000); + mVU->progSize = (vuIndex ? 0x4000 : 0x1000) / 8; + mVU->cacheAddr = 0xC0000000 + (vuIndex ? mVU->cacheSize : 0); + mVU->cache = NULL; + + for (int i; i <= mVU->prog.max; i++) { + for (u32 j; j < mVU->progSize; j++) { + mVU->prog.prog[i].block[j] = new megaBlockManager(); + } + } + + mVUreset(mVU); +} + +// Will Optimize later +__forceinline void mVUreset(megaVU* mVU) { + + mVUclose(mVU); // Close + + // Dynarec Cache + mVU->cache = SysMmap(mVU->cacheAddr, mVU->cacheSize, 0x10000000, "Mega VU"); + if ( mVU->cache == NULL ) throw Exception::OutOfMemory(fmt_string( "megaVU Error: failed to allocate recompiler memory! (addr: 0x%x)", params (u32)mVU->cache)); + + // Other Variables + ZeroMemory(&mVU->prog, sizeof(mVU->prog)); + mVU->prog.finished = 1; + mVU->prog.cleared = 1; + mVU->prog.cur = -1; + mVU->prog.total = -1; +} + +// Free Allocated Resources +__forceinline void mVUclose(megaVU* mVU) { + + if ( mVU->cache ) { SysMunmap( mVU->cache, mVU->cacheSize ); mVU->cache = NULL; } + + for (int i; i <= mVU->prog.max; i++) { + for (u32 j; j < mVU->progSize; j++) { + if (mVU->prog.prog[i].block[j]) delete mVU->prog.prog[i].block[j]; + } + } +} + +// Clears Block Data in specified range (Caches current microProgram if a difference has been found) +__forceinline void mVUclear(megaVU* mVU, u32 addr, u32 size) { + + int i = addr/8; + int end = i+((size+(8-(size&7)))/8); // ToDo: Can be simplified to addr+size if Size is always a multiple of 8 + + if (!mVU->prog.cleared) { + for ( ; i < end; i++) { + if ( mVU->prog.prog[mVU->prog.cur].block[i]->clear() ) { + mVUcacheProg(mVU); + mVU->prog.cleared = 1; + i++; + break; + } + } + } + for ( ; i < end; i++) { + mVU->prog.prog[mVU->prog.cur].block[i]->clearFast(); + } +} + +// Executes for number of cycles +void* mVUexecute(megaVU* mVU, u32 startPC, u32 cycles) { + return NULL; +} + +// Executes till finished +void* mVUexecuteF(megaVU* mVU, u32 startPC) { + //if (!mProg.finished) { + // runMicroProgram(startPC); + //} + //if (mProg.cleared && !mProg.finished) { + + //} + return NULL; +} + +//------------------------------------------------------------------ +// Mega VU - Private Functions +//------------------------------------------------------------------ + +// Finds the least used program +__forceinline int mVUfindLeastUsedProg(megaVU* mVU) { + if (mVU->prog.total < mVU->prog.max) { + mVU->prog.total++; + return mVU->prog.total; + } + else { + int j = 0; + u32 smallest = mVU->prog.prog[0].used; + for (int i = 1; i <= mVU->prog.total; i++) { + if (smallest > mVU->prog.prog[i].used) { + smallest = mVU->prog.prog[i].used; + j = i; + } + } + return j; + } +} + +// Caches Micro Program if appropriate +__forceinline void mVUcacheProg(megaVU* mVU) { + if (!mVU->prog.prog[mVU->prog.cur].cached) { // If uncached, then cache + memcpy_fast(mVU->prog.prog[mVU->prog.cur].data, mVU->regs->Micro, mVU->microSize); + } +} + +// Searches for Cached Micro Program and sets prog.cur (returns -1 if no program found) +__forceinline int mVUsearchProg(megaVU* mVU) { + if (mVU->prog.cleared) { // If cleared, we need to search for new program + for (int i = 0; i <= mVU->prog.total; i++) { + if (!memcmp_mmx(mVU->prog.prog[i].data, mVU->regs->Micro, mVU->microSize)) { + return i; + } + } + mVU->prog.cur = mVUfindLeastUsedProg(mVU); + return -1; + } + else return mVU->prog.cur; +} + +//------------------------------------------------------------------ +// Dispatcher Functions +//------------------------------------------------------------------ + +// Runs till finished +__declspec(naked) void runVU0(megaVU* mVU, u32 startPC) { + __asm { + mov eax, dword ptr [esp] + mov megaVU0.x86callstack, eax + add esp, 4 + call mVUexecuteF + + /*backup cpu state*/ + mov megaVU0.x86ebp, ebp + mov megaVU0.x86esi, esi + mov megaVU0.x86edi, edi + mov megaVU0.x86ebx, ebx + /*mov megaVU0.x86esp, esp*/ + + ldmxcsr g_sseVUMXCSR + + jmp eax + } +} +__declspec(naked) void runVU1(megaVU* mVU, u32 startPC) { + __asm { + mov eax, dword ptr [esp] + mov megaVU1.x86callstack, eax + add esp, 4 + call mVUexecuteF + + /*backup cpu state*/ + mov megaVU1.x86ebp, ebp + mov megaVU1.x86esi, esi + mov megaVU1.x86edi, edi + mov megaVU1.x86ebx, ebx + /*mov megaVU1.x86esp, esp*/ + + ldmxcsr g_sseVUMXCSR + + jmp eax + } +} + +// Runs for number of cycles +__declspec(naked) void runVU0(megaVU* mVU, u32 startPC, u32 cycles) { + __asm { + mov eax, dword ptr [esp] + mov megaVU0.x86callstack, eax + add esp, 4 + call mVUexecute + + /*backup cpu state*/ + mov megaVU0.x86ebp, ebp + mov megaVU0.x86esi, esi + mov megaVU0.x86edi, edi + mov megaVU0.x86ebx, ebx + /*mov megaVU0.x86esp, esp*/ + + ldmxcsr g_sseVUMXCSR + + jmp eax + } +} +__declspec(naked) void runVU1(megaVU* mVU, u32 startPC, u32 cycles) { + __asm { + mov eax, dword ptr [esp] + mov megaVU1.x86callstack, eax + add esp, 4 + call mVUexecute + + /*backup cpu state*/ + mov megaVU1.x86ebp, ebp + mov megaVU1.x86esi, esi + mov megaVU1.x86edi, edi + mov megaVU1.x86ebx, ebx + /*mov megaVU1.x86esp, esp*/ + + ldmxcsr g_sseVUMXCSR + + jmp eax + } +} +//------------------------------------------------------------------ +// Wrapper Functions - Called by other parts of the Emu +//------------------------------------------------------------------ + +__forceinline void initVUrec(VURegs* vuRegs, int vuIndex) { + if (!vuIndex) mVUinit(&megaVU0, vuRegs, 0); + else mVUinit(&megaVU1, vuRegs, 1); +} + +__forceinline void closeVUrec(int vuIndex) { + if (!vuIndex) mVUclose(&megaVU0); + else mVUclose(&megaVU1); +} + +__forceinline void resetVUrec(int vuIndex) { + if (!vuIndex) mVUreset(&megaVU0); + else mVUreset(&megaVU1); +} + +__forceinline void clearVUrec(u32 addr, u32 size, int vuIndex) { + if (!vuIndex) mVUclear(&megaVU0, addr, size); + else mVUclear(&megaVU1, addr, size); +} + +__forceinline void runVUrec(u32 startPC, int vuIndex) { + if (!vuIndex) runVU0(&megaVU0, startPC); + else runVU1(&megaVU1, startPC); +} + +__forceinline void runVUrec(u32 startPC, u32 cycles, int vuIndex) { + if (!vuIndex) runVU0(&megaVU0, startPC, cycles); + else runVU1(&megaVU1, startPC, cycles); +} + +#endif // PCSX2_MEGAVU diff --git a/pcsx2/x86/megaVU.h b/pcsx2/x86/megaVU.h new file mode 100644 index 0000000000..835abb46ab --- /dev/null +++ b/pcsx2/x86/megaVU.h @@ -0,0 +1,132 @@ +/* Pcsx2-Playground - Pc Ps2 Emulator +* Copyright (C) 2009 Pcsx2-Playground 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 +*/ + +#pragma once +#include "Common.h" +#include "VU.h" +#include "megaVU_Tables.h" +//#include + +struct megaBlock { + u32 pipelineState; // FMACx|y|z|w | FDiv | EFU | IALU | BRANCH // Still thinking of how I'm going to do this + u32 x86ptrStart; + u32 x86ptrEnd; + u32 x86ptrBranch; + //u32 size; +}; + +#define mMaxBlocks 32 // Max Blocks With Different Pipeline States (For n = 1, 2, 4, 8, 16, etc...) +class megaBlockManager { +private: + static const int MaxBlocks = mMaxBlocks - 1; + u32 startPC; + u32 endPC; + int listSize; // Total Items - 1 + int callerSize; // Total Callers - 1 + megaBlock blockList[mMaxBlocks]; + megaBlock callersList[mMaxBlocks]; // Foreign Blocks that call Local Blocks + +public: + megaBlockManager() { init(); } + ~megaBlockManager() { close(); } + void init() { + listSize = -1; + callerSize = -1; + ZeroMemory(&blockList, sizeof(blockList)); // Can be Omitted? + } + void close() {}; // Can be Omitted? + void add(u32 pipelineState, u32 x86ptrStart) { + if (!search(pipelineState)) { + listSize++; + listSize &= MaxBlocks; + blockList[listSize].pipelineState = pipelineState; + blockList[listSize].x86ptrStart = x86ptrStart; + } + } + megaBlock* search(u32 pipelineState) { + for (int i = 0; i < listSize; i++) { + if (blockList[i].pipelineState == pipelineState) return &blockList[i]; + } + } + void clearFast() { + listSize = -1; + for ( ; callerSize >= 0; callerSize--) { + //callerList[callerSize]. // Implement Branch Link Removal Code + } + } + int clear() { + if (listSize >= 0) { clearFast(); return 1; } + else return 0; + } +}; + +template +struct microProgram { + u8 data[progSize]; + u32 used; // Number of times its been used + int cached; // Has been Cached? + megaBlockManager* block[progSize]; +}; + +#define mMaxProg 16 // The amount of Micro Programs Recs will 'remember' (For n = 1, 2, 4, 8, 16, etc...) +template +struct microProgManager { + microProgram prog[mMaxProg]; // Store MicroPrograms in memory + static const int max = mMaxProg - 1; + int cur; // Index to Current MicroProgram thats running (-1 = uncached) + int total; // Total Number of valid MicroPrograms minus 1 + int cleared; // Micro Program is Indeterminate so must be searched for (and if no matches are found then recompile a new one) + int finished; // Completed MicroProgram to E-bit Termination +}; + +struct megaVU { + int index; // VU Index (VU0 or VU1) + u32 microSize; // VU Micro Memory Size + u32 progSize; // VU Micro Program Size (microSize/8) + u32 cacheAddr; // VU Cache Start Address + static const u32 cacheSize = 0x400000; // VU Cache Size + + VURegs* regs; // VU Regs Struct + u8* cache; // Dynarec Cache Start (where we will start writing the recompiled code to) + u8* ptr; // Pointer to next place to write recompiled code to + + uptr x86callstack; + uptr x86ebp; + uptr x86esi; + uptr x86edi; + uptr x86ebx; + uptr x86esp; + + microProgManager<0x800> prog; // Micro Program Data +}; + +// Opcode Tables +extern void (*mVU_UPPER_OPCODE[64])( VURegs* VU, s32 info ); +extern void (*mVU_LOWER_OPCODE[128])( VURegs* VU, s32 info ); + +//void invalidateBlocks(u32 addr, u32 size); // Invalidates Blocks in the range [addr, addr+size) +__forceinline void mVUinit(megaVU* mVU, VURegs* vuRegsPtr, const int vuIndex); +__forceinline void mVUreset(megaVU* mVU); +__forceinline void mVUclose(megaVU* mVU); +__forceinline void mVUclear(megaVU* mVU, u32 addr, u32 size); // Clears part of a Micro Program (must use before modifying micro program!) +void* mVUexecute(megaVU* mVU, u32 startPC, u32 cycles); // Recompiles/Executes code for the number of cycles indicated (will always run for >= 'cycles' amount unless 'finished') +void* mVUexecuteF(megaVU* mVU, u32 startPC); // Recompiles/Executes code till finished + +__forceinline int mVUfindLeastUsedProg(megaVU* mVU); +__forceinline int mVUsearchProg(megaVU* mVU); +__forceinline void mVUcacheProg(megaVU* mVU); diff --git a/pcsx2/x86/megaVU_Lower.cpp b/pcsx2/x86/megaVU_Lower.cpp new file mode 100644 index 0000000000..ff5336096f --- /dev/null +++ b/pcsx2/x86/megaVU_Lower.cpp @@ -0,0 +1,19 @@ +/* Pcsx2-Playground - Pc Ps2 Emulator +* Copyright (C) 2009 Pcsx2-Playground 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 "PrecompiledHeader.h" diff --git a/pcsx2/x86/megaVU_Tables.cpp b/pcsx2/x86/megaVU_Tables.cpp new file mode 100644 index 0000000000..0ff0f89360 --- /dev/null +++ b/pcsx2/x86/megaVU_Tables.cpp @@ -0,0 +1,227 @@ +/* Pcsx2-Playground - Pc Ps2 Emulator +* Copyright (C) 2009 Pcsx2-Playground 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 meived 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 "PrecompiledHeader.h" +#include "megaVU.h" + +#ifdef PCSX2_MEGAVU_lulz + +void mVU_UPPER_FD_00(VURegs* VU, s32 info); +void mVU_UPPER_FD_01(VURegs* VU, s32 info); +void mVU_UPPER_FD_10(VURegs* VU, s32 info); +void mVU_UPPER_FD_11(VURegs* VU, s32 info); +void mVULowerOP(VURegs* VU, s32 info); +void mVULowerOP_T3_00(VURegs* VU, s32 info); +void mVULowerOP_T3_01(VURegs* VU, s32 info); +void mVULowerOP_T3_10(VURegs* VU, s32 info); +void mVULowerOP_T3_11(VURegs* VU, s32 info); +void mVUunknown(VURegs* VU, s32 info); + +void (*mVU_LOWER_OPCODE[128])(VURegs* VU, s32 info) = { + mVU_LQ , mVU_SQ , mVUunknown , mVUunknown, + mVU_ILW , mVU_ISW , mVUunknown , mVUunknown, + mVU_IADDIU , mVU_ISUBIU , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVU_FCEQ , mVU_FCSET , mVU_FCAND , mVU_FCOR, /* 0x10 */ + mVU_FSEQ , mVU_FSSET , mVU_FSAND , mVU_FSOR, + mVU_FMEQ , mVUunknown , mVU_FMAND , mVU_FMOR, + mVU_FCGET , mVUunknown , mVUunknown , mVUunknown, + mVU_B , mVU_BAL , mVUunknown , mVUunknown, /* 0x20 */ + mVU_JR , mVU_JALR , mVUunknown , mVUunknown, + mVU_IBEQ , mVU_IBNE , mVUunknown , mVUunknown, + mVU_IBLTZ , mVU_IBGTZ , mVU_IBLEZ , mVU_IBGEZ, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, /* 0x30 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVULowerOP , mVUunknown , mVUunknown , mVUunknown, /* 0x40*/ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, /* 0x50 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, /* 0x60 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, /* 0x70 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, +}; + +void (*mVULowerOP_T3_00_OPCODE[32])(VURegs* VU, s32 info) = { + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVU_MOVE , mVU_LQI , mVU_DIV , mVU_MTIR, + mVU_RNEXT , mVUunknown , mVUunknown , mVUunknown, /* 0x10 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVU_MFP , mVU_XTOP , mVU_XGKICK, + mVU_ESADD , mVU_EATANxy , mVU_ESQRT , mVU_ESIN, +}; + +void (*mVULowerOP_T3_01_OPCODE[32])(VURegs* VU, s32 info) = { + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVU_MR32 , mVU_SQI , mVU_SQRT , mVU_MFIR, + mVU_RGET , mVUunknown , mVUunknown , mVUunknown, /* 0x10 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVU_XITOP , mVUunknown, + mVU_ERSADD , mVU_EATANxz , mVU_ERSQRT , mVU_EATAN, +}; + +void (*mVULowerOP_T3_10_OPCODE[32])(VURegs* VU, s32 info) = { + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVU_LQD , mVU_RSQRT , mVU_ILWR, + mVU_RINIT , mVUunknown , mVUunknown , mVUunknown, /* 0x10 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVU_ELENG , mVU_ESUM , mVU_ERCPR , mVU_EEXP, +}; + +void (*mVULowerOP_T3_11_OPCODE[32])(VURegs* VU, s32 info) = { + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVU_SQD , mVU_WAITQ , mVU_ISWR, + mVU_RXOR , mVUunknown , mVUunknown , mVUunknown, /* 0x10 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVU_ERLENG , mVUunknown , mVU_WAITP , mVUunknown, +}; + +void (*mVULowerOP_OPCODE[64])(VURegs* VU, s32 info) = { + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, /* 0x10 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, /* 0x20 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVU_IADD , mVU_ISUB , mVU_IADDI , mVUunknown, /* 0x30 */ + mVU_IAND , mVU_IOR , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVULowerOP_T3_00, mVULowerOP_T3_01, mVULowerOP_T3_10, mVULowerOP_T3_11, +}; + +void (*mVU_UPPER_OPCODE[64])(VURegs* VU, s32 info) = { + mVU_ADDx , mVU_ADDy , mVU_ADDz , mVU_ADDw, + mVU_SUBx , mVU_SUBy , mVU_SUBz , mVU_SUBw, + mVU_MADDx , mVU_MADDy , mVU_MADDz , mVU_MADDw, + mVU_MSUBx , mVU_MSUBy , mVU_MSUBz , mVU_MSUBw, + mVU_MAXx , mVU_MAXy , mVU_MAXz , mVU_MAXw, /* 0x10 */ + mVU_MINIx , mVU_MINIy , mVU_MINIz , mVU_MINIw, + mVU_MULx , mVU_MULy , mVU_MULz , mVU_MULw, + mVU_MULq , mVU_MAXi , mVU_MULi , mVU_MINIi, + mVU_ADDq , mVU_MADDq , mVU_ADDi , mVU_MADDi, /* 0x20 */ + mVU_SUBq , mVU_MSUBq , mVU_SUBi , mVU_MSUBi, + mVU_ADD , mVU_MADD , mVU_MUL , mVU_MAX, + mVU_SUB , mVU_MSUB , mVU_OPMSUB , mVU_MINI, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, /* 0x30 */ + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVU_UPPER_FD_00, mVU_UPPER_FD_01, mVU_UPPER_FD_10, mVU_UPPER_FD_11, +}; + +void (*mVU_UPPER_FD_00_TABLE[32])(VURegs* VU, s32 info) = { + mVU_ADDAx , mVU_SUBAx , mVU_MADDAx , mVU_MSUBAx, + mVU_ITOF0 , mVU_FTOI0 , mVU_MULAx , mVU_MULAq, + mVU_ADDAq , mVU_SUBAq , mVU_ADDA , mVU_SUBA, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, +}; + +void (*mVU_UPPER_FD_01_TABLE[32])(VURegs* VU, s32 info) = { + mVU_ADDAy , mVU_SUBAy , mVU_MADDAy , mVU_MSUBAy, + mVU_ITOF4 , mVU_FTOI4 , mVU_MULAy , mVU_ABS, + mVU_MADDAq , mVU_MSUBAq , mVU_MADDA , mVU_MSUBA, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, +}; + +void (*mVU_UPPER_FD_10_TABLE[32])(VURegs* VU, s32 info) = { + mVU_ADDAz , mVU_SUBAz , mVU_MADDAz , mVU_MSUBAz, + mVU_ITOF12 , mVU_FTOI12 , mVU_MULAz , mVU_MULAi, + mVU_ADDAi , mVU_SUBAi , mVU_MULA , mVU_OPMULA, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, +}; + +void (*mVU_UPPER_FD_11_TABLE[32])(VURegs* VU, s32 info) = { + mVU_ADDAw , mVU_SUBAw , mVU_MADDAw , mVU_MSUBAw, + mVU_ITOF15 , mVU_FTOI15 , mVU_MULAw , mVU_CLIP, + mVU_MADDAi , mVU_MSUBAi , mVUunknown , mVU_NOP, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, + mVUunknown , mVUunknown , mVUunknown , mVUunknown, +}; + +void mVU_UPPER_FD_00(VURegs* VU, s32 info) { + mVU_UPPER_FD_00_TABLE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} +void mVU_UPPER_FD_01(VURegs* VU, s32 info) { + mVU_UPPER_FD_01_TABLE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} +void mVU_UPPER_FD_10(VURegs* VU, s32 info) { + mVU_UPPER_FD_10_TABLE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} +void mVU_UPPER_FD_11(VURegs* VU, s32 info) { + mVU_UPPER_FD_11_TABLE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} +void mVULowerOP(VURegs* VU, s32 info) { + mVULowerOP_OPCODE[ VU->code & 0x3f ]( VU, info ); +} +void mVULowerOP_T3_00(VURegs* VU, s32 info) { + mVULowerOP_T3_00_OPCODE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} +void mVULowerOP_T3_01(VURegs* VU, s32 info) { + mVULowerOP_T3_01_OPCODE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} +void mVULowerOP_T3_10(VURegs* VU, s32 info) { + mVULowerOP_T3_10_OPCODE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} +void mVULowerOP_T3_11(VURegs* VU, s32 info) { + mVULowerOP_T3_11_OPCODE[ ( VU->code >> 6 ) & 0x1f ]( VU, info ); +} +void mVUunknown(VURegs* VU, s32 info) { + SysPrintf("Unknown Mega VU opcode called\n"); +} + +#endif diff --git a/pcsx2/x86/megaVU_Tables.h b/pcsx2/x86/megaVU_Tables.h new file mode 100644 index 0000000000..6c2a7bc025 --- /dev/null +++ b/pcsx2/x86/megaVU_Tables.h @@ -0,0 +1,195 @@ +/* Pcsx2-Playground - Pc Ps2 Emulator +* Copyright (C) 2009 Pcsx2-Playground 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 meived 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 +*/ + +#pragma once + +//------------------------------------------------------------------ +// Mega VU Micromode Upper instructions +//------------------------------------------------------------------ + +void mVU_ABS(VURegs *vuRegs, int info); +void mVU_ADD(VURegs *vuRegs, int info); +void mVU_ADDi(VURegs *vuRegs, int info); +void mVU_ADDq(VURegs *vuRegs, int info); +void mVU_ADDx(VURegs *vuRegs, int info); +void mVU_ADDy(VURegs *vuRegs, int info); +void mVU_ADDz(VURegs *vuRegs, int info); +void mVU_ADDw(VURegs *vuRegs, int info); +void mVU_ADDA(VURegs *vuRegs, int info); +void mVU_ADDAi(VURegs *vuRegs, int info); +void mVU_ADDAq(VURegs *vuRegs, int info); +void mVU_ADDAx(VURegs *vuRegs, int info); +void mVU_ADDAy(VURegs *vuRegs, int info); +void mVU_ADDAz(VURegs *vuRegs, int info); +void mVU_ADDAw(VURegs *vuRegs, int info); +void mVU_SUB(VURegs *vuRegs, int info); +void mVU_SUBi(VURegs *vuRegs, int info); +void mVU_SUBq(VURegs *vuRegs, int info); +void mVU_SUBx(VURegs *vuRegs, int info); +void mVU_SUBy(VURegs *vuRegs, int info); +void mVU_SUBz(VURegs *vuRegs, int info); +void mVU_SUBw(VURegs *vuRegs, int info); +void mVU_SUBA(VURegs *vuRegs, int info); +void mVU_SUBAi(VURegs *vuRegs, int info); +void mVU_SUBAq(VURegs *vuRegs, int info); +void mVU_SUBAx(VURegs *vuRegs, int info); +void mVU_SUBAy(VURegs *vuRegs, int info); +void mVU_SUBAz(VURegs *vuRegs, int info); +void mVU_SUBAw(VURegs *vuRegs, int info); +void mVU_MUL(VURegs *vuRegs, int info); +void mVU_MULi(VURegs *vuRegs, int info); +void mVU_MULq(VURegs *vuRegs, int info); +void mVU_MULx(VURegs *vuRegs, int info); +void mVU_MULy(VURegs *vuRegs, int info); +void mVU_MULz(VURegs *vuRegs, int info); +void mVU_MULw(VURegs *vuRegs, int info); +void mVU_MULA(VURegs *vuRegs, int info); +void mVU_MULAi(VURegs *vuRegs, int info); +void mVU_MULAq(VURegs *vuRegs, int info); +void mVU_MULAx(VURegs *vuRegs, int info); +void mVU_MULAy(VURegs *vuRegs, int info); +void mVU_MULAz(VURegs *vuRegs, int info); +void mVU_MULAw(VURegs *vuRegs, int info); +void mVU_MADD(VURegs *vuRegs, int info); +void mVU_MADDi(VURegs *vuRegs, int info); +void mVU_MADDq(VURegs *vuRegs, int info); +void mVU_MADDx(VURegs *vuRegs, int info); +void mVU_MADDy(VURegs *vuRegs, int info); +void mVU_MADDz(VURegs *vuRegs, int info); +void mVU_MADDw(VURegs *vuRegs, int info); +void mVU_MADDA(VURegs *vuRegs, int info); +void mVU_MADDAi(VURegs *vuRegs, int info); +void mVU_MADDAq(VURegs *vuRegs, int info); +void mVU_MADDAx(VURegs *vuRegs, int info); +void mVU_MADDAy(VURegs *vuRegs, int info); +void mVU_MADDAz(VURegs *vuRegs, int info); +void mVU_MADDAw(VURegs *vuRegs, int info); +void mVU_MSUB(VURegs *vuRegs, int info); +void mVU_MSUBi(VURegs *vuRegs, int info); +void mVU_MSUBq(VURegs *vuRegs, int info); +void mVU_MSUBx(VURegs *vuRegs, int info); +void mVU_MSUBy(VURegs *vuRegs, int info); +void mVU_MSUBz(VURegs *vuRegs, int info); +void mVU_MSUBw(VURegs *vuRegs, int info); +void mVU_MSUBA(VURegs *vuRegs, int info); +void mVU_MSUBAi(VURegs *vuRegs, int info); +void mVU_MSUBAq(VURegs *vuRegs, int info); +void mVU_MSUBAx(VURegs *vuRegs, int info); +void mVU_MSUBAy(VURegs *vuRegs, int info); +void mVU_MSUBAz(VURegs *vuRegs, int info); +void mVU_MSUBAw(VURegs *vuRegs, int info); +void mVU_MAX(VURegs *vuRegs, int info); +void mVU_MAXi(VURegs *vuRegs, int info); +void mVU_MAXx(VURegs *vuRegs, int info); +void mVU_MAXy(VURegs *vuRegs, int info); +void mVU_MAXz(VURegs *vuRegs, int info); +void mVU_MAXw(VURegs *vuRegs, int info); +void mVU_MIN(VURegs *vuRegs, int info); +void mVU_MINi(VURegs *vuRegs, int info); +void mVU_MINx(VURegs *vuRegs, int info); +void mVU_MINy(VURegs *vuRegs, int info); +void mVU_MINz(VURegs *vuRegs, int info); +void mVU_MINw(VURegs *vuRegs, int info); +void mVU_OPMULA(VURegs *vuRegs, int info); +void mVU_OPMSUB(VURegs *vuRegs, int info); +void mVU_NOP(VURegs *vuRegs, int info); +void mVU_FTOI0(VURegs *vuRegs, int info); +void mVU_FTOI4(VURegs *vuRegs, int info); +void mVU_FTOI12(VURegs *vuRegs, int info); +void mVU_FTOI15(VURegs *vuRegs, int info); +void mVU_ITOF0(VURegs *vuRegs, int info); +void mVU_ITOF4(VURegs *vuRegs, int info); +void mVU_ITOF12(VURegs *vuRegs, int info); +void mVU_ITOF15(VURegs *vuRegs, int info); +void mVU_CLIP(VURegs *vuRegs, int info); + +//------------------------------------------------------------------ +// Mega VU Micromode Lower instructions +//------------------------------------------------------------------ + +void mVU_DIV(VURegs *vuRegs, int info); +void mVU_SQRT(VURegs *vuRegs, int info); +void mVU_RSQRT(VURegs *vuRegs, int info); +void mVU_IADD(VURegs *vuRegs, int info); +void mVU_IADDI(VURegs *vuRegs, int info); +void mVU_IADDIU(VURegs *vuRegs, int info); +void mVU_IAND(VURegs *vuRegs, int info); +void mVU_IOR(VURegs *vuRegs, int info); +void mVU_ISUB(VURegs *vuRegs, int info); +void mVU_ISUBIU(VURegs *vuRegs, int info); +void mVU_MOVE(VURegs *vuRegs, int info); +void mVU_MFIR(VURegs *vuRegs, int info); +void mVU_MTIR(VURegs *vuRegs, int info); +void mVU_MR32(VURegs *vuRegs, int info); +void mVU_LQ(VURegs *vuRegs, int info); +void mVU_LQD(VURegs *vuRegs, int info); +void mVU_LQI(VURegs *vuRegs, int info); +void mVU_SQ(VURegs *vuRegs, int info); +void mVU_SQD(VURegs *vuRegs, int info); +void mVU_SQI(VURegs *vuRegs, int info); +void mVU_ILW(VURegs *vuRegs, int info); +void mVU_ISW(VURegs *vuRegs, int info); +void mVU_ILWR(VURegs *vuRegs, int info); +void mVU_ISWR(VURegs *vuRegs, int info); +void mVU_LOI(VURegs *vuRegs, int info); +void mVU_RINIT(VURegs *vuRegs, int info); +void mVU_RGET(VURegs *vuRegs, int info); +void mVU_RNEXT(VURegs *vuRegs, int info); +void mVU_RXOR(VURegs *vuRegs, int info); +void mVU_WAITQ(VURegs *vuRegs, int info); +void mVU_FSAND(VURegs *vuRegs, int info); +void mVU_FSEQ(VURegs *vuRegs, int info); +void mVU_FSOR(VURegs *vuRegs, int info); +void mVU_FSSET(VURegs *vuRegs, int info); +void mVU_FMAND(VURegs *vuRegs, int info); +void mVU_FMEQ(VURegs *vuRegs, int info); +void mVU_FMOR(VURegs *vuRegs, int info); +void mVU_FCAND(VURegs *vuRegs, int info); +void mVU_FCEQ(VURegs *vuRegs, int info); +void mVU_FCOR(VURegs *vuRegs, int info); +void mVU_FCSET(VURegs *vuRegs, int info); +void mVU_FCGET(VURegs *vuRegs, int info); +void mVU_IBEQ(VURegs *vuRegs, int info); +void mVU_IBGEZ(VURegs *vuRegs, int info); +void mVU_IBGTZ(VURegs *vuRegs, int info); +void mVU_IBLTZ(VURegs *vuRegs, int info); +void mVU_IBLEZ(VURegs *vuRegs, int info); +void mVU_IBNE(VURegs *vuRegs, int info); +void mVU_B(VURegs *vuRegs, int info); +void mVU_BAL(VURegs *vuRegs, int info); +void mVU_JR(VURegs *vuRegs, int info); +void mVU_JALR(VURegs *vuRegs, int info); +void mVU_MFP(VURegs *vuRegs, int info); +void mVU_WAITP(VURegs *vuRegs, int info); +void mVU_ESADD(VURegs *vuRegs, int info); +void mVU_ERSADD(VURegs *vuRegs, int info); +void mVU_ELENG(VURegs *vuRegs, int info); +void mVU_ERLENG(VURegs *vuRegs, int info); +void mVU_EATANxy(VURegs *vuRegs, int info); +void mVU_EATANxz(VURegs *vuRegs, int info); +void mVU_ESUM(VURegs *vuRegs, int info); +void mVU_ERCPR(VURegs *vuRegs, int info); +void mVU_ESQRT(VURegs *vuRegs, int info); +void mVU_ERSQRT(VURegs *vuRegs, int info); +void mVU_ESIN(VURegs *vuRegs, int info); +void mVU_EATAN(VURegs *vuRegs, int info); +void mVU_EEXP(VURegs *vuRegs, int info); +void mVU_XGKICK(VURegs *vuRegs, int info); +void mVU_XTOP(VURegs *vuRegs, int info); +void mVU_XITOP(VURegs *vuRegs, int info); +void mVU_XTOP( VURegs *VU , int info); diff --git a/pcsx2/x86/megaVU_Upper.cpp b/pcsx2/x86/megaVU_Upper.cpp new file mode 100644 index 0000000000..ff5336096f --- /dev/null +++ b/pcsx2/x86/megaVU_Upper.cpp @@ -0,0 +1,19 @@ +/* Pcsx2-Playground - Pc Ps2 Emulator +* Copyright (C) 2009 Pcsx2-Playground 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 "PrecompiledHeader.h" diff --git a/pcsx2/xmlpatchloader.cpp b/pcsx2/xmlpatchloader.cpp new file mode 100644 index 0000000000..5fd232f4cb --- /dev/null +++ b/pcsx2/xmlpatchloader.cpp @@ -0,0 +1,373 @@ +/* Pcsx2 - Pc Ps2 Emulator + * Copyright (C) 2002-2008 Pcsx2 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 "PrecompiledHeader.h" + +using namespace std; + +#include "tinyxml/tinyxml.h" + +#include "Patch.h" +#include "System.h" + +#ifdef _MSC_VER +#pragma warning(disable:4996) //ignore the stricmp deprecated warning +#endif + +#ifdef _WIN32 +#include +#endif + +#if !defined(_WIN32) +#ifndef strnicmp +#define strnicmp strncasecmp +#endif + +#ifndef stricmp +#define stricmp strcasecmp +#endif +#else +#define strnicmp _strnicmp +#define stricmp _stricmp +#endif + +#include "../cheatscpp.h" + +int LoadGroup(TiXmlNode *group, int parent); + +Group::Group(int nParent,bool nEnabled, string &nTitle): + parentIndex(nParent),enabled(nEnabled),title(nTitle) +{ +} + +Patch::Patch(int patch, int grp, bool en, string &ttl): + title(ttl), + group(grp), + enabled(en), + patchIndex(patch) +{ +} + +Patch Patch::operator =(const Patch&p) +{ + title.assign(p.title); + group=p.group; + enabled=p.enabled; + patchIndex=p.patchIndex; + return *this; +} + + +vector groups; +vector patches; + +int LoadPatch( const string& crc) +{ + char pfile[256]; + sprintf(pfile,"patches\\%hs.xml",&crc); + + patchnumber=0; + + TiXmlDocument doc( pfile ); + bool loadOkay = doc.LoadFile(); + if ( !loadOkay ) + { + //SysPrintf("XML Patch Loader: Could not load file '%s'. Error='%s'.\n", pfile, doc.ErrorDesc() ); + return -1; + } else SysPrintf("XML Patch Loader: '%s' Found\n", pfile); + + TiXmlNode *root = doc.FirstChild("GAME"); + if(!root) + { + SysPrintf("XML Patch Loader: Root node is not GAME, invalid patch file.\n"); + return -1; + } + + TiXmlElement *rootelement = root->ToElement(); + + const char *title=rootelement->Attribute("title"); + if(title) + SysPrintf("XML Patch Loader: Game Title: %s\n",title); + + int result=LoadGroup(root,-1); + if(result) { + patchnumber=0; + return result; + } + + Console::SetTitle( + ((title==NULL) || (strlen(title)==0)) ? "" : title ); + + return 0; +} + + +int LoadGroup(TiXmlNode *group,int gParent) +{ + TiXmlElement *groupelement = group->ToElement(); + + const char *gtitle=groupelement->Attribute("title"); + if(gtitle) + SysPrintf("XML Patch Loader: Group Title: %s\n",gtitle); + + const char *enable=groupelement->Attribute("enabled"); + bool gEnabled=true; + if(enable) + { + if(strcmp(enable,"false")==0) + { + SysPrintf("XML Patch Loader: Group is disabled.\n"); + gEnabled=false; + } + } + + TiXmlNode *comment = group->FirstChild("COMMENT"); + if(comment) + { + TiXmlElement *cmelement = comment->ToElement(); + const char *comment = cmelement->GetText(); + if(comment) + SysPrintf("XML Patch Loader: Group Comment:\n%s\n---\n",comment); + } + + string t; + + if(gtitle) + t.assign(gtitle); + else + t.clear(); + + Group gp=Group(gParent,gEnabled,t); + groups.push_back(gp); + + int gIndex=groups.size()-1; + + // only valid for recompilers + TiXmlNode *fastmemory=group->FirstChild("FASTMEMORY"); + if(fastmemory!=NULL) + SetFastMemory(1); + + TiXmlNode *zerogs=group->FirstChild("ZEROGS"); + if(zerogs!=NULL) + { + TiXmlElement *rm=zerogs->ToElement(); + const char* pid = rm->FirstAttribute()->Value(); + if( pid != NULL ) sscanf(pid, "%x", &g_ZeroGSOptions); + else SysPrintf("zerogs attribute wrong"); + } + + TiXmlNode *roundmode=group->FirstChild("ROUNDMODE"); + if(roundmode!=NULL) + { + int eetype=0x0000; + int vutype=0x6000; + + TiXmlElement *rm=roundmode->ToElement(); + if(rm!=NULL) + { + const char *eetext=rm->Attribute("ee"); + const char *vutext=rm->Attribute("vu"); + + if(eetext != NULL) { + eetype = 0xffff; + if( stricmp(eetext, "near") == 0 ) { + eetype = 0x0000; + } + else if( stricmp(eetext, "down") == 0 ) { + eetype = 0x2000; + } + else if( stricmp(eetext, "up") == 0 ) { + eetype = 0x4000; + } + else if( stricmp(eetext, "chop") == 0 ) { + eetype = 0x6000; + } + } + + if(vutext != NULL) { + vutype = 0xffff; + if( stricmp(vutext, "near") == 0 ) { + vutype = 0x0000; + } + else if( stricmp(vutext, "down") == 0 ) { + vutype = 0x2000; + } + else if( stricmp(vutext, "up") == 0 ) { + vutype = 0x4000; + } + else if( stricmp(vutext, "chop") == 0 ) { + vutype = 0x6000; + } + } + } + if(( eetype == 0xffff )||( vutype == 0xffff )) { + printf("XML Patch Loader: WARNING: Invalid value in ROUNDMODE.\n"); + } + else { + SetRoundMode(eetype,vutype); + } + } + + TiXmlNode *cpatch = group->FirstChild("PATCH"); + while(cpatch) + { + TiXmlElement *celement = cpatch->ToElement(); + if(!celement) + { + SysPrintf("XML Patch Loader: ERROR: Couldn't convert node to element.\n" ); + return -1; + } + + + const char *ptitle=celement->Attribute("title"); + const char *penable=celement->Attribute("enabled"); + const char *applymode=celement->Attribute("applymode"); + const char *place=celement->Attribute("place"); + const char *address=celement->Attribute("address"); + const char *size=celement->Attribute("size"); + const char *value=celement->Attribute("value"); + + if(ptitle) { + SysPrintf("XML Patch Loader: Patch title: %s\n", ptitle); + } + + bool penabled=gEnabled; + if(penable) + { + if(strcmp(penable,"false")==0) + { + SysPrintf("XML Patch Loader: Patch is disabled.\n"); + penabled=false; + } + } + + if(!applymode) applymode="frame"; + if(!place) place="EE"; + if(!address) { + SysPrintf("XML Patch Loader: ERROR: Patch doesn't contain an address.\n"); + return -1; + } + if(!value) { + SysPrintf("XML Patch Loader: ERROR: Patch doesn't contain a value.\n"); + return -1; + } + if(!size) { + SysPrintf("XML Patch Loader: WARNING: Patch doesn't contain the size. Trying to deduce from the value size.\n"); + switch(strlen(value)) + { + case 8: + case 7: + case 6: + case 5: + size="32"; + break; + case 4: + case 3: + size="16"; + break; + case 2: + case 1: + size="8"; + break; + case 0: + size="0"; + break; + default: + size="64"; + break; + } + } + + if(strcmp(applymode,"startup")==0) + { + patch[patchnumber].placetopatch=0; + } else + if(strcmp(applymode,"vsync")==0) + { + patch[patchnumber].placetopatch=1; + } else + { + SysPrintf("XML Patch Loader: ERROR: Invalid applymode attribute.\n"); + patchnumber=0; + return -1; + } + + if(strcmp(place,"EE")==0) + { + patch[patchnumber].cpu= CPU_EE; + } else + if(strcmp(place,"IOP")==0) + { + patch[patchnumber].cpu= CPU_IOP; + } else + { + SysPrintf("XML Patch Loader: ERROR: Invalid place attribute.\n"); + patchnumber=0; + return -1; + } + + if(strcmp(size,"64")==0) + { + patch[patchnumber].type = DOUBLE_T; + } else + if(strcmp(size,"32")==0) + { + patch[patchnumber].type = WORD_T; + } else + if(strcmp(size,"16")==0) + { + patch[patchnumber].type = SHORT_T; + } else + if(strcmp(size,"8")==0) + { + patch[patchnumber].type = BYTE_T; + } else + { + SysPrintf("XML Patch Loader: ERROR: Invalid size attribute.\n"); + patchnumber=0; + return -1; + } + + sscanf( address, "%X", &patch[ patchnumber ].addr ); + sscanf( value, "%I64X", &patch[ patchnumber ].data ); + + patch[patchnumber].enabled=penabled?1:0; + + string pt; + + if(ptitle) + pt.assign(ptitle); + else + pt.clear(); + + Patch p=Patch(patchnumber,gIndex,penabled,pt); + patches.push_back(p); + + patchnumber++; + + cpatch = cpatch->NextSibling("PATCH"); + } + + cpatch = group->FirstChild("GROUP"); + while(cpatch) { + int result=LoadGroup(cpatch,gIndex); + if(result) return result; + cpatch = cpatch->NextSibling("GROUP"); + } + + return 0; +} diff --git a/pcsx2_2008.sln b/pcsx2_2008.sln new file mode 100644 index 0000000000..a07c2ac10d --- /dev/null +++ b/pcsx2_2008.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcsx2pg", "pcsx2\windows\VCprojects\pcsx2_2008.vcproj", "{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}" + ProjectSection(ProjectDependencies) = postProject + {26511268-2902-4997-8421-ECD7055F9E28} = {26511268-2902-4997-8421-ECD7055F9E28} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pthreads", "pcsx2\windows\VCprojects\pthreads_2008.vcproj", "{26511268-2902-4997-8421-ECD7055F9E28}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug vtlb|Win32 = Debug vtlb|Win32 + Devel vtlb|Win32 = Devel vtlb|Win32 + Release vtlb|Win32 = Release vtlb|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug vtlb|Win32.ActiveCfg = Debug vtlb|Win32 + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug vtlb|Win32.Build.0 = Debug vtlb|Win32 + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Devel vtlb|Win32.ActiveCfg = Devel vtlb|Win32 + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Devel vtlb|Win32.Build.0 = Devel vtlb|Win32 + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release vtlb|Win32.ActiveCfg = Release vtlb|Win32 + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release vtlb|Win32.Build.0 = Release vtlb|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Debug vtlb|Win32.ActiveCfg = Debug|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Debug vtlb|Win32.Build.0 = Debug|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Devel vtlb|Win32.ActiveCfg = Release|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Devel vtlb|Win32.Build.0 = Release|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Release vtlb|Win32.ActiveCfg = Release|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Release vtlb|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/pcsx2_suite_2008.sln b/pcsx2_suite_2008.sln new file mode 100644 index 0000000000..f65ee63ff0 --- /dev/null +++ b/pcsx2_suite_2008.sln @@ -0,0 +1,74 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcsx2pg", "pcsx2\windows\VCprojects\pcsx2_2008.vcproj", "{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}" + ProjectSection(ProjectDependencies) = postProject + {26511268-2902-4997-8421-ECD7055F9E28} = {26511268-2902-4997-8421-ECD7055F9E28} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pthreads", "pcsx2\windows\VCprojects\pthreads_2008.vcproj", "{26511268-2902-4997-8421-ECD7055F9E28}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SPU2ghz-Pg", "plugins\spu2ghz\src\Win32\SPU2ghz_vs2008.vcproj", "{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZeroGS-Pg", "plugins\zerogs\dx\Win32\zerogs_2008.vcproj", "{5C6B7D28-E73D-4F71-8FC0-17ADA640EBD8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZeroSPU2-Pg", "plugins\zerospu2\Win32\ZeroSPU2_2008.vcproj", "{7F059854-568D-4E08-9D00-1E78E203E4DC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CDVDiso-Pg", "plugins\CDVDiso\src\Win32\CDVDiso_vs2008.vcproj", "{5F78E90B-BD22-47B1-9CA5-7A80F4DF5EF3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZeroPAD", "plugins\zeropad\Windows\ZeroPAD_2008.vcproj", "{CDD9DB83-3BD9-4ED8-BB83-399A2F65F022}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug vtlb|Win32 = Debug vtlb|Win32 + Devel vtlb|Win32 = Devel vtlb|Win32 + Release vtlb|Win32 = Release vtlb|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug vtlb|Win32.ActiveCfg = Debug vtlb|Win32 + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug vtlb|Win32.Build.0 = Debug vtlb|Win32 + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Devel vtlb|Win32.ActiveCfg = Devel vtlb|Win32 + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Devel vtlb|Win32.Build.0 = Devel vtlb|Win32 + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release vtlb|Win32.ActiveCfg = Release vtlb|Win32 + {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release vtlb|Win32.Build.0 = Release vtlb|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Debug vtlb|Win32.ActiveCfg = Debug|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Debug vtlb|Win32.Build.0 = Debug|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Devel vtlb|Win32.ActiveCfg = Release|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Devel vtlb|Win32.Build.0 = Release|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Release vtlb|Win32.ActiveCfg = Release|Win32 + {26511268-2902-4997-8421-ECD7055F9E28}.Release vtlb|Win32.Build.0 = Release|Win32 + {5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Debug vtlb|Win32.ActiveCfg = Debug|Win32 + {5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Debug vtlb|Win32.Build.0 = Debug|Win32 + {5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Devel vtlb|Win32.ActiveCfg = Devel|Win32 + {5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Devel vtlb|Win32.Build.0 = Devel|Win32 + {5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Release vtlb|Win32.ActiveCfg = Release|Win32 + {5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Release vtlb|Win32.Build.0 = Release|Win32 + {5C6B7D28-E73D-4F71-8FC0-17ADA640EBD8}.Debug vtlb|Win32.ActiveCfg = Debug|Win32 + {5C6B7D28-E73D-4F71-8FC0-17ADA640EBD8}.Debug vtlb|Win32.Build.0 = Debug|Win32 + {5C6B7D28-E73D-4F71-8FC0-17ADA640EBD8}.Devel vtlb|Win32.ActiveCfg = Devel|Win32 + {5C6B7D28-E73D-4F71-8FC0-17ADA640EBD8}.Devel vtlb|Win32.Build.0 = Devel|Win32 + {5C6B7D28-E73D-4F71-8FC0-17ADA640EBD8}.Release vtlb|Win32.ActiveCfg = Release|Win32 + {5C6B7D28-E73D-4F71-8FC0-17ADA640EBD8}.Release vtlb|Win32.Build.0 = Release|Win32 + {7F059854-568D-4E08-9D00-1E78E203E4DC}.Debug vtlb|Win32.ActiveCfg = Debug|Win32 + {7F059854-568D-4E08-9D00-1E78E203E4DC}.Debug vtlb|Win32.Build.0 = Debug|Win32 + {7F059854-568D-4E08-9D00-1E78E203E4DC}.Devel vtlb|Win32.ActiveCfg = Release|Win32 + {7F059854-568D-4E08-9D00-1E78E203E4DC}.Devel vtlb|Win32.Build.0 = Release|Win32 + {7F059854-568D-4E08-9D00-1E78E203E4DC}.Release vtlb|Win32.ActiveCfg = Release|Win32 + {7F059854-568D-4E08-9D00-1E78E203E4DC}.Release vtlb|Win32.Build.0 = Release|Win32 + {5F78E90B-BD22-47B1-9CA5-7A80F4DF5EF3}.Debug vtlb|Win32.ActiveCfg = Debug|Win32 + {5F78E90B-BD22-47B1-9CA5-7A80F4DF5EF3}.Debug vtlb|Win32.Build.0 = Debug|Win32 + {5F78E90B-BD22-47B1-9CA5-7A80F4DF5EF3}.Devel vtlb|Win32.ActiveCfg = Release|Win32 + {5F78E90B-BD22-47B1-9CA5-7A80F4DF5EF3}.Devel vtlb|Win32.Build.0 = Release|Win32 + {5F78E90B-BD22-47B1-9CA5-7A80F4DF5EF3}.Release vtlb|Win32.ActiveCfg = Release|Win32 + {5F78E90B-BD22-47B1-9CA5-7A80F4DF5EF3}.Release vtlb|Win32.Build.0 = Release|Win32 + {CDD9DB83-3BD9-4ED8-BB83-399A2F65F022}.Debug vtlb|Win32.ActiveCfg = Debug|Win32 + {CDD9DB83-3BD9-4ED8-BB83-399A2F65F022}.Debug vtlb|Win32.Build.0 = Debug|Win32 + {CDD9DB83-3BD9-4ED8-BB83-399A2F65F022}.Devel vtlb|Win32.ActiveCfg = Debug|Win32 + {CDD9DB83-3BD9-4ED8-BB83-399A2F65F022}.Devel vtlb|Win32.Build.0 = Debug|Win32 + {CDD9DB83-3BD9-4ED8-BB83-399A2F65F022}.Release vtlb|Win32.ActiveCfg = Release|Win32 + {CDD9DB83-3BD9-4ED8-BB83-399A2F65F022}.Release vtlb|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal